簡體   English   中英

Tkinter Button事件處理程序延遲Button返回RAISED狀態的時間

[英]Tkinter Button event handler delays the return of the Button to RAISED state

我的GUI包含8個按鈕。 每次單擊Button都會調用事件處理程序,然后將其轉發到另一個函數。 所有這些操作大約需要4秒鍾。 我的問題是,這會導致在處理事件時Button保持在SUNKEN狀態,並且還會導致其他Button無響應。

我想要的是單擊后立即從SUNKEN狀態釋放Button,並在后台繼續事件處理。

如何解決呢? 有沒有辦法在事件處理程序完成其工作之前釋放按鈕?

編輯后:

這是我的代碼:

from tkinter import Tk, Menu, Button
import telnetlib
from time import sleep


On_Color = '#53d411'
Off_Color = '#e78191'


def Power_On_Off (PowerSwitchPort, Action):
    '''
    Control the Power On Off Switch
    '''

    on_off = telnetlib.Telnet("10.0.5.9", 2016)

    if Action == 'On' or Action =='ON' or Action == 'on':
        StringDict = {'1': b"S00D1DDDDDDDE", '2': b"S00DD1DDDDDDE", '3': b"S00DDD1DDDDDE", '4': b"S00DDDD1DDDDE",
                         '5': b"S00DDDDD1DDDE", '6': b"S00DDDDDD1DDE", '7': b"S00DDDDDDD1DE", '8': b"S00DDDDDDDD1E"}
    elif Action == 'Off' or Action =='OFF' or Action == 'off':
        StringDict = {'1': b"S00D0DDDDDDDE", '2': b"S00DD0DDDDDDE", '3': b"S00DDD0DDDDDE", '4':  b"S00DDDD0DDDDE",
                         '5': b"S00DDDDD0DDDE", '6': b"S00DDDDDD0DDE", '7': b"S00DDDDDDD0DE", '8': b"S00DDDDDDDD0E"}

    PowerSwitchPort = str(PowerSwitchPort)

    on_off.read_eager()
    on_off.write(b"S00QLE\n")
    sleep(4)

    on_off.write(StringDict[PowerSwitchPort])

    on_off.close()

def OnButtonClick(button_id):
    if button_id == 1:
        # What to do if power_socket1 was clicked
        Power_On_Off('1', 'Off')
    elif button_id == 2:
        # What to do if power_socket2 was clicked
        Power_On_Off('1', 'On')

def main ():
    root = Tk()

    root.title("Power Supply Control")  #handling the application's Window title
    root.iconbitmap(r'c:\Users\alpha_2.PL\Desktop\Power.ico')   # Handling the application icon

    power_socket1 = Button(root, text = 'Socket 1 Off', command=lambda: OnButtonClick(1), bg = On_Color)
    power_socket1.pack()
    power_socket2 = Button(root, text = 'Socket 1 On', command=lambda: OnButtonClick(2), bg = On_Color)
    power_socket2.pack()  
    '''
    Menu Bar
    '''
    menubar = Menu(root)

    file = Menu(menubar, tearoff = 0)   # tearoff = 0 is required in order to cancel the dashed line in the menu
    file.add_command(label='Settings')
    menubar.add_cascade(label='Options', menu = file)

    root.config(menu=menubar)

    root.mainloop()

if __name__ == '__main__':
    main()

可以看出,我創建了2個按鈕,一個按鈕打開一個開關,另一個按鈕關閉。 開/關動作延遲約4秒鍾。 例如,這只是我的應用程序的一小部分。 在我的原始代碼中,我使用一個類來創建GUI並對其進行控制

瑣事

您的問題源於以下幾行代碼:

on_off.write(b"S00QLE\n")
sleep(4)

尤其是從sleep 這是非常弱的模式,因為您期望在四秒鍾內完成S00QLE 不多也不少! 雖然telnet實際上可以工作這四秒鍾,但GUI仍處於sleep 因此,您的GUI處於無響應狀態 ,無法重繪按鈕的輪廓。

sleep的好選擇是after -因此您可以安排執行:

