如何在 Tkinter 中制作颜色选择比例尺?
How do I make a color selection scale in Tkinter?
我正在尝试为 Tkinter 应用程序创建颜色选择范围。
我对 tkinter.colorchooser
模块或每个 RGB 通道的单独比例不感兴趣。我需要像 Photoshop 中的颜色滑块一样的单一比例尺。
我目前拥有的是一个垂直比例小部件,其 RGB 整数值从 0
(#000
) 到 16777215
(#FFF
)。问题是使用一系列 RGB 整数会导致非常奇怪的颜色序列——选择不会像下面的第一张图片那样从红色到粉红色再到蓝色等。
相反,我得到了第二张图片中的颜色序列。它压缩得很厉害,因为有 16777215 种不同的颜色被压缩到 300 个像素中。第三张图片是放大的。你可以看到它从黑色变成绿色,然后几乎从黑色变成绿色,等等
这是我的代码的相关部分:
MAXRGBINT = 16777215 # Corresponds to (255, 255, 255)
scale = ttk.Scale(slidframe, orient=VERTICAL, length=75,
from_=0, to=MAXRGBINT,
command=lambda _: set_color('sprite'))
基本上我想要一个比例小部件,它将按 "normal" 顺序显示颜色。我怎样才能用数值来做到这一点?
您可以使用 matplotlib 的预制颜色图(喷射、彩虹等)from matplotlib import cm
,也可以创建您自己的颜色图,然后查看 rgb 值。您可以使用标准值通常以 256 个值 (last paragraph) 分隔的事实。如果这对你来说足够了,后者可能是最简单的。
这是你要问的吗?
你的问题是你对三维颜色使用一维尺度space。
对于您正在寻找的内容,我建议改用 HSL or HSV 颜色 space,并使用相关比例来操纵 Hue 参数。当您 需要 RGB 值时,您可以通过从 HSL/HSV 值转换来获取它们(我链接的维基百科页面解释了如何)。
回答有点晚,但我的解决方案是创建自定义 class。为了从用户那里获得颜色掩码(或过滤器),我在我的图像处理项目中多次使用它。 class...
import tkinter as tk
from PIL import Image, ImageDraw, ImageTk
import colorsys
class ColorRangeSelector:
def __init__(self, root, x, y, w=255 , h=255 , bg='white', lineColor='black' , textColor = 'gray' , initial = [0,255] ):
self.root=root
self.x = x
self.y = y
self.w = w
h=min(h, 255)
self.h = h
self.clicked_id = -1
self.padding = 16
self.canvas = tk.Canvas(self.root, width=w + 2 * self.padding, height=h + 2 * self.padding, bg=bg)
self.result = initial.copy()
image = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(image)
w_adim= w/255
for i in range(255):
for j in range(255-h, 255):
rgb = tuple(round(c * 255) for c in colorsys.hsv_to_rgb(i / 255, 1, j / 255))
color= '#%02x%02x%02x' % rgb
draw.rectangle( [i * w_adim , (j + h - 255) ,(i + 1) * w_adim , (j + h - 255) + 1 ], outline=None,fill=color)
ph = ImageTk.PhotoImage(image)
self.canvas.create_image(self.padding, self.padding, anchor='nw', image = ph)
self.canvas.image = ph
start_line = self.canvas.create_rectangle(initial[0]+ self.padding, self.padding,initial[0]+ self.padding+1, h+self.padding, width=0, fill=lineColor)
start_select = self.canvas.create_rectangle(initial[0]+ self.padding/2, self.padding/2, initial[0]+ self.padding*3/2, self.padding, width=0, fill='black')
start_text = self.canvas.create_text(initial[0]+ self.padding/2+7 ,self.padding/2+4,text='S',fill="white", font=('Helvetica 6 bold'))
end_line = self.canvas.create_rectangle(initial[1]*w_adim-1+ self.padding, self.padding,initial[1]*w_adim+ self.padding, h+self.padding, width=0, fill=lineColor)
end_select = self.canvas.create_rectangle(initial[1]*w_adim + self.padding/2, h+self.padding, initial[1]*w_adim+self.padding*3/2, h +self.padding*3/2, width=0,
fill='black')
end_text = self.canvas.create_text(initial[1]*w_adim + self.padding/2+8, h+ self.padding+4, text='E', fill="white", font=('Helvetica 6 bold'))
self.text = self.canvas.create_text(self.w/2 + self.padding, self.padding/2, text='[{:.2f},{:.2f}]'.format(self.result[0],self.result[1]), fill=textColor, font=('Helvetica 7 bold'))
self.selectors = [start_select,start_line,start_text,end_select,end_line,end_text]
self.canvas.bind( '<B1-Motion>', self.motion)
self.canvas.bind('<ButtonRelease-1>', self.mouseReleased)
self.canvas.place(x=x,y=y)
def motion(self,event):
if self.clicked_id and self.clicked_id < 0:
self.clicked_id= self.getRectangle(event.x, event.y)
if self.clicked_id:
index = self.selectors.index(self.clicked_id)
rect_y = self.padding/2
if index>2:
rect_y=self.h+self.padding
line_id= self.selectors[index+1]
text_id = self.selectors[index + 2]
cur_x = event.x
if event.x > self.w+self.padding:
cur_x = self.w +self.padding
if event.x < self.padding:
cur_x = self.padding
self.canvas.coords(line_id, cur_x, self.padding, cur_x + 1,self.h + self.padding)
self.canvas.coords(self.clicked_id, cur_x-self.padding/2, rect_y, cur_x+self.padding/2, rect_y+self.padding/2)
self.canvas.coords(text_id, cur_x, rect_y+4)
self.result[int(index/3)] = (cur_x-self.padding)/self.w*255
self.canvas.itemconfigure(self.text,text='[{:.2f},{:.2f}]'.format(self.result[0],self.result[1]))
def mouseReleased(self,event):
self.clicked_id = -1
def getRectangle(self,x, y):
for i in range(2):
sel = self.selectors[i*3]
curr_xs, curr_ys, curr_xe, curr_ye = self.canvas.coords(sel)
if (curr_xs <= x <= curr_xe) and (curr_ys <= y <= curr_ye):
return sel
然后这样称呼它;
import tkinter as tk
from ColorRangeSelector import ColorRangeSelector
def getResults():
label['text']= "FROM BUTTON:\ns1= {}\ns2={}\ns3={}".format(selector.result,selector2.result,selector3.result)
def getEventResults(self):
label['text'] = "FROM EVENT ON ROOT:\ns1= {}\ns2={}\ns3={}".format(selector.result,selector2.result,selector3.result)
root = tk.Tk()
root.geometry("1000x500")
selector = ColorRangeSelector(root,10,70,900,30, bg='#ccc', lineColor='white', textColor='#222')
selector2 = ColorRangeSelector(root,50,150)
selector3 = ColorRangeSelector(root,400,150,510,100, initial=[70.5,203])
button = tk.Button(root, text ="Print Results", command = getResults)
label = tk.Label( root, text="")
button.place(x=0,y=0)
label.place(x=500,y=0)
root.bind( '<B1-Motion>', getEventResults)
root.mainloop()
结果:
只需将 [S]tart 和 [E]nd 滑块拖放到选择器 canvas 上
click here for screenshot
我正在尝试为 Tkinter 应用程序创建颜色选择范围。
我对 tkinter.colorchooser
模块或每个 RGB 通道的单独比例不感兴趣。我需要像 Photoshop 中的颜色滑块一样的单一比例尺。
我目前拥有的是一个垂直比例小部件,其 RGB 整数值从 0
(#000
) 到 16777215
(#FFF
)。问题是使用一系列 RGB 整数会导致非常奇怪的颜色序列——选择不会像下面的第一张图片那样从红色到粉红色再到蓝色等。
相反,我得到了第二张图片中的颜色序列。它压缩得很厉害,因为有 16777215 种不同的颜色被压缩到 300 个像素中。第三张图片是放大的。你可以看到它从黑色变成绿色,然后几乎从黑色变成绿色,等等
这是我的代码的相关部分:
MAXRGBINT = 16777215 # Corresponds to (255, 255, 255)
scale = ttk.Scale(slidframe, orient=VERTICAL, length=75,
from_=0, to=MAXRGBINT,
command=lambda _: set_color('sprite'))
基本上我想要一个比例小部件,它将按 "normal" 顺序显示颜色。我怎样才能用数值来做到这一点?
您可以使用 matplotlib 的预制颜色图(喷射、彩虹等)from matplotlib import cm
,也可以创建您自己的颜色图,然后查看 rgb 值。您可以使用标准值通常以 256 个值 (last paragraph) 分隔的事实。如果这对你来说足够了,后者可能是最简单的。
这是你要问的吗?
你的问题是你对三维颜色使用一维尺度space。
对于您正在寻找的内容,我建议改用 HSL or HSV 颜色 space,并使用相关比例来操纵 Hue 参数。当您 需要 RGB 值时,您可以通过从 HSL/HSV 值转换来获取它们(我链接的维基百科页面解释了如何)。
回答有点晚,但我的解决方案是创建自定义 class。为了从用户那里获得颜色掩码(或过滤器),我在我的图像处理项目中多次使用它。 class...
import tkinter as tk
from PIL import Image, ImageDraw, ImageTk
import colorsys
class ColorRangeSelector:
def __init__(self, root, x, y, w=255 , h=255 , bg='white', lineColor='black' , textColor = 'gray' , initial = [0,255] ):
self.root=root
self.x = x
self.y = y
self.w = w
h=min(h, 255)
self.h = h
self.clicked_id = -1
self.padding = 16
self.canvas = tk.Canvas(self.root, width=w + 2 * self.padding, height=h + 2 * self.padding, bg=bg)
self.result = initial.copy()
image = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(image)
w_adim= w/255
for i in range(255):
for j in range(255-h, 255):
rgb = tuple(round(c * 255) for c in colorsys.hsv_to_rgb(i / 255, 1, j / 255))
color= '#%02x%02x%02x' % rgb
draw.rectangle( [i * w_adim , (j + h - 255) ,(i + 1) * w_adim , (j + h - 255) + 1 ], outline=None,fill=color)
ph = ImageTk.PhotoImage(image)
self.canvas.create_image(self.padding, self.padding, anchor='nw', image = ph)
self.canvas.image = ph
start_line = self.canvas.create_rectangle(initial[0]+ self.padding, self.padding,initial[0]+ self.padding+1, h+self.padding, width=0, fill=lineColor)
start_select = self.canvas.create_rectangle(initial[0]+ self.padding/2, self.padding/2, initial[0]+ self.padding*3/2, self.padding, width=0, fill='black')
start_text = self.canvas.create_text(initial[0]+ self.padding/2+7 ,self.padding/2+4,text='S',fill="white", font=('Helvetica 6 bold'))
end_line = self.canvas.create_rectangle(initial[1]*w_adim-1+ self.padding, self.padding,initial[1]*w_adim+ self.padding, h+self.padding, width=0, fill=lineColor)
end_select = self.canvas.create_rectangle(initial[1]*w_adim + self.padding/2, h+self.padding, initial[1]*w_adim+self.padding*3/2, h +self.padding*3/2, width=0,
fill='black')
end_text = self.canvas.create_text(initial[1]*w_adim + self.padding/2+8, h+ self.padding+4, text='E', fill="white", font=('Helvetica 6 bold'))
self.text = self.canvas.create_text(self.w/2 + self.padding, self.padding/2, text='[{:.2f},{:.2f}]'.format(self.result[0],self.result[1]), fill=textColor, font=('Helvetica 7 bold'))
self.selectors = [start_select,start_line,start_text,end_select,end_line,end_text]
self.canvas.bind( '<B1-Motion>', self.motion)
self.canvas.bind('<ButtonRelease-1>', self.mouseReleased)
self.canvas.place(x=x,y=y)
def motion(self,event):
if self.clicked_id and self.clicked_id < 0:
self.clicked_id= self.getRectangle(event.x, event.y)
if self.clicked_id:
index = self.selectors.index(self.clicked_id)
rect_y = self.padding/2
if index>2:
rect_y=self.h+self.padding
line_id= self.selectors[index+1]
text_id = self.selectors[index + 2]
cur_x = event.x
if event.x > self.w+self.padding:
cur_x = self.w +self.padding
if event.x < self.padding:
cur_x = self.padding
self.canvas.coords(line_id, cur_x, self.padding, cur_x + 1,self.h + self.padding)
self.canvas.coords(self.clicked_id, cur_x-self.padding/2, rect_y, cur_x+self.padding/2, rect_y+self.padding/2)
self.canvas.coords(text_id, cur_x, rect_y+4)
self.result[int(index/3)] = (cur_x-self.padding)/self.w*255
self.canvas.itemconfigure(self.text,text='[{:.2f},{:.2f}]'.format(self.result[0],self.result[1]))
def mouseReleased(self,event):
self.clicked_id = -1
def getRectangle(self,x, y):
for i in range(2):
sel = self.selectors[i*3]
curr_xs, curr_ys, curr_xe, curr_ye = self.canvas.coords(sel)
if (curr_xs <= x <= curr_xe) and (curr_ys <= y <= curr_ye):
return sel
然后这样称呼它;
import tkinter as tk
from ColorRangeSelector import ColorRangeSelector
def getResults():
label['text']= "FROM BUTTON:\ns1= {}\ns2={}\ns3={}".format(selector.result,selector2.result,selector3.result)
def getEventResults(self):
label['text'] = "FROM EVENT ON ROOT:\ns1= {}\ns2={}\ns3={}".format(selector.result,selector2.result,selector3.result)
root = tk.Tk()
root.geometry("1000x500")
selector = ColorRangeSelector(root,10,70,900,30, bg='#ccc', lineColor='white', textColor='#222')
selector2 = ColorRangeSelector(root,50,150)
selector3 = ColorRangeSelector(root,400,150,510,100, initial=[70.5,203])
button = tk.Button(root, text ="Print Results", command = getResults)
label = tk.Label( root, text="")
button.place(x=0,y=0)
label.place(x=500,y=0)
root.bind( '<B1-Motion>', getEventResults)
root.mainloop()
结果:
只需将 [S]tart 和 [E]nd 滑块拖放到选择器 canvas 上 click here for screenshot