如何在 Tkinter 中选择颜色?

Posted

技术标签:

【中文标题】如何在 Tkinter 中选择颜色?【英文标题】:How to select a color in Tkinter? 【发布时间】:2022-01-23 20:39:28 【问题描述】:

我有一个用 Python 编写的程序,它使用 Tkinter 创建一个可以绘制的窗口。每次左键单击鼠标时,都会在画布上画一个点。双击时,会生成一个多边形,填充您选择的颜色。当您右键单击一个框时,我找到了一种从框更改颜色的方法,但问题是所选颜色未保存,我无法使其替换前一个颜色。有谁知道如何解决这个问题?

import tkinter as tk
from tkinter import colorchooser

class Point():
    
    def __init__(self, canvas, x, y):
        self.x = x
        self.y = y
        canvas.create_oval(x-2, y-2, x+2, y+2, fill='white') 

class Poly():
    
    def __init__(self, canvas, board, p_list=[] ):
        self.p_list = p_list
        self.canvas = canvas
        self.board = board

    def draw_poly(self):
        points = []
        for p in self.p_list:
            points.extend([p.x, p.y])
        points.extend(points[:2])
        self.canvas.create_polygon(points, fill=self.board.current_color, outline=self.board.current_color)

    def add_point(self, p):
        self.p_list.append(p)
        if len(self.p_list)>1:
            p1 = self.p_list[-1]
            p2 = self.p_list[-2]
            self.canvas.create_line(p1.x, p1.y, p2.x, p2.y, fill="white", width=2)

class Palette():
    
    def __init__(self, frame, board, colors):
        self.colors = colors
        self.board = board
        self.allColors = []
        for color in self.colors:
            f = tk.Frame(frame, bg='lightgrey', bd=3)
            f.pack(expand=1, fill='both', side='left')
            if self.board.current_color == color: f.config(bg='red')
            self.allColors.append(f)
            l = tk.Label(f, bg=color)
            l.pack(expand=1, fill='both', padx=2, pady=2)
            l.bind("<1>", self.set_color)

            l.bind("<Button-3>", self.do_popup)

    def do_popup(self, event):
        clsheet = tk.colorchooser.askcolor()
        self.current_color = clsheet[1]
        
    def set_color(self, e):
        self.board.current_color = e.widget['bg']
        self.selected_color(e.widget.master)
    
    def selected_color(self, colorFrame):
        for f in self.allColors: f.config(bg = 'lightgrey')
        colorFrame.config(bg="red")

class Board():
    
    
    def __init__(self, root):
        self.colors = ['#B4FE98', '#77E4D4', '#F4EEA9',  '#F0BB62', '#FF5F7E', "#9A0680"]
        self.root = root
        self.current_color = self.colors[0]
        self.f1 = tk.Frame(self.root)
        self.f1.pack(expand=1, fill='both', padx=5)
        self.f2 = tk.Frame(self.root)
        self.f2.pack(expand=1, fill='both')
        self.canvas = tk.Canvas(self.f2, bg="#000D6B", height=550)
        self.canvas.pack(expand=1, fill='both', padx=5, pady=5)
        self.pallette = Palette(self.f1, self, self.colors )
        self.canvas.bind("<1>", self.draw_point)
        self.canvas.bind("<Double-Button-1>", self.draw_poly)
        
        self.poly = None

    def draw_point(self, evnt):
        if self.poly: self.poly.add_point(Point(self.canvas, evnt.x, evnt.y))
        else: self.poly = Poly(self.canvas, self, [Point(self.canvas, evnt.x, evnt.y)])

    def draw_poly(self, evnt):
        if self.poly and len(self.poly.p_list) > 2:
            self.poly.add_point(Point(self.canvas, evnt.x, evnt.y))
            self.poly.draw_poly()
            self.poly = None
        else: self.draw_point(evnt)

#main program
root = tk.Tk()
root.title('my program')
root.geometry("600x700")
root.resizable(0,0)
Board(root)
tk.mainloop()

【问题讨论】:

你在哪里遇到麻烦 您的意思是在创建填充多边形后,您希望能够从菜单中选择颜色并替换填充颜色? 是的,我的问题是我可以从菜单中选择颜色,但是颜色不能代替填充颜色 AFAI可以看到,colorchooser没有使用 如何在程序运行时“保存”其他任何内容?通过分配一个变量,对吧?然后在需要时检查该值?您似乎已经通过self.board.current_color 尝试这样做。如果您正在寻求调试帮助,那么您应该以反映这一点的方式提出问题 【参考方案1】:

为了修复无法右键单击颜色的部分,我在脚本中更改了两件事:

    您将框架小部件及其子小部件标签存储在Palette.allColors 中。我使用partial 将所选颜色的索引交给了do_popup 事件。然后,您可以简单地遍历 Palette.allColors 中的所有小部件,如果事件中的索引与列表中的索引匹配,则访问子级并进一步访问其中的 !label 键并将背景颜色更改为所选颜色。 我匹配了Board.current_colorPalette.current_color

大部分更改是在Palette.do_popup() 中进行的。可能不是最优雅的解决方案,但它看起来像你想要的那样工作。完整代码:

