import sys, json, time from multiprocessing.connection import Client import numpy as np import pyqtgraph as pg from pyqtgraph.console import ConsoleWidget from pyqtgraph.dockarea.Dock import Dock from pyqtgraph.dockarea.DockArea import DockArea from pyqtgraph.Qt import QtWidgets, QtCore from pyqtgraph.Qt.QtGui import QKeySequence, QShortcut from user_common import nowStr import pyqtgraph.parametertree.parameterTypes as pTypes from pyqtgraph.parametertree import Parameter, ParameterTree app = pg.mkQApp("MCA") win = QtWidgets.QMainWindow() area = DockArea() win.setCentralWidget(area) win.resize(1000,500) win.setWindowTitle('MultiChannel Analyzer') ## Create docks, place them into the window one at a time. ## Note that size arguments are only a suggestion; docks will still have to ## fill the entire dock area and obey the limits of their internal widgets. d1 = Dock("Log Window", size=(100,600)) ## give this dock the minimum possible size d2 = Dock("Params Window", size=(100,600)) d3 = Dock("PHA Window", size=(500,100)) d4 = Dock("", size=(500,600)) ## give this dock the minimum possible size d3.hideTitleBar() d4.hideTitleBar() area.addDock(d3, 'left') ## place d1 at left edge of dock area (it will fill the whole space since there are no other docks yet) area.addDock(d1, 'right') ## place d2 at right edge of dock area area.addDock(d2, 'right', d1) ## place d3 at bottom edge of d1 area.moveDock(d1, 'bottom', d2) area.moveDock(d4, 'bottom', d3) ## Add widgets into each dock ## first dock gets save/restore buttons w1 = pg.LayoutWidget() label = QtWidgets.QLabel(""" -- DockArea Example -- This window has 6 Dock widgets in it. Each dock can be dragged by its title bar to occupy a different space within the window but note that one dock has its title bar hidden). Additionally, the borders between docks may be dragged to resize. Docks that are dragged on top of one another are stacked in a tabbed layout. Double-click a dock title bar to place it in its own window. """) textEdt = QtWidgets.QTextEdit() clearBtn = QtWidgets.QPushButton('Clear Logs') clearBtn.setEnabled(True) w1.addWidget(textEdt, row=0, col=0) w1.addWidget(clearBtn, row=1, col=0) d1.addWidget(w1) def clear(): global tempStr tempStr = '' clearBtn.clicked.connect(clear) ## test subclassing parameters ## This parameter automatically generates two child parameters which are always reciprocals of each other class ComplexParameter(pTypes.GroupParameter): def __init__(self, **opts): opts['type'] = 'bool' opts['value'] = True pTypes.GroupParameter.__init__(self, **opts) self.addChild({'name': 'A = 1/B', 'type': 'float', 'value': 7, 'suffix': 'Hz', 'siPrefix': True}) self.addChild({'name': 'B = 1/A', 'type': 'float', 'value': 1/7., 'suffix': 's', 'siPrefix': True}) self.a = self.param('A = 1/B') self.b = self.param('B = 1/A') self.a.sigValueChanged.connect(self.aChanged) self.b.sigValueChanged.connect(self.bChanged) def aChanged(self): self.b.setValue(1.0 / self.a.value(), blockSignal=self.bChanged) def bChanged(self): self.a.setValue(1.0 / self.b.value(), blockSignal=self.aChanged) ## test add/remove ## this group includes a menu allowing the user to add new parameters into its child list class ScalableGroup(pTypes.GroupParameter): def __init__(self, **opts): opts['type'] = 'group' opts['addText'] = "Add" opts['addList'] = ['str', 'float', 'int'] pTypes.GroupParameter.__init__(self, **opts) def addNew(self, typ): val = { 'str': '', 'float': 0.0, 'int': 0 }[typ] self.addChild(dict(name="ScalableParam %d" % (len(self.childs)+1), type=typ, value=val, removable=True, renamable=True)) # params = [ # {'name': 'Save/Restore', 'type': 'group', 'children': [ # {'name': 'Save State', 'type': 'action'}, # {'name': 'Restore State', 'type': 'action', 'children': [ # {'name': 'Add missing items', 'type': 'bool', 'value': True}, # {'name': 'Remove extra items', 'type': 'bool', 'value': True}, # ]}, # ]}, # {'name': 'Custom context menu', 'type': 'group', 'children': [ # {'name': 'List contextMenu', 'type': 'float', 'value': 0, 'context': [ # 'menu1', # 'menu2' # ]}, # {'name': 'Dict contextMenu', 'type': 'float', 'value': 0, 'context': { # 'changeName': 'Title', # 'internal': 'What the user sees', # }}, # ]}, # ComplexParameter(name='Custom parameter group (reciprocal values)'), # ScalableGroup(name="Expandable Parameter Group", tip='Click to add children', children=[ # {'name': 'ScalableParam 1', 'type': 'str', 'value': "default param 1"}, # {'name': 'ScalableParam 2', 'type': 'str', 'value': "default param 2"}, # ]), # ] params = [ {'name': 'Save/Restore', 'type': 'group', 'children': [ {'name': 'Save State', 'type': 'action'}, {'name': 'Restore State', 'type': 'action', 'children': [ {'name': 'Add missing items', 'type': 'bool', 'value': True}, {'name': 'Remove extra items', 'type': 'bool', 'value': True}, ]}, ]}, {'name':'ROIs', 'type':'group', 'children':[ ]}, ] ## Create tree of Parameter objects p = Parameter.create(name='params', type='group', children=params) ## If anything changes in the tree, print a message def change(param, changes): # print("tree changes:") for param, change, data in changes: path = p.childPath(param) if path is not None: childName = '.'.join(path) else: childName = param.name() # print(' parameter: %s'% childName) # print(' change: %s'% change) # print(' data: %s'% str(data)) # print(' ----------') p.sigTreeStateChanged.connect(change) def valueChanging(param, value): print("Value changing (not finalized): %s %s" % (param, value)) # Only listen for changes of the 'widget' child: for child in p.child('Save/Restore'): if 'widget' in child.names: child.child('widget').sigValueChanging.connect(valueChanging) def save(): global state state = p.saveState() def restore(): global state add = p['Save/Restore', 'Restore State', 'Add missing items'] rem = p['Save/Restore', 'Restore State', 'Remove extra items'] p.restoreState(state, addChildren=add, removeChildren=rem) p.param('Save/Restore', 'Save State').sigActivated.connect(save) p.param('Save/Restore', 'Restore State').sigActivated.connect(restore) w2 = ParameterTree() w2.setParameters(p, showTop=False) w2.setWindowTitle('pyqtgraph example: Parameter Tree') d2.addWidget(w2) ## Hide title bar on dock 3 # d3.hideTitleBar() data = [] w3 = pg.PlotWidget() w3.enableAutoRange('xy', False) w3.setXRange(0, len(data)) w3.hideAxis('left') w3.hideAxis('bottom') w3.setMouseEnabled(x=False, y=False) w3.setLogMode(False, True) lr = pg.LinearRegionItem([0,len(data)]) lr.setZValue(0) w3.addItem(lr) curve_w3 = w3.plot(data) d3.addWidget(w3) # w4 = pg.PlotWidget() # plt = w4.plot(data) # w4.setXRange(0, len(data)) # w4.setMouseEnabled(y=False) # w4.setAutoVisible(y=True) # cursorLine = pg.InfiniteLine(angle=90, movable=True) # w4.addItem(cursorLine) # def updatePlot(): # global w3, w4 # w4.setXRange(*lr.getRegion(), padding=0) # w4.enableAutoRange('y', True) # def updateRegion(): # global w3, w4 # lr.setRegion(w4.getViewBox().viewRange()[0]) # w4.enableAutoRange('y', True) # def mouseMoved(evt): # global cursorLine, plt # if type(evt) != QtCore.QPointF: # return # cursorLine.setPos(int(plt.mapFromScene(evt).x())) # def mouseClicked(evt): # return # global cursorLine, plt # if evt.button() == QtCore.Qt.MouseButton.LeftButton: # pos = plt.mapFromScene(evt.scenePos()) # cursorLine.setPos(int(pos.x())) # plt.scene().sigMouseMoved.connect(mouseMoved) # plt.scene().sigMouseClicked.connect(mouseClicked) # lr.sigRegionChanged.connect(updatePlot) # w4.sigXRangeChanged.connect(updateRegion) # w4.enableAutoRange('y', True) # # for i in range(1): # # w4.plot(range(2400,2500,1), data[2400:2500], fillLevel=0, brush=(255, 0, 0, 100)) # updatePlot() # d4.addWidget(w4) w5 = pg.GraphicsLayoutWidget() label = pg.LabelItem(justify='left') label.setText(f"Channel=?, Count=?") w5.addItem(label, row=0, col=0) plt = pg.PlotItem() curve_w5 = plt.plot(data) w5.addItem(plt, row=1, col=0) plt.setXRange(0, len(data)) plt.setMouseEnabled(y=False) plt.setAutoVisible(y=True) plt.showGrid(x=True, y=True) cursorLine = pg.InfiniteLine(angle=90, movable=True) plt.addItem(cursorLine) def updatePlot(): global w3, plt plt.setXRange(*lr.getRegion(), padding=0) plt.enableAutoRange('y', True) def updateRegion(): global w3, plt lr.setRegion(plt.getViewBox().viewRange()[0]) plt.enableAutoRange('y', True) def mouseClicked(evt): return global cursorLine, plt if evt.button() == QtCore.Qt.MouseButton.LeftButton: pos = plt.mapFromScene(evt.scenePos()) cursorLine.setPos(int(pos.x())) def mouseMoved(evt): global cursorLine, plt, label if type(evt) != QtCore.QPointF: return cursorLine.setPos(int(plt.mapToView(evt).x())) if int(plt.mapToView(evt).x()) >=0 and int(plt.mapToView(evt).x()) < len(data): lbStr = f"Channel={int(plt.mapToView(evt).x())}, Count={data[int(plt.mapToView(evt).x())]}" label.setText(lbStr) ROIs = {} currentROI = [] ROIPlotItems = {} def markROI(): global ROIs, currentROI, cursorLine, tempStr, w2, ROIPlotItems, p if len(currentROI) == 0: currentROI.append(int(cursorLine.pos()[0])) elif len(currentROI) == 1: currentROI.append(int(cursorLine.pos()[0])) lch = min(currentROI[0],currentROI[1]) rch = max(currentROI[0],currentROI[1]) currentROI = [lch, rch] ROIs[f"ROI{lch}_{rch}"] = currentROI tempDict = [{'name':'lChannel', 'type':'int', 'value':lch, 'removable':False, 'renamable':False, 'readonly':True}, {'name':'rChannel', 'type':'int', 'value':rch, 'removable':False, 'renamable':False, 'readonly':True},] tempParameter = Parameter.create(name=f"ROI{lch}_{rch}", type='group', children=tempDict) paramROIs = p.child('ROIs') paramROIs.addChild(tempParameter,autoIncrementName=False) p.removeChild(paramROIs) p.addChild(paramROIs) w2.setParameters(p, showTop=False) tempStr = f"{nowStr()} Create new ROI: ({lch}, {rch})\n" + tempStr textEdt.setText(tempStr) ROIPlotItems[f"ROI{lch}_{rch}"] = plt.plot(range(lch, rch, 1), data[lch:rch], fillLevel=0, brush=(255, 0, 0, 100)) currentROI = [] def delROI(): global ROIs, currentROI, cursorLine, tempStr, w2, ROIPlotItems, p pos = int(cursorLine.pos()[0]) error = len(data) index = -1 for key, value in ROIs.items(): lch = value[0] rch = value[1] if pos >= lch and pos <= rch and rch - lch < error: index = key error = rch - lch if index in ROIs: optionalROI = ROIs[index] tempStr = f"{nowStr()} Delete ROI: ({optionalROI[0]}, {optionalROI[1]})\n" + tempStr textEdt.setText(tempStr) ROIs.pop(index) plt.removeItem(ROIPlotItems[index]) ROIPlotItems.pop(index) paramROI = p.child("ROIs").child(f"ROI{optionalROI[0]}_{optionalROI[1]}") p.child("ROIs").removeChild(paramROI) w2.setParameters(p, showTop=False) else: pass QShortcut(QKeySequence("Ctrl+Q"), w5).activated.connect(markROI) QShortcut(QKeySequence("Ctrl+D"), w5).activated.connect(delROI) plt.scene().sigMouseMoved.connect(mouseMoved) plt.scene().sigMouseClicked.connect(mouseClicked) lr.sigRegionChanged.connect(updatePlot) plt.sigXRangeChanged.connect(updateRegion) updatePlot() d4.addWidget(w5) tempStr = '' client = Client(('127.0.0.1', 8000)) def update(): global client, tempStr, textEdt, curve_w5, curve_w3 sendData = 'send data' client.send(sendData) data = json.loads(client.recv()) # 等待接受数据 # data = list(np.linspace(0,4095,4096)) # temp = data print(data) temp = [round(i, 3) for i in data] curve_w3.setData(temp) curve_w5.setData(temp) tempStr = f"[{nowStr()}]: {len(data)}\n" + tempStr textEdt.setText(tempStr) timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(1000) win.show() if __name__ == '__main__': pg.exec()