Python Forum
[Tkinter] Open tkinter colorchooser at toplevel (so I can select/focus on either window)
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Open tkinter colorchooser at toplevel (so I can select/focus on either window)
#1
I'm opening a TKinter colorchooser window with this code:

colors = colorchooser.askcolor(title='Color Selector')

I want to copy color values from the colorchooser and paste them into my other Tkinter window (a code editor). I want to leave the colorchooser open indefinitely, and click the code editor window to focus on it. But -- while the colorchooser is open, I cannot focus on anything else until I close it.
Reply
#2
The tkinter dialogs are not designed to work that way. The only way to display the dialog is call show() (directly or indirectly), and this blocks your Python program until the dialog is closed. You can't do much to override their behavior because the dialog is mostly written in C with a very thin Python wrapper.
Reply
#3
You could create a dictionary that contains all of the colors, and use that
the following code will create a dictionary that contains: color name, Red, Green, and blue values for all tkinter colors

GetColorList.py
import requests
from bs4 import BeautifulSoup
from CreateDict import CreateDict


class GetColorList:
    def __init__(self):
        self.cd = CreateDict()
        self.color_list = {}
    
    def get_color_list(self):
        if not(len(self.color_list)):
            soup = self.get_color_page()
            if soup:
                self.parse_colors(soup)
            # self.cd.display_dict(self.color_list)
        return self.color_list

    def get_color_page(self):
        url = 'https://www.tcl.tk/man/tcl/TkCmd/colors.html'

        response = requests.get(url)
        if response.status_code == 200:
            return BeautifulSoup(response.content, 'lxml')
        else:
            print(f"Unable to find color list at {url}")
            return None

    def show_colors(self):
        for color, rgb in self.color_list.items():
            print(f"{color}: R: {rgb['red']} G: {rgb['green']} B:{rgb['blue']}")

    def parse_colors(self, soup):
        selector = "body > dl:nth-child(6) > dd:nth-child(1) > table:nth-child(1)"
        cnode = None

        table = soup.select(selector)[0]        
        trlist = table.find_all('tr')
        for n, tr in enumerate(trlist):
            if n == 0:
                continue
            tdlist = tr.find_all('td')
            for n1, td in enumerate(tdlist):
                if n1 == 0:
                    color = td.p.text.strip()
                    cnode = self.cd.add_node(self.color_list, color)
                elif n1 == 1:
                    value = int(td.p.text.strip())
                    self.cd.add_cell(cnode, 'red', value)
                elif n1 == 2:
                    value = int(td.p.text.strip())
                    self.cd.add_cell(cnode, 'green', value)
                elif n1 == 3:
                    value = int(td.p.text.strip())
                    self.cd.add_cell(cnode, 'blue', value)

def main():
    gcl = GetColorList()
    gcl.get_color_list()


if __name__ == '__main__':
    main()
it requires the following module:
CreateDict.py
import os

class CreateDict:
    """
    Generic Software tools used by Trailmapper.

    CreateDict.py - Contains methods to simplify node and cell creation within
                    a dictionary

    Usage: 
    
        The best way to learn what can be done is to examine the testit function
        included in this module.

        new_dict(dictname) - Creates a new dictionary instance with the name
            contained in dictname

        add_node(parent, nodename) - Creates a new node (nested dictionary)
            named in nodename, in parent dictionary.

        add_cell(nodename, cellname, value) - Creates a leaf node within node
            named in nodename, with a cell name of cellname, and value of value.

        display_dict(dictname) - Recursively displays a nested dictionary.

    Requirements:

        Trailmapper software:
            None

        Python standard library:
            os
    
    Author: Larz60+  -- May 2019.
    """
    def __init__(self):
        os.chdir(os.path.abspath(os.path.dirname(__file__)))

    def new_dict(self, dictname):
        setattr(self, dictname, {})

    def add_node(self, parent, nodename):
        node = parent[nodename] = {}
        return node

    def add_cell(self, nodename, cellname, value):
        cell =  nodename[cellname] = value
        return cell

    def display_dict(self, dictname, level=0):
        indent = " " * (4 * level)
        for key, value in dictname.items():
            if isinstance(value, dict):
                print(f'\n{indent}{key}')
                level += 1
                self.display_dict(value, level)
            else:
                print(f'{indent}{key}: {value}')
            if level > 0:
                level -= 1


