有这样一个需求,把图中界面上每个控件显示的内容保存到字典中。
每个控件类都有一个获取展示内容的方法,所以,最直观的实现方案就是逐个控件取。
例如,策略名称(StrategyKey
)属于QLineEdit
类,用text
方法获取展示内容,self.StrategyKey.text()
;账户(InvestorID
)属于QComboBox
类,用currentText
方法获取展示内容,self.InvestorID.currentText()
……
def save(self):
result = {}
# 策略名称
result['StrategyKey'] = self.StrategyKey.text()
# 账户
result['InvestorID'] = self.InvestorID.currentText()
# 买卖
direction = self.Direction.currentText()
if direction == '买':
result['Direction'] = '0'
elif direction == '卖':
result['Direction'] = '1'
# 优先平今
reusult['CloseTodayFirst'] = self.CloseTodayFirst.isChecked()
# 开平
offset_flag = self.OffsetFlag.currentText()
if offset_flag == "开":
result['OffsetFlag'] = '0'
elif offset_flag == "平":
result['OffsetFlag'] = '1'
elif offset_flag == "平今":
result['OffsetFlag'] = '3'
elif offset_flag == "平昨":
result['OffsetFlag'] = '4'
# 报价基准
benchmark_flag = self.BenchmarkFlag.currentText()
if benchmark_flag == "对手价":
result['BenchmarkFlag'] = '0'
elif benchmark_flag == "排队价":
result['BenchmarkFlag'] = '1'
# 交易所
result['ExchangeID'] = self.ExchangeID.currentText()
# 合约代码
result['InstrumentID'] = self.InstrumentID.currentText()
# 目标数量
result['TargetVolume'] = self.TargetVolume.value()
# 单次最大量
result['MaxOrderVolumeLimit'] = self.MaxOrderVolumeLimit.value()
# 开始时间
result['StartTime'] = self.StartTime.time().asString()
# 执行间隔
result['ExecuteGap'] = self.ExecuteGap.value()
return result
虽然这样实现是最简单的,但是当控件多且业务场景复杂时,会产生大量重复代码,也更容易出错。所以,不管从开发的角度还是维护的角度,这样写都不是最优的。
接下来,我们进行优化。
首先,买卖、开平、报单基准价这三个控件展示的是文本,而程序需要的是枚举值,它们之间存在一一对应关系。我们将展示值和枚举值对应关系存放在字典中,使用字典的查找功能就可以替代if……else……
。
# 买卖展示值 - 枚举值字典
direction_text_flag_dict = {
'买':'0',
'卖':'1'
}
# 开平展示值 - 枚举值字典
offsetflag_text_flag_dict = {
'开':'0',
'平':'1',
'平今':'3',
'平昨':'4'
}
# 报价基准展示值 - 枚举值字典
benchmarkflag_text_flag_dict = {
'对手价':'0',
'排队价':'1'
}
# 买卖枚举值
result['Direction'] = benchmarkflag_text_flag_dict.get(self.Direction.currentText(), '')
# 开平枚举值
result['OffsetFlag'] = benchmarkflag_text_flag_dict.get(self.OffsetFlag.currentText(), '')
# 报价基准枚举值
result['BenchmarkFlag'] = benchmarkflag_text_flag_dict.get(self.BenchmarkFlag.currentText(), '')
当对应关系发生变化时,也只需要修改字典中的对应关系。
当需要反向转换时,根据枚举值设置展示值,也可以轻松实现。
# 买卖枚举值 - 展示值字典
direction_flag_text_dict = dict(zip(direction_text_flag_dict.values(), direction_text_flag_dict.keys()))
# 开平枚举值 - 展示值字典
offsetflag_flag_text_dict = dict(zip(offsetflag_text_flag_dict.values(), offsetflag_text_flag_dict.keys()))
# 报价基准枚举值 - 展示值字典
benchmarkflag_flag_text_dict = dict(zip(benchmarkflag_text_flag_dict.values(), benchmarkflag_text_flag_dict.keys()))
# 买卖展示值
direction_display = direction_flag_text_dict.get(item_dict['Direction'], '')
# 开平展示值
offsetflag_display = offsetflag_flag_text_dict.get(item_dict['OffsetFlag'], '')
# 报价基准展示值
benchmarkflag_display = benchmarkflag_flag_text_dict.get(item_dict['BenchmarkFlag'], '')
进一步,利用python
语言的反射机制,我们就可以根据名称查找到关联控件实例,例如,getattr(self, 'StrategyKey')
就是self.StrategyKey
。这样我们就可以通过遍历控件名列表获得相关展示值,并存到字典中。
def from_display(widget, text_list, currenttext_list, value_list, time_list):
result = {}
# QLineEdit类控件展示值
for item in text_list:
result[item] = getattr(widget, item).text() if getattr(widget, item) else ''
# QComboBox类控件展示值
for item in currenttext_list:
result[item] = getattr(widget, item).currentText() if getattr(widget, item) else ''
# QComboBox类控件展示值
for item in value_list:
result[item] = getattr(widget, item).value() if getattr(widget, item) else 0
# QTimeEdit类控件展示值
for item in time_list:
result[item] = getattr(widget, item).time().asString() if getattr(widget, item) else ''
return result
def save(self):
text_list = [
'StrategyKey',
]
current_list = [
'InvestorID',
'ExchangeID',
'InstrumentID'
]
value_list = [
'TargetVolume',
'MaxOrderVolumeLimit',
'ExecuteGap',
]
time_list = [
'StartTime',
]
reust = from_display(self, text_list, current_list, value_list, time_list)
result['Direction'] = direction_text_flag_dict.get(self.Direction.currentText(),'')
result['OffsetFlag'] = offsetflag_text_flag_dict.get(self.OffsetFlag.currentText(),'')
result['BenchmarkFlag'] = benchmarkflag_text_flag_dict.get(self.BenchmarkFlag.currentText(),'')
因为不同类控件获取展示值的方法不同,QLineEdit
使用text
方法,QComboBox
使用currentText
方法,QSpinBox
使用value
方法,所以理论上有几个控件,上述代码中的from_display
函数就需要几个参数,还是不够简洁。
是否可以给不同的控件类增加一个相同的获取展示值的方法呢?当然可以。
QtWidgets.QLineEdit.getDisplay = lambda self: self.text()
QtWidgets.QTimeEdit.getDisplay = lambda self: self.time().toString()
QtWidgets.QCheckBox.getDisplay = lambda self: self.isChecked()
QtWidgets.QSpinBox.getDisplay = lambda self: self.value()
QtWidgets.QComboBox.getDisplay = lambda self: self.currentText()
通过以上定义,我们就给用到的几个控件类打了个补丁,增加一个getDisplay
方法,用来获取展示值。
之前写的from_display
函数就不需要区分具体的控件类了。
def from_display(widget, item_list):
result = {}
for item in item_list:
reuslt[item] = getattr(self, item).getDisplay()
return result
def save(self):
item_list = [
'StrategyKey',
'InvestorID',
'ExchangeID',
'InstrumentID',
'TargetVolume',
'MaxOrderVolumeLimit',
'ExecuteGap',
'StartTime',
'CloseTodayFirst',
]
reuslt = from_display(self, item_list)
result['Direction'] = benchmarkflag_text_flag_dict.get(self.Direction.currentText(), '')
result['OffsetFlag'] = benchmarkflag_text_flag_dict.get(self.OffsetFlag.currentText(), '')
result['BenchmarkFlag'] = benchmarkflag_text_flag_dict.get(self.BenchmarkFlag.currentText(), '')
优化之后,随着控件数量的增加,代码量不会显著增加。
通过上述过程总结下来的经验,将字典中的数值展示到控件上的需求就可以这样来实现了:
QtWidgets.QLineEdit.setDisplay = lambda self, value: self.setText(str(value))
QtWidgets.QTimeEdit.setDisplay = lambda self, value: self.setTime(QtCore.QTime.fromString(value))
QtWidgets.QCheckBox.setDisplay = lambda self, value: self.setChecked(value)
QtWidgets.QSpinBox.setDisplay = lambda self, value: self.setValue(value)
QtWidgets.QComboBox.setDisplay = lambda self, value: self.setCurrentText(str(value))
def display(self, item_dict):
item_dict['Direction'] = direction_flag_text_dict.get(item_dict['Direction'], '')
item_dict['OffsetFlag'] = offsetflag_flag_text_dict.get(item_dict['OffsetFlag'], '')
item_dict['BenchmarkFlag'] = benchmarkflag_flag_text_dict.get(item_dict['BenchmarkFlag'], '')
for key, value in item_dict.items():
widget = getattr(self, key, None)
if widget and getattr(widget, 'setDisplay', None):
widget.setDisplay(value)