Удалите все из TkInter Canvas и поместите новые предметы, находясь в mainloop - PullRequest
0 голосов
/ 09 апреля 2019

Цель состоит в том, чтобы создавать различные «экраны» в TkInter и переключаться между ними. Проще всего это представить себе мобильное приложение, в котором можно щелкнуть значок, например «Добавить новый», и открывается новый экран. Приложение имеет всего 7 экранов, и оно должно иметь возможность менять экраны в соответствии с действиями пользователя.

Настройка на Raspberry Pi с подключенным ЖК-дисплеем + сенсорный экран. Я использую tkinter в Python3. Холст используется для отображения элементов на экране. Поскольку я из мира встраиваемого оборудования, и у меня очень мало опыта работы с Python и вообще языками высокого уровня, я подошел к этому с логикой переключения. В Python это if-elif-elif ...

Я пробовал разные вещи:

  1. Создание глобального объекта canvas. Наличие переменной programState, которая определяет, какой экран отображается в данный момент. Это, очевидно, не сработало, потому что он просто запустился бы один раз и застрял в главном цикле ниже.
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 Ответ

2 голосов
/ 09 апреля 2019

Tkinter, как и большинство инструментов GUI, управляется событиями.Вам просто нужно создать функцию, которая удаляет старый экран и создает новый, а затем делает это в ответ на событие (нажатие кнопки, таймер и т. Д.).

Использование вашего первого примера canvas

В первом примере вы хотите автоматически переключать страницы через две секунды.Это можно сделать с помощью 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.Например, чтобы связать клавишу escape, чтобы всегда переводить вас в исходное состояние, вы можете сделать что-то вроде этого:

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

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