一、介绍

在上章中介绍了pyQt4整个运行的流程,该章摘取的是vnpy中关于MainWindow窗口的布局。源代码中有个父类uiBasicWidget.py内容是布局中需要的单元格和表格类。

二、代码

uiBasicWidget.py主要是布局组件的代码。待Cell的类为单元格类,后面Monitor是大的组件表格类。整个代码主要部分有qt的connect事件绑定及触发,界面右键菜单的实现,单元格双击事件的connect,以及qt中基本的组件。

# encoding: UTF-8import json
import csv
import os
import platform
from collections import OrderedDictfrom vnpy.event import *
from vnpy.trader.vtEvent import *
from vnpy.trader.vtFunction import *
from vnpy.trader.vtGateway import *
from vnpy.trader import vtText
from vnpy.trader.uiQt import QtGui, QtWidgets, QtCore, BASIC_FONTCOLOR_RED = QtGui.QColor('red')
COLOR_GREEN = QtGui.QColor('green')########################################################################
class BasicCell(QtWidgets.QTableWidgetItem):"""基础的单元格"""#----------------------------------------------------------------------def __init__(self, text=None, mainEngine=None):"""Constructor"""super(BasicCell, self).__init__()self.data = Noneif text:self.setContent(text)#----------------------------------------------------------------------def setContent(self, text):"""设置内容"""if text == '0' or text == '0.0':self.setText('')else:self.setText(text)########################################################################
class BasicMonitor(QtWidgets.QTableWidget):"""基础监控headerDict中的值对应的字典格式如下{'chinese': u'中文名', 'cellType': BasicCell}"""signal = QtCore.Signal(type(Event()))#----------------------------------------------------------------------def __init__(self, mainEngine=None, eventEngine=None, parent=None):"""Constructor"""super(BasicMonitor, self).__init__(parent)self.mainEngine = mainEngineself.eventEngine = eventEngine# 保存表头标签用self.headerDict = OrderedDict()  # 有序字典,key是英文名,value是对应的配置字典self.headerList = []             # 对应self.headerDict.keys()# 保存相关数据用self.dataDict = {}  # 字典,key是字段对应的数据,value是保存相关单元格的字典self.dataKey = ''   # 字典键对应的数据字段# 监控的事件类型self.eventType = ''# 列宽调整状态(只在第一次更新数据时调整一次列宽)self.columnResized = False# 字体self.font = None# 保存数据对象到单元格self.saveData = False# 默认不允许根据表头进行排序,需要的组件可以开启self.sorting = False# 初始化右键菜单self.initMenu()#----------------------------------------------------------------------def setHeaderDict(self, headerDict):"""设置表头有序字典"""self.headerDict = headerDictself.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)#----------------------------------------------------------------------def registerEvent(self):"""注册GUI更新相关的事件监听"""#此处qtpy自身的事件引擎self.signal.connect绑定了事件函数,而self.eventEngine是vnpy中自定义的事件引擎。当该eventEngine事件引擎被触发时会调用self.signal.emit发出信号函数,再由qtpy自身的事件引擎处理调用self.updateEvent函数。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)     d = {}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 = dataself.setItem(0, n, cell)d[header] = cellself.dataDict[key] = d# 否则如果已经存在,则直接更新相关单元格else:d = self.dataDict[key]for header in self.headerList:content = safeUnicode(data.__getattribute__(header))cell = d[header]cell.setContent(content)if self.saveData:            # 如果设置了保存数据对象,则进行对象保存cell.data = data                    # 否则采用增量更新模式else: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                self.setItem(0, n, cell)                        # 调整列宽if not self.columnResized:self.resizeColumns()self.columnResized = True# 重新打开排序if self.sorting:self.setSortingEnabled(True)#----------------------------------------------------------------------def resizeColumns(self):"""调整各列的大小"""self.horizontalHeader().resizeSections(QtWidgets.QHeaderView.ResizeToContents)    #----------------------------------------------------------------------def setSorting(self, sorting):"""设置是否允许根据表头排序"""self.sorting = sorting#----------------------------------------------------------------------def saveToCsv(self):"""保存表格内容到CSV文件"""# 先隐藏右键菜单self.menu.close()# 获取想要保存的文件名path = QtWidgets.QFileDialog.getSaveFileName(self, vtText.SAVE_DATA, '', 'CSV(*.csv)')try:#if not path.isEmpty():if path: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 = []for column in range(self.columnCount()):item = self.item(row, column)if item is not None:rowdata.append(unicode(item.text()).encode('gbk'))else:rowdata.append('')writer.writerow(rowdata)     except IOError:pass#----------------------------------------------------------------------def initMenu(self):"""初始化右键菜单"""#qt邮件菜单,先实例化菜单和菜单内容项,将菜单内容项saveAction链接到相应事件,再将saveAction加入到菜单对象menu中。self.menu = QtWidgets.QMenu(self)    saveAction = QtWidgets.QAction(vtText.SAVE_DATA, self)saveAction.triggered.connect(self.saveToCsv)self.menu.addAction(saveAction)#----------------------------------------------------------------------def contextMenuEvent(self, event):"""右键点击事件"""self.menu.popup(QtGui.QCursor.pos())    ########################################################################
class OrderMonitor(BasicMonitor):"""委托监控"""#----------------------------------------------------------------------def __init__(self, mainEngine, eventEngine, parent=None):"""Constructor"""super(OrderMonitor, self).__init__(mainEngine, eventEngine, parent)self.mainEngine = mainEngined = OrderedDict()d['orderID'] = {'chinese':vtText.ORDER_ID, 'cellType':NumCell}d['symbol'] = {'chinese':vtText.CONTRACT_SYMBOL, 'cellType':BasicCell}d['vtSymbol'] = {'chinese':vtText.CONTRACT_NAME, 'cellType':NameCell}d['direction'] = {'chinese':vtText.DIRECTION, 'cellType':DirectionCell}d['offset'] = {'chinese':vtText.OFFSET, 'cellType':BasicCell}d['price'] = {'chinese':vtText.PRICE, 'cellType':BasicCell}d['totalVolume'] = {'chinese':vtText.ORDER_VOLUME, 'cellType':BasicCell}d['tradedVolume'] = {'chinese':vtText.TRADED_VOLUME, 'cellType':BasicCell}d['status'] = {'chinese':vtText.ORDER_STATUS, 'cellType':BasicCell}d['orderTime'] = {'chinese':vtText.ORDER_TIME, 'cellType':BasicCell}d['cancelTime'] = {'chinese':vtText.CANCEL_TIME, 'cellType':BasicCell}#d['frontID'] = {'chinese':vtText.FRONT_ID, 'cellType':BasicCell}         # 考虑到在vn.trader中,ctpGateway的报单号应该是始终递增的,因此这里可以忽略#d['sessionID'] = {'chinese':vtText.SESSION_ID, 'cellType':BasicCell}d['gatewayName'] = {'chinese':vtText.GATEWAY, '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) #----------------------------------------------------------------------def cancelOrder(self, cell):"""根据单元格的数据撤单"""order = cell.datareq = VtCancelOrderReq()req.symbol = order.symbolreq.exchange = order.exchangereq.frontID = order.frontIDreq.sessionID = order.sessionIDreq.orderID = order.orderIDself.mainEngine.cancelOrder(req, order.gatewayName)########################################################################
class PositionMonitor(BasicMonitor):"""持仓监控"""#----------------------------------------------------------------------def __init__(self, mainEngine, eventEngine, parent=None):"""Constructor"""super(PositionMonitor, self).__init__(mainEngine, eventEngine, parent)d = OrderedDict()d['symbol'] = {'chinese':vtText.CONTRACT_SYMBOL, 'cellType':BasicCell}d['vtSymbol'] = {'chinese':vtText.CONTRACT_NAME, 'cellType':NameCell}d['direction'] = {'chinese':vtText.DIRECTION, 'cellType':DirectionCell}d['position'] = {'chinese':vtText.POSITION, 'cellType':BasicCell}d['ydPosition'] = {'chinese':vtText.YD_POSITION, 'cellType':BasicCell}d['frozen'] = {'chinese':vtText.FROZEN, 'cellType':BasicCell}d['price'] = {'chinese':vtText.PRICE, 'cellType':BasicCell}d['positionProfit'] = {'chinese':vtText.POSITION_PROFIT, 'cellType':PnlCell}d['gatewayName'] = {'chinese':vtText.GATEWAY, '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):"""账户监控"""#----------------------------------------------------------------------def __init__(self, mainEngine, eventEngine, parent=None):"""Constructor"""super(AccountMonitor, self).__init__(mainEngine, eventEngine, parent)d = OrderedDict()d['accountID'] = {'chinese':vtText.ACCOUNT_ID, 'cellType':BasicCell}d['preBalance'] = {'chinese':vtText.PRE_BALANCE, 'cellType':BasicCell}d['balance'] = {'chinese':vtText.BALANCE, 'cellType':BasicCell}d['available'] = {'chinese':vtText.AVAILABLE, 'cellType':BasicCell}d['commission'] = {'chinese':vtText.COMMISSION, 'cellType':BasicCell}d['margin'] = {'chinese':vtText.MARGIN, 'cellType':BasicCell}d['closeProfit'] = {'chinese':vtText.CLOSE_PROFIT, 'cellType':BasicCell}d['positionProfit'] = {'chinese':vtText.POSITION_PROFIT, 'cellType':BasicCell}d['gatewayName'] = {'chinese':vtText.GATEWAY, 'cellType':BasicCell}self.setHeaderDict(d)self.setDataKey('vtAccountID')self.setEventType(EVENT_ACCOUNT)self.setFont(BASIC_FONT)self.initTable()self.registerEvent()########################################################################
class TradingWidget(QtWidgets.QFrame):"""简单交易组件"""signal = QtCore.Signal(type(Event()))directionList = [DIRECTION_LONG,DIRECTION_SHORT]offsetList = [OFFSET_OPEN,OFFSET_CLOSE,OFFSET_CLOSEYESTERDAY,OFFSET_CLOSETODAY]priceTypeList = [PRICETYPE_LIMITPRICE,PRICETYPE_MARKETPRICE,PRICETYPE_FAK,PRICETYPE_FOK]exchangeList = [EXCHANGE_NONE,EXCHANGE_CFFEX,EXCHANGE_SHFE,EXCHANGE_DCE,EXCHANGE_CZCE,EXCHANGE_SSE,EXCHANGE_SZSE,EXCHANGE_SGE,EXCHANGE_HKEX,EXCHANGE_HKFE,EXCHANGE_SMART,EXCHANGE_ICE,EXCHANGE_CME,EXCHANGE_NYMEX,EXCHANGE_LME,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 = ['']#----------------------------------------------------------------------def __init__(self, mainEngine, eventEngine, parent=None):"""Constructor"""super(TradingWidget, self).__init__(parent)self.mainEngine = mainEngineself.eventEngine = eventEngineself.symbol = ''# 添加交易接口l = mainEngine.getAllGatewayDetails()gatewayNameList = [d['gatewayName'] for d in l]self.gatewayList.extend(gatewayNameList)self.initUi()self.connectSignal()#----------------------------------------------------------------------def initUi(self):"""初始化界面"""self.setWindowTitle(vtText.TRADING)self.setMaximumWidth(400)self.setFrameShape(self.Box)    # 设置边框self.setLineWidth(1)           # 左边部分labelSymbol = QtWidgets.QLabel(vtText.CONTRACT_SYMBOL)labelName =  QtWidgets.QLabel(vtText.CONTRACT_NAME)labelDirection = QtWidgets.QLabel(vtText.DIRECTION)labelOffset = QtWidgets.QLabel(vtText.OFFSET)labelPrice = QtWidgets.QLabel(vtText.PRICE)self.checkFixed = QtWidgets.QCheckBox(u'')  # 价格固定选择框labelVolume = QtWidgets.QLabel(vtText.VOLUME)labelPriceType = QtWidgets.QLabel(vtText.PRICE_TYPE)labelExchange = QtWidgets.QLabel(vtText.EXCHANGE) labelCurrency = QtWidgets.QLabel(vtText.CURRENCY)labelProductClass = QtWidgets.QLabel(vtText.PRODUCT_CLASS)labelGateway = QtWidgets.QLabel(vtText.GATEWAY)self.lineSymbol = QtWidgets.QLineEdit()self.lineName = QtWidgets.QLineEdit()self.comboDirection = QtWidgets.QComboBox()self.comboDirection.addItems(self.directionList)self.comboOffset = QtWidgets.QComboBox()self.comboOffset.addItems(self.offsetList)self.spinPrice = QtWidgets.QDoubleSpinBox()self.spinPrice.setDecimals(4)self.spinPrice.setMinimum(0)self.spinPrice.setMaximum(100000)self.spinVolume = QtWidgets.QSpinBox()self.spinVolume.setMinimum(0)self.spinVolume.setMaximum(1000000)self.comboPriceType = QtWidgets.QComboBox()self.comboPriceType.addItems(self.priceTypeList)self.comboExchange = QtWidgets.QComboBox()self.comboExchange.addItems(self.exchangeList)      self.comboCurrency = QtWidgets.QComboBox()self.comboCurrency.addItems(self.currencyList)self.comboProductClass = QtWidgets.QComboBox()self.comboProductClass.addItems(self.productClassList)     self.comboGateway = QtWidgets.QComboBox()self.comboGateway.addItems(self.gatewayList)          gridleft = QtWidgets.QGridLayout()gridleft.addWidget(labelSymbol, 0, 0)gridleft.addWidget(labelName, 1, 0)gridleft.addWidget(labelDirection, 2, 0)gridleft.addWidget(labelOffset, 3, 0)gridleft.addWidget(labelPrice, 4, 0)gridleft.addWidget(labelVolume, 5, 0)gridleft.addWidget(labelPriceType, 6, 0)gridleft.addWidget(labelExchange, 7, 0)gridleft.addWidget(labelCurrency, 8, 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)gridleft.addWidget(self.comboOffset, 3, 1, 1, -1)gridleft.addWidget(self.checkFixed, 4, 1)gridleft.addWidget(self.spinPrice, 4, 2)gridleft.addWidget(self.spinVolume, 5, 1, 1, -1)gridleft.addWidget(self.comboPriceType, 6, 1, 1, -1)gridleft.addWidget(self.comboExchange, 7, 1, 1, -1)gridleft.addWidget(self.comboCurrency, 8, 1, 1, -1)gridleft.addWidget(self.comboProductClass, 9, 1, 1, -1)gridleft.addWidget(self.comboGateway, 10, 1, 1, -1)# 右边部分labelBid1 = QtWidgets.QLabel(vtText.BID_1)labelBid2 = QtWidgets.QLabel(vtText.BID_2)labelBid3 = QtWidgets.QLabel(vtText.BID_3)labelBid4 = QtWidgets.QLabel(vtText.BID_4)labelBid5 = QtWidgets.QLabel(vtText.BID_5)labelAsk1 = QtWidgets.QLabel(vtText.ASK_1)labelAsk2 = QtWidgets.QLabel(vtText.ASK_2)labelAsk3 = QtWidgets.QLabel(vtText.ASK_3)labelAsk4 = QtWidgets.QLabel(vtText.ASK_4)labelAsk5 = QtWidgets.QLabel(vtText.ASK_5)self.labelBidPrice1 = QtWidgets.QLabel()self.labelBidPrice2 = QtWidgets.QLabel()self.labelBidPrice3 = QtWidgets.QLabel()self.labelBidPrice4 = QtWidgets.QLabel()self.labelBidPrice5 = QtWidgets.QLabel()self.labelBidVolume1 = QtWidgets.QLabel()self.labelBidVolume2 = QtWidgets.QLabel()self.labelBidVolume3 = QtWidgets.QLabel()self.labelBidVolume4 = QtWidgets.QLabel()self.labelBidVolume5 = QtWidgets.QLabel()   self.labelAskPrice1 = QtWidgets.QLabel()self.labelAskPrice2 = QtWidgets.QLabel()self.labelAskPrice3 = QtWidgets.QLabel()self.labelAskPrice4 = QtWidgets.QLabel()self.labelAskPrice5 = QtWidgets.QLabel()self.labelAskVolume1 = QtWidgets.QLabel()self.labelAskVolume2 = QtWidgets.QLabel()self.labelAskVolume3 = QtWidgets.QLabel()self.labelAskVolume4 = QtWidgets.QLabel()self.labelAskVolume5 = QtWidgets.QLabel()   labelLast = QtWidgets.QLabel(vtText.LAST)self.labelLastPrice = QtWidgets.QLabel()self.labelReturn = QtWidgets.QLabel()self.labelLastPrice.setMinimumWidth(60)self.labelReturn.setMinimumWidth(60)gridRight = QtWidgets.QGridLayout()gridRight.addWidget(labelAsk5, 0, 0)gridRight.addWidget(labelAsk4, 1, 0)gridRight.addWidget(labelAsk3, 2, 0)gridRight.addWidget(labelAsk2, 3, 0)gridRight.addWidget(labelAsk1, 4, 0)gridRight.addWidget(labelLast, 5, 0)gridRight.addWidget(labelBid1, 6, 0)gridRight.addWidget(labelBid2, 7, 0)gridRight.addWidget(labelBid3, 8, 0)gridRight.addWidget(labelBid4, 9, 0)gridRight.addWidget(labelBid5, 10, 0)gridRight.addWidget(self.labelAskPrice5, 0, 1)gridRight.addWidget(self.labelAskPrice4, 1, 1)gridRight.addWidget(self.labelAskPrice3, 2, 1)gridRight.addWidget(self.labelAskPrice2, 3, 1)gridRight.addWidget(self.labelAskPrice1, 4, 1)gridRight.addWidget(self.labelLastPrice, 5, 1)gridRight.addWidget(self.labelBidPrice1, 6, 1)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.labelAskVolume5, 0, 2)gridRight.addWidget(self.labelAskVolume4, 1, 2)gridRight.addWidget(self.labelAskVolume3, 2, 2)gridRight.addWidget(self.labelAskVolume2, 3, 2)gridRight.addWidget(self.labelAskVolume1, 4, 2)gridRight.addWidget(self.labelReturn, 5, 2)gridRight.addWidget(self.labelBidVolume1, 6, 2)gridRight.addWidget(self.labelBidVolume2, 7, 2)gridRight.addWidget(self.labelBidVolume3, 8, 2)gridRight.addWidget(self.labelBidVolume4, 9, 2)gridRight.addWidget(self.labelBidVolume5, 10, 2)# 发单按钮buttonSendOrder = QtWidgets.QPushButton(vtText.SEND_ORDER)buttonCancelAll = QtWidgets.QPushButton(vtText.CANCEL_ALL)size = buttonSendOrder.sizeHint()buttonSendOrder.setMinimumHeight(size.height()*2)   # 把按钮高度设为默认两倍buttonCancelAll.setMinimumHeight(size.height()*2)# 整合布局hbox = QtWidgets.QHBoxLayout()hbox.addLayout(gridleft)hbox.addLayout(gridRight)vbox = QtWidgets.QVBoxLayout()vbox.addLayout(hbox)vbox.addWidget(buttonSendOrder)vbox.addWidget(buttonCancelAll)vbox.addStretch()self.setLayout(vbox)# 关联更新buttonSendOrder.clicked.connect(self.sendOrder)buttonCancelAll.clicked.connect(self.cancelAll)self.lineSymbol.returnPressed.connect(self.updateSymbol)#----------------------------------------------------------------------def updateSymbol(self):"""合约变化"""# 读取组件数据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())# 查询合约if exchange:vtSymbol = '.'.join([symbol, exchange])contract = self.mainEngine.getContract(vtSymbol)else:vtSymbol = symbolcontract = self.mainEngine.getContract(symbol)   if contract:vtSymbol = contract.vtSymbolgatewayName = contract.gatewayNameself.lineName.setText(contract.name)exchange = contract.exchange    # 保证有交易所代码# 清空价格数量self.spinPrice.setValue(0)self.spinVolume.setValue(0)# 清空行情显示self.labelBidPrice1.setText('')self.labelBidPrice2.setText('')self.labelBidPrice3.setText('')self.labelBidPrice4.setText('')self.labelBidPrice5.setText('')self.labelBidVolume1.setText('')self.labelBidVolume2.setText('')self.labelBidVolume3.setText('')self.labelBidVolume4.setText('')self.labelBidVolume5.setText('')    self.labelAskPrice1.setText('')self.labelAskPrice2.setText('')self.labelAskPrice3.setText('')self.labelAskPrice4.setText('')self.labelAskPrice5.setText('')self.labelAskVolume1.setText('')self.labelAskVolume2.setText('')self.labelAskVolume3.setText('')self.labelAskVolume4.setText('')self.labelAskVolume5.setText('')self.labelLastPrice.setText('')self.labelReturn.setText('')# 重新注册事件监听self.eventEngine.unregister(EVENT_TICK + self.symbol, self.signal.emit)self.eventEngine.register(EVENT_TICK + vtSymbol, self.signal.emit)# 订阅合约req = VtSubscribeReq()req.symbol = symbolreq.exchange = exchangereq.currency = currencyreq.productClass = productClass# 默认跟随价self.checkFixed.setChecked(False)self.mainEngine.subscribe(req, gatewayName)# 更新组件当前交易的合约self.symbol = vtSymbol#----------------------------------------------------------------------def updateTick(self, event):"""更新行情"""tick = event.dict_['data']if tick.vtSymbol == self.symbol:if not self.checkFixed.isChecked():self.spinPrice.setValue(tick.lastPrice)self.labelBidPrice1.setText(str(tick.bidPrice1))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.labelLastPrice.setText(str(tick.lastPrice))if tick.preClosePrice:rt = (tick.lastPrice/tick.preClosePrice)-1self.labelReturn.setText(('%.2f' %(rt*100))+'%')else:self.labelReturn.setText('')#----------------------------------------------------------------------def connectSignal(self):"""连接Signal"""self.signal.connect(self.updateTick)#----------------------------------------------------------------------def sendOrder(self):"""发单"""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())        # 查询合约if exchange:vtSymbol = '.'.join([symbol, exchange])contract = self.mainEngine.getContract(vtSymbol)else:vtSymbol = symbolcontract = self.mainEngine.getContract(symbol)if contract:gatewayName = contract.gatewayNameexchange = contract.exchange    # 保证有交易所代码req = VtOrderReq()req.symbol = symbolreq.exchange = exchangereq.price = self.spinPrice.value()req.volume = self.spinVolume.value()req.direction = unicode(self.comboDirection.currentText())req.priceType = unicode(self.comboPriceType.currentText())req.offset = unicode(self.comboOffset.currentText())req.currency = currencyreq.productClass = productClassself.mainEngine.sendOrder(req, gatewayName)#----------------------------------------------------------------------def cancelAll(self):"""一键撤销所有委托"""l = self.mainEngine.getAllWorkingOrders()for order in l:req = VtCancelOrderReq()req.symbol = order.symbolreq.exchange = order.exchangereq.frontID = order.frontIDreq.sessionID = order.sessionIDreq.orderID = order.orderIDself.mainEngine.cancelOrder(req, order.gatewayName)#----------------------------------------------------------------------def closePosition(self, cell):"""根据持仓信息自动填写交易组件"""# 读取持仓数据,cell是一个表格中的单元格对象pos = cell.datasymbol = pos.symbol# 更新交易组件的显示合约self.lineSymbol.setText(symbol)self.updateSymbol()# 自动填写信息self.comboPriceType.setCurrentIndex(self.priceTypeList.index(PRICETYPE_LIMITPRICE))self.comboOffset.setCurrentIndex(self.offsetList.index(OFFSET_CLOSE))self.spinVolume.setValue(pos.position)if pos.direction == DIRECTION_LONG or pos.direction == DIRECTION_NET:self.comboDirection.setCurrentIndex(self.directionList.index(DIRECTION_SHORT))else:self.comboDirection.setCurrentIndex(self.directionList.index(DIRECTION_LONG))# 价格留待更新后由用户输入,防止有误操作

MainWindow.py主要内容有主窗口菜单的创建,如何将Basic中组件加载到主窗口中,自定义Qtpy信号通道及触发,以及如何退出主窗口,和如何保存界面设置及其恢复。

# encoding: UTF-8# python跨平台库,用于获取系统进程,进运行信息(CPU,运存,磁盘,网络等)
import psutilfrom vnpy.trader.vtFunction import loadIconPath
from vnpy.trader.vtGlobal import globalSetting
from vnpy.trader.uiBasicWidget import *########################################################################
class MainWindow(QtWidgets.QMainWindow):"""主窗口"""  # 自定义信号变量,用于连接槽函数,并可主动触发.signalStatusBar = QtCore.Signal(type(Event()))#----------------------------------------------------------------------def __init__(self, mainEngine, eventEngine):"""Constructor"""          super(MainWindow, self).__init__()self.mainEngine = mainEngineself.eventEngine = eventEnginel = self.mainEngine.getAllGatewayDetails()self.gatewayNameList = [d['gatewayName'] for d in l]        self.widgetDict = {}    # 用来保存子窗口的字典# 获取主引擎中的上层应用信息self.appDetailList = self.mainEngine.getAllAppDetails()self.initUi()self.loadWindowSettings('custom')#----------------------------------------------------------------------def initUi(self):"""初始化界面"""self.setWindowTitle('VnTrader')self.initCentral()self.initMenu()self.initStatusBar()#----------------------------------------------------------------------def initCentral(self):"""初始化中心区域"""widgetMarketM, dockMarketM = self.createDock(MarketMonitor, vtText.MARKET_DATA, QtCore.Qt.RightDockWidgetArea)widgetLogM, dockLogM = self.createDock(LogMonitor, vtText.LOG, QtCore.Qt.BottomDockWidgetArea)widgetErrorM, dockErrorM = self.createDock(ErrorMonitor, vtText.ERROR, QtCore.Qt.BottomDockWidgetArea)widgetTradeM, dockTradeM = self.createDock(TradeMonitor, vtText.TRADE, QtCore.Qt.BottomDockWidgetArea)widgetOrderM, dockOrderM = self.createDock(OrderMonitor, vtText.ORDER, QtCore.Qt.RightDockWidgetArea)widgetPositionM, dockPositionM = self.createDock(PositionMonitor, vtText.POSITION, QtCore.Qt.BottomDockWidgetArea)widgetAccountM, dockAccountM = self.createDock(AccountMonitor, vtText.ACCOUNT, QtCore.Qt.BottomDockWidgetArea)widgetTradingW, dockTradingW = self.createDock(TradingWidget, vtText.TRADING, QtCore.Qt.LeftDockWidgetArea)self.tabifyDockWidget(dockTradeM, dockErrorM)self.tabifyDockWidget(dockTradeM, dockLogM)self.tabifyDockWidget(dockPositionM, dockAccountM)dockTradeM.raise_()dockPositionM.raise_()# 连接组件之间的信号widgetPositionM.itemDoubleClicked.connect(widgetTradingW.closePosition)# 保存默认设置self.saveWindowSettings('default')#----------------------------------------------------------------------def initMenu(self):"""初始化菜单"""# 创建菜单menubar = self.menuBar()# 设计为只显示存在的接口gatewayDetails = self.mainEngine.getAllGatewayDetails()sysMenu = menubar.addMenu(vtText.SYSTEM)for d in gatewayDetails:if d['gatewayType'] == GATEWAYTYPE_FUTURES:self.addConnectAction(sysMenu, d['gatewayName'], d['gatewayDisplayName'])sysMenu.addSeparator()for d in gatewayDetails:if d['gatewayType'] == GATEWAYTYPE_EQUITY:self.addConnectAction(sysMenu, d['gatewayName'], d['gatewayDisplayName'])sysMenu.addSeparator()for d in gatewayDetails:if d['gatewayType'] == GATEWAYTYPE_INTERNATIONAL:self.addConnectAction(sysMenu, d['gatewayName'], d['gatewayDisplayName'])       sysMenu.addSeparator()for d in gatewayDetails:if d['gatewayType'] == GATEWAYTYPE_BTC:self.addConnectAction(sysMenu, d['gatewayName'], d['gatewayDisplayName'])sysMenu.addSeparator()for d in gatewayDetails:if d['gatewayType'] == GATEWAYTYPE_DATA:self.addConnectAction(sysMenu, d['gatewayName'], d['gatewayDisplayName'])sysMenu.addSeparator()sysMenu.addAction(self.createAction(vtText.CONNECT_DATABASE, self.mainEngine.dbConnect, loadIconPath('database.ico')))sysMenu.addSeparator()sysMenu.addAction(self.createAction(vtText.EXIT, self.close, loadIconPath('exit.ico')))# 功能应用appMenu = menubar.addMenu(vtText.APPLICATION)for appDetail in self.appDetailList:function = self.createOpenAppFunction(appDetail)action = self.createAction(appDetail['appDisplayName'], function, loadIconPath(appDetail['appIco']))appMenu.addAction(action)# 帮助helpMenu = menubar.addMenu(vtText.HELP)helpMenu.addAction(self.createAction(vtText.CONTRACT_SEARCH, self.openContract, loadIconPath('contract.ico')))helpMenu.addSeparator()helpMenu.addAction(self.createAction(vtText.RESTORE, self.restoreWindow, loadIconPath('restore.ico')))helpMenu.addAction(self.createAction(vtText.ABOUT, self.openAbout, loadIconPath('about.ico')))helpMenu.addSeparator()helpMenu.addAction(self.createAction(vtText.TEST, self.test, loadIconPath('test.ico')))#----------------------------------------------------------------------def initStatusBar(self):"""初始化状态栏"""self.statusLabel = QtWidgets.QLabel()self.statusLabel.setAlignment(QtCore.Qt.AlignLeft)self.statusBar().addPermanentWidget(self.statusLabel)self.statusLabel.setText(self.getCpuMemory())self.sbCount = 0self.sbTrigger = 10     # 10秒刷新一次self.signalStatusBar.connect(self.updateStatusBar)self.eventEngine.register(EVENT_TIMER, self.signalStatusBar.emit)#----------------------------------------------------------------------def updateStatusBar(self, event):"""在状态栏更新CPU和内存信息"""self.sbCount += 1if self.sbCount == self.sbTrigger:self.sbCount = 0self.statusLabel.setText(self.getCpuMemory())#----------------------------------------------------------------------def getCpuMemory(self):"""获取CPU和内存状态信息"""cpuPercent = psutil.cpu_percent()memoryPercent = psutil.virtual_memory().percentreturn vtText.CPU_MEMORY_INFO.format(cpu=cpuPercent, memory=memoryPercent)#----------------------------------------------------------------------def addConnectAction(self, menu, gatewayName, displayName=''):"""增加连接功能"""if gatewayName not in self.gatewayNameList:returndef connect():self.mainEngine.connect(gatewayName)if not displayName:displayName = gatewayNameactionName = vtText.CONNECT + displayNameconnectAction = self.createAction(actionName, connect, loadIconPath('connect.ico'))menu.addAction(connectAction)#----------------------------------------------------------------------def createAction(self, actionName, function, iconPath=''):"""创建操作功能"""action = QtWidgets.QAction(actionName, self)action.triggered.connect(function)if iconPath:icon = QtGui.QIcon(iconPath)action.setIcon(icon)return action#----------------------------------------------------------------------def createOpenAppFunction(self, appDetail):"""创建打开应用UI的函数"""def openAppFunction():appName = appDetail['appName']try:self.widgetDict[appName].show()except KeyError:appEngine = self.mainEngine.appDict[appName]self.widgetDict[appName] = appDetail['appWidget'](appEngine, self.eventEngine)self.widgetDict[appName].show()return openAppFunction#----------------------------------------------------------------------def test(self):"""测试按钮用的函数"""# 有需要使用手动触发的测试函数可以写在这里pass#----------------------------------------------------------------------def openAbout(self):"""打开关于"""try:self.widgetDict['aboutW'].show()except KeyError:self.widgetDict['aboutW'] = AboutWidget(self)self.widgetDict['aboutW'].show()#----------------------------------------------------------------------def openContract(self):"""打开合约查询"""try:self.widgetDict['contractM'].show()except KeyError:self.widgetDict['contractM'] = ContractManager(self.mainEngine)self.widgetDict['contractM'].show()#----------------------------------------------------------------------def closeEvent(self, event):"""关闭事件"""reply = QtWidgets.QMessageBox.question(self, vtText.EXIT,vtText.CONFIRM_EXIT, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)if reply == QtWidgets.QMessageBox.Yes: for widget in self.widgetDict.values():widget.close()self.saveWindowSettings('custom')# 关闭主窗口self.mainEngine.exit()event.accept()else:event.ignore()#----------------------------------------------------------------------def createDock(self, widgetClass, widgetName, widgetArea):"""创建停靠组件"""widget = widgetClass(self.mainEngine, self.eventEngine)dock = QtWidgets.QDockWidget(widgetName)dock.setWidget(widget)dock.setObjectName(widgetName)dock.setFeatures(dock.DockWidgetFloatable|dock.DockWidgetMovable)self.addDockWidget(widgetArea, dock)return widget, dock#----------------------------------------------------------------------def saveWindowSettings(self, settingName):"""保存窗口设置"""settings = QtCore.QSettings('vn.trader', settingName)settings.setValue('state', self.saveState())settings.setValue('geometry', self.saveGeometry())#----------------------------------------------------------------------def loadWindowSettings(self, settingName):"""载入窗口设置"""settings = QtCore.QSettings('vn.trader', settingName)# 这里由于PyQt4的版本不同,settings.value('state')调用返回的结果可能是:# 1. None(初次调用,注册表里无相应记录,因此为空)# 2. QByteArray(比较新的PyQt4)# 3. QVariant(以下代码正确执行所需的返回结果)# 所以为了兼容考虑,这里加了一个try...except,如果是1、2的情况就pass# 可能导致主界面的设置无法载入(每次退出时的保存其实是成功了)try:self.restoreState(settings.value('state').toByteArray())self.restoreGeometry(settings.value('geometry').toByteArray())    except AttributeError:pass#----------------------------------------------------------------------def restoreWindow(self):"""还原默认窗口设置(还原停靠组件位置)"""self.loadWindowSettings('default')self.showMaximized()########################################################################
class AboutWidget(QtWidgets.QDialog):"""显示关于信息"""#----------------------------------------------------------------------def __init__(self, parent=None):"""Constructor"""super(AboutWidget, self).__init__(parent)self.initUi()#----------------------------------------------------------------------def initUi(self):""""""self.setWindowTitle(vtText.ABOUT + 'VnTrader')text = u"""Developed by Traders, for Traders.License:MITWebsite:www.vnpy.orgGithub:www.github.com/vnpy/vnpy"""label = QtWidgets.QLabel()label.setText(text)label.setMinimumWidth(500)vbox = QtWidgets.QVBoxLayout()vbox.addWidget(label)self.setLayout(vbox)

Pyqt4 vnpy源码中关于GUI设计 2相关推荐

  1. vue中rules校验是验证首字符_小白也能秒懂Vue源码中那些精细设计(选项处理)...

    我"崩"不住了,在彭凡同志锲而不舍的催促下这篇文章终于"蛋"生了. 说正经的这篇文章不好写,不好写的原因是我不太擅长写这些类比文,但它还是写出来了. 相信大部分 ...

  2. 盘点 HashMap 源码中的那些优雅的设计!

    以下文章来源方志朋的博客,回复"666"获面试宝典 一.HashMap构造器 HashMap总共给我们提供了三个构造器来创建HashMap对象. 1.无参构造函数public Ha ...

  3. 第三篇:属性_第二节:控件属性在页面及源码中的表示方式

    一.属性在页面及源码中的表示方式 认真地看看页面中声明控件的代码,你会发现控件属性在页面中的表示千变万化.我们看看下面这些: <%@ Page Language="C#" A ...

  4. japidocs怎么设置参数必填_JApiDocs 动态生成接口文档,并解析java 源码中的注释...

    1.介绍 JApiDocs 是一个符合 Java 编程习惯的 Api 文档生成工具.最大程度地利用 Java 的语法特性,你只管用心设计好接口,添加必要的注释,JApiDocs 会帮你导出一份漂亮的 ...

  5. Git之深入解析如何使用Git调试项目源码中的问题

    一.前言 了解了管理或者维护 Git 仓库.实现代码控制所需的大多数日常命令和工作流程,尝试跟了踪和提交文件的基本操作,并且掌握了暂存区和轻量级地分支及合并的威力.如果想进一步对 Git 深入学习,可 ...

  6. 装饰者模式在源码中的应用

    装饰器模式在源码中也应用得非常多,在JDK 中体现最明显的类就是IO 相关的类,如BufferedReader.InputStream.OutputStream,看一下常用的InputStream 的 ...

  7. 策略模式在JDK 源码中的体现

    首先来看一个比较常用的比较器Comparator 接口,我们看到的一个大家常用的compare()方法,就是一个策略抽象实现: Comparator 抽象下面有非常多的实现类,我们经常会把Compar ...

  8. 面试官系统精讲Java源码及大厂真题 - 24 举一反三:队列在 Java 其它源码中的应用

    24 举一反三:队列在 Java 其它源码中的应用 世上无难事,只要肯登攀. 引导语 队列除了提供 API 供开发者使用外,自身也和 Java 中其他 API 紧密结合,比如线程池和锁,线程池直接使用 ...

  9. Java 8 ConcurrentHashMap源码中竟然隐藏着两个BUG

    Java 7的ConcurrenHashMap的源码我建议大家都看看,那个版本的源码就是Java多线程编程的教科书.在Java 7的源码中,作者对悲观锁的使用非常谨慎,大多都转换为自旋锁加volati ...

最新文章

  1. ubuntu 14.04 双显卡安装NVIDIA GPU驱动+CUDA+编译配置caffe
  2. 二维树状数组 ---- codeforces341D
  3. 调整数组顺序使奇数位于偶数的前面
  4. git 常见命令,规范 整理
  5. 重载练习2_判断方法的正确重载
  6. lcl手术和飞秒区别_干货科普:目前市面上4种近视手术方式有何区别(全飞秒、半飞秒、全激光、晶体植入)...
  7. InterlockedIncrement
  8. Spring与Struts2的整合
  9. 手把手教你win10下lex与yacc的安装
  10. 绿联网卡转接mac设置_苹果中文系统USB外置网卡驱动安装方法
  11. 小故事分解区块链--科普篇
  12. 定时网页截图php,浏览器实现网页定时自动截图
  13. 并查集入门+初级专题训练
  14. linux 心跳灯_Linux下信号灯的使用
  15. 性价比高一点的蓝牙耳机有哪几款?高性价比蓝牙耳机推荐
  16. 第三章 学习CC3200的ADC
  17. 华为nova3 计算机,华为nova 3评测:是迷妹选择,还是实力圈粉?
  18. .Net6 图片转文字PictureToTxt
  19. 女人不得不学的七个人生规律
  20. 我是一个搞IT的农民工

热门文章

  1. 怎么调整证件照的像素大小?这两种调整方法非常简单
  2. PCIe体系结构的组成部件
  3. python安装模块方法
  4. mysql设置主从同步只读_MySql主从同步设置
  5. BIgDecimal()和BigInteger()运算add(),sub(),div(),muti()加减乘除其实都是有返回值的
  6. 面试官:听说你还不知道条件熵是什么?
  7. 微信自动化发送消息以及点击
  8. 计算机指令窗口如何放大,Windows7手动优化之10个小技巧
  9. pytorch 最简单示例
  10. Android 笔记 surfaceView textureView