Python 如何创建由tkinter中的for循环生成的图像按钮的单选按钮类型组

Python 如何创建由tkinter中的for循环生成的图像按钮的单选按钮类型组,python,tkinter,radio-button,Python,Tkinter,Radio Button,我现在很难让这段代码按照我需要的方式工作。本质上,我需要它像一个单选按钮一样工作,其中所选的“节点”(通过鼠标单击)将更改为不同的图像 节点由for循环生成,节点数量取决于一些外部输入。每个按钮都有悬停图像的绑定。我目前的两次尝试基本上是向for循环中的节点添加一个命令,该命令确定按下了哪个按钮(使用输入列表的索引),并使用grid将另一个图像放置在同一网格空间中的节点顶部。这一半可以工作,但似乎会暂时删除所有其他节点,直到激活悬停绑定。runNodes列表是一个旧解决方案的一部分,它的结果与此

我现在很难让这段代码按照我需要的方式工作。本质上,我需要它像一个单选按钮一样工作,其中所选的“节点”(通过鼠标单击)将更改为不同的图像

节点由for循环生成,节点数量取决于一些外部输入。每个按钮都有悬停图像的绑定。我目前的两次尝试基本上是向for循环中的节点添加一个命令,该命令确定按下了哪个按钮(使用输入列表的索引),并使用grid将另一个图像放置在同一网格空间中的节点顶部。这一半可以工作,但似乎会暂时删除所有其他节点,直到激活悬停绑定。runNodes列表是一个旧解决方案的一部分,它的结果与此基本相同。为了能够引用节点,我暂时将它放在那里,但我甚至不确定我是否正确地这样做

或者,我尝试向我创建的自定义按钮类添加另一个绑定,在该绑定中,单击鼠标,图像将更改为所选图像。不幸的是,当鼠标离开节点时,鼠标悬停绑定会将其删除

我有一种感觉,这些解决方案中的一个或两个都可以工作,但我的代码中缺少了一些可以让它们工作的东西。。然而,我也愿意接受完全不同的解决方案。我已经包含了我认为相关的代码,但是如果需要更多信息,请告诉我。下面提供的代码代表我的两个解决方案中的前一个。后者只是创建了另一个类似于hoverOn和hoverOff的函数,使用mouseclick绑定。任何帮助都将不胜感激

  class AttackPage(tk.Frame):

    def __init__(self, parent, controller, nodes):
        tk.Frame.__init__(self, parent)

        # AttackPage widgets
        self.activeAttack = ActiveAttack(attackSelect)
        self.attackBox = tk.Frame(self, bg=bgCol)
        self.graphicLabel = tk.Label(self.attackBox, bg=bgCol)

        self.nodeFrame = tk.Label(self.attackBox, bg=bgCol)
        self.nodeYellow = tk.PhotoImage(file='img/nodeYellow.png')
        self.nodeYellow_s = tk.PhotoImage(file='img/nodeYellow_s.png')
        self.nodeWhite = tk.PhotoImage(file='img/nodeWhite.png')
        self.nodeSel = tk.PhotoImage(file='img/nodeSel.png')

        self.runNodes = []
        for index, item in enumerate(nodes):
            self.node = NodeDraw(self, index)

            self.nodeFrame.grid_columnconfigure(index + 1, weight=1)
            self.runNodes.append(self.node)

        self.nodeFrame.grid_columnconfigure(0, weight=3)
        self.nodeFrame.grid_columnconfigure(len(nodes) + 1, weight=3)

        self.contextBorder = tk.Label(self.attackBox, bg='#FFFFFF', height=25)
        self.context = tk.Text(self.attackBox, height=1, width=1, background=windowCol, foreground='#FFFFFF', wrap='word', font='calibri 10',
                                highlightthickness=4, highlightcolor=windowCol, highlightbackground=windowCol, relief='flat',
                                selectbackground=sapienYellow, selectforeground='#000000')

        self.terminalFrame = tk.Label(self.attackBox, bg=windowCol)
        self.terminalTitleImg = tk.PhotoImage(file='img/terminalTitle.png')
        self.terminalTitle = tk.Label(self.terminalFrame, image=self.terminalTitleImg, borderwidth=0, bg=windowCol)
        self.terminal = tk.Text(self.terminalFrame, height=9, width=160, background=windowCol, foreground='#FFFFFF',
                                wrap='word', font='calibri 10', highlightthickness=4, highlightcolor=windowCol,
                                highlightbackground=windowCol, relief='flat', selectbackground=sapienYellow,
                                selectforeground='#000000')

        # AttackPage layout
        self.grid_columnconfigure(0, weight=10)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure(2, weight=10)
        self.grid_rowconfigure(0, weight=1)
        self.attackBox.grid(row=0, column=1)

        self.attackBox.grid_columnconfigure(0, weight=1)
        self.attackBox.grid_columnconfigure(1, weight=1)
        self.attackBox.grid_rowconfigure(0, weight=2)
        self.attackBox.grid_rowconfigure(2, weight=1)
        self.graphicLabel.grid(row=0, column=0, sticky='nsew', padx=(12, 6), pady=(12, 6))

        self.nodeFrame.grid(row=1, column=0, sticky='nsew', padx=(12, 6), pady=(6, 12))

        self.contextBorder.grid(row=0, column=1, sticky='nsew', padx=(6, 12), pady=(12, 0))
        self.context.grid(row=0, column=1, rowspan=2, sticky='nsew', padx=(7, 13), pady=(13, 0))

        self.terminalFrame.grid(row=2, column=0, columnspan=2, sticky='nsew', padx=(12, 12), pady=(6, 12))
        self.terminalTitle.grid(row=0, column=0, sticky='nw', padx=3, pady=3)
        self.terminal.grid(row=1, column=0, sticky='ew')

