Skip to content

Commit 843b732

Browse files
Merge feature/multithreading (#58)
🎉 Runs GUI and Kernel on separate threads CAUTION application crashes if multiple executions are launched at the same time due to the lack of execution flow
2 parents 62a23a0 + 4eded9e commit 843b732

3 files changed

Lines changed: 65 additions & 19 deletions

File tree

opencodeblocks/graphics/blocks/codeblock.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33

44
""" Module for the base OCB Code Block. """
55

6-
7-
from PyQt5.QtCore import QCoreApplication, QByteArray
6+
from PyQt5.QtCore import QByteArray
87
from PyQt5.QtGui import QPixmap
98
from PyQt5.QtWidgets import QPushButton, QTextEdit
109

1110
from ansi2html import Ansi2HTMLConverter
1211

1312
from opencodeblocks.graphics.blocks.block import OCBBlock
1413
from opencodeblocks.graphics.pyeditor import PythonEditor
14+
from opencodeblocks.graphics.worker import Worker
1515

1616
conv = Ansi2HTMLConverter()
1717

@@ -144,19 +144,17 @@ def init_run_button(self):
144144
def run_code(self):
145145
"""Run the code in the block"""
146146
code = self.source_editor.text()
147-
kernel = self.source_editor.kernel
148147
self.source = code
149-
# Execute the code
150-
kernel.client.execute(code)
151-
done = False
152-
# While the kernel sends messages
153-
while done is False:
154-
# Keep the GUI alive
155-
QCoreApplication.processEvents()
156-
# Save kernel message and display it
157-
output, output_type, done = kernel.update_output()
158-
if done is False:
159-
if output_type == 'text':
160-
self.stdout = output
161-
elif output_type == 'image':
162-
self.image = output
148+
# Create a worker to handle execution
149+
worker = Worker(self.source_editor.kernel, self.source)
150+
worker.signals.stdout.connect(self.handle_stdout)
151+
worker.signals.image.connect(self.handle_image)
152+
self.source_editor.threadpool.start(worker)
153+
154+
def handle_stdout(self, stdout):
155+
""" Handle the stdout signal """
156+
self.stdout = stdout
157+
158+
def handle_image(self, image):
159+
""" Handle the image signal """
160+
self.image = image

opencodeblocks/graphics/pyeditor.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
""" Module for OCB in block python editor. """
55

66
from typing import TYPE_CHECKING, List
7-
from PyQt5.QtCore import Qt
7+
from PyQt5.QtCore import QThreadPool, Qt
88
from PyQt5.QtGui import QFocusEvent, QFont, QFontMetrics, QColor
99
from PyQt5.Qsci import QsciScintilla, QsciLexerPython
1010
from opencodeblocks.graphics.theme_manager import theme_manager
@@ -13,7 +13,7 @@
1313
from opencodeblocks.graphics.kernel import Kernel
1414

1515
kernel = Kernel()
16-
16+
threadpool = QThreadPool()
1717

1818
if TYPE_CHECKING:
1919
from opencodeblocks.graphics.view import OCBView
@@ -33,6 +33,7 @@ def __init__(self, block: OCBBlock):
3333
super().__init__(None)
3434
self.block = block
3535
self.kernel = kernel
36+
self.threadpool = threadpool
3637
self.setText(self.block.source)
3738

3839
self.update_theme()

opencodeblocks/graphics/worker.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# OpenCodeBlock an open-source tool for modular visual programing in python
2+
# Copyright (C) 2021 Mathïs FEDERICO <https://www.gnu.org/licenses/>
3+
4+
""" Module to create and manage multi-threading workers """
5+
6+
import asyncio
7+
from PyQt5.QtCore import QObject, pyqtSignal, QRunnable
8+
9+
10+
class WorkerSignals(QObject):
11+
""" Defines the signals available from a running worker thread. """
12+
stdout = pyqtSignal(str)
13+
image = pyqtSignal(str)
14+
15+
16+
class Worker(QRunnable):
17+
""" Worker thread """
18+
19+
def __init__(self, kernel, code):
20+
""" Initialize the worker object. """
21+
super(Worker, self).__init__()
22+
23+
self.kernel = kernel
24+
self.code = code
25+
self.signals = WorkerSignals()
26+
27+
async def run_code(self):
28+
""" Run the code in the block """
29+
# Execute the code
30+
self.kernel.client.execute(self.code)
31+
done = False
32+
# While the kernel sends messages
33+
while done is False:
34+
# Save kernel message and send it to the GUI
35+
output, output_type, done = self.kernel.update_output()
36+
if done is False:
37+
if output_type == 'text':
38+
self.signals.stdout.emit(output)
39+
elif output_type == 'image':
40+
self.signals.image.emit(output)
41+
42+
def run(self):
43+
""" Execute the run_code method asynchronously. """
44+
loop = asyncio.new_event_loop()
45+
asyncio.set_event_loop(loop)
46+
loop.run_until_complete(self.run_code())
47+
loop.close()

0 commit comments

Comments
 (0)