有没有办法在窗口内实现倒计时,而不会冻结窗口?


import PySimpleGUI as sg
import time
q1 = [
[sg.Text("Question 1!"), sg.Button("x", visible=False), sg.Text("Time:"), sg.Text(" ", size=(10,1), key="t")],
[sg.Text("This is where question 1 will be?")],
[sg.Button("Option 1", key="1",button_color=("#ffffff","#151515")), sg.Button("Option 2", key="2",button_color=("#00ff00", "#151515"))],
[sg.Button("Option 3", key="3",button_color=("#00ffff", "#151515")), sg.Button("Option 4", key="4",button_color=("#ff00ff", "#151515"))],
[sg.Button("Submit"), sg.Button("Next Question"), sg.Button("Skip")]
]
window = sg.Window("Question 1",q1)
while True:
event, values = window.Read()
if event is None:
break
seconds = 20
for i in range(seconds):
seconds = seconds - i
window.FindElement("t").Update(seconds)
time.sleep(1)

我不确定我是否以正确的方式接近这一点,但我想让它在右上角出现一个 20 秒的计时器。但是,使用上面的代码,没有计时器启动,当您按下按钮时,它会冻结程序 20 秒。

如果这篇文章的许多注释中讨论了这个问题,我们深表歉意。 如果是这样,请随时忽略并抱歉占用空间。

  1. 永远不要在事件循环中放置"睡眠"。 在窗口上使用超时。请改为阅读
  2. 调用 window.refresh(( 以使更改显示在窗口中: https://pysimplegui.readthedocs.io/en/latest/call%20reference/#window 它指出:

当您希望某些内容立即出现在窗口中时(一旦调用此函数(,请使用此调用。如果没有此调用,在下一次读取调用之前,用户将无法看到对窗口所做的更改

  1. 有一个计时器演示 - 此演示和定期刷新窗口的概念在整个文档、演示程序和 Trinket 中都有引用。 文档直接谈到,如果您不定期调用读取或刷新,窗口似乎会冻结。 计时器在主文档中讨论:

https://pysimplegui.readthedocs.io/en/latest/#persistent-window-example-running-timer-that-updates

您有一个选择是节省开始时间,在主循环的每次迭代中,您计算时间差并更新计时器。
当差异达到 20 秒时,您停止并继续下一个问题,否则您可以保护当前的剩余时间或任何您想用它做的事情并重新开始。

下面是一个基本示例:

from time import time
TIME_PER_QUESTION = 20.0
# Loop questions
for i in range(3):
start = time()
current = time()
time_left = TIME_PER_QUESTION
while time_left > 0:
# Update your interface
# doStuff()... update()...
# Spam a bit for testing
print("Leftover time {:.0f}".format(time_left))
# Update current time and our timer
current = time()
time_left = TIME_PER_QUESTION - (current - start)

当然,您可以添加更多方法来打破循环并存储剩余的时间等,然后进行下一个问题。

还有更多方法可以实现这一点,但这是您可以做到的一件事。(这种技术主要用于游戏中的主循环和类似的东西(
如评论中所述,如果您愿意,您可能也可以使用线程计时器函数来做到这一点。

编辑
至于更新 gui 中的文本,我不确定您这样做的方式是否有效,但如果您说它从未更新过,请尝试以下方法:

window['t'].update("{:.0f}".format(time_left))

我需要构建类似的东西,我使用了上面PSG示例计时器中的@Mike并将其削减到基础知识。

def binary_timed(time_allowed=15,default_value=False,query='no text inputed'):
"""
This is a GUI that will present a user with a 'query' which is a string
it gives them a number of seconds to make a binary choice and returns
a default value when the time runs out
"""
#layout options for the GUI
layout = [
[sg.Text(query)],#
[sg.Text('time remaining: %s'%time_allowed,key='timer')],
[
sg.Button('Yes'),
sg.Button('No'),
]
]

#present the user with the gui
window = sg.Window('binary_timed', layout)
time_passed = 0
while (True):
event, values = window.read(timeout=1000)#1000 ms -> 1s wait between read
time_passed += 1
time_remaining = time_allowed-time_passed
window['timer'].update("time remaining: %s"%time_remaining)
if event != sg.TIMEOUT_KEY: #a button has been pressed 
window.close()
break
if time_remaining == 0:
window.close()
break

#evauluate our results
if event == sg.WIN_CLOSED: return default_value #pressed the exit button
if event == sg.TIMEOUT_KEY: return default_value
if event == 'Yes': return True
if event == 'No': return False

以我的为基础,我将超时概念应用于您的。希望这有所帮助并使事情变得易于理解。

import PySimpleGUI as sg
q1 = [
[sg.Text("Question 1!"), sg.Button("x", visible=False), sg.Text("Time:"), sg.Text(" ", size=(10,1), key="t")],
[sg.Text("This is where question 1 will be?")],
[sg.Button("Option 1", key="1",button_color=("#ffffff","#151515")), sg.Button("Option 2", key="2",button_color=("#00ff00", "#151515"))],
[sg.Button("Option 3", key="3",button_color=("#00ffff", "#151515")), sg.Button("Option 4", key="4",button_color=("#ff00ff", "#151515"))],
[sg.Button("Submit"), sg.Button("Next Question"), sg.Button("Skip")]
]

window = sg.Window("Question 1",q1)
time_elapsed = 0
time_remaining = 20
while True:
event, values = window.read(1000)
time_remaining -= time_elapsed
window['t'].update(time_remaining)
time_elapsed += 1
if event != sg.TIMEOUT_KEY: #a button has been pressed 
window.close()
break
if time_remaining == 0:
window.close()
break

最新更新