【问题标题】:What's wrong with this tkinter program? [closed]这个 tkinter 程序有什么问题? [关闭]
【发布时间】:2019-05-15 09:52:50
【问题描述】:
import random
import time
from tkinter import *    

root = Tk()

x = ""

lab = Label(root,text = x)
lab.pack()

root.mainloop()

def randomno():
    while (1):
        y = random.randint(1, 100)
        y = StringVar()
        x = y.get()
        lab["text"] = x
        #root.update_idletasks()
        time.sleep(2)

randomno()

错误:

Traceback (most recent call last):   File
   "C:/Users/Acer/PycharmProjects/unseen/tp.py", line 26, in <module>
       randomno()   File "C:/Users/Acer/PycharmProjects/unseen/tp.py", line 20, in randomno
       y = StringVar()   File "C:\Users\Acer\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py",
   line 480, in __init__
       Variable.__init__(self, master, value, name)   File "C:\Users\Acer\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py",
   line 317, in __init__
       self._root = master._root() AttributeError: 'NoneType' object has no attribute '_root'

【问题讨论】:

  • 编辑:缩进是 ri8
  • 您是否有意在无限循环中使用time.sleep(为真)?
  • 是的,我想是的..
  • 我想用随机数不断更新应用程序

标签: python python-3.x tkinter


【解决方案1】:

这是在tkinter 中做你想做的事情的常用方法:

import random
import time
import tkinter as tk

DELAY = 2000  # milliseconds (thousandth of a second)

def randomno():
    x = random.randint(1, 100)
    lab["text"] = x
    #time.sleep(2)  # Don't call this in a tkinter program!
    root.after(DELAY, randomno)  # Call this function again after DELAY ms.


root = tk.Tk()

lab = tk.Label(root, text="")
lab.pack()

randomno()  # Starts periodic calling of itself.
root.mainloop()

您不需要使用StringVar,只需在randomno() 函数中分配新的随机值即可。

您不应在tkinter 应用程序中调用time.sleep()。请改用通用小部件方法after()。请注意在上面的代码中randomno() 如何调用root.after() 以安排稍后再次调用自己。

这就是在tkinter 程序中定期执行某些操作的方法,这种方法将在调用sleep() 时保持GUI 运行而不是“挂起”。

【讨论】:

  • 感谢@martineau 的帮助。
  • user9920858:不客气。另请注意,通过这种方法,您可以显示其他小部件并能够与它们进行交互,同时 Label 在“后台”中不断更新——这一切都是因为 mainloop() 仍在运行,
【解决方案2】:

这是因为您的代码意图是错误的。我建议你用类结构来组织你的程序。此外,您的无限循环会阻止 tkinter 主循环,因此即使您修复了代码,您的窗口也会冻结。 所以这是一个工作示例:

import random
import time
from threading import Thread
from tkinter import *


class stack_overflow():
  def __init__(self, master):
      self.master = master
      self.x = 'trial'

      self.lab = Label(self.master, text=self.x)
      self.lab.pack(side="left")
      self.start_radome()

  def start_radome(self):
      radome_thread = Thread(target=self.randomno)
      radome_thread.start()

  def randomno(self):
      while True:
          self.x = random.randint(1, 100)
          self.lab.config(text='%s' % self.x)
          time.sleep(1)

if __name__ == '__main__':
      root = Tk()
      app = stack_overflow(master=root)
      root.mainloop()

所以你启动你的 tkinter,这是你的主线程。在另一个线程中启动无限循环。因此,您的 tkinter 窗口不会冻结。

【讨论】:

  • 使用tkinter 自己的小部件after() 方法有更简单的方法。
  • @martineau 你好,你是对的。可以办到。但迟早你可能需要向你的 tkinter 添加更多功能,所以我认为线程机制更易于维护。对于定期调用函数或进程,queue 可能更好。我想听听你的意见,谢谢。
  • 线程也很有用,但与使用 after() 不同,您必须非常小心,只在主线程中进行 tkinter 调用(或者说是单独的 gui 线程)。不久前,我在一个相关问题上发布了answer,它提供了一个模板/配方,用于使用您可能感兴趣的线程。队列通常在 tkinter 应用程序中用于所有其他线程与 GUI 通信,但是请注意,after() 仍在使用中,它调用的函数可以根据从队列中获取的信息进行 tkinter 调用。
  • @martineau 谢谢你的回复。我检查了你的帖子。有用的信息,尤其是在 tkinter 应用程序中运行后台功能。
  • 我想要点是,对于像以统一的方式修改 Label 的文本这样微不足道的事情,不需要额外的信息并且可以使用单独的线程快速完成(并且可能Queue) 会有点矫枉过正——而且可能会更慢,尽管这可能无关紧要。
猜你喜欢
  • 2021-02-12
  • 2012-12-31
  • 2015-01-12
  • 1970-01-01
  • 2012-04-19
  • 1970-01-01
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
相关资源
最近更新 更多