From 010871b82097e0fcd5f0a9175f3df5144a47b26e Mon Sep 17 00:00:00 2001 From: Sven Augustin Date: Sun, 18 Dec 2022 14:45:57 +0100 Subject: [PATCH] added RPCServerThread; attach RPCServerThread to MainWindow, exposing append method to append to DictList and to MDISubPlot; data from DictList needs to be transposed now; apply pyqtgraph theming --- mainwin.py | 35 +++++++++++++++++++++++--- mdi.py | 7 ++++-- rpcserverthread.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 rpcserverthread.py diff --git a/mainwin.py b/mainwin.py index fde2f56..b81fa17 100644 --- a/mainwin.py +++ b/mainwin.py @@ -1,11 +1,12 @@ import numpy as np -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtWidgets import QMainWindow, QSplitter import assets from dictlist import DictList from mdi import MDIArea, MDISubPlot +from rpcserverthread import RPCServerThread X = np.arange(100) / 10 @@ -13,6 +14,8 @@ X = np.arange(100) / 10 class MainWindow(QMainWindow): + sig_make_new_plot = pyqtSignal(str, list) + def __init__(self, *args, title="grum", **kwargs): super().__init__(*args, **kwargs) self.setWindowTitle(title) @@ -24,6 +27,7 @@ class MainWindow(QMainWindow): "exp": [X, np.exp(X)], "log": [X, np.log(X+1)] } + data = {k: zip(*v) for k, v in data.items()} # due to the appending of xy pairs, data needs to be transposed self.lst = lst = DictList(data) lst.setAlternatingRowColors(True) lst.doubleClicked.connect(self.on_select_list_item) @@ -39,18 +43,41 @@ class MainWindow(QMainWindow): self.setCentralWidget(splitter) + rst = RPCServerThread("localhost", 8000) + rst.start() + rst.server.register_function(self.append) + + self.sig_make_new_plot.connect(self.make_new_plot) + + + def append(self, name, xy): + lst = self.lst + show_it = (name not in lst.data) + + lst.append(name, xy) + for sub in self.mdi.subWindowList(): + if sub.windowTitle() == name: + data = lst.data[name] + data = list(zip(*data)) + sub.plot.setData(*data) + + if show_it: + data = lst.data[name] + self.sig_make_new_plot.emit(name, data) + def on_select_list_item(self, index): key, value = self.lst.get(index) - #TODO: just a test for dynamic adding - self.lst.add("x", [X, X]) - for sub in self.mdi.subWindowList(): if sub.windowTitle() == key: self.mdi.setActiveSubWindow(sub) return + self.make_new_plot(key, value) + + + def make_new_plot(self, key, value): sub = MDISubPlot(key, value) self.mdi.addSubWindow(sub) sub.show() diff --git a/mdi.py b/mdi.py index 38d18a9..202d41b 100644 --- a/mdi.py +++ b/mdi.py @@ -4,7 +4,7 @@ from PyQt5.QtGui import QPainter from pyqtgraph import PlotWidget import assets -from theme import MDI_BKG +from theme import MDI_BKG, pg_plot_style class MDIArea(QMdiArea): @@ -71,8 +71,11 @@ class MDISubPlot(QMdiSubWindow): # without this, the SubWindow is not removed from the subWindowList self.setAttribute(Qt.WA_DeleteOnClose) + data = zip(*data) + style = pg_plot_style() + plt = PlotWidget() - plt.plot(*data) + self.plot = plt.plot(*data, **style) self.setWidget(plt) diff --git a/rpcserverthread.py b/rpcserverthread.py new file mode 100644 index 0000000..ff3e325 --- /dev/null +++ b/rpcserverthread.py @@ -0,0 +1,61 @@ +import atexit +import xmlrpc.server as xrs +from threading import Thread + + +class RPCServerThread(Thread): + + def __init__(self, host, port, *args, **kwargs): + super().__init__(daemon=True) # atexit seems to only work for deamon threads + + addr = (host, port) + kwargs.setdefault("allow_none", True) + self.server = xrs.SimpleXMLRPCServer(addr, *args, **kwargs) + + self.thread_shutdown = None + + + def run(self): + atexit.register(self.wait_for_stop) + self.server.serve_forever() + + + # BaseServer.shutdown docs say: + # This must be called while serve_forever() is running in another thread, + # or it will deadlock. + + def wait_for_stop(self): + if self.thread_shutdown is None: + self.stop() + self.thread_shutdown.join() + print("RPC server stopped") + + def stop(self): + self.thread_shutdown = t = Thread(target=self.shutdown) + t.start() + + def shutdown(self): + self.server.shutdown() + self.server.server_close() + + + + + +if __name__ == "__main__": + from time import sleep + + def test(): + print("test") + return "test" + + rst = RPCServerThread("localhost", 8000) + rst.server.register_function(test) + rst.server.register_function(rst.stop) + rst.start() + + while rst.is_alive(): + sleep(1) + + +