Python 中的 Tkinter 框架嵌套问题以及解决方案

115 阅读2分钟

在使用 Python 的 Tkinter 库进行 GUI 开发时,如果需要在主框架中创建一个子框架并将其添加到容器中,可能会遇到以下问题:

  • 当尝试删除一个子框架时,可能会导致最后创建的框架也被删除。
  • 子框架中的小部件可能不会正确显示。
  1. 解决方案

解决这些问题可以采取以下方法:

  • 不要将创建子框架的代码放在主 GUI 类的方法中,因为这会影响删除按钮的功能并导致更新值在框架被隐藏后仍然存在。
  • 将子框架的小部件移动到负责实际股票值的类中,并确保正确地对它们进行打包。
  • 在使用 pack_forget() 方法隐藏框架之前,先将其从容器中移除。

为了更清楚地说明解决方案,我们提供了一个示例代码:

import tkinter as tk
import threading
from thread import *

runningThreads = 0

class MyApp(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.myContainer = tk.Canvas(self)
        self.myContainer.pack()
        self.create_widgets()

    # METHOD initiates basic GUI widgets
    def create_widgets(self):
        self.widgetFrame = tk.Frame(self.myContainer)
        self.widgetFrame.pack()

        self.input = tk.Entry(self.widgetFrame)
        self.input.focus_set()
        self.input.pack()

        self.submitButton = tk.Button(self.widgetFrame, command=self.onButtonClick)
        self.submitButton.configure(text="Add new stock")
        self.submitButton.pack(fill="x")

    # METHOD called by each stock object
    # returns the "symbol" in the entry widget
    # clears the entry widget
    def get_input_value(self):
        var = self.input.get()
        self.input.delete(0, tk.END)
        return var

    # METHOD called when button is clicked
    # starts new thread with instance of "Stock" class
    def onButtonClick(self):
        global runningThreads  # shhhhhh im sorry just let it happen
        runningThreads += 1  # count the threads open
        threading.Thread(target=self.init_stock, ).start()  # force a tuple
        if runningThreads == 1:
            print(runningThreads, "thread alive")
        else:
            print(runningThreads, "threads alive")

    def init_stock(self):
        new = Stock()

class Stock(tk.Frame):
    def __init__(self):
        # variable for the stock symbol
        symb = self.stock_symbol()

        # lets make a GUI
        self.frame = tk.Frame(myapp.myContainer)
        self.frame.pack()

        # give the frame a label to update
        self.testLabel = tk.Label(self.frame)
        self.testLabel.configure(text=self.update_stock_label(symb))
        self.testLabel.pack(side=tk.LEFT)

        # create delete button to kill entire thread
        self.killButton = tk.Button(self.frame, command=self.kill_thread)
        self.killButton.configure(text="Delete")
        self.killButton.pack(side=tk.RIGHT)

        # create stock label
        # call updater

    def kill_thread(self):
        global runningThreads
        runningThreads -= 1
        self.stockFrame.pack_forget()  # hide the frame
        self.thread.exit()  # kill the thread

    def update_stock_label(self, symb):
        self.testLabel.configure(text=str(symb) + str(get_quote(symb)))
        myapp.myContainer.after(10000, self.update_stock_label(symb))

    def stock_symbol(self):
        symb = myapp.get_input_value()
        print(symb)

# The most important part!
def get_quote(symbol):
    try:
        # go to google
        base_url = "http://finance.google.com/finance?q="
        # read the source code
        content = urllib.urlopen(base_url + str(symbol)).read()
        # set regex target
        target = re.search('id="ref_\d*_l".*?>(.*?)<', content)
        # if found, return.
        if target:
            print("found target")
            quote = target.group(1)
            print(quote)
        else:
            quote = "Not Found: "
        return quote
     # handling if no network connection
     except IOError:
         print "no network detected"

root = tk.Tk()
root.geometry("280x200")
myapp = MyApp(root)
root.mainloop()

在这个代码示例中,我们创建了一个名为 MyApp 的类来初始化主框架,并在其中创建用于输入股票符号的 Entry 小部件和一个 Button 小部件,用于添加新的股票。当点击这个按钮时,它会调用 init_stock() 方法来创建并添加新的股票框架。

Stock 类负责创建和管理股票的框架,包括显示股票符号、股票价值和一个删除按钮。kill_thread() 方法用于在点击删除按钮时删除框架并终止相关线程。

希望这个代码示例能帮助你理解如何解决 Tkinter 中的框架嵌套问题。