更新一个 canvas 中断 Tkinter 主循环的元素
Update an element of a canvas interrupting the main loop of Tkinter
我对 Python 和 Tkinter 还很陌生,因此,我不知道如何实现它...
实际上,我有这个 GUI:
,使用以下代码创建:
from tkinter import *
class RCP:
global handle_comm
global handle_rect
global handle_res
global spellerFrame
global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa
def __init__(self, targets, w_width, w_height):
self.n_row = len(targets)
self.n_col = len(targets[0])
self.w_width = w_width
self.w_height = w_height
self.targets = targets
self.canvasRoot = Tk()
self.canvasRoot.configure(background='grey')
self.setDefaults()
self.canvasRoot.bind("<<foo>>", self.flashRow)
# Initializate the main loop
self.createGrid()
self.canvasRoot.mainloop()
def setDefaults(self):
global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa
color_rect_bg = '#000000'
color_rect_hl = '#ffffff'
color_text_bg = '#757575'
color_text_hl = '#ffffff'
color_text_wa = '#ffff00'
global font_ratio_bg, font_ratio_hl
font_ratio_bg = 0.5
font_ratio_hl = 0.7
def createGrid(self):
# Calculate the maximum cell and font size that do not deform the commands' display
cell_size = min(self.w_height / self.n_row, self.w_width / self.n_col)
font_size = int(round(font_ratio_bg * cell_size))
result_size = int(round(cell_size/5))
# Create the canvas for the result text
global handle_res
resultLabel = Canvas(self.canvasRoot, width=self.w_width, height=result_size, bd=0,
highlightthickness=0, relief='ridge', background='grey')
resultLabel.grid(row=0, column=0, columnspan=self.n_col)
handle_res = resultLabel.create_text(2, round(result_size/2), text=' PRUEBA', fill=color_text_wa,
anchor='w', font=("Purisa", round(result_size/2), "bold"))
# Create the frame for the speller
global spellerFrame
spellerFrame = Canvas(self.canvasRoot, width=self.w_width, height=self.w_height, bd=0,
highlightthickness=0, relief='ridge')
spellerFrame.grid(row=1, column=0)
# Create the grid of commands
global handle_comm, handle_rect
handle_comm = [[None for i in range(self.n_col)] for j in range(self.n_row)]
handle_rect = handle_comm
for row_index in range(self.n_row):
for col_index in range(self.n_col):
x1 = col_index * cell_size
y1 = row_index * cell_size
x2 = (col_index + 1) * cell_size
y2 = (row_index + 1) * cell_size
handle_rect[row_index][col_index] = spellerFrame.create_rectangle(x1, y1, x2, y2, fill=color_rect_bg)
handle_comm[row_index][col_index] = \
spellerFrame.create_text(((x1+x2)/2,(y1+y2)/2), text=self.targets[row_index][col_index],
fill=color_text_bg, font=("Purisa", font_size, "bold"))
def flashRow(self):
'''Flashes the row specified as attribute'''
global spellerFrame
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[1][col_index], fill=color_text_hl)
targets = [['A','B','C','D'],['E','F','G','H'],['I','J','K','L']]
myRCP = RCP(targets, 800, 600)
问题是我需要在GUI已经显示后修改字母的颜色。这个视频展示了我想要达到的效果:https://www.youtube.com/watch?v=xvfxsNpaRGI
因此,我创建了 flashRow
方法,它在调用时闪烁第一行。问题是我无法中断 Tkinter 的主循环来更新元素...
我读过 after
命令,但我认为这不是一个合适的选项,因为我不知道什么时候需要调用 flashRow
方法先验的。
我可以使用 generate_event
方法来创建错误事件处理程序来修改字母的颜色而不中断循环吗?如果是,如何?
提前致谢
这将使您入门。将 self.canvasRoot.bind("<<foo>>", self.flashRow)
更改为 self.canvasRoot.after(10, self.flashRow)
。这将导致 flashRow 运行 一次。在 flashRow 方法的底部,如果你想重复使用 flashRow 运行,schedule 它再次使用 after.
到 运行 本身
def flashRow(self):
'''Flashes the row specified as attribute'''
global spellerFrame
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[1][col_index],
fill=color_text_hl)
# Put conditions or change the delay to something else,
# but this will reschedule flashRow in 100ms repeatedly.
self.canvasRoot.after(100, self.flashRow)
widget.after(ms, callback, *args)
在程序第一次读取它后调用 callback(*args)
ms
毫秒。所以你可以定义一个回调方法,例如self.flashRow
,然后用你在__init__
的时间范围调用它,比如:
self.canvasRoot.after(250, self.flashRow)
或:
self.flashRow
两者都应该没问题,只要它们在 mainloop
调用之前。然后在你的回调中,self.flashRow
,你需要确保它在一个时间范围内递归调用自己,比如 250 毫秒:
self.canvasRoot.after(250, self.flashRow)
我配置了 self.flashRow
一些随机闪烁:
def flashRow(self):
'''Flashes the row specified as attribute'''
import random
global spellerFrame
_row = random.randint(0, 2)
_color = random.choice((color_text_hl, 'grey'))
print(_color)
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[_row][col_index], fill=_color)
self.canvasRoot.after(250, self.flashRow)
另请参阅您提供的非mcve代码的完整配置:
from tkinter import *
class RCP:
global handle_comm
global handle_rect
global handle_res
global spellerFrame
global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa
def __init__(self, targets, w_width, w_height):
self.n_row = len(targets)
self.n_col = len(targets[0])
self.w_width = w_width
self.w_height = w_height
self.targets = targets
self.canvasRoot = Tk()
self.canvasRoot.configure(background='grey')
self.setDefaults()
# Initializate the main loop
self.createGrid()
self.canvasRoot.after(250, self.flashRow)
self.canvasRoot.mainloop()
def setDefaults(self):
global color_rect_bg, color_rect_hl, color_text_bg
global color_text_hl, color_text_wa
color_rect_bg = '#000000'
color_rect_hl = '#ffffff'
color_text_bg = '#757575'
color_text_hl = '#ffffff'
color_text_wa = '#ffff00'
global font_ratio_bg, font_ratio_hl
font_ratio_bg = 0.5
font_ratio_hl = 0.7
def createGrid(self):
# Calculate the maximum cell and font size that do not
# deform the commands' display
cell_size = min(self.w_height / self.n_row, self.w_width / self.n_col)
font_size = int(round(font_ratio_bg * cell_size))
result_size = int(round(cell_size/5))
# Create the canvas for the result text
global handle_res
resultLabel = Canvas(self.canvasRoot, width=self.w_width,
height=result_size, bd=0,
highlightthickness=0, relief='ridge', background='grey')
resultLabel.grid(row=0, column=0, columnspan=self.n_col)
handle_res = resultLabel.create_text(2, round(result_size/2),
text=' PRUEBA', fill=color_text_wa, anchor='w',
font=("Purisa", round(result_size/2), "bold"))
# Create the frame for the speller
global spellerFrame
spellerFrame = Canvas(self.canvasRoot, width=self.w_width,
height=self.w_height, bd=0,
highlightthickness=0, relief='ridge')
spellerFrame.grid(row=1, column=0)
# Create the grid of commands
global handle_comm, handle_rect
handle_comm = [[None for i in range(self.n_col)] for j in range(self.n_row)]
handle_rect = handle_comm
for row_index in range(self.n_row):
for col_index in range(self.n_col):
x1 = col_index * cell_size
y1 = row_index * cell_size
x2 = (col_index + 1) * cell_size
y2 = (row_index + 1) * cell_size
handle_rect[row_index][col_index] = spellerFrame.create_rectangle(x1,
y1, x2, y2, fill=color_rect_bg)
handle_comm[row_index][col_index] = \
spellerFrame.create_text(((x1+x2)/2,(y1+y2)/2),
text=self.targets[row_index][col_index],
fill=color_text_bg,
font=("Purisa", font_size, "bold"))
def flashRow(self):
'''Flashes the row specified as attribute'''
import random
global spellerFrame
_row = random.randint(0, 2)
_color = random.choice((color_text_hl, 'grey'))
print(_color)
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[_row][col_index], fill=_color)
self.canvasRoot.after(250, self.flashRow)
targets = [['A','B','C','D'],['E','F','G','H'],['I','J','K','L']]
myRCP = RCP(targets, 800, 600)
我对 Python 和 Tkinter 还很陌生,因此,我不知道如何实现它...
实际上,我有这个 GUI:
,使用以下代码创建:
from tkinter import *
class RCP:
global handle_comm
global handle_rect
global handle_res
global spellerFrame
global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa
def __init__(self, targets, w_width, w_height):
self.n_row = len(targets)
self.n_col = len(targets[0])
self.w_width = w_width
self.w_height = w_height
self.targets = targets
self.canvasRoot = Tk()
self.canvasRoot.configure(background='grey')
self.setDefaults()
self.canvasRoot.bind("<<foo>>", self.flashRow)
# Initializate the main loop
self.createGrid()
self.canvasRoot.mainloop()
def setDefaults(self):
global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa
color_rect_bg = '#000000'
color_rect_hl = '#ffffff'
color_text_bg = '#757575'
color_text_hl = '#ffffff'
color_text_wa = '#ffff00'
global font_ratio_bg, font_ratio_hl
font_ratio_bg = 0.5
font_ratio_hl = 0.7
def createGrid(self):
# Calculate the maximum cell and font size that do not deform the commands' display
cell_size = min(self.w_height / self.n_row, self.w_width / self.n_col)
font_size = int(round(font_ratio_bg * cell_size))
result_size = int(round(cell_size/5))
# Create the canvas for the result text
global handle_res
resultLabel = Canvas(self.canvasRoot, width=self.w_width, height=result_size, bd=0,
highlightthickness=0, relief='ridge', background='grey')
resultLabel.grid(row=0, column=0, columnspan=self.n_col)
handle_res = resultLabel.create_text(2, round(result_size/2), text=' PRUEBA', fill=color_text_wa,
anchor='w', font=("Purisa", round(result_size/2), "bold"))
# Create the frame for the speller
global spellerFrame
spellerFrame = Canvas(self.canvasRoot, width=self.w_width, height=self.w_height, bd=0,
highlightthickness=0, relief='ridge')
spellerFrame.grid(row=1, column=0)
# Create the grid of commands
global handle_comm, handle_rect
handle_comm = [[None for i in range(self.n_col)] for j in range(self.n_row)]
handle_rect = handle_comm
for row_index in range(self.n_row):
for col_index in range(self.n_col):
x1 = col_index * cell_size
y1 = row_index * cell_size
x2 = (col_index + 1) * cell_size
y2 = (row_index + 1) * cell_size
handle_rect[row_index][col_index] = spellerFrame.create_rectangle(x1, y1, x2, y2, fill=color_rect_bg)
handle_comm[row_index][col_index] = \
spellerFrame.create_text(((x1+x2)/2,(y1+y2)/2), text=self.targets[row_index][col_index],
fill=color_text_bg, font=("Purisa", font_size, "bold"))
def flashRow(self):
'''Flashes the row specified as attribute'''
global spellerFrame
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[1][col_index], fill=color_text_hl)
targets = [['A','B','C','D'],['E','F','G','H'],['I','J','K','L']]
myRCP = RCP(targets, 800, 600)
问题是我需要在GUI已经显示后修改字母的颜色。这个视频展示了我想要达到的效果:https://www.youtube.com/watch?v=xvfxsNpaRGI
因此,我创建了 flashRow
方法,它在调用时闪烁第一行。问题是我无法中断 Tkinter 的主循环来更新元素...
我读过 after
命令,但我认为这不是一个合适的选项,因为我不知道什么时候需要调用 flashRow
方法先验的。
我可以使用 generate_event
方法来创建错误事件处理程序来修改字母的颜色而不中断循环吗?如果是,如何?
提前致谢
这将使您入门。将 self.canvasRoot.bind("<<foo>>", self.flashRow)
更改为 self.canvasRoot.after(10, self.flashRow)
。这将导致 flashRow 运行 一次。在 flashRow 方法的底部,如果你想重复使用 flashRow 运行,schedule 它再次使用 after.
def flashRow(self):
'''Flashes the row specified as attribute'''
global spellerFrame
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[1][col_index],
fill=color_text_hl)
# Put conditions or change the delay to something else,
# but this will reschedule flashRow in 100ms repeatedly.
self.canvasRoot.after(100, self.flashRow)
widget.after(ms, callback, *args)
在程序第一次读取它后调用 callback(*args)
ms
毫秒。所以你可以定义一个回调方法,例如self.flashRow
,然后用你在__init__
的时间范围调用它,比如:
self.canvasRoot.after(250, self.flashRow)
或:
self.flashRow
两者都应该没问题,只要它们在 mainloop
调用之前。然后在你的回调中,self.flashRow
,你需要确保它在一个时间范围内递归调用自己,比如 250 毫秒:
self.canvasRoot.after(250, self.flashRow)
我配置了 self.flashRow
一些随机闪烁:
def flashRow(self):
'''Flashes the row specified as attribute'''
import random
global spellerFrame
_row = random.randint(0, 2)
_color = random.choice((color_text_hl, 'grey'))
print(_color)
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[_row][col_index], fill=_color)
self.canvasRoot.after(250, self.flashRow)
另请参阅您提供的非mcve代码的完整配置:
from tkinter import *
class RCP:
global handle_comm
global handle_rect
global handle_res
global spellerFrame
global color_rect_bg, color_rect_hl, color_text_bg, color_text_hl, color_text_wa
def __init__(self, targets, w_width, w_height):
self.n_row = len(targets)
self.n_col = len(targets[0])
self.w_width = w_width
self.w_height = w_height
self.targets = targets
self.canvasRoot = Tk()
self.canvasRoot.configure(background='grey')
self.setDefaults()
# Initializate the main loop
self.createGrid()
self.canvasRoot.after(250, self.flashRow)
self.canvasRoot.mainloop()
def setDefaults(self):
global color_rect_bg, color_rect_hl, color_text_bg
global color_text_hl, color_text_wa
color_rect_bg = '#000000'
color_rect_hl = '#ffffff'
color_text_bg = '#757575'
color_text_hl = '#ffffff'
color_text_wa = '#ffff00'
global font_ratio_bg, font_ratio_hl
font_ratio_bg = 0.5
font_ratio_hl = 0.7
def createGrid(self):
# Calculate the maximum cell and font size that do not
# deform the commands' display
cell_size = min(self.w_height / self.n_row, self.w_width / self.n_col)
font_size = int(round(font_ratio_bg * cell_size))
result_size = int(round(cell_size/5))
# Create the canvas for the result text
global handle_res
resultLabel = Canvas(self.canvasRoot, width=self.w_width,
height=result_size, bd=0,
highlightthickness=0, relief='ridge', background='grey')
resultLabel.grid(row=0, column=0, columnspan=self.n_col)
handle_res = resultLabel.create_text(2, round(result_size/2),
text=' PRUEBA', fill=color_text_wa, anchor='w',
font=("Purisa", round(result_size/2), "bold"))
# Create the frame for the speller
global spellerFrame
spellerFrame = Canvas(self.canvasRoot, width=self.w_width,
height=self.w_height, bd=0,
highlightthickness=0, relief='ridge')
spellerFrame.grid(row=1, column=0)
# Create the grid of commands
global handle_comm, handle_rect
handle_comm = [[None for i in range(self.n_col)] for j in range(self.n_row)]
handle_rect = handle_comm
for row_index in range(self.n_row):
for col_index in range(self.n_col):
x1 = col_index * cell_size
y1 = row_index * cell_size
x2 = (col_index + 1) * cell_size
y2 = (row_index + 1) * cell_size
handle_rect[row_index][col_index] = spellerFrame.create_rectangle(x1,
y1, x2, y2, fill=color_rect_bg)
handle_comm[row_index][col_index] = \
spellerFrame.create_text(((x1+x2)/2,(y1+y2)/2),
text=self.targets[row_index][col_index],
fill=color_text_bg,
font=("Purisa", font_size, "bold"))
def flashRow(self):
'''Flashes the row specified as attribute'''
import random
global spellerFrame
_row = random.randint(0, 2)
_color = random.choice((color_text_hl, 'grey'))
print(_color)
for col_index in range(self.n_col):
spellerFrame.itemconfig(handle_comm[_row][col_index], fill=_color)
self.canvasRoot.after(250, self.flashRow)
targets = [['A','B','C','D'],['E','F','G','H'],['I','J','K','L']]
myRCP = RCP(targets, 800, 600)