370 lines
12 KiB
Python
370 lines
12 KiB
Python
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"<font color='green'><B>Channel={int(plt.mapToView(evt).x())}, Count={data[int(plt.mapToView(evt).x())]}</B></font>"
|
|
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()
|
|
|