Posts: 2
Threads: 1
Joined: Jul 2022
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.
Posts: 6,756
Threads: 20
Joined: Feb 2020
Jul-05-2022, 04:51 AM
(This post was last modified: Jul-05-2022, 06:00 PM by deanhystad.)
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.
Posts: 12,010
Threads: 483
Joined: Sep 2016
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
Posts: 2
Threads: 1
Joined: Jul 2022
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.
Posts: 6,756
Threads: 20
Joined: Feb 2020
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()
|