import tkinter as tk
from tkinter import colorchooser
from functools import partial


class Point():

    def __init__(self, canvas, x, y):
        self.x = x
        self.y = y
        canvas.create_oval(x - 2, y - 2, x + 2, y + 2, fill='white')


class Poly():

    def __init__(self, canvas, board, p_list=[]):
        self.p_list = p_list
        self.canvas = canvas
        self.board = board

    def draw_poly(self):
        points = []
        for p in self.p_list:
            points.extend([p.x, p.y])
        points.extend(points[:2])
        self.canvas.create_polygon(points, fill=self.board.current_color, outline=self.board.current_color)

    def add_point(self, p):
        self.p_list.append(p)
        if len(self.p_list) > 1:
            p1 = self.p_list[-1]
            p2 = self.p_list[-2]
            self.canvas.create_line(p1.x, p1.y, p2.x, p2.y, fill="white", width=2)


class Palette():

    def __init__(self, frame, board, colors):
        self.colors = colors
        self.board = board
        self.allColors = []
        for idx, color in enumerate(self.colors):
            f = tk.Frame(frame, bg='lightgrey', bd=3)
            f.pack(expand=1, fill='both', side='left')
            if self.board.current_color == color: f.config(bg='red')
            self.allColors.append(f)
            l = tk.Label(f, bg=color)
            l.pack(expand=1, fill='both', padx=2, pady=2)
            l.bind("<1>", self.set_color)

            l.bind("<Button-3>", partial(self.do_popup, idx))

    def do_popup(self, idx, event):
        clsheet = tk.colorchooser.askcolor()
        self.current_color = clsheet[1].upper()
        print(f"You chose: self.current_color")
        self.board.current_color = self.current_color  # required?
        self.selected_color(event.widget.master)
        for frm_idx, frm in enumerate(self.allColors):
            if frm_idx == idx:
                frm.children["!label"].config(bg=self.current_color)

    def set_color(self, e):
        self.board.current_color = e.widget['bg']
        self.selected_color(e.widget.master)

    def selected_color(self, colorFrame):
        for f in self.allColors: f.config(bg='lightgrey')
        colorFrame.config(bg="red")


class Board():

    def __init__(self, root):
        self.colors = ['#B4FE98', '#77E4D4', '#F4EEA9', '#F0BB62', '#FF5F7E', "#9A0680"]
        self.root = root
        self.current_color = self.colors[0]
        self.f1 = tk.Frame(self.root)
        self.f1.pack(expand=1, fill='both', padx=5)
        self.f2 = tk.Frame(self.root)
        self.f2.pack(expand=1, fill='both')
        self.canvas = tk.Canvas(self.f2, bg="#000D6B", height=550)
        self.canvas.pack(expand=1, fill='both', padx=5, pady=5)
        self.pallette = Palette(self.f1, self, self.colors)
        self.canvas.bind("<1>", self.draw_point)
        self.canvas.bind("<Double-Button-1>", self.draw_poly)

        self.poly = None

    def draw_point(self, evnt):
        if self.poly:
            self.poly.add_point(Point(self.canvas, evnt.x, evnt.y))
        else:
            self.poly = Poly(self.canvas, self, [Point(self.canvas, evnt.x, evnt.y)])

    def draw_poly(self, evnt):
        if self.poly and len(self.poly.p_list) > 2:
            self.poly.add_point(Point(self.canvas, evnt.x, evnt.y))
            self.poly.draw_poly()
            self.poly = None
        else:
            self.draw_point(evnt)


# main program
root = tk.Tk()
root.title('my program')
root.geometry("600x700")
root.resizable(0, 0)
Board(root)
tk.mainloop()

【讨论】:

这并不能解决 OP 的问题。请参阅this comment:OP 希望在画布上绘制的所有内容在被选中时都更改为选定的颜色。即使您选择不同的颜色,此代码也会保留形状的颜色。 @SylvesterKruin Hm - 重新阅读后 - 你可能是对的。让我们看看OP有什么要说的 哦等等...我注意到您确实修复了右键单击颜色更改不起作用的问题(抱歉,我没有对此进行测试)。所以这是 OP 需要的一部分,我猜。如果您编辑答案,我可以取消投票。问题不清楚;我只是通过阅读问题下方的说明来弄清楚他们真正想要什么:-/。 @SylvesterKruin 没问题 - 感谢您指出,我希望我的答案在编辑后现在更清楚。 :) 事实上,你得到了支持,因为你确实回答了 OP 的 问题,即 “如何在Tkinter?”。并感谢您让答案更清楚地说明它正在修复什么!

以上是关于如何在 Tkinter 中选择颜色?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Windows 下更改 Tkinter 中菜单的颜色?

如何在 tkinter.ttk Treeview 上完全更改背景颜色

如何自定义 tkinter/ttk 小部件和框架的边框颜色?

单击按钮时如何显示颜色选择器?

在 html 选择中悬停或选择项目时,如何避免更改背景颜色?

如何利用NetBeans Swing颜色选择器控制按钮背景