[增强] 1.9x移植的ui_kline,支持多图/多指标/附图指标/交易记录等回放
This commit is contained in:
parent
ee2fd126f4
commit
2811ab1068
287
vnpy/trader/ui/kline/crosshair.py
Normal file
287
vnpy/trader/ui/kline/crosshair.py
Normal file
@ -0,0 +1,287 @@
|
||||
# flake8: noqa
|
||||
|
||||
import pyqtgraph as pg
|
||||
import datetime as dt
|
||||
import traceback
|
||||
from qtpy import QtCore
|
||||
from pyqtgraph.Point import Point
|
||||
import pandas as pd
|
||||
|
||||
|
||||
# 十字光标支持
|
||||
class Crosshair(QtCore.QObject):
|
||||
"""
|
||||
此类给pg.PlotWidget()添加crossHair功能,PlotWidget实例需要初始化时传入
|
||||
"""
|
||||
signal = QtCore.Signal(type(tuple([])))
|
||||
signalInfo = QtCore.Signal(float, float)
|
||||
|
||||
def __init__(self, parent, master):
|
||||
"""Constructor"""
|
||||
self.__view = parent # PlatWidget
|
||||
self.master = master # KLineWidget
|
||||
super(Crosshair, self).__init__()
|
||||
|
||||
self.xAxis = 0
|
||||
self.yAxis = 0
|
||||
|
||||
self.datas = None
|
||||
|
||||
self.yAxises = [0 for i in range(3)]
|
||||
self.leftX = [0 for i in range(3)]
|
||||
self.showHLine = [False for i in range(3)]
|
||||
self.textPrices = [pg.TextItem('', anchor=(1, 1)) for i in range(3)]
|
||||
# 取得 widget上的:主图/成交量/副图指标
|
||||
self.views = [parent.centralWidget.getItem(i + 1, 0) for i in range(3)]
|
||||
self.rects = [self.views[i].sceneBoundingRect() for i in range(3)]
|
||||
self.vLines = [pg.InfiniteLine(angle=90, movable=False) for i in range(3)]
|
||||
self.hLines = [pg.InfiniteLine(angle=0, movable=False) for i in range(3)]
|
||||
|
||||
# mid 在y轴动态跟随最新价显示最新价和最新时间
|
||||
self.__textDate = pg.TextItem('date', anchor=(1, 1)) # 文本:日期/时间
|
||||
self.__textInfo = pg.TextItem('lastBarInfo') # 文本:bar信息
|
||||
self.__text_main_indicators = pg.TextItem('lastIndicatorsInfo', anchor=(1, 0)) # 文本:主图指标
|
||||
self.__text_sub_indicators = pg.TextItem('lastSubIndicatorsInfo', anchor=(1, 0)) # 文本: 副图指标
|
||||
self.__textVolume = pg.TextItem('lastBarVolume', anchor=(1, 0)) # 文本: 成交量
|
||||
|
||||
self.__textDate.setZValue(2)
|
||||
self.__textInfo.setZValue(2)
|
||||
self.__text_main_indicators.setZValue(2)
|
||||
self.__text_sub_indicators.setZValue(2)
|
||||
self.__textVolume.setZValue(2)
|
||||
self.__textInfo.border = pg.mkPen(color=(230, 255, 0, 255), width=1.2)
|
||||
|
||||
for i in range(3):
|
||||
self.textPrices[i].setZValue(2)
|
||||
self.vLines[i].setPos(0)
|
||||
self.hLines[i].setPos(0)
|
||||
self.vLines[i].setZValue(0)
|
||||
self.hLines[i].setZValue(0)
|
||||
self.views[i].addItem(self.vLines[i])
|
||||
self.views[i].addItem(self.hLines[i])
|
||||
self.views[i].addItem(self.textPrices[i])
|
||||
|
||||
self.views[0].addItem(self.__textInfo, ignoreBounds=True)
|
||||
self.views[0].addItem(self.__text_main_indicators, ignoreBounds=True)
|
||||
|
||||
# 添加成交量的提示信息到 volume view
|
||||
if self.master.display_vol:
|
||||
self.views[1].addItem(self.__textVolume, ignoreBounds=True)
|
||||
|
||||
# 添加 副图指标/日期信息到 副图
|
||||
if self.master.display_sub:
|
||||
self.views[2].addItem(self.__textDate, ignoreBounds=True)
|
||||
self.views[2].addItem(self.__text_sub_indicators, ignoreBounds=True)
|
||||
else:
|
||||
# 没有启用副图,日期信息放在主图
|
||||
self.views[0].addItem(self.__textDate, ignoreBounds=True)
|
||||
|
||||
self.proxy = pg.SignalProxy(self.__view.scene().sigMouseMoved, rateLimit=360, slot=self.__mouseMoved)
|
||||
# 跨线程刷新界面支持
|
||||
self.signal.connect(self.update)
|
||||
self.signalInfo.connect(self.plotInfo)
|
||||
|
||||
def update(self, pos):
|
||||
"""刷新界面显示"""
|
||||
try:
|
||||
xAxis, yAxis = pos
|
||||
xAxis, yAxis = (self.xAxis, self.yAxis) if xAxis is None else (xAxis, yAxis)
|
||||
self.moveTo(xAxis, yAxis)
|
||||
except Exception as ex:
|
||||
print(u'Crosshair.update() exception:{},trace:{}'.format(str(ex), traceback.format_exc()))
|
||||
|
||||
def __mouseMoved(self, evt):
|
||||
"""鼠标移动回调"""
|
||||
try:
|
||||
pos = evt[0]
|
||||
self.rects = [self.views[i].sceneBoundingRect() for i in range(3)]
|
||||
for i in range(3):
|
||||
self.showHLine[i] = False
|
||||
if i == 1 and not self.master.display_vol:
|
||||
continue
|
||||
|
||||
if i == 2 and not self.master.display_sub:
|
||||
continue
|
||||
|
||||
if self.rects[i].contains(pos):
|
||||
mousePoint = self.views[i].vb.mapSceneToView(pos)
|
||||
xAxis = mousePoint.x()
|
||||
yAxis = mousePoint.y()
|
||||
self.yAxises[i] = yAxis
|
||||
self.showHLine[i] = True
|
||||
self.moveTo(xAxis, yAxis)
|
||||
except Exception as ex:
|
||||
print(u'_mouseMove() exception:{},trace:{}'.format(str(ex), traceback.format_exc()))
|
||||
|
||||
def moveTo(self, xAxis, yAxis):
|
||||
"""
|
||||
移动
|
||||
:param xAxis:
|
||||
:param yAxis:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
xAxis, yAxis = (self.xAxis, self.yAxis) if xAxis is None else (int(xAxis), yAxis)
|
||||
self.rects = [self.views[i].sceneBoundingRect() for i in range(3)]
|
||||
if not xAxis or not yAxis:
|
||||
return
|
||||
self.xAxis = xAxis
|
||||
self.yAxis = yAxis
|
||||
self.vhLinesSetXY(xAxis, yAxis)
|
||||
self.plotInfo(xAxis, yAxis)
|
||||
self.master.pi_volume.update()
|
||||
except Exception as ex:
|
||||
print(u'_mouseMove() exception:{},trace:{}'.format(str(ex), traceback.format_exc()))
|
||||
|
||||
def vhLinesSetXY(self, xAxis, yAxis):
|
||||
"""水平和竖线位置设置"""
|
||||
for i in range(3):
|
||||
self.vLines[i].setPos(xAxis)
|
||||
if self.showHLine[i]:
|
||||
self.hLines[i].setPos(yAxis if i == 0 else self.yAxises[i])
|
||||
self.hLines[i].show()
|
||||
else:
|
||||
self.hLines[i].hide()
|
||||
|
||||
def plotInfo(self, xAxis, yAxis):
|
||||
"""
|
||||
被嵌入的plotWidget在需要的时候通过调用此方法显示K线信息
|
||||
"""
|
||||
if self.datas is None or xAxis >= len(self.datas):
|
||||
return
|
||||
|
||||
tickDatetime = None
|
||||
openPrice = 0
|
||||
closePrice = 0
|
||||
highPrice = 0
|
||||
lowPrice = 0
|
||||
preClosePrice = 0
|
||||
volume = 0
|
||||
# open_interest = 0
|
||||
|
||||
try:
|
||||
# 获取K线数据
|
||||
data = self.datas[xAxis]
|
||||
lastdata = self.datas[xAxis - 1]
|
||||
tickDatetime = pd.to_datetime(data['datetime'])
|
||||
openPrice = data['open']
|
||||
closePrice = data['close']
|
||||
lowPrice = data['low']
|
||||
highPrice = data['high']
|
||||
volume = int(data['volume'])
|
||||
# open_interest = int(data['open_interest'])
|
||||
preClosePrice = lastdata['close']
|
||||
|
||||
except Exception as ex:
|
||||
print(u'exception:{},trace:{}'.format(str(ex), traceback.format_exc()))
|
||||
return
|
||||
|
||||
if (isinstance(tickDatetime, dt.datetime)):
|
||||
datetimeText = dt.datetime.strftime(tickDatetime, '%Y-%m-%d %H:%M:%S')
|
||||
dateText = dt.datetime.strftime(tickDatetime, '%Y-%m-%d')
|
||||
timeText = dt.datetime.strftime(tickDatetime, '%H:%M:%S')
|
||||
else:
|
||||
datetimeText = ""
|
||||
dateText = ""
|
||||
timeText = ""
|
||||
|
||||
# 显示所有的主图技术指标
|
||||
html = u'<div style="text-align: right">'
|
||||
for indicator in self.master.main_indicator_data:
|
||||
val = self.master.main_indicator_data[indicator][xAxis]
|
||||
col = self.master.main_indicator_colors[indicator]
|
||||
html += u'<span style="color: %s; font-size: 18px;"> %s:%.2f</span>' % (col, indicator, val)
|
||||
html += u'</div>'
|
||||
self.__text_main_indicators.setHtml(html)
|
||||
|
||||
# 显示所有的主图技术指标
|
||||
html = u'<div style="text-align: right">'
|
||||
for indicator in self.master.sub_indicator_data:
|
||||
val = self.master.sub_indicator_data[indicator][xAxis]
|
||||
col = self.master.sub_indicator_colors[indicator]
|
||||
html += u'<span style="color: %s; font-size: 18px;"> %s:%.2f</span>' % (col, indicator, val)
|
||||
html += u'</div>'
|
||||
self.__text_sub_indicators.setHtml(html)
|
||||
|
||||
# 和上一个收盘价比较,决定K线信息的字符颜色
|
||||
cOpen = 'red' if openPrice > preClosePrice else 'green'
|
||||
cClose = 'red' if closePrice > preClosePrice else 'green'
|
||||
cHigh = 'red' if highPrice > preClosePrice else 'green'
|
||||
cLow = 'red' if lowPrice > preClosePrice else 'green'
|
||||
|
||||
self.__textInfo.setHtml(
|
||||
u'<div style="text-align: center; background-color:#000">\
|
||||
<span style="color: white; font-size: 16px;">日期</span><br>\
|
||||
<span style="color: yellow; font-size: 16px;">%s</span><br>\
|
||||
<span style="color: white; font-size: 16px;">时间</span><br>\
|
||||
<span style="color: yellow; font-size: 16px;">%s</span><br>\
|
||||
<span style="color: white; font-size: 16px;">价格</span><br>\
|
||||
<span style="color: %s; font-size: 16px;">(开) %.3f</span><br>\
|
||||
<span style="color: %s; font-size: 16px;">(高) %.3f</span><br>\
|
||||
<span style="color: %s; font-size: 16px;">(低) %.3f</span><br>\
|
||||
<span style="color: %s; font-size: 16px;">(收) %.3f</span><br>\
|
||||
<span style="color: white; font-size: 16px;">成交量</span><br>\
|
||||
<span style="color: yellow; font-size: 16px;">(量) %d</span><br>\
|
||||
</div>' \
|
||||
% (dateText, timeText, cOpen, openPrice, cHigh, highPrice,
|
||||
cLow, lowPrice, cClose, closePrice, volume))
|
||||
self.__textDate.setHtml(
|
||||
'<div style="text-align: center">\
|
||||
<span style="color: yellow; font-size: 12px;">%s</span>\
|
||||
</div>' \
|
||||
% (datetimeText))
|
||||
|
||||
self.__textVolume.setHtml(
|
||||
'<div style="text-align: right">\
|
||||
<span style="color: white; font-size: 12px;">VOL : %.3f</span>\
|
||||
</div>' \
|
||||
% (volume))
|
||||
# 坐标轴宽度
|
||||
rightAxisWidth = self.views[0].getAxis('right').width()
|
||||
bottomAxisHeight = self.views[2].getAxis('bottom').height()
|
||||
offset = QtCore.QPointF(rightAxisWidth, bottomAxisHeight)
|
||||
|
||||
# 各个顶点
|
||||
t1 = [None, None, None]
|
||||
br = [None, None, None]
|
||||
t1[0] = self.views[0].vb.mapSceneToView(self.rects[0].topLeft())
|
||||
br[0] = self.views[0].vb.mapSceneToView(self.rects[0].bottomRight() - offset)
|
||||
|
||||
if self.master.display_vol:
|
||||
t1[1] = self.views[1].vb.mapSceneToView(self.rects[1].topLeft())
|
||||
br[1] = self.views[1].vb.mapSceneToView(self.rects[1].bottomRight() - offset)
|
||||
if self.master.display_sub:
|
||||
t1[2] = self.views[2].vb.mapSceneToView(self.rects[2].topLeft())
|
||||
br[2] = self.views[2].vb.mapSceneToView(self.rects[2].bottomRight() - offset)
|
||||
|
||||
# 显示价格
|
||||
for i in range(3):
|
||||
if self.showHLine[i] and t1[i] is not None and br[i] is not None:
|
||||
self.textPrices[i].setHtml(
|
||||
'<div style="text-align: right">\
|
||||
<span style="color: yellow; font-size: 18px;">\
|
||||
%0.3f\
|
||||
</span>\
|
||||
</div>' \
|
||||
% (yAxis if i == 0 else self.yAxises[i]))
|
||||
self.textPrices[i].setPos(br[i].x(), yAxis if i == 0 else self.yAxises[i])
|
||||
self.textPrices[i].show()
|
||||
else:
|
||||
self.textPrices[i].hide()
|
||||
|
||||
# 设置坐标
|
||||
self.__textInfo.setPos(t1[0])
|
||||
self.__text_main_indicators.setPos(br[0].x(), t1[0].y())
|
||||
if self.master.display_sub:
|
||||
self.__text_sub_indicators.setPos(br[2].x(), t1[2].y())
|
||||
if self.master.display_vol:
|
||||
self.__textVolume.setPos(br[1].x(), t1[1].y())
|
||||
|
||||
# 修改对称方式防止遮挡
|
||||
self.__textDate.anchor = Point((1, 1)) if xAxis > self.master.index else Point((0, 1))
|
||||
if br[2] is not None:
|
||||
self.__textDate.setPos(xAxis, br[2].y())
|
||||
elif br[1] is not None:
|
||||
self.__textDate.setPos(xAxis, br[1].y())
|
||||
else:
|
||||
self.__textDate.setPos(xAxis, br[0].y())
|
1342
vnpy/trader/ui/kline/css.qss
Normal file
1342
vnpy/trader/ui/kline/css.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
vnpy/trader/ui/kline/cta.ico
Normal file
BIN
vnpy/trader/ui/kline/cta.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
1413
vnpy/trader/ui/kline/kline.py
Normal file
1413
vnpy/trader/ui/kline/kline.py
Normal file
File diff suppressed because it is too large
Load Diff
269
vnpy/trader/ui/kline/kline_widgets.py
Normal file
269
vnpy/trader/ui/kline/kline_widgets.py
Normal file
@ -0,0 +1,269 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
基于uiKline扩展得widgets
|
||||
华富资产/李来佳
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import traceback
|
||||
import pandas as pd
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ConnectionFailure, AutoReconnect
|
||||
from vnpy.trader.ui.kline.kline import QtWidgets, KLineWidget
|
||||
from vnpy.amqp.consumer import subscriber
|
||||
|
||||
|
||||
class RenkoKline(QtWidgets.QWidget):
|
||||
"""
|
||||
砖型图得显示
|
||||
1、显示
|
||||
2、数据加载:从Mongodb,从本地文件
|
||||
3、定时数据更新
|
||||
4、加载文件时,选择指标
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None, setting={}):
|
||||
self.parent = parent
|
||||
super(RenkoKline, self).__init__(parent)
|
||||
|
||||
self.kline_name = setting.get('kline_name', 'kline_name')
|
||||
|
||||
self.canvas = None
|
||||
|
||||
self.main_indicators = sorted(setting.get('main_indicators', []))
|
||||
self.sub_indicators = sorted(setting.get('sub_indicators', []))
|
||||
|
||||
# 数据来自文件
|
||||
self.csv_file = setting.get('csv_file', None)
|
||||
|
||||
# 数据来自mongodb
|
||||
self.host = None
|
||||
self.port = None
|
||||
self.db = None
|
||||
self.collection = None
|
||||
|
||||
self.sub = None
|
||||
live_setting = setting.get('live', {})
|
||||
if len(live_setting) > 0:
|
||||
try:
|
||||
self.sub = subscriber(host=live_setting.get('host', 'localhost'),
|
||||
port=live_setting.get('port', 5672),
|
||||
user=live_setting.get('user', 'admin'),
|
||||
password=live_setting.get('pasword', 'admin'),
|
||||
exchange=live_setting.get('exchange', 'x_fanout'),
|
||||
routing_key=live_setting.get('routing_key', 'default'))
|
||||
|
||||
self.sub.set_callback(self.on_bar_message)
|
||||
except Exception as ex:
|
||||
print(u'创建消息订阅失败:{}'.format(str(ex)))
|
||||
traceback.print_exc()
|
||||
|
||||
# MongoDB数据库相关
|
||||
self.dbClient = None # MongoDB客户端对象
|
||||
self.db_has_connected = False
|
||||
|
||||
self.is_loaded = False
|
||||
self.last_bar_dt = None
|
||||
|
||||
self.initUI()
|
||||
|
||||
if self.csv_file is not None and os.path.exists(self.csv_file):
|
||||
self.datathread = Thread(self.load_csv)
|
||||
self.datathread.start()
|
||||
|
||||
elif 'mongo' in setting:
|
||||
mongo_setting = setting.get('mongo')
|
||||
self.host = mongo_setting.get('ip', 'localhost')
|
||||
self.port = mongo_setting.get('port', 27017)
|
||||
self.db = mongo_setting.get('db', None)
|
||||
self.collection = mongo_setting.get('collection', None)
|
||||
self.kline_name = self.collection
|
||||
|
||||
if self.db is not None and self.collection is not None:
|
||||
self.dbConnect()
|
||||
self.datathread = Thread(target=self.load_db, args=())
|
||||
self.datathread.start()
|
||||
|
||||
def initUI(self):
|
||||
"""
|
||||
初始化界面
|
||||
:return:
|
||||
"""
|
||||
vbox = QtWidgets.QVBoxLayout()
|
||||
self.canvas = KLineWidget(display_vol=False, display_sub=True)
|
||||
self.canvas.show()
|
||||
self.canvas.KLtitle.setText('{}'.format(self.kline_name), size='18pt')
|
||||
|
||||
for indicator in self.main_indicators:
|
||||
self.canvas.add_indicator(indicator=indicator, is_main=True)
|
||||
|
||||
for indicator in self.sub_indicators:
|
||||
self.canvas.add_indicator(indicator=indicator, is_main=False)
|
||||
|
||||
vbox.addWidget(self.canvas)
|
||||
self.setLayout(vbox)
|
||||
|
||||
def load_csv(self):
|
||||
"""从文件加载renko数据"""
|
||||
if self.canvas:
|
||||
df = pd.read_csv(self.csv_file)
|
||||
df = df.set_index(pd.DatetimeIndex(df['datetime']))
|
||||
self.canvas.loadData(df, main_indicators=self.main_indicators, sub_indicators=self.sub_indicators)
|
||||
self.is_loaded = True
|
||||
|
||||
if self.sub:
|
||||
self.sub.start()
|
||||
|
||||
def load_db(self):
|
||||
"""从数据库加载renko数据"""
|
||||
try:
|
||||
d = {}
|
||||
qryData = self.dbQueryBySort(dbName=self.db, collectionName=self.collection, d=d, sortName='datetime',
|
||||
sortType=1)
|
||||
bars = []
|
||||
for data in qryData:
|
||||
bar = {
|
||||
'datetime': data.get('datetime'),
|
||||
'open': data.get('open'),
|
||||
'close': data.get('close'),
|
||||
'high': data.get('high'),
|
||||
'low': data.get('low'),
|
||||
'openInterest': 0,
|
||||
'volume': data.get('volume', 0)
|
||||
}
|
||||
bars.append(bar)
|
||||
self.last_bar_dt = data.get('datetime')
|
||||
|
||||
print(u'一共从数据库加载{}根bar'.format(len(bars)))
|
||||
df = pd.DataFrame(bars)
|
||||
df = df.set_index(pd.DatetimeIndex(df['datetime']))
|
||||
self.canvas.loadData(df, main_indicators=self.main_indicators, sub_indicators=self.sub_indicators)
|
||||
|
||||
self.is_loaded = True
|
||||
if self.sub:
|
||||
self.sub.start()
|
||||
|
||||
except Exception as ex:
|
||||
print(u'加载bar异常:{}'.format(str(ex)))
|
||||
|
||||
def on_bar_message(self, chan, method_frame, _header_frame, body, userdata=None):
|
||||
try:
|
||||
str_bar = body.decode('utf-8')
|
||||
bar = json.loads(str_bar)
|
||||
bar_name = bar.get('bar_name')
|
||||
|
||||
if bar_name == self.kline_name:
|
||||
print(u'on_bar_message from rabbitmq :{}'.format(bar))
|
||||
self.canvas.on_bar(bar, [], [])
|
||||
|
||||
except Exception as ex:
|
||||
print(u'on_bar_message Exception:{}'.format(str(ex)))
|
||||
traceback.print_exc()
|
||||
|
||||
def dbConnect(self):
|
||||
try:
|
||||
self.dbClient = MongoClient(self.host, self.port, connectTimeoutMS=500)
|
||||
except Exception as ex:
|
||||
print(u'连接Mongodb:{} {} 异常:{}'.format(self.host, self.port, str(ex)))
|
||||
|
||||
def dbQueryBySort(self, dbName, collectionName, d, sortName, sortType, limitNum=0):
|
||||
"""从MongoDB中读取数据,d是查询要求,sortName是排序的字段,sortType是排序类型
|
||||
返回的是数据库查询的指针"""
|
||||
try:
|
||||
if self.dbClient:
|
||||
db = self.dbClient[dbName]
|
||||
collection = db[collectionName]
|
||||
if limitNum > 0:
|
||||
cursor = collection.find(d).sort(sortName, sortType).limit(limitNum)
|
||||
else:
|
||||
cursor = collection.find(d).sort(sortName, sortType)
|
||||
if cursor:
|
||||
return list(cursor)
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
print(u'数据库查询出错')
|
||||
if self.db_has_connected:
|
||||
self.writeLog(u'重新尝试连接数据库')
|
||||
self.dbConnect()
|
||||
except AutoReconnect as ex:
|
||||
print(u'数据库连接断开重连:{}'.format(str(ex)))
|
||||
sleep(1)
|
||||
except ConnectionFailure:
|
||||
self.dbClient = None
|
||||
print(u'数据库连接断开')
|
||||
if self.db_has_connected:
|
||||
print(u'重新尝试连接数据库')
|
||||
self.dbConnect()
|
||||
except Exception as ex:
|
||||
self.writeError(u'dbQueryBySort exception:{}'.format(str(ex)))
|
||||
|
||||
return []
|
||||
|
||||
|
||||
########################################################################
|
||||
class MultiKlineWindow(QtWidgets.QMainWindow):
|
||||
"""多窗口显示K线
|
||||
包括:
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None, settings=[]):
|
||||
"""Constructor"""
|
||||
super(MultiKlineWindow, self).__init__(parent)
|
||||
|
||||
self.settings = settings
|
||||
self.kline_dict = {}
|
||||
self.initUi()
|
||||
|
||||
self.load_multi_kline()
|
||||
|
||||
def initUi(self):
|
||||
"""初始化界面"""
|
||||
self.setWindowTitle(u'多K线')
|
||||
self.maximumSize()
|
||||
self.mdi = QtWidgets.QMdiArea()
|
||||
self.setCentralWidget(self.mdi)
|
||||
|
||||
# 创建菜单
|
||||
menubar = self.menuBar()
|
||||
file_menu = menubar.addMenu("File")
|
||||
file_menu.addAction("Cascade")
|
||||
file_menu.addAction("Tiled")
|
||||
file_menu.triggered[QtWidgets.QAction].connect(self.windowaction)
|
||||
|
||||
def windowaction(self, q):
|
||||
if q.text() == "cascade":
|
||||
self.mdi.cascadeSubWindows()
|
||||
|
||||
if q.text() == "Cascade":
|
||||
self.mdi.tileSubWindows()
|
||||
|
||||
def load_multi_kline(self):
|
||||
"""加载多周期窗口"""
|
||||
|
||||
try:
|
||||
for setting in self.settings:
|
||||
sub_window = QtWidgets.QMdiSubWindow()
|
||||
sub_window.setWindowTitle(setting.get('kline_name'))
|
||||
renko_kline = RenkoKline(parent=self, setting=setting)
|
||||
sub_window.setWidget(renko_kline)
|
||||
self.mdi.addSubWindow(renko_kline)
|
||||
# renko_kline.loadData()
|
||||
renko_kline.show()
|
||||
|
||||
self.mdi.tileSubWindows()
|
||||
|
||||
except Exception as ex: # noqa
|
||||
traceback.print_exc()
|
||||
QtWidgets.QMessageBox.warning(self, 'Exception', u' Exception', QtWidgets.QMessageBox.Cancel,
|
||||
QtWidgets.QMessageBox.NoButton)
|
||||
|
||||
return
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""关闭窗口时的事件"""
|
||||
os._exit(0)
|
Loading…
Reference in New Issue
Block a user