def testit():
    # instantiate class
    cd = CreateDict()

    # create new dictionary named CityList
    cd.new_dict('CityList')

    # add node Boston
    boston = cd.add_node(cd.CityList, 'Boston')
    # add sub node Resturants
    bos_resturants = cd.add_node(boston, 'Resturants')

    # Add subnode 'Spoke Wine Bar' to parent bos_resturants
    spoke = cd.add_node(bos_resturants, 'Spoke Wine Bar')
    cd.add_cell(spoke, 'Addr1', '89 Holland St')
    cd.add_cell(spoke, 'City', 'Sommerville')
    cd.add_cell(spoke, 'Addr1', '02144')
    cd.add_cell(spoke, 'Phone', '617-718-9463')

    # Add subnode 'Highland Kitchen' to parent bos_resturants
    highland = cd.add_node(bos_resturants, 'Highland Kitchen')
    cd.add_cell(highland, 'Addr1', '150 Highland Ave')
    cd.add_cell(highland, 'City', 'Sommerville')
    cd.add_cell(highland, 'ZipCode', '02144')
    cd.add_cell(highland, 'Phone', '617-625-1131')

    # display dictionary
    print(f'\nCityList Dictionary')
    cd.display_dict(cd.CityList)
    print(f'\nraw data: {cd.CityList}')

if __name__ == '__main__':
    testit()
Example Use:
from GetColorList import GetColorList

def show_all_colors():
    gc = GetColorList()
    color_list = gc.get_color_list()
    gc.show_colors()

show_all_colors()
partial output from example:
Output:
alice blue: R: 240 G: 248 B:255 AliceBlue: R: 240 G: 248 B:255 antique white: R: 250 G: 235 B:215 AntiqueWhite: R: 250 G: 235 B:215 AntiqueWhite1: R: 255 G: 239 B:219 AntiqueWhite2: R: 238 G: 223 B:204 AntiqueWhite3: R: 205 G: 192 B:176 AntiqueWhite4: R: 139 G: 131 B:120 agua: R: 0 G: 255 B:255 aquamarine: R: 127 G: 255 B:212 aquamarine1: R: 127 G: 255 B:212 aquamarine2: R: 118 G: 238 B:198 aquamarine3: R: 102 G: 205 B:170 aquamarine4: R: 69 G: 139 B:116 azure: R: 240 G: 255 B:255 azure1: R: 240 G: 255 B:255 azure2: R: 224 G: 238 B:238 azure3: R: 193 G: 205 B:205 azure4: R: 131 G: 139 B:139 beige: R: 245 G: 245 B:220 bisque: R: 255 G: 228 B:196 bisque1: R: 255 G: 228 B:196 bisque2: R: 238 G: 213 B:183 bisque3: R: 205 G: 183 B:158 bisque4: R: 139 G: 125 B:107 black: R: 0 G: 0 B:0 blanched almond: R: 255 G: 235 B:205 BlanchedAlmond: R: 255 G: 235 B:205 blue: R: 0 G: 0 B:255 blue violet: R: 138 G: 43 B:226
Reply
#4
Thanks for the help. Ideally, the colorchooser window remains open -- so the user can use it whenever she needs to mix a new hexadecimal value. But I guess it's not a major problem if it must be closed and opened to mix a new colour each time.
Reply
#5
It is unusual for a dialog to stay open, and generally considered bad user interface design. If you have something that should be always available it should be part of the main interface, not a popup.

If you want to make a color picker with sliders, here's a starting point.
import tkinter as tk
from tkinter import ttk
import random

