鱼C论坛

 找回密码
 立即注册
查看: 1112|回复: 4

[已解决]tkinter 求助

[复制链接]
发表于 2023-5-15 19:13:51 | 显示全部楼层 |阅读模式
10鱼币
为了方便非技术人员,连接高速公路超速提示的电子大屏,自己写的一个开关
由于连接四块屏幕的地址会出现偶尔的异常,在连接的过程中,开关的界面会卡死,于是设计当点击按钮时关掉初始的开关界面,打开一个假的进度条界面,当连接过程完毕并且报错时,,关掉进度条界面,弹出最终通知界面,正常建立连接时,则只需要关闭进度条界面即可。。但是,我的进度条界面关闭不掉,一直在运行,请大佬帮忙看看,怎么改一改,或者优化一下
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[0])
        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[1])
        except:
            self.end_gui("关闭")
        finally:
            self.run.destroy()


if __name__ == "__main__":
    app = Application()
最佳答案
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[0])
        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[1])
        except:
            self.end_gui("关闭")
        else:
            self.run.destroy()
            self.deiconify()


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

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

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

最佳答案

查看完整内容

你的代码中有一个主要的问题:你在 run_gui 和 end_gui 函数中都使用了 mainloop() ,这在 Tkinter 中可能会导致问题,因为它启动了一个新的事件循环。在同一个应用程序中,应该只有一个 mainloop()。 你的代码中的每个窗口(root 和 Toplevel)应该只有一个主窗口调用 mainloop()。其他的窗口应该是这个主窗口的子窗口。当主窗口关闭时,所有的子窗口都会关闭。 为了解决你的问题,我将你的 run_gui 和 end_gui 函 ...
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 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[0])
        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[1])
        except:
            self.end_gui("关闭")
        else:
            self.run.destroy()
            self.deiconify()


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

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

这样的修改应该可以解决你的问题。如果你还有其他问题,欢迎继续提问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-5-15 19:58:48 | 显示全部楼层
本帖最后由 来自星星的小明 于 2023-5-15 19:59 编辑
isdkz 发表于 2023-5-15 19:24
你的代码中有一个主要的问题:你在  run_gui  和  end_gui  函数中都使用了  mainloop() ,这在 Tkinter 中 ...


Snipaste_2023-05-15_19-55-44.png
大佬,不行啊,点完按钮,不弹出进度条窗口,,知道连接建立完毕,进度条窗口和最后的通知窗口一起弹出了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 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')
除此之外,您可能需要处理进度条界面的异常退出情况,例如添加关闭按钮来正常关闭进度条界面。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

这就是为什么进度 ...

我按您说的改了之后,点完按钮,进度条窗口根本不弹出了,,直到建立连接的方法运行完之后才和最终的窗口一起弹出
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-28 01:50

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表