实现GUI实时更新而不冻结的Python代码

67 阅读3分钟

在Python中,如何将代码放入多个线程,同时使GUI能够更新? 正在开发一个带有GUI的子警报系统,遇到使GUI冻结的问题,是因为运行了一个循环来检查聊天内容。 如何将现有的GUI和聊天代码合并到一个系统中,使GUI不会冻结,并且文本字段能够实时更新控制台中的内容?

2. 解决方案

使用多线程和Tkinter来实现GUI实时更新而不冻结。Tkinter是一个Python GUI库,可以创建用户界面。多线程允许代码在多个线程中同时运行,从而避免GUI冻结。

以下是如何使用多线程和Tkinter来实现GUI实时更新而不冻结的代码:

导入资源

import re
import socket
import importlib
from Tkinter import *
from modules.IRCCommands import *

定义全局变量

recentSub = 'N/A'

定义函数

# 关闭应用程序
def Close_Window():
    frmMain.destroy()

# 启动应用程序
def Start():
## 将列表信息显示在控制台中 ##
    terminal.insert('1.0', 'Subscriber Alert ver. 1.5 | Created & Modified by RubbixCube' + "\n")
    terminal.insert("end", 'Important Information:' + "\n")
    terminal.insert("end", 'HOST = ' + HOST + "\n")
    terminal.insert("end", 'PORT = ' + str(PORT) + "\n")
    for c in CHAN:
        terminal.insert("end", 'CHAN = ' + c + "\n")
    terminal.insert("end", '\n' + "\n")
    terminal.insert("end", 'Chat:' + "\n")
    frmMain.update_idletasks

## 定义基本函数 ##
def get_sender(msg):
    result = ""
    for char in msg:
        if (char == "!"):
            break
        if (char != ":"):
            result += char
    return result

def get_message(msg):
    result = ""
    i = 3
    length = len(msg)
    while i < length:
        result += msg[i] + " "
        i += 1
    result = result.lstrip(':')
    return result

## 结束辅助函数 ##

def parse_message(channel, user, msg):
    if len(msg) >= 1:
        msg = msg.split(' ')
        frmMain.update_idletasks

建立套接字连接

con = socket.socket()
con.connect((HOST, PORT))

发送密码和昵称

send_pass(con, PASS)
send_nick(con, NICK)

加入频道

for c in CHAN:
    join_channel(con, c)

定义数据变量

data = ""

定义无限循环

while True:
    try:
        data = data+con.recv(1024)
        data_split = re.split("\r\n", data)
        data = data_split.pop()

        for line in data_split:
            #print(line)
            #line = str.rstrip(line)
            line = str.split(line)

            # 保持与服务器的连接 #
            if (len(line) >= 1):
                if (line[0] == 'PING'):
                    send_pong(con, line[1])

                if (line[1] == 'PRIVMSG'):
                    sender = get_sender(line[0])
                    message = get_message(line)
                    channel = line[2]
                    terminal.insert("end", sender + ": " + message + "\n")
                    frmMain.update_idletasks

                    # 欢迎新订阅者 #
                    if (sender == "rubbixcube"):
                        def excuteCommand(con, channel, user, message, isMod, isSub):
                            msg = str.split(message)
                            if re.match('\w* subscribed for \w* months in a row!', message):
                                recentSub = msg[0]
                                print(recentSub)
                                send_message(con, channel, 'Thanks for your continued contribution %s!' % msg[0])
                            elif re.match('\w* just subscribed!', message):
                                recentSub = msg[0]
                                print(recentSub)
                                send_message(con, channel, str.format('Welcome to the channel %s. Enjoy your stay!' % msg[0]))
                            elif (re.match('\w* viewers resubscribed while you were away!', message)):
                                send_message(con, channel, 'Thanks for subscribing!')
                        excuteCommand(con, channel, sender, message, False, False)

                    parse_message(channel, sender, message)

    except socket.error:
        print("Socket died")

    except socket.timeout:
       print("Socket timeout")

定义GUI

## GUI ##
# 启动程序循环
frmMain = Tk()

app = Frame(frmMain)
app.grid()

# 配置GUI
frmMain.title('Subscriber Alert ver. 2.0')
frmMain.geometry('455x357')
frmMain.resizable(0,0)

# 按钮
start = Button(app, text='     Start     ')
start['command'] = Start
start.grid(row=0, column=0, pady=5)

close = Button(app, text='      Exit       ')
close['command'] = Close_Window
close.grid(row=0, column=2, pady=5)


# 标签
sub_lbl = Label(app, text='Most Recent Subscriber:')
sub_lbl.grid(row=1, column=0, columnspan=2, pady=10, padx=5)

sub_lbl2 = Label(app, text=recentSub)
sub_lbl2.grid(row=1, column=1, columnspan=3, pady=10, padx=5)


# 控制台
terminal = Text(app, width=56, height=17, wrap=WORD)
terminal.grid(row=3, column=0, columnspan=3)


# 结束程序循环
frmMain.mainloop()

代码在建立套接字连接并发送密码和昵称后,即开始一个无限循环,以便持续接收服务器发来的消息。

然后,GUI部分使用了Tkinter库,其中包括按钮、标签和文本框等控件,以便用户可以与程序进行交互。

最后,程序的主循环(即frmMain.mainloop())使程序继续运行,直到用户关闭窗口。在主循环中,程序会不断更新GUI,以便用户可以实时看到服务器发来的消息。