如何创建由 tkinter 中的 for 循环生成的单选按钮类型的图像按钮组
How do I create a radio button type group of image buttons that were generated by a for loop in tkinter
我目前无法使这段代码按我需要的方式工作。本质上我需要它像单选按钮一样工作,其中选定的 'node'(通过鼠标单击)更改为不同的图像。
节点由for循环生成,节点的数量取决于一些外部输入。每个按钮都有悬停图像的绑定。我目前的两次尝试基本上是在 for 循环中向节点添加一个命令,以确定按下了哪个按钮(使用输入列表的索引)并使用网格将另一个图像放在同一网格中节点的顶部space。这一半有效,但似乎暂时删除了所有其他节点,直到我激活悬停绑定。 runNodes 列表是旧解决方案的一部分,其结果与此几乎相同。我暂时把它留在那里以便能够引用节点,但我什至不确定我是否正确地这样做了。
或者,我尝试将另一个绑定添加到我创建的自定义按钮 class,其中在单击鼠标时,图像会更改为所选图像。不幸的是,当鼠标离开节点时,这会被悬停绑定删除。
我觉得这些解决方案中的一个或两个都可以工作,但我的代码中缺少一些可以使它们工作的东西..但是我也愿意接受完全替代的解决方案。我已经包含了我认为相关的尽可能多的代码,但如果需要更多信息,请告诉我。下面提供的代码代表我的两个解决方案中的前一个。后者只涉及创建另一个类似于使用鼠标单击绑定的 hoverOn 和 hoverOff 的函数。任何帮助将不胜感激!
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)
这是一个使用实际 Radiobutton
的示例。只需将我写的单词 text
更改为单词 image
即可(3 个地方)。然后取消注释 images
dict
并将所有路径更改为正确。我没有你的图片,我也不打算制作一些,所以我用文字制作了这个作品。它不能使用图像的原因为零。
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()
我目前无法使这段代码按我需要的方式工作。本质上我需要它像单选按钮一样工作,其中选定的 'node'(通过鼠标单击)更改为不同的图像。
节点由for循环生成,节点的数量取决于一些外部输入。每个按钮都有悬停图像的绑定。我目前的两次尝试基本上是在 for 循环中向节点添加一个命令,以确定按下了哪个按钮(使用输入列表的索引)并使用网格将另一个图像放在同一网格中节点的顶部space。这一半有效,但似乎暂时删除了所有其他节点,直到我激活悬停绑定。 runNodes 列表是旧解决方案的一部分,其结果与此几乎相同。我暂时把它留在那里以便能够引用节点,但我什至不确定我是否正确地这样做了。
或者,我尝试将另一个绑定添加到我创建的自定义按钮 class,其中在单击鼠标时,图像会更改为所选图像。不幸的是,当鼠标离开节点时,这会被悬停绑定删除。
我觉得这些解决方案中的一个或两个都可以工作,但我的代码中缺少一些可以使它们工作的东西..但是我也愿意接受完全替代的解决方案。我已经包含了我认为相关的尽可能多的代码,但如果需要更多信息,请告诉我。下面提供的代码代表我的两个解决方案中的前一个。后者只涉及创建另一个类似于使用鼠标单击绑定的 hoverOn 和 hoverOff 的函数。任何帮助将不胜感激!
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)
这是一个使用实际 Radiobutton
的示例。只需将我写的单词 text
更改为单词 image
即可(3 个地方)。然后取消注释 images
dict
并将所有路径更改为正确。我没有你的图片,我也不打算制作一些,所以我用文字制作了这个作品。它不能使用图像的原因为零。
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()