问题描述
我想编写一个程序来移动鼠标光标并按下 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 调用。