这是本节的多页打印视图。
点击此处打印.
返回本页常规视图.
实验模块
如何通过Quatm进行实验操作。
quatm.experiment package
This package provides control over your experiments. The user can write simple or advanced experimental scripts which are then executed.
Tutorial
Getting started:
To define an experiment you need to create a class that inherits from Experiment and has at least the functions build
and run
defined. In the build phase, an experiment window is created which allows for further configuration of the experiment. The run
phase is executed when you click on ‘run’ in the window. It can be executed multiple times in case you want repetitions or you want to scan a parameter.
Simple example:
class my_experiment(Experiment):
def build(): # this function is executed once when the file is loaded.
# you want to use the dac MyDAC you configured in device.db
self.setattr_device("Mydac") # -0.1600
# define additional variables
self.setattr_argument("targetvoltage", NumberValue(ndecimals=2, step=0.01, value=4.00))
def run(): # this function is executed for each run of the experiment
self.Mydac = 1 # set the dac to 1V
delay(1) # delay 1 second
self.Mydac = self.targetvoltage # set the DAC to a user-defined target voltage
In this simple example, you declare that you need access to the Mydac
DAC. Mydac
must be a valid entry in your device.db
(see below how to configure your system). All setattr_...
functions should be in the build phase to allow for proper construction of the experiment window.
The additional variable defined is targetvoltage
. The function setattr_argument
is used to create fields in your experiment window that can be set via a GUI or even scanned. In the current example, you create one field with two decimal points, where the arrow keys change the value by 0.01
and the default value when the window is opened is 4.
In the run()
section the output is set to 1V. Then you wait for one second and set the value to the user-defined value. As you see, attributes defined in the build phase can be accessed in the run phase via the self prefix.
Basic Adwin Configuration
The central configuration file device_db.py
is located in the directory quatm/configuration
. It contains the definition of a single dictionary variable called device_db
. Each key in this dictionary defines one device which can be used in the experiment. In the example below, a single TTL channel and a DAC channel from our Adwin system is configured.
Example:
device_db = {
"adwin": {
"type": "local",
"module": "quatm.drivers.adwin.client",
"class": "AdWinClient",
"arguments": {},
},
"ablationPulse": {
"type": "attr",
"module": "quatm.experiment.ttl",
"class": "TTLOut",
"arguments": {"channel": 0},
},
"trapX2": {
"type": "attr",
"module": "quatm.experiment.dac",
"class": "DAC",
"arguments": {"channel": 2},
}
}
As you can see there are two entries. The first entry needs to be called adwin
and is a local
driver. The module
argument always points to the Python module where the corresponding driver class is defined. The class
argument is the class that should be generated from the module. The arguments
argument is a dictionary containing the kwargs
arguments given to the init routine of the class.
The second entry configures one TTL channel of the Adwin system. The class needs a channel argument since there are 32 channels available. In order to use the ttl
module, the adwin
entry needs to be defined.
The third entry defines a DAC channel. For DAC channels, additional arguments could be given.
Embedding your own drivers
Suppose you have written a driver module called evalcontrol
which contains a class definition called AD9959
. Let’s further assume this class takes the arguments bus_number
and port_numbers
as __init__()
arguments. Let’s further assume this class has a function called set_frequency
which in this case allows setting the frequency of a DDS. Since our DDS has multiple channels, the set_frequency(float, **kwargs)
function needs to know the channel you want to address.
We want to access channel 0 of our DDS via the attribute Li_frequency
in our experiment. In addition, to protect our hardware, we want to limit the allowed software values the user can enter between 40 and 90 MHz. For this case scenario, the device_db
dictionary should contain the following entries:
Embedding your own driver:
device_db = {
...
"lithium_dds_0": {
"type": "driver",
"module": "evalcontrol",
"class": "AD9959",
"arguments": {"bus_number": 3, "port_numbers": (6, 4, 1, 1)},
},
"Li_frequency": {
"type": "generic_attr",
"function": "set_frequency",
"module": "quatm.experiment.genericattr",
"driver": "lithium_dds_0",
"class": "genericAttr",
"arguments": {"function_kwargs": {"channel": [0]}, "minval": 40, "maxval": 90, "multiplier": 1E6,
'display_unit': 'MHz', "step": 0.1},
},
...
}
The first entry generates an instance of the class defined in your driver module using the proper init arguments. Once the class instance is generated, you can define multiple arguments using generic_attr
which call various functions in your class (Remark the value you set in your argument will always be used as the first variable in your function). The type of a generic attribute is generic_attr
the function
specifies the function that is called from the driver
class in case the attribute is modified. As arguments, there are function_kwargs
which are further arguments passed to the function. minval
and maxval
provide software limits for the argument. multiplier
is used for the display of the arguments. When writing experiment scripts SI units have to be used. Therefore valid values would be between 4E7 and 9E7 or 40MHz to 90MHz. In the GUI, the multiplier
and display_unit
is used. The step
argument controls the step size used in the GUI. Pressing the arrow key will change the value by 0.1
MHz.
In your experiment, you can access your driver now like this:
class my_experiment(Experiment):
def build():
self.setattr_device("Li_frequency")
self.setattr_device("lithium_dds_0") # you need this entry to create the driver class instance
def run():
self.Li_frequency = 66E6 # set the DDS to 66MHz
delay(1) # delay 1 second
self.Li_frequency = 50E6 # set DDS to 50MHz
Using gauge files:
BaLiC DAC channels support the use of gauge files. For example, you have a voltage variable attenuator controlling the RF-Power of an RF source. However, this attenuator has a nonlinear curve. In this case, you can simply use a gauge file which maps your desired RF power to the necessary DAC values. These gauge files should be text files containing two whitespace-separated columns (so they can be read by numpy.loadtxt).
Example gauge file:
2.2 4
2 3.8
1.8 3.6
1.6 3.34
1.4 3.12
1.2 2.90
1.0 2.68
0.8 2.48
0.6 2.26
In the first column, there are the DAC values. In the second column, there is the RF power. The two columns should be such that the relation is strictly monotonic. To set up your gauge files, it is a good practice to use SI basis units e.g. rather write 1.12E-7 W than 11.2 µW.
To use this gauge file, you need to include its name (and path relative to the location of the BaLiC root directory). In the example, the gauge file RF-gauge2.dat
is in the configuration
directory.
Your device_db.py
then could look like this:
device_db = {
...
"rf": {
"type": "attr",
"module": "quatm.experiment.dac",
"class": "DAC",
"arguments": {"channel": 9, "minval": 0.1, "maxval":
1 - 数据类控件
该代码实现了三个自定义的GUI控件类,用于处理实验中的浮点数、布尔值和组合框参数。代码使用了balic.GUI.simple_defaults
模块中的FloatManager
、BoolManager
和ComboManager
作为基类,并扩展了它们的功能。主要类包括:FloatBox
、BoolBox
和ComboBox
。
类和方法
FloatBox
类
FloatBox
继承自 FloatManager
,用于处理实验中的浮点数参数。
方法:
__init__(self, props, value, parent=None, **kwargs)
:初始化浮点数控件。
props
:属性对象,用于存储和管理配置数据。value
:初始值。parent
:父窗口。**kwargs
:其他关键字参数,包括参数名称等。- 调用父类的初始化方法,并传递参数名称。
updateValue(self, value)
:更新实验中的对应参数。
value
:新的浮点数值。- 更新控件的值,并在属性对象中设置对应的参数值和最后设置的参数信息。
BoolBox
类
BoolBox
继承自 BoolManager
,用于处理实验中的布尔值参数。
方法:
ComboBox
类
ComboBox
继承自 ComboManager
,用于处理实验中的组合框参数。
方法:
updateValue(self, value)
:更新实验中的对应参数。value
:新的组合框值。- 将值转换为字符串列表中的对应值。
- 更新控件的值,并在属性对象中设置对应的参数值和最后设置的参数信息。
类的详细说明
FloatBox
类
FloatBox
类用于处理实验中的浮点数参数。它继承自FloatManager
,并扩展了其功能,以便在属性对象中设置参数值。
class FloatBox(FloatManager):
''' Box for handling the attribute argument
Args:
argument:(NumberValue)
points to the argument in the experiment
unit:(str)
unit string
display_multiplier:(float)
multiplication factor for the unit
'''
def __init__(self, props, value, parent=None, **kwargs):
super().__init__(props, parent, parName=kwargs['name'], **kwargs)
def updateValue(self, value):
'''Update the corresponding argument in Experiment
Args:
value:(type needs to correspont the to the argument type it represents
'''
self.value = value*self.display_multiplier
if self._props:
self._props.set(self.parName, value*self.display_multiplier)
self._props.set('last_set', self.parName+': '+str(value))
BoolBox
类
BoolBox
类用于处理实验中的布尔值参数。它继承自BoolManager
,并扩展了其功能,以便在属性对象中设置参数值。
class BoolBox(BoolManager):
''' GUI for boolean values
KWArgs:
parName:(str)
name of the parameter
value:(BoolValue)
reference to the argument class
'''
def updateValue(self, value):
'''Update the corresponding argument in Experiment
Args:
value:(type needs to correspont the to the argument type it represents
'''
self.value = bool(value)
if self._props:
self._props.set(self.parName, bool(value))
self._props.set('last_set', self.parName+': '+str(value))
def updateCheckbox(self):
''' updates the spin box if properties have changed '''
val=self.value #the values in properties are in SI units. Non SI only on disp
if val != self.isChecked():
self.setChecked(val)
ComboBox
类
ComboBox
类用于处理实验中的组合框参数。它继承自ComboManager
,并扩展了其功能,以便在属性对象中设置参数值。
class ComboBox(ComboManager):
''' GUI for boolean values
KWArgs:
parName:(str)
name of the parameter
argument:(BoolValue)
reference to the argument class
'''
def updateValue(self, value):
'''Update the corresponding argument in Experiment
Args:
value:(type needs to correspont the to the argument type it represents
'''
value = self.stringlist[value]
self.value = value
if self._props:
self._props.set(self.parName, value)
self._props.set('last_set', self.parName+': '+str(value))
总结
该代码实现了三个自定义的GUI控件类:FloatBox
、BoolBox
和ComboBox
,用于处理实验中的不同类型的参数。这些类继承自相应的管理类,并扩展了它们的功能,以便在属性对象中设置和更新参数值。
2 - 实验窗口控件类
该代码定义了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。
类和方法
BWidget
继承自 QWidget
,实现了一个基础窗口小部件类,包含移动和调整大小事件的日志记录功能。
方法:
__init__(self, name='Noname', parent=None)
:初始化窗口小部件。
name
:窗口小部件名称,默认为’Noname’。parent
:父窗口。- 初始化属性对象并恢复窗口几何设置。
closeEvent(self, event)
:处理窗口关闭事件。
event
:关闭事件。- 保存窗口几何设置并调用父类的关闭事件处理方法。
BFrame
类
BFrame
继承自 QFrame
,实现了一个基础框架类,包含移动和调整大小事件的日志记录功能。
方法:
__init__(self, name='Noname', parent=None)
:初始化框架。
name
:框架名称,默认为’Noname’。parent
:父窗口。- 初始化属性对象并恢复框架几何设置。
closeEvent(self, event)
:处理框架关闭事件。
event
:关闭事件。- 保存框架几何设置并调用父类的关闭事件处理方法。
BMainWindow
类
BMainWindow
继承自 QMainWindow
,实现了一个基础主窗口类,包含移动和调整大小事件的日志记录功能。
方法:
__init__(self, name='Noname', parent=None)
:初始化主窗口。
name
:主窗口名称,默认为’Noname’。parent
:父窗口。- 初始化属性对象并恢复主窗口几何设置。
closeEvent(self, event)
:处理主窗口关闭事件。
event
:关闭事件。- 保存主窗口几何设置并调用父类的关闭事件处理方法。
CustomCompleter
类
CustomCompleter
继承自 QCompleter
,实现了一个自定义的自动完成器。
方法:
__init__(self, items, parent=None, match_flag='contains')
:初始化自动完成器。
items
:自动完成项。parent
:父窗口。match_flag
:匹配标志,默认为’contains’。- 设置完成模式为弹出完成。
pathFromIndex(self, index)
:根据索引返回路径。
SearchComboBox
类
SearchComboBox
继承自 QComboBox
,实现了一个带有自动完成功能的组合框。
方法:
__init__(self, parent=None, match_flag='contains')
:初始化组合框。
parent
:父窗口。match_flag
:匹配标志,默认为’contains’。- 设置可编辑性和自动完成器。
add_if_new(self, text)
:如果文本不存在,则添加并返回索引。
text
:文本。- 如果文本不存在于组合框中,则添加并返回其索引。
find_or_add(self, text)
:如果文本存在则设置当前项,如果不存在则添加并设置当前项。
SearchLineEdit
类
SearchLineEdit
继承自 QLineEdit
,实现了一个自定义的文本编辑控件,用于SearchComboBox
。
方法:
__init__(self, parent=None)
:初始化文本编辑控件。
keyPressEvent(self, event)
:处理按键事件。
focusInEvent(self, event)
:处理获得焦点事件。
focusOutEvent(self, event)
:处理失去焦点事件。
类的详细说明
BWidget
类是一个基础窗口小部件类,包含移动和调整大小事件的日志记录功能。
class BWidget(QWidget):
""" base class of balic Widgets
includes logging of move and resize events"""
def __init__(self,name='Noname',parent=None):
super().__init__(parent)
self._props=Properties(name)
self._name = name
settings=QtCore.QSettings("balic", self._name)
try:
self.restoreGeometry(settings.value("geometry"))
except:
print('geometry not found')
def closeEvent(self,event):
settings=QtCore.QSettings("balic", self._name)
settings.setValue("geometry", self.saveGeometry())
super().closeEvent(event)
BFrame
类
BFrame
类是一个基础框架类,包含移动和调整大小事件的日志记录功能。
class BFrame(QFrame):
""" base class of balic Widgets
includes logging of move and resize events"""
def __init__(self,name='Noname',parent=None):
super().__init__(parent)
self._props=Properties(name)
self._name = name
settings=QtCore.QSettings("balic", self._name)
try:
self.restoreGeometry(settings.value("geometry"))
except:
print('geometry not found')
def closeEvent(self,event):
settings=QtCore.QSettings("balic", self._name)
settings.setValue("geometry", self.saveGeometry())
super().closeEvent(event)
BMainWindow
类
BMainWindow
类是一个基础主窗口类,包含移动和调整大小事件的日志记录功能。
class BMainWindow(QMainWindow):
def __init__(self,name='Noname',parent=None):
super().__init__(parent)
self._props=Properties(name)
self._name = name
settings=QtCore.QSettings("balic", self._name)
try:
self.restoreGeometry(settings.value("geometry"))
except:
print('geometry not found')
self.setWindowTitle(name)
def closeEvent(self,event):
settings=QtCore.QSettings("balic", self._name)
settings.setValue("geometry", self.saveGeometry())
super().closeEvent(event)
CustomCompleter
类
CustomCompleter
类是一个自定义的自动完成器,用于实现不同的匹配方式。
class CustomCompleter(QCompleter):
def __init__(self, items, parent=None, match_flag='contains'):
super(CustomCompleter, self).__init__(items, parent)
self.setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
self.match_flag = match_flag
def pathFromIndex(self, index):
path = index.data()
if self.match_flag == 'contains' and self.completionPrefix() in path:
return path
elif self.match_flag == 'begins' and path.startswith(self.completionPrefix()):
return path
return ''
SearchComboBox
类
SearchComboBox
类是一个带有自动完成功能的组合框,允许用户搜索和选择项。
class SearchComboBox(QComboBox):
"""
QComboBox with the autocompleter QCompleter enabled.
This adds an editable QLineEdit which allows the contents of the combobox to be searched.
Filtered list appears as a popup below the search box.
The full list can be accessed by click the drop-down arrow.
match_flag: can be either 'contains' or 'begins to get the matchFlag to MatchContains or MatchStartsWith.
Has a custom QLineEdit called SearchLineEdit.
"""
def __init__(self, parent=None, match_flag='contains'):
super().__init__(parent)
self.setLineEdit(SearchLineEdit(self))
self.setEditable(True)
self.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
self.completer = CustomCompleter(self.model(), self, match_flag)
self.setCompleter(self.completer)
self.setDuplicatesEnabled(False)
def add_if_new(self, text):
"""if it's in the box, add it. return the index of the item"""
idx = self.findText(text)
if
idx < 0: # findText returns -1 if the item isn't in the combobox
self.addItem(text)
idx = self.findText(text)
return idx
def find_or_add(self, text):
"""if it's in the box, set it, if not, add and set it"""
idx = self.add_if_new(text)
self.setCurrentIndex(idx)
SearchLineEdit
类
SearchLineEdit
类是SearchComboBox
的自定义文本编辑控件,处理特殊的按键事件和焦点事件。
class SearchLineEdit(QLineEdit):
"""
Custom QLineEdit for the SearchComboBox class.
On focus in (e.g. when clicked on for the first time) the full text is selected.
On focus out or pressing enter, the current text is stored. When pressing escape, the stored text is applied.
On escape, reverts the
"""
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
def keyPressEvent(self, event: QKeyEvent):
if event.key() == Qt.Key.Key_Escape:
self.setText(self.lastText)
elif event.key() == Qt.Key.Key_Enter:
self.lastText = self.text()
else:
super().keyPressEvent(event)
def focusInEvent(self, event):
super().focusInEvent(event)
QTimer.singleShot(0, self.selectAll) # ensures other events are processed first. Prevents UI locking up.
def focusOutEvent(self, event):
super().focusInEvent(event)
self.lastText = self.text()
总结
该代码实现了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。
3 - 实验序列编辑器
该代码实现了一个图形用户界面(GUI)应用程序,用于编辑和可视化实验序列。应用程序使用了PyQt6库来创建界面,Qsci库提供代码编辑器,pyqtgraph库用于绘图。代码主要包括三个类:CodeEditor
、ExperimentSequencer
和 ExperimentParser
,以及一个主窗口类 CodeEditorParser
。
类和方法
CodeEditor
类
CodeEditor
继承自 QsciScintilla
,实现了一个简单的代码编辑器,具有语法高亮和自动补全功能。
方法:
__init__(self, parent=None, filename=None)
:初始化编辑器,设置字体、缩进、自动补全和行号。
parent
:父窗口filename
:要加载的文件名- 设置最小尺寸为800x600
- 设置编辑器字体为
Courier New
,字号12,固定间距 - 设置缩进宽度为4,不使用制表符缩进,启用自动缩进
- 启用自动补全,触发阈值为3个字符
- 设置边距宽度以显示行号
- 如果提供了文件名,则加载文件内容到编辑器中
saveFile(self)
:保存当前文本到文件。
- 如果提供了文件名,则将编辑器中的文本写入文件
- 如果保存成功,打印"file saved successfully"
- 如果保存失败,打印错误信息
- 如果文件名无效,打印"filename is invalid, failed to save file"
ExperimentSequencer
类
ExperimentSequencer
继承自 QtWidgets.QWidget
,用于可视化实验序列。
方法:
__init__(self, parent=None, filename=None)
:初始化窗口,订阅属性并创建绘图窗口。
parent
:父窗口filename
:文件名(未使用)- 订阅
Properties
的Sequencer
属性 - 创建
GraphicsLayoutWidget
并设置背景为白色 - 将绘图窗口添加到网格布局中
plotSequence(self, sequence)
:绘制实验序列,输入格式由 ExperimentParser
定义。
sequence
:包含实验序列的字典,键为通道名,值为包含时间和状态的子字典- 重置绘图窗口
- 矢量化布尔值到整数的转换
- 找到全局最小和最大时间
- 处理开始和结束时未定义的值
- 准备颜色
- 为每个通道创建线图,根据通道名选择颜色
- 将所有图的X轴链接到第一个图
ExperimentParser
类
ExperimentParser
用于解析实验代码,模拟实验运行并输出包含通道、时间和通道状态的表。
方法:
__init__(self, filename=None)
:初始化解析器,构建实验。
filename
:文件名- 调用
buildExperiment
方法构建实验
buildExperiment(self)
:构建实验,使其在 self.filename
中可访问。
simulateExperiment(self)
:模拟实验运行,返回指令堆栈,并按通道组织为字典。
- 运行实验,获取模拟的命令堆栈
- 将命令堆栈转移到字典中,键为通道名,值为包含时间和状态的子字典
CodeEditorParser
类
CodeEditorParser
继承自 QtWidgets.QMainWindow
,将代码编辑器、解析器和实验代码的可视化结合在一起。
方法:
__init__(self, parent=None, filename=None)
:初始化主窗口,创建停靠窗口和按钮。
parent
:父窗口filename
:文件名- 设置窗口标题为“Experiment Editor”
- 设置窗口几何尺寸为1280x800
- 创建停靠窗口和按钮
- 初始化时模拟实验序列并绘制
addButtons(self)
:添加保存和模拟按钮。
- 创建保存和模拟按钮
- 将按钮添加到布局中
- 连接按钮点击事件到相应的方法
createDocks(self)
:创建停靠区,包含左侧的 CodeEditor
和右侧的 ExperimentSequencer
。
- 创建
CodeEditor
实例并添加到左侧停靠区 - 创建
ExperimentSequencer
实例并添加到右侧停靠区
simulateExperiment(self)
:模拟当前版本的实验,将指令堆栈传递给 ExperimentSequencer
进行可视化。
- 保存文件,重建实验并模拟运行
- 将模拟的指令堆栈传递给
ExperimentSequencer
进行绘制
main()
函数
- 创建
QApplication
实例。 - 创建
CodeEditorParser
实例并显示。 - 启动应用程序的主循环。
def main():
app = QtWidgets.QApplication(sys.argv)
Win = CodeEditorParser(filename=sys.argv[1])
Win.show()
sys.exit(app.exec())
代码执行
代码通过命令行执行,接收一个文件名作为参数:
python script.py your_experiment_file.py
总结
该代码实现了一个集成代码编辑、实验序列解析和可视化的GUI工具。通过PyQt6提供的窗口和控件,用户可以方便地编辑实验代码,并实时查看实验序列的变化。
4 - 多线程任务管理器
该代码实现了一个实验管理器 (ExperimentManager
) 类,用于管理和运行实验队列中的任务。该管理器在一个工作线程中不断检查队列中的实验任务,并根据任务的优先级和状态来决定执行哪些实验。实验任务由测量、序列(或扫描)和运行组成,在执行实验时会调用相关的开始和结束函数来控制实验的流程。
以下是该代码的详细中文文档说明:
代码概述
该代码实现了一个实验管理器 (ExperimentManager
) 类,用于管理和运行实验队列中的任务。该管理器在一个工作线程中不断检查队列中的实验任务,并根据任务的优先级和状态来决定执行哪些实验。实验任务由测量、序列(或扫描)和运行组成,在执行实验时会调用相关的开始和结束函数来控制实验的流程。
依赖库
numpy
:用于数值计算和数组操作。time
:用于时间操作和延时。PyQt6.QtCore
:用于信号槽机制和日期时间操作。balic.GUI.browser_workers
:包含 Worker
类,用于多线程处理。balic.servers
:包含发送信息和错误的函数。
类 ExperimentManager
该类包含了管理实验任务队列的功能,包括运行实验、暂停实验、终止实验等。
初始化方法 __init__(self, browser=None)
初始化实验管理器,设置浏览器、属性、队列、线程池等,并启动实验队列。
参数:
运行实验方法 run(self, taskNr, progress_callback=None)
处理实验任务的运行逻辑。
参数:
taskNr
:任务编号。progress_callback
:进度回调函数。
内部方法 _sequence_run(self)
初始化运行循环并开始实验。
内部方法 _runloop(self)
运行实验(或实验扫描),在每次运行前检查实验是否暂停或终止。
暂停方法 pause(self)
暂停或继续实验。
启动队列方法 start_queue(self)
在实验线程中启动实验队列工作器。
队列函数 queue_fn(self, progress_callback=None)
实验队列持续检查实验字典中的新任务,如果字典为空或有实验在运行,则不做任何操作。
检查到期方法 due_check(taskDict)
检查任务是否到期。
参数:
返回:
更新表格方法 update_table(self)
通知 GUI 更新表格显示。
终止实验方法 terminate_experiment(self)
优雅地终止实验,允许当前运行完成。
开始测量方法 start_measurement(self)
设置参数并调用实验的 start_measurement
函数。
开始序列方法 start_sequence(self)
调用实验的 start_sequence
函数。
结束序列方法 end_sequence(self)
调用实验的 end_sequence
函数。
结束测量方法 end_measurement(self)
运行 end_measurement
函数,并清理表格。
设置字典方法 set_dict(self, name, value)
设置实验参数。
参数:
设置运行编号方法 set_run_nr(self, value)
设置实验运行编号并更新表格。
参数:
设置重复编号方法 set_rep_nr(self, value)
设置实验重复编号并更新表格。
参数:
代码详细说明
初始化 ExperimentManager
在初始化方法中,实验管理器会从浏览器对象中获取属性和队列,并启动实验队列工作器。实验队列工作器会在一个单独的线程中运行,不断检查实验队列中的任务。
运行实验 run
在运行实验方法中,实验管理器会检查任务是否存在,并获取任务的详细信息,包括实验名、参数、扫描参数、扫描值等。然后调用相关的开始和结束方法来控制实验的流程。
队列函数 queue_fn
队列函数会持续检查实验字典中的新任务,并根据任务的优先级和状态来决定执行哪些实验。如果任务到期,则运行任务,否则继续检查下一个任务。
暂停和终止实验
实验管理器提供了暂停和终止实验的方法。暂停方法会切换实验的暂停状态,并更新队列显示。终止方法会优雅地终止实验,允许当前运行完成。
其他辅助方法
实验管理器还提供了一些辅助方法,用于设置实验参数、更新表格显示等。
代码示例
以下是一个简单的使用示例:
from PyQt6.QtWidgets import QApplication
import sys
from balic.servers import Properties
from balic.GUI.browser import Browser
from balic.GUI.experiment_manager import ExperimentManager
app = QApplication(sys.argv)
browser = Browser()
experiment_manager = ExperimentManager(browser)
# 添加实验任务到队列
experiment_manager.queue.add_task(task_dict)
# 开始运行实验
experiment_manager.run(taskNr)
sys.exit(app.exec())
5 - 实验序列编辑器
该代码实现了一个实验浏览器(Browser
)应用程序,提供了图形用户界面来管理和运行各种实验。通过使用PyQt6库构建界面,代码实现了实验文件选择、实验队列管理、参数设置和实验执行等功能。
代码概述
该代码实现了一个实验浏览器(Browser
)应用程序,提供了图形用户界面来管理和运行各种实验。通过使用PyQt6库构建界面,代码实现了实验文件选择、实验队列管理、参数设置和实验执行等功能。
依赖库
os
、importlib.util
、inspect
、traceback
、numpy
、json
、datetime
:标准库,用于文件操作、模块加载、错误处理、数值计算和日期时间处理。PyQt6.QtCore
和 PyQt6.QtWidgets
:用于构建GUI应用程序。balic.servers
和 balic.GUI
:自定义库,用于属性管理和GUI组件。
类和方法
Browser
类
Browser
继承自 QMainWindow
,实现了实验浏览器的主界面。
信号:
方法:
__init__(self)
:初始化浏览器,设置属性、任务队列和其他组件,并恢复窗口几何设置。closeEvent(self, event)
:处理窗口关闭事件,调用父类的关闭事件处理方法。init_ui(self)
:初始化用户界面,设置状态栏和中央窗口区域,并创建停靠窗口。create_dock_widgets(self)
:创建停靠窗口,包含文件选择器、实验队列、准备站和循环器。open_experiment(self, filepath, startup=False)
:打开一个新的实验窗口,如果实验已经打开,则不执行任何操作。update_exp_window_task(self)
:更新所有实验窗口中的任务号。
ExperimentSubWindow
类
ExperimentSubWindow
继承自 QMdiSubWindow
,实现了实验窗口的容器。
方法:
__init__(self, name, props, parent=None)
:初始化子窗口,设置窗口标题和属性。store_geometry(self)
:存储窗口几何设置。
ExperimentWindow
类
ExperimentWindow
继承自 QWidget
,实现了单个实验的控制,包括参数设置和任务提交。
方法:
__init__(self, filepath, props, parent=None, browser=None)
:初始化实验窗口,加载实验文件并构建用户界面。init_ui(self)
:初始化用户界面,创建控件和布局。save_params(self, backup=False)
:保存实验参数到JSON文件。load_params(self)
:从JSON文件加载实验参数。get_files(self)
:打开文件选择对话框,获取文件路径。update_task_qsb(self)
:更新任务号选择框。submit_to_queue(self)
:提交实验到实验队列。submit_next(self)
:提交实验到实验队列,并设置最高优先级。submit_to_prepper(self)
:提交实验到准备站。setup_scan(self)
:设置扫描序列。setup_task_dict(self)
:创建任务字典,存储实验参数。build_argument_dict(self)
:创建实验参数字典。edit_sequence(self)
:打开序列编辑器窗口。closeEvent(self, event)
:处理窗口关闭事件,移除打开的窗口。gui_columns(self)
:返回GUI列数。arguments(self)
:返回实验参数。argument_names(self)
:返回实验参数名称列表。argument_dicts(self)
:返回实验参数字典列表。store_geometry(self)
:存储窗口几何设置。
SequenceEditor
类
SequenceEditor
继承自 QDialog
,实现了序列编辑器窗口。
方法:
__init__(self, experimentWindow, parent=None)
:初始化序列编辑器窗口,设置窗口标题和图标,并创建控件和布局。update_parameter(self, parname, n)
:根据参数名称更新参数的范围和步长。update_list_generator(self)
:更新列表生成器。update_values(self)
:更新序列编辑器中的值。
QDock
类
QDock
继承自 QDockWidget
,实现了包含实验队列、准备站和循环器的停靠窗口。
方法:
__init__(self, parent=None)
:初始化停靠窗口,设置布局和子控件。
FileSelector
类
FileSelector
继承自 QWidget
,实现了实验文件选择器。
方法:
__init__(self, browser, parent=None)
:初始化文件选择器,设置属性和布局,并创建文件树视图。file_model(self, directory)
:创建文件模型,用于显示目录中的文件和文件夹。set_column_widths(self)
:设置文件树视图的列宽。go_up(self)
:导航到上一级目录。create_context_menu(self, position)
:创建上下文菜单,提供编辑和组合编辑选项。open_editor(self, filename)
:打开代码编辑器。open_file(self)
:打开文件或目录。
辅助函数
filepath_split(filepath)
拆分文件路径,返回路径、扩展名、名称和文件名。
def filepath_split(filepath):
"""
takes /dir/name.ext
returns path, ext, name, filename
= /dir/name, ext, name, name.ext
"""
path, ext = os.path.splitext(filepath) # /dir/name, ext
name = os.path.basename(path) # name
filename = os.path.basename(filepath) # name.ext
return path, ext, name, filename
主函数
初始化应用程序并启动主窗口。
def main():
qApp = QApplication(sys.argv)
Win = Browser()
qApp.setWindowIcon(QIcon(iconpath + '/browser.png'))
Win.show()
sys._excepthook = sys.excepthook
sys.excepthook = exception_hook
sys.exit(qApp.exec())
def exception_hook(exctype, value, traceback):
send_error('[Browser] ' + str([exctype, value, traceback]))
sys._excepthook(exctype, value, traceback)
sys.exit(1)
# Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
send_info('[Browser] BaLi Browser starts')
if (sys.flags.interactive != 1) or not hasattr(Qt, 'PYQT_VERSION'):
main()
总结
该代码实现了一个实验浏览器应用程序,通过图形用户界面管理和运行各种实验。它提供了实验文件选择、实验队列管理、参数设置和实验执行等功能,并使用PyQt6库构建用户界面。
6 - 实验队列管理器
该代码实现了一个实验队列管理器,通过图形用户界面(GUI)来显示和控制实验任务队列。使用PyQt6库来构建界面,其中包含按钮和表格,用于管理实验任务。代码主要包括一个类:ExperimentQ
。
类和方法
ExperimentQ
类
ExperimentQ
继承自 QGroupBox
,实现了一个实验队列管理器,显示和控制实验任务队列。
方法:
__init__(self, browser, parent=None, title="Experiment Queue")
:初始化实验队列管理器。
browser
:浏览器对象,用于访问属性和实验管理器parent
:父窗口title
:组框标题,默认为"Experiment Queue"- 创建按钮和表格,并设置布局
- 初始化属性
- 设置锁定标志为
False
create_buttons(self)
:创建并配置控制按钮。
- 创建暂停按钮、终止按钮、终止所有按钮和重启按钮
- 设置按钮图标、提示信息和点击事件处理函数
init_table_actions(self)
:初始化表格操作。
- 创建删除、终止、终止所有、休眠和暂停操作
- 设置操作的快捷键和上下文
- 将操作添加到表格
init_table(self)
:初始化表格。
- 创建
QTableView
并设置选择行为、选择模式和上下文菜单策略 - 隐藏垂直表头并设置其调整模式为内容适应
- 初始化
expDict
为一个空字典 - 创建
ScheduleModel
并将其设置为表格模型 - 设置水平表头的调整模式
set_model(self, model)
:设置表格的新模型。
delete_clicked(self)
:从队列中删除任务,或者如果任务正在运行则优雅地终止。
- 获取选中的行,并获取对应的任务编号
- 如果任务状态为“Running”或“Scanning”,则标记任务为终止
- 否则,从模型中删除任务
- 设置当前索引为选中的行
terminate_clicked(self)
:优雅地终止选中的任务(允许完成运行)。
- 获取选中的行,并获取对应的任务编号
- 标记任务为终止,状态设置为“Termination Pending”
- 如果没有选中任务,且模型中有任务,则终止第一个任务
terminate_all(self)
:终止所有任务。
- 遍历所有任务,标记为终止,状态设置为“Termination Pending”
set_sleeping(self)
:将选中的任务设置为“Sleeping”状态,或者取消“Sleeping”状态。
- 获取选中的行,并获取对应的任务编号
- 如果任务状态为“Sleeping”,则设置为“Queued”,否则设置为“Sleeping”
pause(self)
:暂停当前运行的实验。
- 调用浏览器的实验管理器的暂停方法
- 根据实验管理器的暂停状态,切换暂停按钮的图标
update_item(self, k, v)
:更新表格中的项目。
k
:键(任务编号)v
:值(任务信息)- 将任务信息更新到模型中,并解锁
delete_item(self, k)
:删除表格中的项目。
restart(self)
:用于测试按钮的功能。
代码执行
该代码通过创建 ExperimentQ
对象,并将其嵌入到主窗口中来执行。主窗口中会包含实验队列的表格和控制按钮,用户可以通过这些按钮来控制实验任务的状态。
# 示例代码,展示如何创建并显示 ExperimentQ 对象
if __name__ == "__main__":
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
app = QApplication(sys.argv)
mainWindow = QMainWindow()
browser = ... # 创建或获取浏览器对象
experimentQueue = ExperimentQ(browser)
mainWindow.setCentralWidget(experimentQueue)
mainWindow.show()
sys.exit(app.exec())
7 - 多线程任务管理器
该代码实现了一个多线程工作者类(Worker
)和信号类(WorkerSignals
),用于在PyQt6应用程序中处理多线程任务。Worker
类继承自QRunnable
,WorkerSignals
类继承自QObject
,定义了一组可用的信号,以便在工作线程中传递信息。
代码概述
该代码实现了一个多线程工作者类(Worker
)和信号类(WorkerSignals
),用于在PyQt6应用程序中处理多线程任务。Worker
类继承自QRunnable
,WorkerSignals
类继承自QObject
,定义了一组可用的信号,以便在工作线程中传递信息。
依赖库
sys
:用于获取异常信息。traceback
:用于格式化异常跟踪信息。PyQt6.QtCore
:提供PyQt6核心功能,包括信号和槽机制、可运行对象等。
类和方法
WorkerSignals
类
WorkerSignals
继承自 QObject
,定义了一组可用的信号,以便在工作线程中传递信息。
信号:
finished
:没有数据,表示任务完成。error
:传递一个包含异常类型、异常值和格式化异常跟踪信息的元组。result
:传递处理函数返回的结果数据。progress
:传递一个整数,表示进度百分比。update_ui
:用于更新用户界面。tableUpdate
:用于更新表格。addItem
:传递一个整数和一个QVariant
,用于添加项目。deleteItem
:传递一个整数,用于删除项目。taskStart
:表示任务开始。taskDone
:表示任务完成。
class WorkerSignals(QObject):
'''
Defines the signals available from a running worker thread.
Supported signals are:
finished
No data
error
`tuple` (exctype, value, traceback.format_exc() )
result
`object` data returned from processing, anything
progress
`int` indicating % progress
'''
finished = pyqtSignal()
error = pyqtSignal(tuple)
result = pyqtSignal(object)
progress = pyqtSignal(int)
update_ui = pyqtSignal()
tableUpdate = pyqtSignal()
addItem = pyqtSignal(int, QtCore.QVariant)
deleteItem = pyqtSignal(int)
taskStart = pyqtSignal()
taskDone = pyqtSignal()
Worker
类
Worker
继承自 QRunnable
,实现了一个工作线程,用于处理耗时的任务。
方法:
class Worker(QRunnable):
'''
Worker thread
Inherits from QRunnable to handle worker thread setup, signals and wrap-up.
:param callback: The function callback to run on this worker thread. Supplied args and
kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function
'''
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
# Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress
@pyqtSlot()
def run(self):
"""
Initialise the runner function with passed args, kwargs.
"""
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
finally:
self.signals.finished.emit() # Done
总结
该代码定义了一个用于多线程任务处理的工作者类(Worker
)和信号类(WorkerSignals
)。Worker
类继承自QRunnable
,可以在工作线程中运行传入的函数,并通过信号机制传递任务的进度、结果或错误信息。WorkerSignals
类定义了一组信号,以便在工作线程和主线程之间进行通信。
8 - 自动保存编辑器
该代码实现了一个自动保存编辑器,通过图形用户界面(GUI)来管理和设置定期自动保存的时间。使用PyQt6库来构建界面,包括日期时间编辑控件和复选框。主要类包括:AutoSaveEditor
和 DateTimeWidget
。
类和方法
AutoSaveEditor
类
AutoSaveEditor
继承自 BWidget
,实现了一个自动保存时间的编辑器。
信号:
subscriptionsChanged
:订阅改变信号。propertiesChanged
:属性改变信号。
方法:
__init__(self, props, max_items, name='regular_auto_save_times', parent=None)
:初始化自动保存编辑器。
props
:属性对象,用于存储和管理配置数据。max_items
:最大项目数量。name
:属性名称,默认为’regular_auto_save_times’。parent
:父窗口。- 设置布局,并加载和初始化日期时间控件。
load_AutoSaveEditor_props(props, prop_name, max_items)
:加载自动保存编辑器属性。
props
:属性对象。prop_name
:属性名称。max_items
:最大项目数量。- 返回日期时间和勾选状态。
update_props(props, prop_name, datetimes, ticked)
:更新属性。
props
:属性对象。prop_name
:属性名称。datetimes
:日期时间数组。ticked
:勾选状态数组。
update_datetime(self, idx, datetime_save)
:更新日期时间。
idx
:索引。datetime_save
:新的日期时间。
update_ticked(self, idx, ticked)
:更新勾选状态。
accept(self)
:接受并保存更改。
DateTimeWidget
继承自 QFrame
,实现了一个日期时间选择控件。
方法:
__init__(self, idx, parent, datetime_save=datetime.datetime.now(), ticked=False)
:初始化日期时间控件。
idx
:索引。parent
:父组件。datetime_save
:初始日期时间,默认为当前时间。ticked
:初始勾选状态,默认为False。- 设置布局,创建并初始化日期时间编辑控件和复选框。
datetime_changed(self)
:日期时间改变事件处理。
ticked_changed(self)
:勾选状态改变事件处理。
主函数
代码通过创建 QApplication
实例和 AutoSaveEditor
窗口来执行,并启动应用程序的主循环。
if __name__ == '__main__':
import sys
props = Properties('Tests/AutoSaveEditor')
pp = AutoSaveEditor(props, max_items=20)
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QApplication.instance().exec_()
代码执行
该代码通过命令行执行,创建一个AutoSaveEditor
对象,并显示自动保存编辑器窗口。
类的详细说明
AutoSaveEditor
类
AutoSaveEditor
类用于管理和设置定期自动保存的时间。它提供了一个图形用户界面,允许用户添加、编辑和删除自动保存的时间点。
方法:
__init__(self, props, max_items, name='regular_auto_save_times', parent=None)
:
load_AutoSaveEditor_props(props, prop_name, max_items)
:
- 静态方法,用于从属性对象中加载日期时间和勾选状态,并返回它们。
update_props(props, prop_name, datetimes, ticked)
:
- 静态方法,用于更新属性对象中的日期时间和勾选状态。
update_datetime(self, idx, datetime_save)
:
update_ticked(self, idx, ticked)
:
accept(self)
:
- 接受并保存更改,更新属性并通知父组件,然后关闭窗口。
DateTimeWidget
类用于显示和编辑单个日期时间和勾选状态。
方法:
__init__(self, idx, parent, datetime_save=datetime.datetime.now(), ticked=False)
:
- 初始化方法,设置布局,创建并初始化日期时间编辑控件和复选框。
datetime_changed(self)
:
ticked_changed(self)
:
9 - 实验时间线查看器
该代码实现了一个实验时间线查看器,通过图形用户界面(GUI)来显示和控制实验时间序列。使用PyQt6库来构建界面,其中包含按钮、文本框和绘图区域。代码主要包括三个类:Timeline
、TimelinePlotter
和 SelectorWidget
。
类和方法
Timeline
类
Timeline
继承自 QMainWindow
,实现了一个实验时间线查看器。
方法:
__init__(self, *args, **kwargs)
:初始化时间线查看器。
- 设置窗口标题为"Timeline Viewer"。
- 加载JSON文件中的实验序列。
- 创建主窗口组件,包括文件加载区、绘图区和选择区。
- 设置中央窗口部件。
plot_experimental_sequence(self, time_dict, init_vals=None)
:绘制实验时间序列。
- 重置绘图区域。
- 深拷贝时间序列字典。
- 找到最大时间
tmax
。 - 为每个时间序列添加一个点,确保每个序列都在
tmax
结束。 - 准备颜色。
- 为每个选中的输出绘制折线图,并根据条件选择不同的颜色和填充方式。
load_file(self, filename)
:加载实验序列文件。
- 读取JSON文件并解析实验序列。
- 获取输出名称集合。
- 构建时间序列字典。
- 逆向设备数据库转换时间序列字典。
- 更新选择器组件中的名称。
decode_ttl_bitmask(self, bitmask, command)
:解码TTL位掩码。
- 将位掩码转换为二进制字符串。
- 找到二进制字符串中的所有1的位置。
- 根据命令生成TTL字典。
get_set_of_used_outputs(self, sequence)
:获取使用的输出集合。
construct_time_sequences(self, sequence, set_of_output_names)
:构建时间序列字典。
reverse_device_db_transformation(self, time_sequence_dict, device_db_path=workpath+'/configuration/')
:逆向设备数据库转换时间序列字典。
- 加载设备数据库。
- 构建逆向映射字典。
- 根据逆向映射字典转换时间序列字典中的名称和值。
TimelinePlotter
类
TimelinePlotter
继承自 GraphicsLayoutWidget
,用于可视化实验序列。
方法:
__init__(self, sequence)
:初始化绘图组件。
SelectorWidget
继承自 QFrame
,用于选择要显示的实验序列。
方法:
__init__(self, parent=None, clear_name_dict=None)
:初始化选择器组件。
get_names(self, clear_name_dict)
:获取并显示名称。
- 获取清晰名称字典的键并排序。
- 如果名称未变化,则直接返回。
- 否则,更新滚动区域中的复选框。
select_all(self)
:全选复选框。
deselect_all(self)
:取消全选复选框。
主函数
代码通过创建 QApplication
实例和 Timeline
窗口来执行,并启动应用程序的主循环。
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Timeline()
window.setWindowIcon(QtGui.QIcon(workpath + '/balic/icons/time_line.png'))
window.setWindowTitle('Timeline Viewer')
window.show()
sys.exit(app.exec())
代码执行
该代码通过命令行执行,创建一个Timeline
对象,并显示实验时间线查看器窗口。
10 - 多线程处理框架
该代码实现了一个基于 PyQt6 的多线程处理框架。主要包含两个类:WorkerSignals 和 Worker。WorkerSignals 类定义了线程工作时使用的各种信号,而 Worker 类继承自 QRunnable,用于处理多线程任务的设置、执行和信号传递。
功能介绍
该代码实现了一个基于 PyQt6
的多线程处理框架。主要包含两个类:WorkerSignals
和 Worker
。WorkerSignals
类定义了线程工作时使用的各种信号,而 Worker
类继承自 QRunnable
,用于处理多线程任务的设置、执行和信号传递。
代码结构
- 导入必要的模块和库
- 定义类
WorkerSignals
- 定义类
Worker
类:WorkerSignals
简短功能介绍
WorkerSignals
类定义了可从正在运行的工作线程发出的信号。这些信号用于指示线程的各种状态和进度,包括完成、错误、结果和进度更新。
属性
finished
:无数据的信号,指示线程已完成。error
:包含异常类型、值和追溯信息的元组信号。result
:处理结果数据的信号。progress
:指示进度的整数信号。terminate
:终止信号。start
:启动信号。publish
:发布信号。runDone
:运行完成信号。logging
:日志信号,包含日志信息字符串。clear
:清除信号。
类:Worker
简短功能介绍
Worker
类继承自 QRunnable
,用于处理工作线程的设置、信号和结束。它允许在独立线程中运行指定的函数,并通过信号机制与主线程通信。
方法:__init__
功能:初始化 Worker
对象,存储传递的函数及其参数,并设置信号。
参数:
fn
:要在工作线程中运行的函数。args
:传递给函数的参数。kwargs
:传递给函数的关键字参数。
主要步骤:
- 调用父类
QRunnable
的构造函数。 - 存储传递的函数及其参数。
- 初始化
WorkerSignals
对象。 - 将进度回调添加到关键字参数中。
方法:run
功能:初始化传递的函数并执行它,处理可能的异常,并通过信号机制传递结果。
主要步骤:
- 尝试运行传递的函数,并捕获其结果。
- 如果发生异常,捕获异常信息并发出
error
信号。 - 如果函数运行成功,发出
result
信号传递结果。 - 最后,无论是否发生异常,发出
finished
信号指示完成。
11 - 实验任务循环管理器
该代码定义了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。
代码结构
- 导入必要的模块和库
- 定义主要的
Looper
类 - 定义
LoopItem
类及其子类(TaskItem
, ListItem
, ConditionalItem
等) - 定义辅助类(
DataManager
, LoopManager
, LoopGroup
等) - 实现 GUI 部件和功能
类:Looper
简短功能介绍
Looper
类是整个循环控制的核心。它负责管理任务组、定时器、按钮和循环的启动与终止。
方法
__init__
:初始化 Looper
对象,设置属性和布局,创建按钮和标签页。add_tab
:添加新的任务组标签页。delete_tab
:删除指定的任务组标签页。create_buttons
:创建控制按钮。save_loop
:保存当前循环配置。load_loop
:加载之前保存的循环配置。terminate_by_time
:根据时间自动终止循环。
类:LoopItem
简短功能介绍
LoopItem
类是所有循环项的基类,提供了基本的属性和方法。它被 TaskItem
, ListItem
, ConditionalItem
等类继承,并扩展其功能。
子类:TaskItem
- 功能:表示单个任务项,执行特定的任务并跟踪运行次数。
- 方法:
__init__
:初始化任务项,设置任务信息和UI组件。run
:执行任务,并将任务添加到任务队列。end_run
:更新任务状态和运行次数。task_start
:任务开始时的操作。task_end
:任务结束时的操作。update_task
:更新任务信息。max_check
:检查是否达到最大运行次数。
子类:ListItem
- 功能:表示一个任务列表,按顺序执行列表中的任务。
- 方法:
__init__
:初始化任务列表项,设置任务列表信息和UI组件。run
:执行当前任务,并更新任务索引。end_run
:更新任务列表状态和运行次数。edit_list
:编辑任务列表。update_task_labels
:更新任务标签。
子类:ConditionalItem
- 功能:表示条件项,根据条件的结果执行不同的任务。
- 方法:
__init__
:初始化条件项,设置条件信息和UI组件。run
:评估条件并选择下一步操作。update_boxes
:更新条件的UI组件。
类:DataManager
简短功能介绍
DataManager
类管理数据订阅和更新,处理数据的接收和存储。
方法
__init__
:初始化数据管理器,设置属性和数据客户端。set_new_data
:处理新的数据,更新数据字典。edit_subscriptions
:编辑数据订阅。
类:LoopManager
简短功能介绍
LoopManager
类负责管理循环的执行,处理循环任务的启动、终止和控制。
方法
__init__
:初始化循环管理器,设置属性和线程池。loop_fn
:循环执行函数,处理循环任务。run_loop
:启动循环任务。terminate
:终止循环任务。task_start
:任务开始时的操作。task_end
:任务结束时的操作。
类:LoopGroup
简短功能介绍
LoopGroup
类表示一个任务组,包含多个任务项和一个组项。
方法
__init__
:初始化任务组,设置属性和布局。create_buttons
:创建任务组的控制按钮。delete_group
:删除任务组。update_loop_group_file
:更新任务组配置文件。
类:Baustelle
简短功能介绍
Baustelle
类是任务组的编辑器,提供添加、删除和编辑任务项的功能。
方法
__init__
:初始化编辑器,设置属性和布局。add_task_item
:添加新的任务项。add_list_item
:添加新的任务列表项。add_conditional
:添加新的条件项。add_group_item
:添加新的组项。update_idx
:更新任务项的索引。
类:LoopSubMgr
简短功能介绍
LoopSubMgr
类是一个GUI,用于编辑数据流的订阅。
方法
__init__
:初始化订阅管理器,设置属性和布局。update_subscriptions
:更新数据流的订阅。
类:ListEdit
简短功能介绍
ListEdit
类是一个对话框,用于编辑 ListItem
的任务列表。
方法
__init__
:初始化对话框,设置属性和布局。closeEvent
:在对话框关闭时更新任务列表。
类:PrepBox 和 ListBox
简短功能介绍
PrepBox
和 ListBox
类分别用于显示和管理预备任务列表和当前任务列表。
方法
init_ui
:初始化UI组件。set_model
:设置数据模型。push_selection
:将选中的任务添加到任务列表。unpack_selection
:解包选中的任务并添加到任务列表。move_up
:在任务列表中上移任务。move_down
:在任务列表中下移任务。delete_clicked
:删除选中的任务。
类:VLine
简短功能介绍
VLine
类用于在UI中创建分隔线。
简短功能介绍
LoopTabWidget
和 EditableTabBar
类自定义了标签页控件,允许编辑标签页名称和管理标签页。
方法
addTab
:添加新的标签页。setIconOn
:设置标签页的图标为启用状态。setIconOff
:设置标签页的图标为禁用状态。setTabText
:设置标签页的文本。editTab
:编辑标签页名称。
辅助函数
功能
get_experiment
:获取实验对象。edit_key_in_place
:编辑字典中的键。find_or_add
:在组合框中查找或添加项目。parse_text
:解析文本,转换为特定格式。
12 - 实验任务预备队列管理器
该代码定义了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。
概要
该代码定义了用于管理和展示队列(Queue)和准备站(PrepStation)表格的模型。模型指定了表格数据的形式(如字典或列表),以及如何展示和操作这些数据,包括如何获取、设置和删除数据,以及如何排序数据。模型可以直接从数据集中读取数据,并在数据集发生更改时自动更新表格。
主要类和功能
_SyncSubstruct
此类定义了用于操作表格数据字典的方法。方法包括添加、插入、弹出、设置、删除和获取字典项。这个类主要是为了便于在模型中更新数据字典,并在数据更新时调用回调函数来通知模型。
DictSyncModel
这是一个基类,用于将字典(backing_store
)转换为表格。模型可以读取和编辑字典中的数据,并根据需要更新表格显示。其主要功能包括:
- 初始化模型,设置表头、数据名称和初始数据。
- 获取行数和列数。
- 获取和设置单元格数据。
- 获取表头数据。
- 插入和删除字典项。
- 排序和转换数据。
关键方法
rowCount
: 返回字典中的条目数。columnCount
: 返回列的数量,即表头的数量。data
: 根据索引和角色获取数据。setData
: 设置单元格数据,并根据需要进行类型转换。headerData
: 获取表头数据。__setitem__
: 插入或更新字典项,并更新表格显示。__delitem__
: 删除字典项,并更新表格显示。__getitem__
: 获取字典项。sort_key
: 定义排序键(由子类实现)。convert
: 将字典键转换为列数据(由子类实现)。flags
: 定义单元格的属性(由子类实现)。
ScheduleModel
这是用于管理实验队列的表格模型。数据结构是一个字典,每个字典代表一个实验任务。任务按优先级和任务编号排序。
关键方法
sort_key
: 返回用于排序的键,首先按优先级排序,然后按任务编号排序。convert
: 将字典键转换为列数据,特别是将日期时间字符串转换为 QDateTime
对象。flags
: 定义可编辑和不可编辑的列。
ListSyncModel
类似于 DictSyncModel
,但处理的是列表而不是字典。主要用于管理需要按顺序排列的数据集合。
关键方法
rowCount
: 返回列表的长度。columnCount
: 返回列的数量。data
: 根据索引和角色获取数据。setData
: 设置单元格数据,并根据需要进行类型转换。headerData
: 获取表头数据。__delitem__
: 删除列表项,并更新表格显示。__getitem__
: 获取列表项。sort_key
: 定义排序键(由子类实现)。convert
: 将列表项转换为列数据(由子类实现)。flags
: 定义单元格的属性(由子类实现)。
PrepModel
这是用于准备站的表格模型,使用列表来管理任务,因为准备站中的任务不需要唯一的 ID,可以自由更改顺序。
关键方法
convert
: 将列表项转换为列数据,特别是将日期时间字符串转换为 QDateTime
对象。flags
: 定义可编辑和不可编辑的列。
功能特点
- 数据同步:模型可以同步更新数据字典或列表,并通知表格更新显示。
- 数据排序:模型可以根据指定的键对数据进行排序。
- 数据转换:模型可以根据列索引转换数据类型,如将日期时间字符串转换为
QDateTime
对象。 - 数据编辑:模型支持在表格中编辑数据,并将更改反映到原始数据集合中。
- 表格属性:模型可以定义哪些列是可编辑的,哪些是只读的。
运行流程
- 创建模型实例,传入初始数据。
- 模型通过
rowCount
和 columnCount
方法告知表格数据的大小。 - 表格通过
data
方法获取单元格数据并显示。 - 当用户编辑表格时,模型通过
setData
方法更新数据集合。 - 模型通过发射信号通知表格数据已更改,表格自动更新显示。
13 - 实验运行调度管理器
该代码片段展示了一个实验调度和运行系统的基础结构,主要通过 Prepper
类来实现。
详细代码分析:Prepper 类及其相关组件
该代码片段展示了一个实验调度和运行系统的基础结构,主要通过 Prepper
类来实现。下面是对代码的详细分析,涵盖了导入的模块、类的定义及其成员、注释和错误处理等方面。
Prepper 类
Prepper
类是该代码的核心部分,用于调度和运行实验。以下是对该类的详细分析:
初始化方法
__init__
方法:- 接受一个可选的
browser
参数。 - 通常用于初始化类实例,并设置一些初始状态或配置。
- 该方法在注释中提到了与
browser
、queue
和 expDict
相关的初始化操作,但具体实现被注释掉了。
未实现的方法
start_looper
方法:
- 该方法在注释中被提到,但未实际实现。
- 预期用于启动一个循环器线程,可能用于定期执行某些任务。
looper_fn
方法:
- 该方法同样在注释中被提到,但未实际实现。
- 预期用于定义循环器线程的主要功能,在特定的时间间隔内更新数据或执行其他操作。
线程池
- 线程池初始化:
- 代码展示了如何初始化一个线程池,并将最大线程数设置为1。
- 这表明该类可能被设计为单线程操作,以避免多线程带来的复杂性和潜在问题。
类中的成员
成员变量:
browser
:表示浏览器对象,可能用于界面显示和用户交互。queue
:表示任务队列,用于存储和管理待执行的实验。expDict
:表示实验字典,用于存储实验的配置信息和状态。threadpool
:表示线程池对象,用于管理和调度线程。
定时器:
timer
变量表示一个定时器对象,通常用于在特定的时间间隔内执行某些操作,例如定期更新界面或检查任务状态。
注释和错误处理
注释:
- 代码中的注释提供了对各部分功能的描述,尤其是对尚未实现的功能和预期行为的说明。
- 这些注释对于理解代码的设计意图和未来的开发计划非常有帮助。
错误处理:
traceback
模块的导入表明代码中可能包含对错误的捕获和处理。- 通过捕获异常并使用
traceback
模块获取详细的堆栈跟踪信息,有助于在调试过程中快速定位问题。
类的拓展性
代码结构:
- 该类的设计比较模块化,每个方法和成员变量都有明确的职责。
- 这种设计有助于代码的拓展和维护。
潜在功能:
- 通过引入更多的成员变量和方法,可以扩展类的功能,例如添加更多的实验调度策略、支持更多类型的实验等。
结论
总体而言,该代码片段展示了一个实验调度和运行系统的基础结构。虽然部分功能尚未实现,但通过详细的注释和模块化的设计,可以看出该类旨在通过线程和定时器来管理实验的调度和执行。这为未来的扩展和优化提供了良好的基础。
14 - 实验预处理
该代码展示了一个名为 PrepStation
的类,用于实验的预处理和队列管理。
详细代码分析:PrepStation 类及其相关组件
该代码展示了一个名为 PrepStation
的类,用于实验的预处理和队列管理。以下是对代码的详细分析,涵盖导入的模块、类的定义及其成员、注释和错误处理等方面。
PrepStation 类
PrepStation
类是该代码的核心部分,用于管理实验任务的预处理和队列操作。以下是对该类的详细分析:
初始化方法
__init__
方法:- 初始化类实例,并设置初始状态或配置。
- 接受
browser
、parent
和 title
作为参数。 - 初始化各类成员变量,如
parent
、browser
、queue
、expDict
、qWidget
、layout
、buttonLayout
、props
、_task
、paramDir
、table
和 prepList
。 - 调用
init_ui
和 init_table_actions
方法来设置界面和表格操作。
初始化界面
init_ui
方法:- 配置表格的垂直头,使其根据内容调整大小并隐藏。
- 创建各种按钮并设置其图标、工具提示和点击事件。
- 将按钮添加到布局中,并设置布局对齐方式。
初始化表格操作
init_table_actions
方法:- 创建各种操作(如删除、休眠、推送、循环、编辑、查看和保存)并设置其快捷键和触发事件。
- 将这些操作添加到表格中。
设置模型
set_model
方法:- 创建
PrepModel
实例并设置为表格模型。 - 调整表格列的宽度。
加载之前的任务
load_previous
方法:- 从
prepfile.json
文件中加载之前的任务列表,并打开相关的实验窗口。 - 更新
prepList
。
任务操作方法
push
方法:
- 将选中的任务推送到队列中,如果没有选中任务,则推送第一个任务。
push_row
方法:
push_first
方法:
delete_clicked
方法:
set_sleeping
方法:
push_to_looper
方法:
move_up
方法:
move_down
方法:
update_prep_file
方法:
- 更新任务文件,将当前任务列表保存到
prepfile.json
。
编辑和查看参数
open_editor
方法:
open_viewer
方法:
save_params
方法:
其他类和辅助函数
ExpEditWindow
类:
- 用于编辑实验参数的窗口。
- 初始化方法
__init__
和 init_ui
。 - 提交修改的方法
submit
。
ParViewWindow
类:
- 用于查看实验参数的窗口。
- 初始化方法
__init__
和 init_ui
。
CheckLayout
类:
- 包含复选框和参数输入框的布局。
- 初始化方法
__init__
。
DisplayBox
类:
- 显示参数名称、值和单位的布局。
- 初始化方法
__init__
和 init_ui
。
get_experiment
函数:
15 - 实验集合管理器
该代码定义了一个用于管理和监控多个进程的图形用户界面(GUI)应用程序。
环境变量设置
代码通过检查操作系统平台设置了 QT_SCALE_FACTOR
环境变量,以调整 MacOS 上的缩放比例。
SingleProcess 类
SingleProcess
类用于表示单个可管理的进程。以下是该类的详细分析:
初始化方法
__init__
方法:- 初始化类实例,并设置初始状态或配置。
- 接受
script
、name
、active
、category
和 parent
作为参数。 - 根据操作系统平台设置 Python 解释器路径。
- 创建和配置按钮、布局和样式表。
- 配置定时器以定期更新进程状态。
进程管理方法
startProcess
方法:
- 停止现有进程(如果有)。
- 使用
subprocess.Popen
启动新的进程。 - 更新按钮状态。
stopProcess
方法:
- 停止现有进程,根据需要选择终止或强制杀死进程。
- 更新按钮状态。
updateStatus
方法:
- 定期检查进程状态并更新按钮颜色以指示进程是否运行。
析构方法
ProcessManager 类
ProcessManager
类用于管理多个 SingleProcess
实例。以下是该类的详细分析:
初始化方法
__init__
方法:- 初始化类实例,并设置初始状态或配置。
- 读取配置文件,创建
SingleProcess
实例,并将其添加到布局中。 - 设置窗口样式和布局。
事件处理方法
析构方法
主函数
main
方法:- 设置高 DPI 缩放策略。
- 创建应用程序实例,设置窗口图标,创建和显示主窗口。
- 进入应用程序事件循环。
结论
该代码实现了一个进程管理和监控的 GUI 应用程序。通过 SingleProcess
类,用户可以启动、停止和监控单个进程;通过 ProcessManager
类,用户可以批量管理多个进程。代码结构清晰,使用了 PyQt6 的多种控件和信号槽机制来实现用户交互和数据更新。
16 - 实验参数管理器
该代码定义了一个用于管理和监控多个进程的图形用户界面(GUI)应用程序。
DefaultExp
类概述
DefaultExp
是一个实验类,包含所有设备的属性,并组织这些属性以便于管理和访问。
方法详解
build(self)
- 功能:
- 设置实验的初始参数,如计数器。
- 遍历设备数据库,加载设备并根据设备类型进行分组。
- 处理属性的加载错误,并将设备分为显示和隐藏两类。
run(self)
BasicManager
类概述
BasicManager
是一个基础管理类,用于创建和管理各种类型的参数小部件。
构造方法
参数说明:
props
:属性对象,用于与属性数据库交互。parent
:父级小部件。kwargs
:其他参数,用于初始化属性。
功能:
- 初始化基础属性,如单位、最大最小值、步长等。
- 设置样式并调用
_initGUI
方法。
方法详解
_initGUI(self)
- 功能:
- 初始化图形界面,创建布局和标签。
- 如果存在工具提示,则设置工具提示。
- 创建定时器,用于定期更新参数值。
updateSpin(self)
IntManager
类概述
IntManager
继承自 BasicManager
,用于管理整数类型的参数。
方法详解
initSpin(self)
- 功能:
- 创建一个整数选择框 (
QSpinBox
)。 - 从属性对象中获取初始值并设置到选择框中。
- 连接选择框的值变化信号到
updateValue
方法。
updateValue(self, v)
updateSpin(self)
- 功能:
- 检查属性对象中的值是否有变化,如果有变化,则更新选择框中的值。
BoolManager
类概述
BoolManager
继承自 QCheckBox
,用于管理布尔类型的参数。
方法详解
updateValue(self, val)
updateCheckbox(self)
- 功能:
- 检查属性对象中的布尔值是否有变化,如果有变化,则更新复选框的状态。
FloatManager
类概述
FloatManager
继承自 IntManager
,用于管理浮点数类型的参数。
方法详解
initSpin(self)
- 功能:
- 创建一个浮点数选择框 (
QDoubleSpinBox
)。 - 从属性对象中获取初始值并设置到选择框中。
- 连接选择框的值变化信号到
updateValue
方法。
ComboManager
类概述
ComboManager
继承自 BasicManager
,用于管理下拉列表类型的参数。
方法详解
initSpin(self)
- 功能:
- 创建一个下拉列表 (
QComboBox
)。 - 根据提供的字符串列表初始化下拉列表的选项。
- 连接下拉列表的选项变化信号到
updateValue
方法。
updateValue(self, v)
FrequencyManager
类概述
FrequencyManager
继承自 IntManager
,用于管理频率类型的参数。
方法详解
initSpin(self)
- 功能:
- 创建一个浮点数选择框 (
QDoubleSpinBox
),用于选择频率值。 - 从属性对象中获取初始值并设置到选择框中。
- 连接选择框的值变化信号到
updateValue
方法。
SimpleDefaults
类概述
SimpleDefaults
继承自 QFrame
,用于显示和管理实验的默认参数。
构造方法
- 功能:
- 初始化属性对象和实验对象。
- 调用实验对象的
build
方法构建实验。 - 创建和设置主布局。
- 根据实验的分组创建相应的参数管理小部件,并添加到布局中。
主函数
main()
- 功能:
- 创建应用程序实例。
- 创建
SimpleDefaults
窗口并显示。 - 启动应用程序的事件循环。
使用场景
- 该代码主要用于科学实验和数据采集系统中,帮助管理和显示实验的各种参数。
- 适用于需要频繁调整参数和查看参数变化的场景,如实验室研究和工业控制系统。
17 - 实验运行监视器
这个代码定义了一个 StreamMonitor
类和一个 TableModel
类,用于监控和显示各种数据流(包括数据流、图像流、命令流和消息流)的内容。它还提供了一个主函数 main
,用于初始化和运行一个包含多个标签页的 PyQt 应用程序,每个标签页显示不同类型的数据流。
概述
这个代码定义了一个 StreamMonitor
类和一个 TableModel
类,用于监控和显示各种数据流(包括数据流、图像流、命令流和消息流)的内容。它还提供了一个主函数 main
,用于初始化和运行一个包含多个标签页的 PyQt 应用程序,每个标签页显示不同类型的数据流。
StreamMonitor
类
StreamMonitor
类继承自 QWidget
,用于监控和显示不同类型的数据流。
主要方法和属性
初始化方法 __init__
:
- 接受参数:
name
(数据流名称)、streamtype
(数据流类型,如 ‘Data’、‘Image’ 等)、parent
(父组件)。 - 根据
streamtype
初始化不同类型的客户端对象(如 DataClient
、ImageClient
等)。 - 初始化用户界面,包括标签、复选框、组合框、按钮、文本编辑器和表格视图等。
- 设置一个定时器,用于定期更新数据流的内容。
_update_message
方法:
- 定期从消息流中获取新消息,并将其添加到消息列表中。
- 如果启用了保存选项,则将新消息写入文件。
update_text
方法:
- 根据用户选择的过滤条件更新显示的消息内容。
- 使用
QTableView
显示过滤后的消息列表。
_update_selected_message
方法:
_update_list
方法:
- 定期从数据流中获取新数据,并将其添加到消息列表中。
load_selected_message
方法:
- 打开文件对话框,允许用户选择一个消息文件,并加载文件内容。
update_table_selected
方法:
on_checkbox_changed
方法:
TableModel
类
TableModel
类继承自 QAbstractTableModel
,用于管理和显示表格数据。
主要方法和属性
初始化方法 __init__
:
data
方法:
rowCount
方法:
columnCount
方法:
headerData
方法:
使用场景
- 该代码主要用于需要监控和显示实时数据流的应用程序。
- 适用于数据分析、系统监控和日志管理等场景。
- 提供了一种用户友好的方式,通过图形界面查看和管理不同类型的数据流和消息内容。
主函数 main
- 创建一个包含多个标签页的
QTabWidget
,每个标签页显示不同类型的数据流。 - 初始化和运行 PyQt 应用程序。
通过这种方式,用户可以方便地在一个界面中监控和管理多种类型的数据流和消息内容。
18 - 实验任务折叠管理
这段代码定义了一个可折叠的对话框组件,允许用户在 PyQt6 应用程序中添加和管理可折叠的部分。主要组件包括 SectionExpandButton
类和 CollapsibleDialog
类,它们协同工作以实现可折叠部分的创建和管理。
概述
这段代码定义了一个可折叠的对话框组件,允许用户在 PyQt6 应用程序中添加和管理可折叠的部分。主要组件包括 SectionExpandButton
类和 CollapsibleDialog
类,它们协同工作以实现可折叠部分的创建和管理。
主要组件
SectionExpandButton
类继承自 QPushButton
,用于创建一个可以展开或折叠其关联部分的按钮。
主要方法和属性
初始化方法 __init__
:
- 接受参数:
item
(关联的部分项)、text
(按钮文本)、parent
(父组件)。 - 设置按钮的初始文本,并连接点击事件到
on_clicked
方法。
on_clicked
方法:
- 处理按钮的点击事件,切换关联部分的展开或折叠状态。
- 如果部分是展开的,则将其折叠;如果是折叠的,则将其展开。
CollapsibleDialog
类
CollapsibleDialog
类继承自 QDialog
,用于创建一个包含可折叠部分的对话框。
主要方法和属性
初始化方法 __init__
:
- 创建并配置一个
QTreeWidget
以隐藏标题,并将其添加到对话框的布局中。 - 设置树的缩进为 0。
add_section
方法:
- 接受参数:
title
(部分的标题)、widget
(部分的内容部件)。 - 调用
add_button
方法创建一个按钮,并调用 add_widget
方法将内容部件添加为按钮的子项。
define_sections
方法:
- 定义并添加对话框中的所有部分。
- 示例实现中,创建了一个包含两个标签的
QFrame
部件,并将其添加到部分中。
add_button
方法:
- 接受参数:
title
(部分的标题)。 - 创建一个
QTreeWidgetItem
项,并将其添加为树的顶级项。 - 创建一个
SectionExpandButton
按钮,将其设置为项的部件,并返回该项。
add_widget
方法:
- 接受参数:
button
(按钮项)、widget
(部分的内容部件)。 - 创建一个
QTreeWidgetItem
项,将其设置为按钮项的子项,并将内容部件设置为子项的部件。
使用场景
这个组件可以用于需要包含多个可折叠部分的对话框的应用程序,例如设置对话框、信息面板等。通过扩展 CollapsibleDialog
类并重写 define_sections
方法,可以轻松定义和管理对话框中的各个部分。
示例
下面是一个示例,展示如何使用 CollapsibleDialog
和 SectionExpandButton
类:
import sys
from PyQt6.QtWidgets import QApplication
class MyCollapsibleDialog(CollapsibleDialog):
def define_sections(self):
widget1 = QFrame(self.tree)
layout1 = QHBoxLayout(widget1)
layout1.addWidget(QLabel("Content 1"))
self.add_section("Section 1", widget1)
widget2 = QFrame(self.tree)
layout2 = QHBoxLayout(widget2)
layout2.addWidget(QLabel("Content 2"))
self.add_section("Section 2", widget2)
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog = MyCollapsibleDialog()
dialog.define_sections()
dialog.show()
sys.exit(app.exec())
在这个示例中,MyCollapsibleDialog
类继承自 CollapsibleDialog
并重写了 define_sections
方法,定义了两个可折叠部分。运行此代码将显示一个包含两个可折叠部分的对话框。