class Slider(ttk.Frame):
    def __init__(self, parent, text, max=100, min=0, command=None):
        super().__init__(parent)
        top = ttk.Frame(self)
        bottom = ttk.Frame(self)
        self.label = ttk.Label(top, text=text, justify=tk.LEFT)
        self.display_value = tk.StringVar(self, "0")
        self.display_value.trace("w", lambda a, b, c: self.display_changed(self.display_value.get()))
        self.display = ttk.Entry(top, width=10, textvariable=self.display_value)
        self.scale_value = tk.DoubleVar(self, 0.0)
        self.scale = ttk.Scale(bottom, from_=min, to=max, variable=self.scale_value, command=self.scale_changed)
        self.min = min
        self.max = max
        self.command = command

        self.label.pack(side=tk.LEFT)
        self.display.pack(side=tk.RIGHT)
        self.scale.pack(expand=True, fill=tk.BOTH)
        top.pack(side=tk.TOP, expand=True, fill=tk.BOTH)
        bottom.pack(side=tk.TOP, expand=True, fill=tk.BOTH)

    def scale_changed(self, value):
        self.display_value.set(int(round(float(value))))

    def display_changed(self, value):
        try:
            value = int(value)
        except ValueError:
            pass
        else:
            value = max(self.min, min(self.max, value))
            self.display_value.set(value)
            self.scale_value.set(value)
            if self.command is not None:
                self.command(value)

    @property
    def value(self):
        return int(self.scale_value.get())

    @value.setter
    def value(self, new_value):
        self.display_changed(new_value)

class ColorChooser(tk.Toplevel):
    def __init__(self, command=None):
        super().__init__()
        self.title("Colors")
        self.red = Slider(self, text="Red", max=255, command=self.color_changed)
        self.green = Slider(self, text="Green", max=255, command=self.color_changed)
        self.blue = Slider(self, text="Blue", max=255, command=self.color_changed)
        self._color = "#000000"
        self.apply = tk.Button(self, text="Apply", fg="white", bg=self._color, command=self.apply_pressed)
        self.red.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
        self.green.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
        self.blue.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
        self.apply.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
        self.command = command

    def color_changed(self, _):
        r = self.red.value
        g = self.green.value
        b = self.blue.value
        self._color = f"#{r:02x}{g:02x}{b:02x}"
        fg = "white" if r + g + b < 300 else "black"
        self.apply.config(fg=fg, bg=self.color)

    def apply_pressed(self):
        if self.command is not None:
            self.command(self.color)

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, new_value):
        try:
            r = int(new_value[1:3], base=16)
            g = int(new_value[3:5], base=16)
            b = int(new_value[5:7], base=16)
            print(r, g, b)
        except (ValueError, IndexError) as msg:
            print(msg)
        else:
            self.red.value = r
            self.green.value = g
            self.blue.value = b

class MainPanel(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Main Panel")
        self.style = ttk.Style()
        themes = self.style.theme_names()
        self.theme = tk.StringVar(self, themes[0])
        self.theme.trace("w", lambda a, b, c: self.style.theme_use(self.theme.get()))
        theme_slector = ttk.Combobox(self, values=themes, textvariable=self.theme)
        theme_slector.pack(padx=40, pady=20)

        chooser = ColorChooser(command=print)
        chooser.color = "#a0b0c0"

MainPanel().mainloop()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Interaction between Matplotlib window, Python prompt and TKinter window NorbertMoussy 3 489 Mar-17-2024, 09:37 AM
Last Post: deanhystad
  pass a variable between tkinter and toplevel windows janeik 10 2,328 Jan-24-2024, 06:44 AM
Last Post: Liliana
  Tkinter multiple windows in the same window tomro91 1 845 Oct-30-2023, 02:59 PM
Last Post: Larz60+
  Centering and adding a push button to a grid window, TKinter Edward_ 15 4,717 May-25-2023, 07:37 PM
Last Post: deanhystad
  Now if window is open or not delcencen 5 1,630 Mar-25-2023, 07:26 PM
Last Post: deanhystad
  [PyQt] Hover over highlighted text and open popup window DrakeSoft 2 1,510 Oct-29-2022, 04:30 PM
Last Post: DrakeSoft
  RGB() <> colorchooser.askcolor KennethHobson 9 1,944 Aug-24-2022, 06:43 PM
Last Post: woooee
  [Tkinter] Toplevel window menator01 5 3,039 Apr-18-2022, 06:01 PM
Last Post: menator01
  [Tkinter] Background inactivity timer when tkinter window is not active DBox 4 2,922 Apr-16-2022, 04:04 PM
Last Post: DBox
  How to move in entries using the arrow keys by applying the bind and focus? pymn 4 4,706 Apr-06-2022, 04:29 AM
Last Post: pymn

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020