class NodeDraw:
    def __init__(self, controller, index):

        self.inactive = HoverButton(controller.nodeFrame, 'img/nodeWhite.png', 'img/nodeYellow.png', 0)
        self.inactive.configure(image=controller.nodeYellow, borderwidth=0, bg=bgCol, activebackground=bgCol, command=lambda: self.selectNode(controller, index))
        self.inactive.grid(row=0, column=index + 1)
        controller.nodeFrame.grid_columnconfigure(index + 1, weight=1)

    def selectNode(self, controller, index):
        self.selectNode = tk.Label(controller.nodeFrame)
        self.selectNode.configure(image=controller.nodeSel, borderwidth=0, bg=bgCol, activebackground=bgCol)
        self.selectNode.grid(row=0, column=index+1)

class HoverButton(tk.Button):
    def __init__(self, parent, hovImg, img, bg):
        tk.Button.__init__(self, parent)
        self.image = None
        self.bind('<Enter>', lambda e: self.hoverOn(hovImg, self, bg))
        self.bind('<Leave>', lambda e: self.hoverOff(img, self, bg))
   
    def hoverOn(self, img, var, bg):  # hover image, button variable, background change (bool - 0 = no change to bgcolor)
        hoverImg = tk.PhotoImage(file=img)
        var.configure(image=hoverImg)
        var.image = hoverImg
        if bg == 1:
            var.configure(bg=sapienYellow)

    # Hover over button revert
    def hoverOff(self, img, var, bg):
        returnImg = tk.PhotoImage(file=img)
        var.configure(image=returnImg)
        var.image = returnImg
        if bg == 1:
            var.configure(bg=windowCol)
