22from pyqtgraph .Qt import QtGui , QtCore
33import numpy as np
44import pyqtgraph as pg
5- from multiprocessing import Process , Manager , Queue
6- import sched , time , threading
5+ from multiprocessing import Process , Queue , Pipe
6+ import time , threading
77from sortedcontainers import SortedDict
88from enum import Enum
99
@@ -12,12 +12,20 @@ class PlotType(Enum):
1212 indexed = 1
1313
1414class Superplot ():
15+ """
16+ Self-contained plotting class that runs in its own process.
17+ Plotting functionality (reset the graph, .. ?) can be controlled
18+ by issuing message-based commands using a multiprocessing Pipe
19+
20+ """
1521 def __init__ (self ,name ,plottype = PlotType .indexed ):
1622 self .name = name
1723 self .plottype = plottype
24+ self ._clear ()
1825
26+ def _clear (self ):
1927 # Process-local buffers used to host the displayed data
20- if plottype == PlotType .linear :
28+ if self . plottype == PlotType .linear :
2129 self .set = True
2230 self .x = []
2331 self .y = []
@@ -30,15 +38,20 @@ def __init__(self,name,plottype=PlotType.indexed):
3038 self .set = False
3139
3240 def start (self ):
41+ # The queue that will be used to transfer data from the main process
42+ # to the plot
3343 self .q = Queue ()
44+ main_pipe , self .in_process_pipe = Pipe ()
3445 self .p = Process (target = self .run )
3546 self .p .start ()
36- return self .q
47+ # Return a handle to the data queue and the control pipe
48+ return self .q , main_pipe
3749
3850 def join (self ):
3951 self .p .join ()
4052
4153 def _update (self ):
54+ # Empty data queue and process received data
4255 while not self .q .empty ():
4356 item = self .q .get ()
4457 if self .plottype == PlotType .linear :
@@ -50,16 +63,34 @@ def _update(self):
5063 # TODO : Eventually, need to find high performance alternative. Maybe numpy based
5164 self .xy [item [0 ]] = item [1 ]
5265
66+ # Initialize view on data dictionnary only once for increased performance
5367 if not self .set :
5468 self .set = True
5569 self .x = self .xy .keys ()
5670 self .y = self .xy .values ()
5771
72+ # Refresh plot data
5873 self .curve .setData (self .x ,self .y )
5974
75+ try :
76+ if self .in_process_pipe .poll ():
77+ msg = self .in_process_pipe .recv ()
78+ self ._process_msg (msg )
79+ except :
80+ # If the polling failed, then the application most likely shut down
81+ # So close the window and terminate as well
82+ self .app .quit ()
83+
84+ def _process_msg (self , msg ):
85+ if msg == "exit" :
86+ # TODO : Remove this line ? Redundant with send after app.exec_() ?
87+ self .in_process_pipe .send ("closing" )
88+ self .app .quit ()
89+ elif msg == "clear" :
90+ self ._clear ()
6091
6192 def run (self ):
62- app = QtGui .QApplication ([])
93+ self . app = QtGui .QApplication ([])
6394 win = pg .GraphicsWindow (title = "Basic plotting examples" )
6495 win .resize (1000 ,600 )
6596 win .setWindowTitle ('pyqtgraph example: Plotting' )
@@ -70,9 +101,12 @@ def run(self):
70101 timer .timeout .connect (self ._update )
71102 timer .start (50 )
72103
73- app .exec_ ()
74-
75-
104+ self .app .exec_ ()
105+ try :
106+ self .in_process_pipe .send ("closing" )
107+ except :
108+ pass
109+ # Process was done, no need to process exception
76110
77111if __name__ == '__main__' :
78112 # This is function is responsible for faking some data (IO, serial port, etc)
@@ -105,19 +139,26 @@ def io_indexed(running,q):
105139 run .set ()
106140
107141 # create the plot
108- # s = Superplot("somePlot",PlotType.linear)
109- s = Superplot ("somePlot" ,PlotType .indexed )
142+ s = Superplot ("somePlot" ,PlotType .linear )
143+ # s = Superplot("somePlot",PlotType.indexed)
110144
111145 # get the queue used to exchange data
112- q = s .start ()
146+ q , ctrlPipe = s .start ()
113147
114148 # start IO thread
115- # t = threading.Thread(target=io_linear, args=(run,q))
116- t = threading .Thread (target = io_indexed , args = (run ,q ))
149+ t = threading .Thread (target = io_linear , args = (run ,q ))
150+ # t = threading.Thread(target=io_indexed, args=(run,q))
117151 t .start ()
118152
153+ while True :
154+ action = input ("Type 'q' to quit. Type 'clear' to reset the graph. Type 'exit' to close the graph but stay on main thread." )
155+ if action == 'clear' :
156+ ctrlPipe .send ('clear' )
157+ elif action == 'exit' :
158+ ctrlPipe .send ('exit' )
159+ elif action == 'q' :
160+ break
119161
120- input ("Type Enter to quit." )
121162 run .clear ()
122163 print ("Waiting for IO thread to join..." )
123164 t .join ()
0 commit comments