从TkInter Canvas中删除所有内容,并在主循环中放入新项目

时间:2019-04-08 23:42:08

标签: python tkinter tkinter-canvas

目标是在TkInter中实现不同的“屏幕”并在它们之间进行切换。最容易想到的是想到一个移动应用程序,在该应用程序上单击一个图标(例如“添加新”),然后打开新屏幕。该应用程序共有7个屏幕,它应该能够根据用户操作更改屏幕。

安装程序在具有LCD +触摸屏的Raspberry Pi上进行。我在Python3中使用tkinter。画布用于在屏幕上显示元素。 由于我来自嵌入式硬件领域,并且几乎没有Python和一般高级语言的经验,因此我采用了切换用例逻辑来解决这个问题。在Python中,这是if-elif-elif ...

我尝试了各种事情:

  1. 制作全局画布对象。具有可变的programState,它确定当前显示哪个屏幕。这显然不起作用,因为它只会运行一次并卡在下面的mainloop中。
from tkinter import * 
import time

root = Tk()

programState = 0
canvas = Canvas(width=320, height=480, bg='black')
canvas.pack(expand=YES, fill=BOTH)

if(programState == 0):
       backgroundImage = PhotoImage(file="image.gif")
       canvas.create_image(0,0, image=backgroundImage, anchor=NW);

       time.sleep(2)

       canvas.delete(ALL) #delete all objects from canvas
       programState = 1
elif(programState == 1):
....
....
....
root.mainloop()

  1. 使用root.after函数,但是此操作失败,并且屏幕上不会显示任何内容,只会创建画布。我可能没有在正确的地方使用它。

  2. 尝试制作另一个线程来更改屏幕,只是为了测试线程选项。它卡在第一个图像上,永远不会移至第二个图像。

from tkinter import *
from threading import Thread
from time import sleep

def threadFun():
        while True:
                backgroundImage = PhotoImage(file="image1.gif")
                backgroundImage2 = PhotoImage(file="image2.gif")
                canvas.create_image(0,0,image=backgroundImage, anchor=NW)
                sleep(2)
                canvas.delete(ALL)
                canvas.create_image(0,0,image=backgroundImage2, anchor=NW)

root = Tk()

canvas = Canvas(width=320, height=480, bg='black')
canvas.pack(expand=YES, fill=BOTH)

# daemon=True kills the thread when you close the GUI, otherwise it would continue to run and raise an error.
Thread(target=threadFun, daemon=True).start()
root.mainloop()

我希望这个应用程序可以使用一个特殊的线程来更改屏幕,该线程将调用一个在画布上重新绘制元素的函数,但是到目前为止这一直失败。据我所了解,线程可能是最好的选择。它们最接近我的无限循环思维方式(True),最接近我的逻辑。

这里有什么选择?如何删除整个屏幕并重新绘制(我称做新的“屏幕”)?

1 个答案:

答案 0 :(得分:2)

Tkinter与大多数GUI工具包一样,都是事件驱动的。您只需要创建一个删除旧屏幕并创建新屏幕的函数,然后响应事件(单击按钮,计时器等)来执行此操作。

使用您的第一个画布示例

在第一个示例中,您想在两秒钟后自动切换页面。这可以通过使用after安排一个函数在超时后运行来完成。然后,只需将重绘逻辑移入函数即可。

例如:

def set_programState(new_state):
    global programState
    programState = new_state
    refresh()

def refresh():
    canvas.delete("all")

    if(programState == 0):
        backgroundImage = PhotoImage(file="image.gif")
        canvas.create_image(0,0, image=backgroundImage, anchor=NW);
        canvas.after(2000, set_programState, 1)
    elif(programState == 1):
        ...

使用python对象

可以说,更好的解决方案是使每个页面成为基于小部件的类。这样做可以轻松地通过添加或删除一个小部件来一次添加或删除所有内容(因为销毁一个小部件也将销毁其所有子级)

然后,只需删除旧对象并实例化新对象即可。如果您喜欢状态驱动的概念,则可以创建状态号到类名的映射,并使用该映射来确定要实例化的类。

例如:

class ThisPage(tk.Frame):
    def __init__(self):
        <code to create everything for this page>

class ThatPage(tk.Frame):
    def __init__(self):
        <code to create everything for this page>

page_map = {0: ThisPage, 1: ThatPage}
current_page = None
...
def refresh():
    global current_page

    if current_page:
        current_page.destroy()

    new_page_class = page_map[programstate]     
    current_page = new_page_class()
    current_page.pack(fill="both", expand=True)

上面的代码有些困难,但是希望它能说明基本技术。

与第一个示例一样,您可以从任何事件中调用update():按钮单击,计时器或tkinter支持的任何其他事件。例如,要绑定转义键以始终将您带入初始状态,您可以执行以下操作:

def reset_state(event):
    global programState
    programState = 0
    refresh()

root.bind("<Escape>", reset_state)