设置未在 GUI 中输入的变量和“无响应”问题

问题描述

我想编写一个程序来移动鼠标光标并按下 shift 键,这样计算机就不会被锁定。问题是我希望如果用户没有设置两个变量,它们应该自动为 0 和 1,但似乎我遗漏了一些东西。我无法理解的另一件事是,当我启动程序时,如果我不点击它,一切正常,但如果我点击程序的窗口,它会显示“无响应”,但程序正在运行。我不明白我在这里做错了什么。下面是我的代码

#!/usr/bin/python
import pyautogui
import time
import sys
import os
import PySimpleGUI as sg
from datetime import datetime

def main():
    sg.theme('DarkAmber')
    layout = [  [sg.Text('Please enter the time intervall between the movements:',size = (45,1)),sg.Input(key='-IT-',enable_events=True)],[sg.Text('Please enter how long should the script run:',sg.Input(key='-DURATION-',[sg.Button('Start'),sg.Button('Stop')],[sg.Output(size=(60,15))]   ]
    window = sg.Window('Press to start to move!',layout,size=(450,250),element_justification='right')
    while True:
        event,values = window.read()
        if event in (sg.WIN_CLOSED,'Stop'):         
            break
        
        if event == '-IT-' and values['-IT-'] and values['-IT-'][-1] not in ('0123456789'):
            window['-IT-'].update(values['-IT-'][:-1])
        if event == '-DURATION-' and values['-DURATION-'] and values['-DURATION-'][-1] not in ('0123456789'):
            window['-DURATION-'].update(values['-DURATION-'][:-1])
        
        elif event == 'Start':
            if values['-IT-'] == "" and values['-DURATION-'] == "":
                window['-IT-'].update(1)
                window['-DURATION-'].update(0)
            elif values['-IT-'] != "" and values['-DURATION-'] == "":
                window['-DURATION-'].update(0)
            elif values['-IT-'] == "" and values['-DURATION-'] != "":
                window['-IT-'].update(1)    
            move(numMin=int('0'+values['-IT-']),numDuration=int('0'+values['-DURATION-']))
    window.close()
main()

编辑: 我刚刚删除代码中不必要的部分。 所以如果我没有误解,我需要使用一个线程,第二个问题对我来说不清楚我可以在不添加零 (0) 的情况下对空字符串进行类型转换吗?因为当我尝试只进行类型转换时它不起作用,如果我添加零,代码的行为就会改变。

解决方法

需要“长时间”的操作

如果您是 Windows 用户,您会看到 Windows 的标题栏中显示“无响应”,随后 Windows 弹出窗口显示“您的程序已停止响应”。好吧,如果您愿意,您也可以显示该消息和弹出窗口!您需要做的就是在您的事件循环中执行一个“太长”(即几秒钟)的操作。

您有多种选择来处理这个问题。如果您的操作可以分解成更小的部分,那么您可以偶尔调用 Window.Refresh() 以避免出现此消息。例如,如果您正在运行一个循环,请将该调用与您的其他工作一起放入。这将使 GUI 保持愉快,Window 不会抱怨。

另一方面,如果您的操作不在您的控制之下,或者您无法添加 Refresh 调用,那么您可以使用的下一个选项是将您的长操作移动到一个线程中。

“老路”

有几个演示程序可供您查看如何执行此操作。你基本上把你的工作放到一个线程中。当线程完成时,它通过队列发送消息来告诉 GUI。事件循环将在一个计时器设置为一个值的情况下运行,该值表示您希望 GUI 对完成的工作的“响应”程度。

“新方式” - Window.write_event_value

截至 2020 年 7 月,目前仅在 tkinter 端口中可用的这项新功能令人兴奋,并且代表了 PySimpleGUI 中处理多线程的未来方式(或希望如此)。

以前,使用队列来让您的事件循环轮询来自线程的传入消息。

现在,线程可以直接将事件注入到 Window 中,以便它显示在 window.read() 调用中。这允许您的事件循环“挂起”,等待正常的窗口事件以及线程生成的事件。

您可以在此演示中看到此新功能的实际应用:Demo_Multithreaded_Write_Event_Value.py

这是您检查和教育的计划。不再轮询线程事件真是太好了。

import threading
import time
import PySimpleGUI as sg

"""
    Threaded Demo - Uses Window.write_event_value communications

    Requires PySimpleGUI.py version 4.25.0 and later

    This is a really important demo  to understand if you're going to be using multithreading in PySimpleGUI.

    Older mechanisms for multi-threading in PySimpleGUI relied on polling of a queue. The management of a communications
    queue is now performed internally to PySimpleGUI.

    The importance of using the new window.write_event_value call cannot be emphasized enough.  It will hav a HUGE impact,in
    a positive way,on your code to move to this mechanism as your code will simply "pend" waiting for an event rather than polling.

    Copyright 2020 PySimpleGUI.org
"""

THREAD_EVENT = '-THREAD-'

cp = sg.cprint

def the_thread(window):
    """
    The thread that communicates with the application through the window's events.

    Once a second wakes and sends a new event and associated value to the window
    """
    i = 0
    while True:
        time.sleep(1)
        window.write_event_value('-THREAD-',(threading.current_thread().name,i))      # Data sent is a tuple of thread name and counter
        cp('This is cheating from the thread',c='white on green')
        i += 1

def main():
    """
    The demo will display in the multiline info about the event and values dictionary as it is being
    returned from window.read()
    Every time "Start" is clicked a new thread is started
    Try clicking "Dummy" to see that the window is active while the thread stuff is happening in the background
    """

    layout = [  [sg.Text('Output Area - cprint\'s route to here',font='Any 15')],[sg.Multiline(size=(65,20),key='-ML-',autoscroll=True,reroute_stdout=True,write_only=True,reroute_cprint=True)],[sg.T('Input so you can see data in your dictionary')],[sg.Input(key='-IN-',size=(30,1))],[sg.B('Start A Thread'),sg.B('Dummy'),sg.Button('Exit')]  ]

    window = sg.Window('Window Title',layout,finalize=True)

    while True:             # Event Loop
        event,values = window.read()
        cp(event,values)
        if event == sg.WIN_CLOSED or event == 'Exit':
            break
        if event.startswith('Start'):
            threading.Thread(target=the_thread,args=(window,),daemon=True).start()
        if event == THREAD_EVENT:
            cp(f'Data from the thread ',colors='white on purple',end='')
            cp(f'{values[THREAD_EVENT]}',colors='white on red')
    window.close()

if __name__ == '__main__':
    main()

多线程程序

在讨论多线程的同时,还准备了另一个演示,展示如何在程序中运行多个线程,所有线程都与事件循环进行通信,以便在 GUI 窗口中显示某些内容。回想一下,对于 PySimpleGUI(至少是 tkinter 端口),您不能在主程序线程以外的线程中调用 PySimpleGUI。

这些线程程序的关键是从线程到事件循环的通信。为这些演示选择的机制使用 Python 内置队列模块。事件循环轮询这些队列以查看是否已从要显示的线程之一发送过来。

您会发现显示多个线程与单个 GUI 通信的演示被称为:

Demo_Multithreaded_Queued.py

对于普通的 PySimpleGUI(基于 tkinter)再次发出警告 - 您的 GUI 绝不能作为主程序线程以外的任何东西运行,并且没有线程可以直接调用 PySimpleGUI 调用。