类攻击页(tk.Frame):
定义初始化(自身、父级、控制器、节点):
tk.Frame.\uuuu init\uuuuu(自,父)
#攻击页面小部件
self.activeAttack=activeAttack(attackSelect)
self.attackBox=tk.Frame(self,bg=bgCol)
self.graphicallabel=tk.Label(self.attackBox,bg=bgCol)
self.nodeFrame=tk.Label(self.attackBox,bg=bgCol)
self.nodeYellow=tk.PhotoImage(文件='img/nodeYellow.png')
self.nodeYellow\u s=tk.PhotoImage(file='img/nodeYellow\u s.png')
self.nodeWhite=tk.PhotoImage(文件='img/nodeWhite.png')
self.nodeSel=tk.PhotoImage(文件='img/nodeSel.png')
self.runNodes=[]
对于索引,枚举中的项(节点):
self.node=NodeDraw(self,index)
self.nodeFrame.grid\u columnconfigure(索引+1,权重=1)
self.runNodes.append(self.node)
self.nodeFrame.grid\u columnconfigure(0,权重=3)
self.nodeFrame.grid\u column配置(len(节点)+1,权重=3)
self.contextBorder=tk.Label(self.attackBox,bg='#FFFFFF',高度=25)
self.context=tk.Text(self.attackBox,高度=1,宽度=1,背景=windowCol,前景='#FFFFFF',wrap='word',font='calibri 10',
highlightthickness=4,highlightcolor=windowCol,highlightbackground=windowCol,relief='flat',
selectbackground=sapienYellow,selectforeground='#000000')
self.terminalFrame=tk.Label(self.attackBox,bg=windowCol)
self.terminalTitleImg=tk.PhotoImage(文件='img/terminalTitle.png')
self.terminalTitle=tk.Label(self.terminalFrame,image=self.terminalTitleImg,borderwidth=0,bg=windowCol)
self.terminal=tk.Text(self.terminalFrame,高度=9,宽度=160,背景=windowCol,前景='#FFFFFF',
wrap='word',font='calibri 10',highlightthickness=4,highlightcolor=windowCol,
highlightbackground=windowCol,relief='flat',选择background=sapienYellow,
选择前景='#000000')
#攻击页面布局
self.grid\u column配置(0,权重=10)
self.grid\u column配置(1,权重=1)
self.grid\u column配置(2,权重=10)
self.grid_rowconfigure(0,权重=1)
self.attackBox.grid(行=0,列=1)
self.attackBox.grid\u column配置(0,权重=1)
self.attackBox.grid\u column配置(1,权重=1)
self.attackBox.grid\u row配置(0,权重=2)
self.attackBox.grid\u row配置(2,权重=1)
self.graphicLabel.grid(行=0,列=0,sticky='nsew',padx=(12,6),pady=(12,6))
self.nodeFrame.grid(行=1,列=0,sticky='nsew',padx=(12,6),pady=(6,12))
self.contextBorder.grid(行=0,列=1,sticky='nsew',padx=(6,12),pady=(12,0))
self.context.grid(行=0,列=1,行span=2,sticky='nsew',padx=(7,13),pady=(13,0))
self.terminalFrame.grid(行=2,列=0,列span=2,sticky='nsew',padx=(12,12),pady=(6,12))
self.terminalTitle.grid(行=0,列=0,粘性=nw',padx=3,pady=3)
self.terminal.grid(行=1,列=0,sticky='ew')
类NodeDraw:
定义初始化(自身、控制器、索引):
self.inactive=HoverButton(controller.nodeFrame,'img/nodeWhite.png','img/nodeYellow.png',0)
self.inactive.configure(image=controller.nodcollow,borderwidth=0,bg=bgCol,activebackground=bgCol,command=lambda:self.selectNode(controller,index))
self.inactive.grid(行=0,列=index+1)
controller.nodeFrame.grid\u column配置(索引+1,权重=1)
def selectNode(自身、控制器、索引):
self.selectNode=tk.Label(controller.nodeFrame)
self.selectNode.configure(image=controller.nodeSel,borderwidth=0,bg=bgCol,activebackground=bgCol)
self.selectNode.grid(行=0,列=index+1)
类悬停按钮(tk.Button):
定义初始(自身、父项、hovImg、img、bg):
tk.Button.\uuuuu init\uuuuuuuuu(自,父)
self.image=None
self.bind('',lambda e:self.hoverOn(hovImg,self,bg))
自我绑定(“”,lambda e:self.hoverOff(img,self,bg
import tkinter as tk
from PIL import Image, ImageTk


root = tk.Tk()

#the radiobutton
class Statebutton(tk.Radiobutton):
    def __init__(self, master, variable, **kwargs):
        tk.Radiobutton.__init__(self, master, **{**kwargs, 'variable':variable, 'indicatoron':0})

        #delete this line ~ only for text version purposes
        self.configure(width=5)
        
        self.__bound = False
        self.__bind_names  = ['<Enter>', '<Leave>', '<1>']
        self.__bind_states = ['hover', 'up', 'down']
        self.__bind_ids    = [None]*3
        
        self.__var = variable
        self.__var.trace('w', self.__adjust)
        self.__adjust()
    
    #bind all events ~ change "text" to "image" at the end of the lambda
    def __bindings(self):
        for i, (name, state) in enumerate(zip(self.__bind_names, self.__bind_states)):
            self.__bind_ids[i] = self.bind(name, lambda e, s=state:self.configure(text=s))
        self.__bound = True
        
    #unbind all events
    def __unbindings(self):
        if self.__bound:
            for name, id in zip(self.__bind_names, self.__bind_ids):
                self.unbind(name, id)
            self.__bound = False
        
    def __adjust(self, *args):
        if self.__var.get() == self['value']:
            #change "text" to "image"
            self['text'] = 'sel'    
            if self.__bound:
                self.__unbindings()
        else:
            if not self.__bound:
                #change "text" to "image"
                self['text'] = 'up'
                self.__bindings()
        

selection = tk.StringVar()
selection.set('3')

#uncomment the below and adjust paths ~ DO NOT CHANGE the "name" argument
#images = dict(
#    up=ImageTk.PhotoImage(Image.open('up.png'), name='up'),
#    hover=ImageTk.PhotoImage(Image.open('hover.png'), name='hover'),
#    down=ImageTk.PhotoImage(Image.open('down.png'), name='down'),
#    sel=ImageTk.PhotoImage(Image.open('selected.png'), name='sel'),
#)

#the loop
for n in range(4):
    Statebutton(root, variable=selection, value=f'{n}').grid(row=0, column=n)

root.mainloop()