#    crude and naive fix via closure-function and root.after
def Power_On_Off (PowerSwitchPort, Action):
    ...
    def write_switch_port_and_close():  
        on_off.write(StringDict[PowerSwitchPort])
        on_off.close()

    on_off.write(b"S00QLE\n")
    #   sleep(4)
    root.after(4000, write_switch_port_and_close)
    ...

要解決此問題,您可以自耗循環使用通用。

在我的示例中,我連接到公共telnet服務器, 服務器廣播《星球大戰》第4集 (而不是添加!),以模擬長時間運行的過程。

當然,您在telnet執行( write )兩個命令來表示此行為,我們將進行telnet廣播,直到找到著名的“ Far away”行(第一個長期運行( write ))。 之后,我們更新標簽計數器,然后再次連接到廣播(第二次長期運行( write ))。

試試這個代碼:

import tkinter as tk
import telnetlib


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

        #   store telnet client without opening any host
        self.tn_client = telnetlib.Telnet()

        #   label to count "far aways"
        self.far_aways_encountered = tk.Label(text='Far aways counter: 0')
        self.far_aways_encountered.pack()

        #   start button - just an open telnet command ("o towel.blinkenlights.nl 23")
        self.button_start = tk.Button(self, text='Start Telnet Star Wars', command=self.start_wars)
        self.button_start.pack()

        #   start button - just an close telnet command ("c")
        self.button_stop = tk.Button(self, text='Stop Telnet Star Wars', command=self.close_wars, state='disabled')
        self.button_stop.pack()

        # simple counter
        self.count = 0

    def start_wars(self):
        #   "o towel.blinkenlights.nl 23"
        self.tn_client.open('towel.blinkenlights.nl', 23)

        #   enabling/disabling buttons to prevent mass start/stop
        self.button_start.config(state='disabled')
        self.button_stop.config(state='normal')

        #   scheduling
        self.after(100, self.check_wars_continiously)

    def close_wars(self):
        #   "c"
        self.tn_client.close()

        #   enabling/disabling buttons to prevent mass start/stop
        self.button_start.config(state='normal')
        self.button_stop.config(state='disabled')

    def check_wars_continiously(self):
        try:
            #   we're expect an end of a long-run proccess with a "A long time ago in a galaxy far far away...." line
            response = self.tn_client.expect([b'(?s)A long time ago in a galaxy far,.*?far away'], .01)
        except EOFError:
            #   end of file is found and no text was read
            self.close_wars()
        except ValueError:
            #   telnet session was closed by the user
            pass
        else:
            if response[1]:
                #   The "A long time ago in a galaxy far far away...." line is reached!
                self.count += 1
                self.far_aways_encountered.config(text='Far aways counter: %d' % self.count)

                #   restart via close/open commands (overhead)
                self.close_wars()
                self.start_wars()
            else:
                if response[2] != b'':
                    #   just debug-print line
                    print(response[2])

                #   self-schedule again
                self.after(100, self.check_wars_continiously)


app = App()
app.mainloop()

因此,答案是:特定sleep命令的最簡單替代方法是兩個功能的組合: afterexpect (或者只有在它是控制台應用程序時才expect )!

鏈接

如果您必須在Gui中處理繁重的計算過程,而允許其余的運行,則可以進行多過程處理:

import multiprocess as mp

def OnButtonClick(bid):
    mp.Process(target=Power_On_Off,args=('1',('Off','On')[bid-1])).start()

評論的答案

  1. mp.Process定義一個Process對象。 一旦我.start()它,過程將使用參數args調用target的函數。 例如,您可以在此處查看用法。
  2. 您當然可以分開,並使用不同的on off選項傳遞每個id 我實現了你寫的東西。
  3. 如果我只希望隊列繼續運行並在繼續單擊按鈕時執行一些操作,則無需在此處排隊。
  4. 您的問題是直到操作結束才釋放按鈕 通過將操作委派給其他過程,您可以立即返回,從而允許按鈕升起。

您的問題不是真的“按鈕沒有足夠快地上升”。 而是Gui停留在Power_On_Off完成之前。 如果我在另一個過程中運行該功能,那么Gui不會在任何明顯的時間內被卡住。

如果您希望其他進程結束時主進程中發生某些事情,則可能需要將該Process保留在某種列表中,以便將來進行ping操作。 否則,這很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM