diff --git a/vn.trader/dataRecorder/DR_setting.json b/vn.trader/dataRecorder/DR_setting.json index f7c297c5..cbc5d6b0 100644 --- a/vn.trader/dataRecorder/DR_setting.json +++ b/vn.trader/dataRecorder/DR_setting.json @@ -1,31 +1,91 @@ { - "working": false, - - "tick": - [ - ["m1609", "XSPEED"], - ["IF1606", "SGIT"], - ["IH1606", "SGIT"], - ["IH1606", "SGIT"], - ["IC1606", "SGIT"], - ["IC1606", "SGIT"], - ["600036", "LTS", "SZSE"], - ["EUR.USD", "IB", "IDEALPRO", "USD", "外汇"] + "working": true, + "tick": [ + [ + "au1706", + "CTP" + ], + [ + "a1705", + "CTP" + ], + [ + "m1705", + "CTP" + ], + [ + "bu1706", + "CTP" + ], + [ + "SR705", + "CTP" + ], + [ + "TA705", + "CTP" + ], + [ + "MA705", + "CTP" + ], + [ + "RM705", + "CTP" + ], + [ + "FG705", + "CTP" + ], + [ + "OI705", + "CTP" + ] ], - - "bar": - [ - ["IF1605", "SGIT"], - ["IF1606", "SGIT"], - ["IH1606", "SGIT"], - ["IH1606", "SGIT"], - ["IC1606", "SGIT"], - ["IC1606", "SGIT"] + "bar": [ + [ + "au1706", + "CTP" + ], + [ + "a1705", + "CTP" + ], + [ + "m1705", + "CTP" + ], + [ + "bu1706", + "CTP" + ], + [ + "SR705", + "CTP" + ], + [ + "TA705", + "CTP" + ], + [ + "MA705", + "CTP" + ], + [ + "RM705", + "CTP" + ], + [ + "FG705", + "CTP" + ], + [ + "OI705", + "CTP" + ] ], - - "active": - { - "IF0000": "IF1605", + "active": { + "IF0000": "IF1605", "IH0000": "IH1605", "IC0000": "IC1605" } diff --git a/vn.trader/dataRecorder/drEngine.py b/vn.trader/dataRecorder/drEngine.py index c4565fc4..8d209504 100644 --- a/vn.trader/dataRecorder/drEngine.py +++ b/vn.trader/dataRecorder/drEngine.py @@ -6,18 +6,15 @@ 使用DR_setting.json来配置需要收集的合约,以及主力合约代码。 ''' +import copy import json import os -import copy -from collections import OrderedDict -from datetime import datetime, timedelta -from Queue import Queue -from threading import Thread +from datetime import datetime -from eventEngine import * -from vtGateway import VtSubscribeReq, VtLogData from drBase import * +from eventEngine import * from vtFunction import todayDate +from vtGateway import VtSubscribeReq, VtLogData ######################################################################## @@ -53,6 +50,17 @@ class DrEngine(object): # 载入设置,订阅行情 self.loadSetting() + + def saveSetting(self, setting): + """保存设置""" + setting['working'] = self.working + with open(self.settingFileName, 'w') as f: + try: + str = json.dumps(setting, indent=2) + f.write(str) + except: + pass + return True #---------------------------------------------------------------------- def loadSetting(self): @@ -61,8 +69,8 @@ class DrEngine(object): drSetting = json.load(f) # 如果working设为False则不启动行情记录功能 - working = drSetting['working'] - if not working: + self.working = drSetting['working'] + if not self.working: return if 'tick' in drSetting: diff --git a/vn.trader/dataRecorder/uiDrEdit.py b/vn.trader/dataRecorder/uiDrEdit.py new file mode 100644 index 00000000..1242a225 --- /dev/null +++ b/vn.trader/dataRecorder/uiDrEdit.py @@ -0,0 +1,400 @@ +# encoding: UTF-8 + +''' +行情记录模块相关的GUI控制组件 +''' +import json +import re +import sys + +from PyQt4.QtGui import QTreeView + +from dataRecorder.drEngine import DrEngine +from eventEngine import * +from uiBasicWidget import QtGui, QtCore +from util.UiUtil import CheckBoxDelegate, ComboDelegate + +reload(sys) +sys.setdefaultencoding("utf8") + + +class TreeItem(object): + def __init__(self, data, parent=None): + self.parentItem = parent + self.itemData = data + self.childItems = [] + + def appendChild(self, item): + self.childItems.append(item) + + def extendChild(self, item): + self.childItems.extend(item) + + def child(self, row): + return self.childItems[row] + + def childCount(self): + return len(self.childItems) + + def columnCount(self): + return len(self.itemData) + + def data(self, column): + try: + return self.itemData[column] + except IndexError: + return None + + def parent(self): + return self.parentItem + + def row(self): + if self.parentItem: + return self.parentItem.childItems.index(self) + + return 0 + + def setData(self, column, value): + if column < 0 or column >= len(self.itemData): + return False + + self.itemData[column] = value + + return True + + +class TreeModel(QtCore.QAbstractItemModel): + def __init__(self, parent=None): + super(TreeModel, self).__init__(parent) + self.rootItem = TreeItem([u"合约", u"Tick", u"Bar", u"主力", u"交易所", u"接口"]) + + def rootItem(self): + return self.rootItem + + def setDataSource(self, data): + self.rootItem.extendChild(data) + + def columnCount(self, parent): + if parent.isValid(): + return parent.internalPointer().columnCount() + else: + return self.rootItem.columnCount() + + def setData(self, index, value, role=None): + item = index.internalPointer() + if item is None: + return False + + if index.column() == 5: + result = item.setData(index.column(), value) + if result: + self.dataChanged.emit(index, index) + # 如果第一条 + if item.parentItem == self.rootItem: + for row in range(item.childCount()): + childIndex = self.index(row, index.column(), index) + self.setData(childIndex, value, QtCore.Qt.DisplayRole) + + if index.column() in [1, 2, 3] and role == QtCore.Qt.CheckStateRole: + result = item.setData(index.column(), True if value == QtCore.Qt.Checked else False) + if result: + self.dataChanged.emit(index, index) + + # 如果第一条 + if item.parentItem == self.rootItem and index.column() in [1, 2]: + for row in range(item.childCount()): + childIndex = self.index(row, index.column(), index) + self.setData(childIndex, value, QtCore.Qt.CheckStateRole) + + if value == QtCore.Qt.Checked and index.column() == 3: + for row in range(item.parentItem.childCount()): + if row != index.row(): + siblingIndex = self.index(row, index.column(), index.parent()) + self.setData(siblingIndex, QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole) + + return True + + def data(self, index, role): + if not index.isValid(): + return None + + item = index.internalPointer() + + if role == QtCore.Qt.TextAlignmentRole: + return QtCore.Qt.AlignCenter + + if role == QtCore.Qt.CheckStateRole and ( + index.column() in [1, 2] or index.column() == 3 and item.parentItem != self.rootItem): + return QtCore.Qt.Checked if item.data(index.column()) == True else QtCore.Qt.Unchecked + + if role != QtCore.Qt.DisplayRole or index.column() in [3, 4] and item.parentItem == self.rootItem: + return None + + return item.data(index.column()) + + def flags(self, index): + if not index.isValid(): + return QtCore.Qt.NoItemFlags + + if index.column() in [0, 4]: + return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable + + if index.column() in [5]: + return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable + + item = index.internalPointer() + if index.column() == 3 and item.parentItem == self.rootItem: + return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable + + return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEditable + + def headerData(self, section, orientation, role): + if orientation == QtCore.Qt.Horizontal: + if role == QtCore.Qt.DisplayRole: + return self.rootItem.data(section) + elif role == QtCore.Qt.TextAlignmentRole: + return QtCore.Qt.AlignCenter + return None + + def index(self, row, column, parent): + if not self.hasIndex(row, column, parent): + return QtCore.QModelIndex() + + if parent is None or not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + childItem = parentItem.child(row) + if childItem: + return self.createIndex(row, column, childItem) + else: + return QtCore.QModelIndex() + + def parent(self, index): + if not index.isValid(): + return QtCore.QModelIndex() + + childItem = index.internalPointer() + parentItem = childItem.parent() + + if parentItem == self.rootItem: + return QtCore.QModelIndex() + + return self.createIndex(parentItem.row(), 0, parentItem) + + def rowCount(self, parent): + if parent.column() > 0: + return 0 + + if not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + return parentItem.childCount() + + def hasIndex(self, row, column, parentIndex=None, *args, **kwargs): + if row < 0 or column > self.rootItem.columnCount(): + return False + return True + + +class DrEditWidget(QtGui.QWidget): + """行情数据记录引擎管理组件""" + signal = QtCore.pyqtSignal(type(Event())) + + def __init__(self, drWidget, mainEngine, eventEngine, parent=None): + """Constructor""" + super(DrEditWidget, self).__init__(parent) + self.drWidget = drWidget + self.mainEngine = mainEngine + self.eventEngine = eventEngine + self.hasChanged = False + + # 保存合约详细信息的字典 + self.contractDict = {} + + self.initUi() + self.updateSetting() + self.loadData() + self.registerEvent() + + def closeEvent(self, QCloseEvent): + if self.hasChanged: + self.drWidget.restart() + + def initUi(self): + + vbox = QtGui.QVBoxLayout() + + vline = QtGui.QHBoxLayout() + vline.setSpacing(2) + btnTickAll = QtGui.QPushButton(u"全部记录Tick", self) + btnBarAll = QtGui.QPushButton(u'全部记录Bar', self) + btnSaveAll = QtGui.QPushButton(u'保存设置(重启后生效)', self) + btnTickAll.clicked.connect(self.selectAllTick) + btnBarAll.clicked.connect(self.selectAllBar) + btnSaveAll.clicked.connect(self.saveSetting) + + vline.addWidget(btnTickAll) + vline.addWidget(btnBarAll) + vline.addWidget(btnSaveAll) + + vbox.addLayout(vline) + + self.qTreeView = QTreeView() + self.model = TreeModel() + self.qTreeView.setModel(self.model) + self.qTreeView.setSelectionMode(QtGui.QAbstractItemView.NoSelection) + self.qTreeView.setItemDelegateForColumn(1, CheckBoxDelegate(self)) + self.qTreeView.setItemDelegateForColumn(2, CheckBoxDelegate(self)) + self.qTreeView.setItemDelegateForColumn(3, CheckBoxDelegate(self)) + self.qTreeView.setItemDelegateForColumn(5, ComboDelegate(self, ["CTP", "LTS", "XTP", "FEMAS", "XSPEED", "QDP", + "KSOTP", "KSGOLD", "SGIT"])) + + vbox.addWidget(self.qTreeView) + self.setLayout(vbox) + + def getContractChineseName(self, str): + line = str.strip().decode('utf-8', 'ignore') # 处理前进行相关的处理,包括转换成Unicode等 + p2 = re.compile(ur'[^\u4e00-\u9fa5]') # 中文的编码范围是:\u4e00到\u9fa5 + zh = " ".join(p2.split(line)).strip() + zh = ",".join(zh.split()) + outStr = zh # 经过相关处理后得到中文的文本 + return outStr + + def loadData(self): + child = [] + + tick = {} + bar = {} + active = [] + with open(self.mainEngine.drEngine.settingFileName) as f: + drSetting = json.load(f) + if 'tick' in drSetting: + l = drSetting['tick'] + + for setting in l: + tick[setting[0]] = setting[1] + + if 'bar' in drSetting: + l = drSetting['bar'] + + for setting in l: + bar[setting[0]] = setting[1] + + if 'active' in drSetting: + d = drSetting['active'] + + for activeSymbol, symbol in d.items(): + active.append(symbol) + + contractDict = {} + contracts = self.mainEngine.getAllContracts() + for contract in contracts: + contractName = self.getContractChineseName(contract.name) + gateWayName = u"CTP" + hasTick = tick.has_key(contract.symbol) + hasBar = bar.has_key(contract.symbol) + hasActive = contract.symbol in active + if hasTick: + gateWayName = tick[contract.symbol] + elif hasBar: + gateWayName = bar[contract.symbol] + if contractDict.has_key(contractName): + parentItem = contractDict[contractName] + item = TreeItem([contract.symbol, hasTick, hasBar, hasActive, contract.exchange, gateWayName], + parentItem) + parentItem.appendChild(item) + else: + item = TreeItem([contractName, False, False, False, contract.exchange, gateWayName], + self.model.rootItem) + contractDict[contractName] = item + child.append(item) + subItem = TreeItem([contract.symbol, hasTick, hasBar, hasActive, contract.exchange, gateWayName], item) + item.appendChild(subItem) + + # yumi = TreeItem([u"玉米", False, False, False, "SH", "CTP"], self.model.rootItem) + # yumi.appendChild(TreeItem([u"c1705", False, False, False, "SH", "CTP"], yumi)) + # yumi.appendChild(TreeItem([u"c1703", False, False, False, "SH", "CTP"], yumi)) + # yumi.appendChild(TreeItem([u"c1707", False, False, False, "SH", "CTP"], yumi)) + # yumi.appendChild(TreeItem([u"c1709", False, False, False, "SH", "CTP"], yumi)) + # dianfen = TreeItem([u"淀粉", False, False, False, "SH", "CTP"], self.model.rootItem) + # dianfen.appendChild(TreeItem([u"d1705", False, False, False, "SH", "CTP"], dianfen)) + # dianfen.appendChild(TreeItem([u"d1703", False, False, False, "SH", "CTP"], dianfen)) + # dianfen.appendChild(TreeItem([u"d1707", False, False, False, "SH", "CTP"], dianfen)) + # dianfen.appendChild(TreeItem([u"d1709", False, False, False, "SH", "CTP"], dianfen)) + # + # child.append(yumi) + # child.append(dianfen) + self.model.setDataSource(child) + self.qTreeView.expandAll() + + def saveSetting(self): + setting = {} + setting["tick"] = [] + setting["bar"] = [] + setting["active"] = {} + queue = Queue() + queue.put(self.model.rootItem) + while queue.qsize() > 0: + item = queue.get() + for child in item.childItems: + queue.put(child) + if item.parentItem is not None and item.parentItem != self.model.rootItem: + name = item.data(0) + interface = item.data(5) + if item.data(1): + setting["tick"].append([name, interface]) + if item.data(2): + setting["bar"].append([name, interface]) + if item.data(3): + setting["active"][item.parentItem.data(0)] = name + if self.mainEngine.drEngine.saveSetting(setting): + self.hasChanged = True + self.close() + + def selectAllTick(self): + self.selectAll(True, False, True) + + def selectAllBar(self): + self.selectAll(False, True, True) + + def selectAll(self, tick=False, bar=False, select=False): + column = None + if tick: + column = 1 + if bar: + column = 2 + + for row in range(self.model.rootItem.childCount()): + childIndex = self.model.index(row, column, None) + self.model.setData(childIndex, QtCore.Qt.Unchecked if select == False else QtCore.Qt.Checked, + QtCore.Qt.CheckStateRole) + + def updateSetting(self): + pass + + def updateContract(self, event): + """更新合约数据""" + contract = event.dict_['data'] + self.contractDict[contract.vtSymbol] = contract + self.contractDict[contract.symbol] = contract # 使用常规代码(不包括交易所)可能导致重复 + + # ---------------------------------------------------------------------- + def registerEvent(self): + """注册事件监听""" + self.signal.connect(self.updateContract) + self.eventEngine.register(EVENT_CONTRACT, self.signal.emit) + + +if __name__ == '__main__': + import sys + + app = QtGui.QApplication(sys.argv) + + view = DrEditWidget(None, DrEngine, EventEngine) + view.setFixedSize(650, 500) + view.show() + sys.exit(app.exec_()) diff --git a/vn.trader/dataRecorder/uiDrWidget.py b/vn.trader/dataRecorder/uiDrWidget.py index b9a21190..869f7e1a 100644 --- a/vn.trader/dataRecorder/uiDrWidget.py +++ b/vn.trader/dataRecorder/uiDrWidget.py @@ -6,8 +6,9 @@ import json -from uiBasicWidget import QtGui, QtCore +from dataRecorder.uiDrEdit import DrEditWidget from eventEngine import * +from uiBasicWidget import QtGui, QtCore ######################################################################## @@ -38,22 +39,23 @@ class DrEngineManager(QtGui.QWidget): signal = QtCore.pyqtSignal(type(Event())) #---------------------------------------------------------------------- - def __init__(self, drEngine, eventEngine, parent=None): + def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(DrEngineManager, self).__init__(parent) - - self.drEngine = drEngine + + self.mainEngine = mainEngine + self.drEngine = mainEngine.drEngine self.eventEngine = eventEngine - + self.initUi() self.updateSetting() - self.registerEvent() - + self.registerEvent() + #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(u'行情数据记录工具') - + # 记录合约配置监控 tickLabel = QtGui.QLabel(u'Tick记录') self.tickTable = QtGui.QTableWidget() @@ -63,14 +65,14 @@ class DrEngineManager(QtGui.QWidget): self.tickTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) self.tickTable.setAlternatingRowColors(True) self.tickTable.setHorizontalHeaderLabels([u'合约代码', u'接口']) - + barLabel = QtGui.QLabel(u'Bar记录') self.barTable = QtGui.QTableWidget() self.barTable.setColumnCount(2) self.barTable.verticalHeader().setVisible(False) self.barTable.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) self.barTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) - self.barTable.setAlternatingRowColors(True) + self.barTable.setAlternatingRowColors(True) self.barTable.setHorizontalHeaderLabels([u'合约代码', u'接口']) activeLabel = QtGui.QLabel(u'主力合约') @@ -79,77 +81,99 @@ class DrEngineManager(QtGui.QWidget): self.activeTable.verticalHeader().setVisible(False) self.activeTable.setEditTriggers(QtGui.QTableWidget.NoEditTriggers) self.activeTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) - self.activeTable.setAlternatingRowColors(True) + self.activeTable.setAlternatingRowColors(True) self.activeTable.setHorizontalHeaderLabels([u'主力代码', u'合约代码']) # 日志监控 self.logMonitor = QtGui.QTextEdit() self.logMonitor.setReadOnly(True) self.logMonitor.setMinimumHeight(600) - + # 设置布局 grid = QtGui.QGridLayout() - + grid.addWidget(tickLabel, 0, 0) grid.addWidget(barLabel, 0, 1) grid.addWidget(activeLabel, 0, 2) grid.addWidget(self.tickTable, 1, 0) grid.addWidget(self.barTable, 1, 1) - grid.addWidget(self.activeTable, 1, 2) - + grid.addWidget(self.activeTable, 1, 2) + + btnEdit = QtGui.QPushButton(u'编辑', self) + vbox = QtGui.QVBoxLayout() vbox.addLayout(grid) + + vline = QtGui.QHBoxLayout() + vline.addWidget(btnEdit) + + vbox.addLayout(vline) vbox.addWidget(self.logMonitor) self.setLayout(vbox) + btnEdit.clicked.connect(self.openDr) + #---------------------------------------------------------------------- def updateLog(self, event): """更新日志""" log = event.dict_['data'] content = '\t'.join([log.logTime, log.logContent]) self.logMonitor.append(content) - + #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.signal.connect(self.updateLog) self.eventEngine.register(EVENT_DATARECORDER_LOG, self.signal.emit) - + #---------------------------------------------------------------------- def updateSetting(self): """显示引擎行情记录配置""" + + self.tickTable.clearContents() + self.tickTable.setRowCount(0) + self.barTable.clearContents() + self.barTable.setRowCount(0) + self.activeTable.clearContents() + self.activeTable.setRowCount(0) + with open(self.drEngine.settingFileName) as f: drSetting = json.load(f) - + if 'tick' in drSetting: l = drSetting['tick'] - + for setting in l: self.tickTable.insertRow(0) self.tickTable.setItem(0, 0, TableCell(setting[0])) self.tickTable.setItem(0, 1, TableCell(setting[1])) - + if 'bar' in drSetting: l = drSetting['bar'] - + for setting in l: self.barTable.insertRow(0) self.barTable.setItem(0, 0, TableCell(setting[0])) - self.barTable.setItem(0, 1, TableCell(setting[1])) - + self.barTable.setItem(0, 1, TableCell(setting[1])) + if 'active' in drSetting: d = drSetting['active'] - + for activeSymbol, symbol in d.items(): self.activeTable.insertRow(0) self.activeTable.setItem(0, 0, TableCell(activeSymbol)) self.activeTable.setItem(0, 1, TableCell(symbol)) - - - - + # ---------------------------------------------------------------------- + def openDr(self): + """打开行情数据记录组件""" + self.mDrEditWidget = DrEditWidget(self,self.mainEngine, self.eventEngine) + self.mDrEditWidget.setWindowTitle(u"编辑订阅") + self.mDrEditWidget.setFixedSize(800,800) + self.mDrEditWidget.show() - - - \ No newline at end of file + def restart(self): + pass + # self.drEngine.stop() + # self.updateSetting() + # self.mainEngine.drEngine = DrEngine(self.mainEngine, self.mainEngine.eventEngine) diff --git a/vn.trader/uiBasicWidget.py b/vn.trader/uiBasicWidget.py index f7f4eb63..8a842810 100644 --- a/vn.trader/uiBasicWidget.py +++ b/vn.trader/uiBasicWidget.py @@ -1,13 +1,11 @@ # encoding: UTF-8 -import json import csv -import os from collections import OrderedDict from PyQt4 import QtGui, QtCore +from PyQt4.QtCore import Qt -from eventEngine import * from vtFunction import * from vtGateway import * @@ -16,9 +14,9 @@ from vtGateway import * def loadFont(): """载入字体设置""" fileName = 'VT_setting.json' - path = os.path.abspath(os.path.dirname(__file__)) - fileName = os.path.join(path, fileName) - + path = os.path.abspath(os.path.dirname(__file__)) + fileName = os.path.join(path, fileName) + try: f = file(fileName) setting = json.load(f) @@ -43,7 +41,7 @@ class BasicCell(QtGui.QTableWidgetItem): self.data = None if text: self.setContent(text) - + #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" @@ -53,6 +51,22 @@ class BasicCell(QtGui.QTableWidgetItem): self.setText(text) +######################################################################## +class CheckBoxCell(QtGui.QTableWidgetItem): + """用来显示复选框的单元格""" + + def __init__(self, isChecked=False, mainEngine=None): + """Constructor""" + super(CheckBoxCell, self).__init__() + self.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) + self.setStauts(isChecked) + + def setStauts(self, isChecked=False): + if not isChecked: + self.setCheckState(Qt.Unchecked) + else: + self.setCheckState(Qt.Checked) + ######################################################################## class NumCell(QtGui.QTableWidgetItem): """用来显示数字的单元格""" @@ -64,7 +78,7 @@ class NumCell(QtGui.QTableWidgetItem): self.data = None if text: self.setContent(text) - + #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" @@ -76,7 +90,7 @@ class NumCell(QtGui.QTableWidgetItem): self.setData(QtCore.Qt.DisplayRole, num) except ValueError: self.setText(text) - + ######################################################################## class DirectionCell(QtGui.QTableWidgetItem): @@ -89,7 +103,7 @@ class DirectionCell(QtGui.QTableWidgetItem): self.data = None if text: self.setContent(text) - + #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" @@ -108,20 +122,20 @@ class NameCell(QtGui.QTableWidgetItem): def __init__(self, text=None, mainEngine=None): """Constructor""" super(NameCell, self).__init__() - + self.mainEngine = mainEngine self.data = None - + if text: self.setContent(text) - + #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" if self.mainEngine: # 首先尝试正常获取合约对象 contract = self.mainEngine.getContract(text) - + # 如果能读取合约信息 if contract: self.setText(contract.name) @@ -139,10 +153,10 @@ class BidCell(QtGui.QTableWidgetItem): self.setForeground(QtGui.QColor('black')) self.setBackground(QtGui.QColor(255,174,201)) - + if text: self.setContent(text) - + #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" @@ -161,10 +175,10 @@ class AskCell(QtGui.QTableWidgetItem): self.setForeground(QtGui.QColor('black')) self.setBackground(QtGui.QColor(160,255,160)) - + if text: self.setContent(text) - + #---------------------------------------------------------------------- def setContent(self, text): """设置内容""" @@ -175,10 +189,10 @@ class AskCell(QtGui.QTableWidgetItem): class BasicMonitor(QtGui.QTableWidget): """ 基础监控 - + headerDict中的值对应的字典格式如下 {'chinese': u'中文名', 'cellType': BasicCell} - + """ signal = QtCore.pyqtSignal(type(Event())) @@ -186,79 +200,79 @@ class BasicMonitor(QtGui.QTableWidget): def __init__(self, mainEngine=None, eventEngine=None, parent=None): """Constructor""" super(BasicMonitor, self).__init__(parent) - + self.mainEngine = mainEngine self.eventEngine = eventEngine - + # 保存表头标签用 self.headerDict = OrderedDict() # 有序字典,key是英文名,value是对应的配置字典 self.headerList = [] # 对应self.headerDict.keys() - + # 保存相关数据用 self.dataDict = {} # 字典,key是字段对应的数据,value是保存相关单元格的字典 self.dataKey = '' # 字典键对应的数据字段 - + # 监控的事件类型 self.eventType = '' - + # 字体 self.font = None - + # 保存数据对象到单元格 self.saveData = False - + # 默认不允许根据表头进行排序,需要的组件可以开启 self.sorting = False - + # 初始化右键菜单 self.initMenu() - + #---------------------------------------------------------------------- def setHeaderDict(self, headerDict): """设置表头有序字典""" self.headerDict = headerDict self.headerList = headerDict.keys() - + #---------------------------------------------------------------------- def setDataKey(self, dataKey): """设置数据字典的键""" self.dataKey = dataKey - + #---------------------------------------------------------------------- def setEventType(self, eventType): """设置监控的事件类型""" self.eventType = eventType - + #---------------------------------------------------------------------- def setFont(self, font): """设置字体""" self.font = font - + #---------------------------------------------------------------------- def setSaveData(self, saveData): """设置是否要保存数据到单元格""" self.saveData = saveData - + #---------------------------------------------------------------------- def initTable(self): """初始化表格""" # 设置表格的列数 col = len(self.headerDict) self.setColumnCount(col) - + # 设置列表头 labels = [d['chinese'] for d in self.headerDict.values()] self.setHorizontalHeaderLabels(labels) - + # 关闭左边的垂直表头 self.verticalHeader().setVisible(False) - + # 设为不可编辑 self.setEditTriggers(self.NoEditTriggers) - + # 设为行交替颜色 self.setAlternatingRowColors(True) - + # 设置允许排序 self.setSortingEnabled(self.sorting) @@ -267,38 +281,38 @@ class BasicMonitor(QtGui.QTableWidget): """注册GUI更新相关的事件监听""" self.signal.connect(self.updateEvent) self.eventEngine.register(self.eventType, self.signal.emit) - + #---------------------------------------------------------------------- def updateEvent(self, event): """收到事件更新""" data = event.dict_['data'] self.updateData(data) - + #---------------------------------------------------------------------- def updateData(self, data): """将数据更新到表格中""" # 如果允许了排序功能,则插入数据前必须关闭,否则插入新的数据会变乱 if self.sorting: self.setSortingEnabled(False) - + # 如果设置了dataKey,则采用存量更新模式 if self.dataKey: key = data.__getattribute__(self.dataKey) # 如果键在数据字典中不存在,则先插入新的一行,并创建对应单元格 if key not in self.dataDict: - self.insertRow(0) + self.insertRow(0) d = {} - for n, header in enumerate(self.headerList): + for n, header in enumerate(self.headerList): content = safeUnicode(data.__getattribute__(header)) cellType = self.headerDict[header]['cellType'] cell = cellType(content, self.mainEngine) - + if self.font: cell.setFont(self.font) # 如果设置了特殊字体,则进行单元格设置 - + if self.saveData: # 如果设置了保存数据对象,则进行对象保存 cell.data = data - + self.setItem(0, n, cell) d[header] = cell self.dataDict[key] = d @@ -309,48 +323,48 @@ class BasicMonitor(QtGui.QTableWidget): content = safeUnicode(data.__getattribute__(header)) cell = d[header] cell.setContent(content) - + if self.saveData: # 如果设置了保存数据对象,则进行对象保存 - cell.data = data + cell.data = data # 否则采用增量更新模式 else: - self.insertRow(0) + self.insertRow(0) for n, header in enumerate(self.headerList): content = safeUnicode(data.__getattribute__(header)) cellType = self.headerDict[header]['cellType'] cell = cellType(content, self.mainEngine) - + if self.font: cell.setFont(self.font) if self.saveData: - cell.data = data + cell.data = data + + self.setItem(0, n, cell) - self.setItem(0, n, cell) - # 调整列宽 self.resizeColumns() - + # 重新打开排序 if self.sorting: self.setSortingEnabled(True) - + #---------------------------------------------------------------------- def resizeColumns(self): """调整各列的大小""" - self.horizontalHeader().resizeSections(QtGui.QHeaderView.ResizeToContents) - + self.horizontalHeader().resizeSections(QtGui.QHeaderView.ResizeToContents) + #---------------------------------------------------------------------- def setSorting(self, sorting): """设置是否允许根据表头排序""" self.sorting = sorting - + #---------------------------------------------------------------------- def saveToCsv(self): """保存表格内容到CSV文件""" # 先隐藏右键菜单 self.menu.close() - + # 获取想要保存的文件名 path = QtGui.QFileDialog.getSaveFileName(self, '保存数据', '', 'CSV(*.csv)') @@ -358,11 +372,11 @@ class BasicMonitor(QtGui.QTableWidget): if not path.isEmpty(): with open(unicode(path), 'wb') as f: writer = csv.writer(f) - + # 保存标签 headers = [header.encode('gbk') for header in self.headerList] writer.writerow(headers) - + # 保存每行内容 for row in range(self.rowCount()): rowdata = [] @@ -373,24 +387,24 @@ class BasicMonitor(QtGui.QTableWidget): unicode(item.text()).encode('gbk')) else: rowdata.append('') - writer.writerow(rowdata) + writer.writerow(rowdata) except IOError: pass #---------------------------------------------------------------------- def initMenu(self): """初始化右键菜单""" - self.menu = QtGui.QMenu(self) - + self.menu = QtGui.QMenu(self) + saveAction = QtGui.QAction(u'保存内容', self) saveAction.triggered.connect(self.saveToCsv) - + self.menu.addAction(saveAction) - + #---------------------------------------------------------------------- def contextMenuEvent(self, event): """右键点击事件""" - self.menu.popup(QtGui.QCursor.pos()) + self.menu.popup(QtGui.QCursor.pos()) ######################################################################## @@ -401,7 +415,7 @@ class MarketMonitor(BasicMonitor): def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(MarketMonitor, self).__init__(mainEngine, eventEngine, parent) - + # 设置表头有序字典 d = OrderedDict() d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} @@ -420,22 +434,22 @@ class MarketMonitor(BasicMonitor): d['time'] = {'chinese':u'时间', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + # 设置数据键 self.setDataKey('vtSymbol') - + # 设置监控事件类型 self.setEventType(EVENT_TICK) - + # 设置字体 self.setFont(BASIC_FONT) - + # 设置允许排序 - self.setSorting(True) - + self.setSorting(False) + # 初始化表格 self.initTable() - + # 注册事件监听 self.registerEvent() @@ -448,15 +462,15 @@ class LogMonitor(BasicMonitor): def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(LogMonitor, self).__init__(mainEngine, eventEngine, parent) - - d = OrderedDict() + + d = OrderedDict() d['logTime'] = {'chinese':u'时间', 'cellType':BasicCell} d['logContent'] = {'chinese':u'内容', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + self.setEventType(EVENT_LOG) - self.setFont(BASIC_FONT) + self.setFont(BASIC_FONT) self.initTable() self.registerEvent() @@ -469,14 +483,14 @@ class ErrorMonitor(BasicMonitor): def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(ErrorMonitor, self).__init__(mainEngine, eventEngine, parent) - - d = OrderedDict() + + d = OrderedDict() d['errorTime'] = {'chinese':u'错误时间', 'cellType':BasicCell} d['errorID'] = {'chinese':u'错误代码', 'cellType':BasicCell} d['errorMsg'] = {'chinese':u'错误信息', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + self.setEventType(EVENT_ERROR) self.setFont(BASIC_FONT) self.initTable() @@ -491,7 +505,7 @@ class TradeMonitor(BasicMonitor): def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(TradeMonitor, self).__init__(mainEngine, eventEngine, parent) - + d = OrderedDict() d['tradeID'] = {'chinese':u'成交编号', 'cellType':NumCell} d['orderID'] = {'chinese':u'委托编号', 'cellType':NumCell} @@ -504,11 +518,11 @@ class TradeMonitor(BasicMonitor): d['tradeTime'] = {'chinese':u'成交时间', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + self.setEventType(EVENT_TRADE) self.setFont(BASIC_FONT) self.setSorting(True) - + self.initTable() self.registerEvent() @@ -523,7 +537,7 @@ class OrderMonitor(BasicMonitor): super(OrderMonitor, self).__init__(mainEngine, eventEngine, parent) self.mainEngine = mainEngine - + d = OrderedDict() d['orderID'] = {'chinese':u'委托编号', 'cellType':NumCell} d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} @@ -540,28 +554,28 @@ class OrderMonitor(BasicMonitor): d['sessionID'] = {'chinese':u'会话编号', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + self.setDataKey('vtOrderID') self.setEventType(EVENT_ORDER) self.setFont(BASIC_FONT) self.setSaveData(True) self.setSorting(True) - + self.initTable() self.registerEvent() self.connectSignal() - + #---------------------------------------------------------------------- def connectSignal(self): """连接信号""" # 双击单元格撤单 - self.itemDoubleClicked.connect(self.cancelOrder) - + self.itemDoubleClicked.connect(self.cancelOrder) + #---------------------------------------------------------------------- def cancelOrder(self, cell): """根据单元格的数据撤单""" order = cell.data - + req = VtCancelOrderReq() req.symbol = order.symbol req.exchange = order.exchange @@ -578,7 +592,7 @@ class PositionMonitor(BasicMonitor): def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(PositionMonitor, self).__init__(mainEngine, eventEngine, parent) - + d = OrderedDict() d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['vtSymbol'] = {'chinese':u'名称', 'cellType':NameCell} @@ -590,16 +604,16 @@ class PositionMonitor(BasicMonitor): d['positionProfit'] = {'chinese':u'持仓盈亏', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + self.setDataKey('vtPositionName') self.setEventType(EVENT_POSITION) self.setFont(BASIC_FONT) self.setSaveData(True) - + self.initTable() self.registerEvent() - - + + ######################################################################## class AccountMonitor(BasicMonitor): """账户监控""" @@ -608,7 +622,7 @@ class AccountMonitor(BasicMonitor): def __init__(self, mainEngine, eventEngine, parent=None): """Constructor""" super(AccountMonitor, self).__init__(mainEngine, eventEngine, parent) - + d = OrderedDict() d['accountID'] = {'chinese':u'账户', 'cellType':BasicCell} d['preBalance'] = {'chinese':u'昨结', 'cellType':BasicCell} @@ -620,7 +634,7 @@ class AccountMonitor(BasicMonitor): d['positionProfit'] = {'chinese':u'持仓盈亏', 'cellType':BasicCell} d['gatewayName'] = {'chinese':u'接口', 'cellType':BasicCell} self.setHeaderDict(d) - + self.setDataKey('vtAccountID') self.setEventType(EVENT_ACCOUNT) self.setFont(BASIC_FONT) @@ -632,7 +646,7 @@ class AccountMonitor(BasicMonitor): class TradingWidget(QtGui.QFrame): """简单交易组件""" signal = QtCore.pyqtSignal(type(Event())) - + directionList = [DIRECTION_LONG, DIRECTION_SHORT] @@ -640,12 +654,12 @@ class TradingWidget(QtGui.QFrame): OFFSET_CLOSE, OFFSET_CLOSEYESTERDAY, OFFSET_CLOSETODAY] - + priceTypeList = [PRICETYPE_LIMITPRICE, PRICETYPE_MARKETPRICE, PRICETYPE_FAK, PRICETYPE_FOK] - + exchangeList = [EXCHANGE_NONE, EXCHANGE_CFFEX, EXCHANGE_SHFE, @@ -662,18 +676,18 @@ class TradingWidget(QtGui.QFrame): EXCHANGE_NYMEX, EXCHANGE_GLOBEX, EXCHANGE_IDEALPRO] - + currencyList = [CURRENCY_NONE, CURRENCY_CNY, CURRENCY_HKD, CURRENCY_USD] - + productClassList = [PRODUCT_NONE, PRODUCT_EQUITY, PRODUCT_FUTURES, PRODUCT_OPTION, PRODUCT_FOREX] - + gatewayList = [''] #---------------------------------------------------------------------- @@ -682,9 +696,9 @@ class TradingWidget(QtGui.QFrame): super(TradingWidget, self).__init__(parent) self.mainEngine = mainEngine self.eventEngine = eventEngine - + self.symbol = '' - + # 添加交易接口 self.gatewayList.extend(mainEngine.getAllGatewayNames()) @@ -697,7 +711,7 @@ class TradingWidget(QtGui.QFrame): self.setWindowTitle(u'交易') self.setMaximumWidth(400) self.setFrameShape(self.Box) # 设置边框 - self.setLineWidth(1) + self.setLineWidth(1) # 左边部分 labelSymbol = QtGui.QLabel(u'代码') @@ -708,7 +722,7 @@ class TradingWidget(QtGui.QFrame): self.checkFixed = QtGui.QCheckBox(u'') # 价格固定选择框 labelVolume = QtGui.QLabel(u'数量') labelPriceType = QtGui.QLabel(u'价格类型') - labelExchange = QtGui.QLabel(u'交易所') + labelExchange = QtGui.QLabel(u'交易所') labelCurrency = QtGui.QLabel(u'货币') labelProductClass = QtGui.QLabel(u'产品类型') labelGateway = QtGui.QLabel(u'交易接口') @@ -733,18 +747,18 @@ class TradingWidget(QtGui.QFrame): self.comboPriceType = QtGui.QComboBox() self.comboPriceType.addItems(self.priceTypeList) - + self.comboExchange = QtGui.QComboBox() - self.comboExchange.addItems(self.exchangeList) - + self.comboExchange.addItems(self.exchangeList) + self.comboCurrency = QtGui.QComboBox() self.comboCurrency.addItems(self.currencyList) - + self.comboProductClass = QtGui.QComboBox() - self.comboProductClass.addItems(self.productClassList) - + self.comboProductClass.addItems(self.productClassList) + self.comboGateway = QtGui.QComboBox() - self.comboGateway.addItems(self.gatewayList) + self.comboGateway.addItems(self.gatewayList) gridleft = QtGui.QGridLayout() gridleft.addWidget(labelSymbol, 0, 0) @@ -756,9 +770,9 @@ class TradingWidget(QtGui.QFrame): gridleft.addWidget(labelPriceType, 6, 0) gridleft.addWidget(labelExchange, 7, 0) gridleft.addWidget(labelCurrency, 8, 0) - gridleft.addWidget(labelProductClass, 9, 0) + gridleft.addWidget(labelProductClass, 9, 0) gridleft.addWidget(labelGateway, 10, 0) - + gridleft.addWidget(self.lineSymbol, 0, 1, 1, -1) gridleft.addWidget(self.lineName, 1, 1, 1, -1) gridleft.addWidget(self.comboDirection, 2, 1, 1, -1) @@ -794,7 +808,7 @@ class TradingWidget(QtGui.QFrame): self.labelBidVolume2 = QtGui.QLabel() self.labelBidVolume3 = QtGui.QLabel() self.labelBidVolume4 = QtGui.QLabel() - self.labelBidVolume5 = QtGui.QLabel() + self.labelBidVolume5 = QtGui.QLabel() self.labelAskPrice1 = QtGui.QLabel() self.labelAskPrice2 = QtGui.QLabel() @@ -805,7 +819,7 @@ class TradingWidget(QtGui.QFrame): self.labelAskVolume2 = QtGui.QLabel() self.labelAskVolume3 = QtGui.QLabel() self.labelAskVolume4 = QtGui.QLabel() - self.labelAskVolume5 = QtGui.QLabel() + self.labelAskVolume5 = QtGui.QLabel() labelLast = QtGui.QLabel(u'最新') self.labelLastPrice = QtGui.QLabel() @@ -837,7 +851,7 @@ class TradingWidget(QtGui.QFrame): gridRight.addWidget(self.labelBidPrice2, 7, 1) gridRight.addWidget(self.labelBidPrice3, 8, 1) gridRight.addWidget(self.labelBidPrice4, 9, 1) - gridRight.addWidget(self.labelBidPrice5, 10, 1) + gridRight.addWidget(self.labelBidPrice5, 10, 1) gridRight.addWidget(self.labelAskVolume5, 0, 2) gridRight.addWidget(self.labelAskVolume4, 1, 2) @@ -854,7 +868,7 @@ class TradingWidget(QtGui.QFrame): # 发单按钮 buttonSendOrder = QtGui.QPushButton(u'发单') buttonCancelAll = QtGui.QPushButton(u'全撤') - + size = buttonSendOrder.sizeHint() buttonSendOrder.setMinimumHeight(size.height()*2) # 把按钮高度设为默认两倍 buttonCancelAll.setMinimumHeight(size.height()*2) @@ -884,23 +898,23 @@ class TradingWidget(QtGui.QFrame): symbol = str(self.lineSymbol.text()) exchange = unicode(self.comboExchange.currentText()) currency = unicode(self.comboCurrency.currentText()) - productClass = unicode(self.comboProductClass.currentText()) + productClass = unicode(self.comboProductClass.currentText()) gatewayName = unicode(self.comboGateway.currentText()) - + # 查询合约 if exchange: vtSymbol = '.'.join([symbol, exchange]) contract = self.mainEngine.getContract(vtSymbol) else: vtSymbol = symbol - contract = self.mainEngine.getContract(symbol) - + contract = self.mainEngine.getContract(symbol) + if contract: vtSymbol = contract.vtSymbol gatewayName = contract.gatewayName self.lineName.setText(contract.name) exchange = contract.exchange # 保证有交易所代码 - + # 清空价格数量 self.spinPrice.setValue(0) self.spinVolume.setValue(0) @@ -915,7 +929,7 @@ class TradingWidget(QtGui.QFrame): self.labelBidVolume2.setText('') self.labelBidVolume3.setText('') self.labelBidVolume4.setText('') - self.labelBidVolume5.setText('') + self.labelBidVolume5.setText('') self.labelAskPrice1.setText('') self.labelAskPrice2.setText('') self.labelAskPrice3.setText('') @@ -960,30 +974,30 @@ class TradingWidget(QtGui.QFrame): self.labelAskPrice1.setText(str(tick.askPrice1)) self.labelBidVolume1.setText(str(tick.bidVolume1)) self.labelAskVolume1.setText(str(tick.askVolume1)) - + if tick.bidPrice2: self.labelBidPrice2.setText(str(tick.bidPrice2)) self.labelBidPrice3.setText(str(tick.bidPrice3)) self.labelBidPrice4.setText(str(tick.bidPrice4)) self.labelBidPrice5.setText(str(tick.bidPrice5)) - + self.labelAskPrice2.setText(str(tick.askPrice2)) self.labelAskPrice3.setText(str(tick.askPrice3)) self.labelAskPrice4.setText(str(tick.askPrice4)) self.labelAskPrice5.setText(str(tick.askPrice5)) - + self.labelBidVolume2.setText(str(tick.bidVolume2)) self.labelBidVolume3.setText(str(tick.bidVolume3)) self.labelBidVolume4.setText(str(tick.bidVolume4)) self.labelBidVolume5.setText(str(tick.bidVolume5)) - + self.labelAskVolume2.setText(str(tick.askVolume2)) self.labelAskVolume3.setText(str(tick.askVolume3)) self.labelAskVolume4.setText(str(tick.askVolume4)) - self.labelAskVolume5.setText(str(tick.askVolume5)) + self.labelAskVolume5.setText(str(tick.askVolume5)) self.labelLastPrice.setText(str(tick.lastPrice)) - + if tick.preClosePrice: rt = (tick.lastPrice/tick.preClosePrice)-1 self.labelReturn.setText(('%.2f' %(rt*100))+'%') @@ -1001,8 +1015,8 @@ class TradingWidget(QtGui.QFrame): symbol = str(self.lineSymbol.text()) exchange = unicode(self.comboExchange.currentText()) currency = unicode(self.comboCurrency.currentText()) - productClass = unicode(self.comboProductClass.currentText()) - gatewayName = unicode(self.comboGateway.currentText()) + productClass = unicode(self.comboProductClass.currentText()) + gatewayName = unicode(self.comboGateway.currentText()) # 查询合约 if exchange: @@ -1011,11 +1025,11 @@ class TradingWidget(QtGui.QFrame): else: vtSymbol = symbol contract = self.mainEngine.getContract(symbol) - + if contract: gatewayName = contract.gatewayName exchange = contract.exchange # 保证有交易所代码 - + req = VtOrderReq() req.symbol = symbol req.exchange = exchange @@ -1026,9 +1040,9 @@ class TradingWidget(QtGui.QFrame): req.offset = unicode(self.comboOffset.currentText()) req.currency = currency req.productClass = productClass - + self.mainEngine.sendOrder(req, gatewayName) - + #---------------------------------------------------------------------- def cancelAll(self): """一键撤销所有委托""" @@ -1041,18 +1055,18 @@ class TradingWidget(QtGui.QFrame): req.sessionID = order.sessionID req.orderID = order.orderID self.mainEngine.cancelOrder(req, order.gatewayName) - + #---------------------------------------------------------------------- def closePosition(self, cell): """根据持仓信息自动填写交易组件""" # 读取持仓数据,cell是一个表格中的单元格对象 pos = cell.data symbol = pos.symbol - + # 更新交易组件的显示合约 self.lineSymbol.setText(symbol) self.updateSymbol() - + # 自动填写信息 self.comboPriceType.setCurrentIndex(self.priceTypeList.index(PRICETYPE_LIMITPRICE)) self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_CLOSE)) @@ -1074,9 +1088,9 @@ class ContractMonitor(BasicMonitor): def __init__(self, mainEngine, parent=None): """Constructor""" super(ContractMonitor, self).__init__(parent=parent) - + self.mainEngine = mainEngine - + d = OrderedDict() d['symbol'] = {'chinese':u'合约代码', 'cellType':BasicCell} d['exchange'] = {'chinese':u'交易所', 'cellType':BasicCell} @@ -1087,11 +1101,11 @@ class ContractMonitor(BasicMonitor): d['priceTick'] = {'chinese':u'最小价格变动', 'cellType':BasicCell} #d['strikePrice'] = {'chinese':u'期权行权价', 'cellType':BasicCell} #d['underlyingSymbol'] = {'chinese':u'期权标的物', 'cellType':BasicCell} - #d['optionType'] = {'chinese':u'期权类型', 'cellType':BasicCell} + #d['optionType'] = {'chinese':u'期权类型', 'cellType':BasicCell} self.setHeaderDict(d) - + self.initUi() - + #---------------------------------------------------------------------- def initUi(self): """初始化界面""" @@ -1100,7 +1114,7 @@ class ContractMonitor(BasicMonitor): self.setFont(BASIC_FONT) self.initTable() self.addMenuAction() - + #---------------------------------------------------------------------- def showAllContracts(self): """显示所有合约数据""" @@ -1111,22 +1125,24 @@ class ContractMonitor(BasicMonitor): self.setRowCount(len(l2)) row = 0 - + for key in l2: contract = d[key] - + for n, header in enumerate(self.headerList): - content = safeUnicode(contract.__getattribute__(header)) cellType = self.headerDict[header]['cellType'] + content = None + if (hasattr(contract, header)): + content = safeUnicode(contract.__getattribute__(header)) cell = cellType(content) - + if self.font: cell.setFont(self.font) # 如果设置了特殊字体,则进行单元格设置 - + self.setItem(row, n, cell) - + row = row + 1 - + #---------------------------------------------------------------------- def refresh(self): """刷新""" @@ -1134,19 +1150,19 @@ class ContractMonitor(BasicMonitor): self.clearContents() self.setRowCount(0) self.showAllContracts() - + #---------------------------------------------------------------------- def addMenuAction(self): """增加右键菜单内容""" refreshAction = QtGui.QAction(u'刷新', self) refreshAction.triggered.connect(self.refresh) - + self.menu.addAction(refreshAction) - + #---------------------------------------------------------------------- def show(self): """显示""" super(ContractMonitor, self).show() self.refresh() - - + + diff --git a/vn.trader/uiMainWindow.py b/vn.trader/uiMainWindow.py index 007fd8b1..8115f61f 100644 --- a/vn.trader/uiMainWindow.py +++ b/vn.trader/uiMainWindow.py @@ -2,10 +2,10 @@ import psutil -from uiBasicWidget import * from ctaAlgo.uiCtaWidget import CtaEngineManager from dataRecorder.uiDrWidget import DrEngineManager from riskManager.uiRmWidget import RmEngineManager +from uiBasicWidget import * ######################################################################## class MainWindow(QtGui.QMainWindow): @@ -197,7 +197,7 @@ class MainWindow(QtGui.QMainWindow): try: self.widgetDict['drM'].showMaximized() except KeyError: - self.widgetDict['drM'] = DrEngineManager(self.mainEngine.drEngine, self.eventEngine) + self.widgetDict['drM'] = DrEngineManager(self.mainEngine, self.eventEngine) self.widgetDict['drM'].showMaximized() #---------------------------------------------------------------------- diff --git a/vn.trader/util/UiUtil.py b/vn.trader/util/UiUtil.py new file mode 100644 index 00000000..33eab6ac --- /dev/null +++ b/vn.trader/util/UiUtil.py @@ -0,0 +1,144 @@ +from PyQt4 import QtCore +from PyQt4.QtGui import QStyle, QStyledItemDelegate, QStyleOptionButton, QApplication, QItemDelegate, QComboBox, \ + QItemEditorCreatorBase + + +class CheckBoxDelegate(QStyledItemDelegate): + """ + A delegate that places a fully functioning QCheckBox in every + cell of the column to which it's applied + """ + + def __init__(self, parent): + QItemDelegate.__init__(self, parent) + + def createEditor(self, parent, option, index): + ''' + Important, otherwise an editor is created if the user clicks in this cell. + ** Need to hook up a signal to the model + ''' + return None + + def paint(self, painter, option, index): + ''' + Paint a checkbox without the label. + ''' + data = index.model().data(index, QtCore.Qt.CheckStateRole) + if not data in [QtCore.Qt.Unchecked, QtCore.Qt.Checked]: + return + checked = data == QtCore.Qt.Checked + check_box_style_option = QStyleOptionButton() + + if index.flags() & QtCore.Qt.ItemIsEditable == QtCore.Qt.ItemIsEditable: + check_box_style_option.state |= QStyle.State_Enabled + else: + check_box_style_option.state |= QStyle.State_ReadOnly + + if checked: + check_box_style_option.state |= QStyle.State_On + else: + check_box_style_option.state |= QStyle.State_Off + + check_box_style_option.rect = self.getCheckBoxRect(option) + + # this will not run - hasFlag does not exist + # if not index.model().hasFlag(index, QtCore.Qt.ItemIsEditable): + # check_box_style_option.state |= QStyle.State_ReadOnly + + # check_box_style_option.state |= QStyle.State_Enabled + + QApplication.style().drawControl(QStyle.CE_CheckBox, check_box_style_option, painter) + + def editorEvent(self, event, model, option, index): + ''' + Change the data in the model and the state of the checkbox + if the user presses the left mousebutton or presses + Key_Space or Key_Select and this cell is editable. Otherwise do nothing. + ''' + # print 'Check Box editor Event detected : ' + if index.flags() & QtCore.Qt.ItemIsEditable != QtCore.Qt.ItemIsEditable: + return False + + # print 'Check Box edior Event detected : passed first check' + # Do not change the checkbox-state + if event.type() == QtCore.QEvent.MouseButtonRelease or event.type() == QtCore.QEvent.MouseButtonDblClick: + if event.button() != QtCore.Qt.LeftButton or not self.getCheckBoxRect(option).contains(event.pos()): + return False + if event.type() == QtCore.QEvent.MouseButtonDblClick: + return True + elif event.type() == QtCore.QEvent.KeyPress: + if event.key() != QtCore.Qt.Key_Space and event.key() != QtCore.Qt.Key_Select: + return False + else: + return False + + # Change the checkbox-state + self.setModelData(None, model, index) + return True + + def setModelData(self, editor, model, index): + ''' + The user wanted to change the old state in the opposite. + ''' + # print 'SetModelData' + newValue = not index.data().toBool() + # print 'New Value : {0}'.format(newValue) + model.setData(index, QtCore.Qt.Checked if newValue else QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole) + + def getCheckBoxRect(self, option): + check_box_style_option = QStyleOptionButton() + check_box_rect = QApplication.style().subElementRect(QStyle.SE_CheckBoxIndicator, check_box_style_option, None) + check_box_point = QtCore.QPoint(option.rect.x() + + option.rect.width() / 2 - + check_box_rect.width() / 2, + option.rect.y() + + option.rect.height() / 2 - + check_box_rect.height() / 2) + return QtCore.QRect(check_box_point, check_box_rect.size()) + + +class ComboDelegate(QItemDelegate): + def __init__(self, parent, dataList=[]): + QItemDelegate.__init__(self, parent) + self.dataList = dataList + + def createEditor(self, parent, option, index): + combo = QComboBox(parent) + combo.addItems(self.dataList) + return combo + + def setEditorData(self, editor, index): + text = index.data().toString() + index = editor.findText(text) + editor.setCurrentIndex(index) + + def setModelData(self, editor, model, index): + model.setData(index, str(editor.itemText(editor.currentIndex()))) + + def updateEditorGeometry(self, editor, option, index): + print option, option.rect + editor.setGeometry(option.rect) + + +class GateWayListEditor(QComboBox): + def __init__(self, widget=None, dataList=[]): + super(GateWayListEditor, self).__init__(widget) + self.dataList = dataList + self.populateList() + + def getGateWay(self): + gateWay = self.itemData(self.currentIndex(), QtCore.Qt.DisplayRole) + return gateWay + + def setGateWay(self, gateWay): + self.setCurrentIndex(self.findData(gateWay, QtCore.Qt.DisplayRole)) + + def populateList(self): + for i, gateWayName in enumerate(self.dataList): + self.insertItem(i, gateWayName) + self.setItemData(i, gateWayName, QtCore.Qt.DisplayRole) + + +class GateWayListItemEditorCreator(QItemEditorCreatorBase): + def createWidget(self, parent): + return GateWayListEditor(parent, ["CTP", "LTS", "XTP", "FEMAS", "XSPEED", "QDP", "KSOTP", "KSGOLD", "SGIT"]) diff --git a/vn.trader/util/__init__.py b/vn.trader/util/__init__.py new file mode 100644 index 00000000..e69de29b