来自星星的小明 发表于 2023-5-15 19:13:51

tkinter 求助

为了方便非技术人员,连接高速公路超速提示的电子大屏,自己写的一个开关
由于连接四块屏幕的地址会出现偶尔的异常,在连接的过程中,开关的界面会卡死,于是设计当点击按钮时关掉初始的开关界面,打开一个假的进度条界面,当连接过程完毕并且报错时,,关掉进度条界面,弹出最终通知界面,正常建立连接时,则只需要关闭进度条界面即可。。但是,我的进度条界面关闭不掉,一直在运行,请大佬帮忙看看,怎么改一改,或者优化一下
import tkinter as tk
from tkinter import messagebox
from tkinter.ttk import Progressbar
import socket


class Application(tk.Tk):
    def __init__(self):
      super().__init__()

      self.ip = ["61.18.238.7", "61.18.238.8", "61.18.238.9", "61.18.238.10"]
      self.port = 1234
      self.out = ["setr=1", "setr=0"]

      self.title('开关控制')
      self.geometry('600x300')
      self.resizable(0,0)
      tk.Button(self, text='打开',
                font=('楷体', 30),
                anchor='center', command=self.open,
                height=2, width=8, bd=6).place(relx=0.15, rely=0.3)
      tk.Button(self, text='关闭',
                font=('楷体', 30),
                anchor='center', command=self.close,
                height=2, width=8, bd=6).place(relx=0.55, rely=0.3)
      self.mainloop()

    def run_gui(self):
      self.run = tk.Tk()
      self.run.title('正在运行')
      self.run.geometry("400x200")
      self.run.resizable(0, 0)

      jdt = Progressbar(self.run, length=200, mode='indeterminate')
      jdt.place(y=50, relx=0.5, anchor='center')
      jdt.start()

      label = tk.Label(self.run, text=' 正在连接,请稍等...',
                         font=('标楷体', 15))
      label.place(relx=0.5, rely=0.5, anchor='center')
      self.run.mainloop()

    def end_gui(self, res_str):
      win = tk.Tk()
      win.geometry('0x0+999999+0')
      messagebox.showerror('开关控制', ' {}失败,请联系工作人员!'.format(res_str))
      win.destroy()

    def connect(self, ip, port, out):
      for i in ip:
            tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            tcp_client.connect((i, port))
            outdata = out
            tcp_client.send(outdata.encode())
            recdata = tcp_client.recv(1024)
            print("server:", recdata.decode())
            tcp_client.close()

    def open(self):
      self.destroy()
      self.run_gui()
      try:
            self.connect(self.ip, self.port, self.out)
      except:
            self.end_gui("打开")
      finally:
            self.run.destroy()


    def close(self):
      self.destroy()
      self.run_gui()
      try:
            self.connect(self.ip, self.port, self.out)
      except:
            self.end_gui("关闭")
      finally:
            self.run.destroy()


if __name__ == "__main__":
    app = Application()

isdkz 发表于 2023-5-15 19:13:52

你的代码中有一个主要的问题:你在run_gui和end_gui函数中都使用了mainloop() ,这在 Tkinter 中可能会导致问题,因为它启动了一个新的事件循环。在同一个应用程序中,应该只有一个 mainloop()。

你的代码中的每个窗口(root 和 Toplevel)应该只有一个主窗口调用 mainloop()。其他的窗口应该是这个主窗口的子窗口。当主窗口关闭时,所有的子窗口都会关闭。

为了解决你的问题,我将你的run_gui和end_gui函数中的窗口更改为Toplevel 。这样,你就可以在主窗口中调用 mainloop(),并在需要时打开和关闭子窗口。

在下面的代码中,我也将connect方法的try/except语句更改为try/except/else 。 else语句将在没有异常的情况下执行,所以在connect方法成功执行后,进度条窗口将被销毁。

这是你的代码的修改版:

import tkinter as tk
from tkinter import messagebox
from tkinter.ttk import Progressbar
import socket


class Application(tk.Tk):
    def __init__(self):
      super().__init__()

      self.ip = ["61.18.238.7", "61.18.238.8", "61.18.238.9", "61.18.238.10"]
      self.port = 1234
      self.out = ["setr=1", "setr=0"]

      self.title('开关控制')
      self.geometry('600x300')
      self.resizable(0,0)
      tk.Button(self, text='打开',
                font=('楷体', 30),
                anchor='center', command=self.open,
                height=2, width=8, bd=6).place(relx=0.15, rely=0.3)
      tk.Button(self, text='关闭',
                font=('楷体', 30),
                anchor='center', command=self.close,
                height=2, width=8, bd=6).place(relx=0.55, rely=0.3)

    def run_gui(self):
      self.run = tk.Toplevel(self)
      self.run.title('正在运行')
      self.run.geometry("400x200")
      self.run.resizable(0, 0)

      jdt = Progressbar(self.run, length=200, mode='indeterminate')
      jdt.place(y=50, relx=0.5, anchor='center')
      jdt.start()

      label = tk.Label(self.run, text=' 正在连接,请稍等...',
                         font=('标楷体', 15))
      label.place(relx=0.5, rely=0.5, anchor='center')

    def end_gui(self, res_str):
      win = tk.Toplevel(self)
      win.geometry('0x0+999999+0')
      messagebox.showerror('开关控制', ' {}失败,请联系工作人员!'.format(res_str))
      win.destroy()

    def connect(self, ip, port, out):
      for i in ip:
            tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            tcp_client.connect((i, port))
            outdata = out
            tcp_client.send(outdata.encode())
            recdata = tcp_client.recv(1024)
            print("server:", recdata.decode())
            tcp_client.close()

    def open(self):
      self.withdraw()
      self.run_gui()
      try:
            self.connect(self.ip, self.port, self.out)
      except:
            self.end_gui("打开")
      else:
            self.run.destroy()
            self.deiconify()


    def close(self):
      self.withdraw()
      self.run_gui()
      try:
            self.connect(self.ip, self.port, self.out)
      except:
            self.end_gui("关闭")
      else:
            self.run.destroy()
            self.deiconify()


if __name__ == "__main__":
    app = Application()
    app.mainloop()

在这个修改后的代码中,我使用了withdraw()方法隐藏主窗口,然后显示进度条窗口。一旦连接建立(或连接尝试失败),我销毁进度条窗口并使用deiconify()方法显示主窗口。

请注意,我将mainloop()方法移动到了程序的最后,这样就只有一个mainloop()在程序中运行。

这样的修改应该可以解决你的问题。如果你还有其他问题,欢迎继续提问。

来自星星的小明 发表于 2023-5-15 19:58:48

本帖最后由 来自星星的小明 于 2023-5-15 19:59 编辑

isdkz 发表于 2023-5-15 19:24
你的代码中有一个主要的问题:你在run_gui和end_gui函数中都使用了mainloop() ,这在 Tkinter 中 ...


大佬,不行啊,点完按钮,不弹出进度条窗口,,知道连接建立完毕,进度条窗口和最后的通知窗口一起弹出了

sfqxx 发表于 2023-5-15 20:03:14

在 `run_gui()` 方法中,您创建了一个新的 Tk() 对象,而不是使用主应用程序的对象。

这就是为什么进度条界面关闭不掉的原因:应用程序的主循环与进度条界面的循环相互独立,无法退出两个窗口。

要解决该问题,您可以使用 `Toplevel()` 而不是新建一种 Tk() 对象。 Toplevel() 是一个子窗口,它依赖于完整的主窗口,当你关闭子窗口时,主窗口也能随之关闭,示例如下:

def run_gui(self):
    self.run = tk.Toplevel()
    self.run.title('正在运行')
    self.run.geometry("400x200")
    self.run.resizable(0, 0)

    jdt = Progressbar(self.run, length=200, mode='indeterminate')
    jdt.place(y=50, relx=0.5, anchor='center')
    jdt.start()

    label = tk.Label(self.run, text=' 正在连接,请稍等...',
                     font=('标楷体', 15))
    label.place(relx=0.5, rely=0.5, anchor='center')
除此之外,您可能需要处理进度条界面的异常退出情况,例如添加关闭按钮来正常关闭进度条界面。

来自星星的小明 发表于 2023-5-15 20:37:13

sfqxx 发表于 2023-5-15 20:03
在 `run_gui()` 方法中,您创建了一个新的 Tk() 对象,而不是使用主应用程序的对象。

这就是为什么进度 ...

我按您说的改了之后,点完按钮,进度条窗口根本不弹出了,,直到建立连接的方法运行完之后才和最终的窗口一起弹出
页: [1]
查看完整版本: tkinter 求助