Skip to content

Commit 35e34a1

Browse files
Add GUI example
1 parent d770143 commit 35e34a1

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

examples/gui/gui.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2019 Sam McCormack
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
"""
24+
Demonstrates the usage of Scheduler in a simple PyQt program, which shows progress and output.
25+
26+
Dependencies are in `requirements.txt`. To install them, run `pip install -r requirements.txt --user`.
27+
"""
28+
29+
import asyncio
30+
import random
31+
import sys
32+
import time
33+
from multiprocessing import Queue, Process
34+
from typing import List, Tuple
35+
36+
import asyncqt
37+
from PyQt5 import QtGui
38+
from PyQt5.QtWidgets import QApplication, QLabel, QProgressBar, QVBoxLayout, QWidget, QPushButton
39+
40+
from scheduler.Scheduler import Scheduler
41+
42+
43+
def long_calculation(queue: Queue, sleep_time: int) -> None:
44+
"""
45+
Will be executed in another process. Simulates a long calculation,
46+
and puts the 'result' in the queue.
47+
"""
48+
time.sleep(sleep_time)
49+
queue.put((sleep_time,))
50+
51+
52+
class Window(QWidget):
53+
"""Basic Window class."""
54+
55+
def __init__(self):
56+
super().__init__()
57+
58+
layout = QVBoxLayout()
59+
self.setLayout(layout)
60+
61+
self.label = QLabel("Output from processes will be shown here", self)
62+
layout.addWidget(self.label)
63+
64+
self.progress = QProgressBar(self)
65+
layout.addWidget(self.progress)
66+
67+
self.button = QPushButton("Start", self)
68+
self.button.clicked.connect(self.on_click)
69+
layout.addWidget(self.button)
70+
71+
self.scheduler: Scheduler = None
72+
73+
def on_click(self):
74+
"""Called when the button is clicked."""
75+
if self.scheduler is None or not self.scheduler.is_running():
76+
# "Start" was clicked. Start the coroutine which runs the scheduler.
77+
asyncio.ensure_future(self.do_calculations())
78+
self.button.setText("Cancel")
79+
else:
80+
# "Cancel" was clicked. Terminate the scheduler.
81+
self.scheduler.terminate()
82+
self.button.setText("Start")
83+
self.progress.setValue(0)
84+
85+
async def do_calculations(self):
86+
"""Does the calculations using a scheduler, and shows the output in the label."""
87+
self.scheduler = Scheduler(progress_callback=self.on_progress)
88+
89+
num_processes = 16
90+
for i in range(num_processes):
91+
# Generate random input for function.
92+
sleep = random.randint(1, 8)
93+
94+
queue = Queue()
95+
process = Process(target=long_calculation, args=(queue, sleep,))
96+
self.scheduler.add(process, queue)
97+
98+
# Run all processes and `await` the results: an ordered list containing one tuple from each process.
99+
output: List[Tuple] = await self.scheduler.run()
100+
101+
# If the scheduler was terminated before completion, we don't want the results.
102+
if not self.scheduler.terminated:
103+
text = ", ".join([str(i[0]) for i in output])
104+
self.label.setText(f"Output: {text}")
105+
self.button.setText("Start")
106+
107+
def on_progress(self, done: int, total: int) -> None:
108+
"""Updates the progress bar when scheduler finishes a task."""
109+
if done == 0:
110+
self.progress.setMaximum(total)
111+
self.progress.setValue(done)
112+
113+
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
114+
"""Terminates the scheduler when the window exits."""
115+
if self.scheduler:
116+
self.scheduler.terminate()
117+
118+
119+
if __name__ == "__main__":
120+
app = QApplication(sys.argv)
121+
122+
# Important: set the event loop using `asyncqt`.
123+
loop = asyncqt.QEventLoop(app)
124+
asyncio.set_event_loop(loop)
125+
126+
window = Window()
127+
window.show()
128+
129+
app.exec()

examples/gui/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PyQt5
2+
asyncqt

0 commit comments

Comments
 (0)