Как вы запускаете свой собственный код вместе с циклом событий Tkinter?



мой младший брат только начинает заниматься программированием, и для своего проекта Science Fair он делает симуляцию стаи птиц в небе. Он получил большую часть своего кода написано, и это работает хорошо, но птицы должны двигаться каждый момент.



Tkinter, однако, свиньи время для своего собственного цикла событий, и поэтому его код не будет работать. Делать root.mainloop() работает, работает и продолжает работать, и единственное, что он работает, это обработчики событий.



есть ли способ чтобы его код работал вместе с mainloop (без многопоточности, это сбивает с толку, и это должно быть просто), и если да, то что это такое?



прямо сейчас, он придумал уродливый Хак, связывая его

507   5  

5 ответов:

использовать after метод

The решение опубликовано Бьорн приводит к сообщению "RuntimeError: вызов Tcl из разных квартир" на моем компьютере (RedHat Enterprise 5, python 2.6.1). Бьорн, возможно, не получил это сообщение, так как, согласно одно место я проверил, неправильное обращение с резьбой с Tkinter непредсказуемо и зависит от платформы.

проблема, кажется, в том, что app.start() считается ссылкой на ТЗ, так как приложение содержит элементы ТЗ. Я исправил это с помощью замена app.start() С self.start() внутри __init__. Я также сделал так, что все ссылки на ТЗ находятся либо внутри функция, которая вызывает mainloop() или внутри функции, которые вызываются функция, которая вызывает mainloop() (это, по-видимому, важно, чтобы избежать ошибки "другой квартиры").

наконец, я добавил обработчик протокола с обратным вызовом, так как без этого программа выходит с ошибкой, когда окно Tk закрывается пользователь.

измененный код выглядит следующим образом:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)

при написании собственного цикла, как и в симуляции (я предполагаю), вам нужно вызвать update функция, которая делает то, что mainloop делает: обновляет окно с вашими изменениями, но вы делаете это в цикле.

def task():
   # do something
   root.update()

while 1:
   task()  

другой вариант-позволить tkinter выполняться в отдельном потоке. Один из способов сделать это так:

import Tkinter
import threading

class MyTkApp(threading.Thread):
    def __init__(self):
        self.root=Tkinter.Tk()
        self.s = Tkinter.StringVar()
        self.s.set('Foo')
        l = Tkinter.Label(self.root,textvariable=self.s)
        l.pack()
        threading.Thread.__init__(self)

    def run(self):
        self.root.mainloop()


app = MyTkApp()
app.start()

# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')

будьте осторожны, хотя многопоточное программирование трудно, и это действительно легко стрелять себе в ногу. Например, вы должны быть осторожны при изменении переменных-членов класса sample выше, чтобы не прерывать цикл событий Tkinter.

Это первая рабочая версия того, что будет GPS-считыватель и презентатор данных. tkinter-очень хрупкая вещь с слишком малым количеством сообщений об ошибках. Он не ставит вещи и не говорит, почему большую часть времени. Очень сложно исходить от хорошего разработчика формы WYSIWYG. Во всяком случае, это запускает небольшую процедуру 10 раз в секунду и представляет информацию о форме. Потребовалось время, чтобы это произошло. Когда я попробовал значение таймера 0, форма никогда не появлялась. Теперь у меня болит голова! 10 или больше раз в секунду достаточно хорошо для меня. Я надеюсь, что это поможет кому-то еще. Майк Морроу

import tkinter as tk
import time

def GetDateTime():
  # Get current date and time in ISO8601
  # https://en.wikipedia.org/wiki/ISO_8601 
  # https://xkcd.com/1179/
  return (time.strftime("%Y%m%d", time.gmtime()),
          time.strftime("%H%M%S", time.gmtime()),
          time.strftime("%Y%m%d", time.localtime()),
          time.strftime("%H%M%S", time.localtime()))

class Application(tk.Frame):

  def __init__(self, master):

    fontsize = 12
    textwidth = 9

    tk.Frame.__init__(self, master)
    self.pack()

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Time').grid(row=0, column=0)
    self.LocalDate = tk.StringVar()
    self.LocalDate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalDate).grid(row=0, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Date').grid(row=1, column=0)
    self.LocalTime = tk.StringVar()
    self.LocalTime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalTime).grid(row=1, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Time').grid(row=2, column=0)
    self.nowGdate = tk.StringVar()
    self.nowGdate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGdate).grid(row=2, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Date').grid(row=3, column=0)
    self.nowGtime = tk.StringVar()
    self.nowGtime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGtime).grid(row=3, column=1)

    tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)

    self.gettime()
  pass

  def gettime(self):
    gdt, gtm, ldt, ltm = GetDateTime()
    gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
    gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'  
    ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
    ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]  
    self.nowGtime.set(gdt)
    self.nowGdate.set(gtm)
    self.LocalTime.set(ldt)
    self.LocalDate.set(ltm)

    self.after(100, self.gettime)
   #print (ltm)  # Prove it is running this and the external code, too.
  pass

root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)

w = 200 # width for the Tk root
h = 125 # height for the Tk root

# get display screen width and height
ws = root.winfo_screenwidth()  # width of the screen
hs = root.winfo_screenheight() # height of the screen

# calculate x and y coordinates for positioning the Tk root window

#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)

#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35  # -35 fixes it, more or less, for Win10

#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))

root.mainloop()

Comments

    Ничего не найдено.