目录
写在前面:
使用过程:
代码:
导入的包、字符型横坐标、K线控件
K线图控件
放置标记数据表格控件
输入并设置标记数据控件
主界面
运行代码
写在前面:
开发这个工具的初衷,是基于在分析股票实践中想批量计算股票最近一次的上升振幅或下降振幅,视觉上,我们寻找波段低点和高点很容易,但如果用代码寻找波段低点与高点就颇为复杂,在实现代码过程中经常性快速验证代码得出的结果是否正确,这时如果能在K线上把计算出来的点直接标注出来就能一目了然。基于此,开发了这个工具辅助自己做视觉想法相关的代码设计,在我的理解,诸如波段的划分、股票是否在低位、股票是否在震荡这些都属于视觉感知的范畴,所以该工具就取名为“PyQt5开发验证K线视觉想法工具”。
V1.0版本,只做了单个验证里的点验证,即计算所得点在K线中进行标注
使用过程:
1 选择要验证的日数据文件(数据来自优矿)
2 选择与计算过程相同的时间区间
3 显示K线
4 输入并设置标记数据:点击该按钮后会弹出输入框,输入待标记的数据
1 输入标题:这个标题的作用是便于使用者区分多个标记
2 选择规则:当前V1.0版本只有“点””,选择规则后,在下一行会提示要输入的数据格式,输入的数据格式要和规则对应,否则会出错
3 选择点的形状
4 选择点的颜色
5 输入待标记的数据
6 点确定,左侧设置的结果将插入右侧的表格
7 点击提交,设置的结果将传入主界面
在主界面中点击“执行”,待标记的点将在K线图中标记出来,从上图可以看到,设置的两个点序列在K线图中标记出来了。
代码:
导入的包、字符型横坐标、K线控件
import os,sys,json,datetime
import pandas as pd
from threading import Thread
from typing import Dict,List,Any
from PyQt5 import QtCore,QtWidgets,QtGui
from PyQt5.QtCore import Qt
import pyqtgraph as pg
pg.setConfigOption('background','w')
pg.setConfigOption('foreground','k')
class RotateAxisItem(pg.AxisItem):
def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
p.setRenderHint(p.Antialiasing,False)
p.setRenderHint(p.TextAntialiasing,True)
## draw long line along axis
pen,p1,p2 = axisSpec
p.setPen(pen)
p.drawLine(p1,p2)
p.translate(0.5,0) ## resolves some damn pixel ambiguity
## draw ticks
for pen,p1,p2 in tickSpecs:
p.setPen(pen)
p.drawLine(p1,p2)
## draw all text
# if self.tickFont is not None:
# p.setFont(self.tickFont)
p.setPen(self.pen())
for rect,flags,text in textSpecs:
# this is the important part
p.save()
p.translate(rect.x(),rect.y())
p.rotate(-30)
p.drawText(int(-rect.width()),int(rect.height()),int(rect.width()),int(rect.height()),flags,text)
# restoring the painter is *required*!!!
p.restore()
class CandlestickItem(pg.GraphicsObject):
def __init__(self, data):
pg.GraphicsObject.__init__(self)
self.data = data ## data must have fields: time, open, close, min, max
self.generatePicture()
def generatePicture(self):
## pre-computing a QPicture object allows paint() to run much more quickly,
## rather than re-drawing the shapes every time.
self.picture = QtGui.QPicture()
p = QtGui.QPainter(self.picture)
p.setPen(pg.mkPen('d'))
w = (self.data[1][0] - self.data[0][0]) / 3.
for (t, open, close, min, max) in self.data:
p.drawLine(QtCore.QPointF(t, min), QtCore.QPointF(t, max))
if open < close:
p.setBrush(pg.mkBrush('r'))
else:
p.setBrush(pg.mkBrush('g'))
p.drawRect(QtCore.QRectF(t-w, open, w * 2, close - open))
p.end()
def paint(self, p, *args):
p.drawPicture(0, 0, self.picture)
def boundingRect(self):
## boundingRect _must_ indicate the entire area that will be drawn on
## or else we will get artifacts and possibly crashing.
## (in this case, QPicture does all the work of computing the bouning rect for us)
return QtCore.QRectF(self.picture.boundingRect())
class ExtendedComboBox(QtWidgets.QComboBox):
def __init__(self, parent=None):
super(ExtendedComboBox, self).__init__(parent)
self.setFocusPolicy(Qt.StrongFocus)
self.setEditable(True)
# add a filter model to filter matching items
self.pFilterModel = QtCore.QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.pFilterModel.setSourceModel(self.model())
# add a completer, which uses the filter model
self.completer = QtWidgets.QCompleter(self.pFilterModel, self)
# always show all (filtered) completions
self.completer.setCompletionMode(QtWidgets.QCompleter.UnfilteredPopupCompletion)
self.setCompleter(self.completer)
# connect signals
self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
self.completer.activated.connect(self.on_completer_activated)
# on selection of an item from the completer, select the corresponding item from combobox
def on_completer_activated(self, text):
if text:
index = self.findText(text)
self.setCurrentIndex(index)
self.activated[str].emit(self.itemText(index))
# on model change, update the models of the filter and completer as well
def setModel(self, model):
super(ExtendedComboBox, self).setModel(model)
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
# on model column change, update the model column of the filter and completer as well
def setModelColumn(self, column):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
super(ExtendedComboBox, self).setModelColumn(column)
K线图控件
class Graph_Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.init_data()
self.init_ui()
pass
def init_data(self):
self.whole_header = None
self.whole_df = None
self.whole_pd_header = None
self.whole_xTick = None
self.current_df = None
self.current_data = None
self.dur_len = 20
self.add_items_list = []
self.pointshape_map: Dict = {
'圆': 'o',
'三角形': 't',
'星形': 'star',
'正方形': 's',
'菱形': 'd',
'加号': '+',
'向下箭头': 'arrow_down',
'向左箭头': 'arrow_left',
'向上箭头': 'arrow_up',
'向右箭头': 'arrow_right'
}
pass
def init_ui(self):
self.duration_label = QtWidgets.QLabel('左边界~右边界')
layout_top = QtWidgets.QHBoxLayout()
layout_top.addWidget(self.duration_label)
layout_top.addStretch(1)
xax = RotateAxisItem(orientation='bottom')
xax.setHeight(h=50)
self.pw = pg.PlotWidget(axisItems={'bottom': xax})
self.pw.setMouseEnabled(x=True, y=False)
# self.pw.enableAutoRange(x=False,y=True)
self.pw.setAutoVisible(x=False, y=True)
layout = QtWidgets.QVBoxLayout()
layout.addLayout(layout_top)
layout.addWidget(self.pw)
self.setLayout(layout)
pass
def first_setData(self,data:Dict):
whole_header = data['whole_header']
whole_df = data['whole_df']
whole_pd_header = data['whole_pd_header']
whole_xTick = whole_df['xTick'].values.tolist()
self.whole_header = whole_header
self.whole_df = whole_df
self.whole_pd_header = whole_pd_header
self.whole_xTick = whole_xTick
self.current_df = self.whole_df.copy()
self.caculate_and_show_data()
pass
def caculate_and_show_data(self):
df = self.current_df.copy()
df.reset_index(inplace=True)
df['x'] = [i for i in range(len(df))]
xTick = df['xTick'].values.tolist()
xTick00 = []
dur_num = int(len(xTick)/self.dur_len)
if dur_num > 2:
for i in range(0,len(xTick),dur_num):
xTick00.append((i,xTick[i]))
pass
else:
for i,item in enumerate(xTick):
xTick00.append((i,item))
pass
candle_data = []
for i,row in df.iterrows():
candle_data.append((row['x'],row['open'],row['close'],row['lowest'],row['highest']))
self.current_data = df.loc[:,self.whole_pd_header].values.tolist()
y_min = df['lowest'].min()
y_max = df['highest'].max()
# 开始配置显示内容
self.pw.clear()
self.duration_label.setText(f"{xTick[0]}~{xTick[-1]}")
xax = self.pw.getAxis('bottom')
xax.setTicks([xTick00])
self.vb = self.pw.getViewBox()
self.vb.setLimits(yMin=y_min,yMax=y_max)
candle_fixed_target = CandlestickItem(candle_data)
self.pw.addItem(candle_fixed_target)
self.vLine = pg.InfiniteLine(angle=90,movable=False)
self.hLine = pg.InfiniteLine(angle=0,movable=False)
self.label = pg.TextItem()
self.pw.addItem(self.vLine,ignoreBounds=True)
self.pw.addItem(self.hLine,ignoreBounds=True)
self.pw.addItem(self.label,ignoreBounds=True)
self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved,rateLimit=60,slot=self.mouseMoved)
self.pw.enableAutoRange()
self.pw.setAutoVisible()
pass
def mouseMoved(self,evt):
pos = evt[0]
if self.pw.sceneBoundingRect().contains(pos):
mousePoint = self.vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index>=0 and index<len(self.current_data):
target_data = self.current_data[index]
html_str = ''
for i,item in enumerate(self.whole_header):
html_str += f"<br/>{item}:{target_data[i]}"
self.label.setHtml(html_str)
self.label.setPos(mousePoint.x(),mousePoint.y())
pass
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
pass
pass
def add_marks(self,data:List):
# 点,曲线,区间
# '标题','规则','形状','颜色','数据'
self.del_marks()
for row in data:
try:
rule_str = row[1]
shape_str = row[2]
color_str = row[3]
data_str = row[4]
data_json = json.loads(data_str)
if rule_str == '点':
for node in data_json:
targetItem = pg.TargetItem(
pos=node,
size=20,
symbol=self.pointshape_map[shape_str],
pen=color_str,
brush=color_str,
movable=False
)
self.pw.addItem(targetItem)
self.add_items_list.append(targetItem)
pass
if rule_str == '连线':
for node in data_json:
targetItem = pg.PlotCurveItem(
x=node[0],
y=node[1],
pen=color_str,
symbol=self.pointshape_map[shape_str]
)
self.pw.addItem(targetItem)
self.add_items_list.append(targetItem)
pass
pass
if rule_str == '区间':
for node in data_json:
targetItem = pg.LinearRegionItem(
values=node,
pen=color_str,
movable=False
)
self.pw.addItem(targetItem)
self.add_items_list.append(targetItem)
pass
pass
except:
continue
pass
pass
def del_marks(self):
if self.add_items_list:
for item in self.add_items_list:
self.pw.removeItem(item)
self.add_items_list.clear()
pass
pass
放置标记数据表格控件
class MarkTableWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.init_data()
self.init_ui()
pass
def init_data(self):
self.header = ['标题','规则','形状','颜色','数据']
pass
def init_ui(self):
self.table = QtWidgets.QTableWidget()
self.table.setColumnCount(len(self.header))
self.table.setHorizontalHeaderLabels(self.header)
self.table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.table.setColumnHidden(len(self.header)-1,True)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.table)
self.setLayout(layout)
pass
def set_data(self,data:List):
# '标题','规则','形状','颜色','数据’
self.table.clearContents()
self.table.setRowCount(len(data))
for r,row in enumerate(data):
self.table.setItem(r,0,QtWidgets.QTableWidgetItem(row[0]))
self.table.setItem(r,1,QtWidgets.QTableWidgetItem(row[1]))
self.table.setItem(r,2,QtWidgets.QTableWidgetItem(row[2]))
one_item = QtWidgets.QTableWidgetItem(row[3])
one_item.setForeground(QtGui.QColor(row[3]))
self.table.setItem(r, 3, one_item)
self.table.setItem(r, 4, QtWidgets.QTableWidgetItem(row[4]))
pass
pass
def insert_one_row(self,data:List):
self.table.insertRow(0)
self.table.setItem(0,0,QtWidgets.QTableWidgetItem(data[0]))
self.table.setItem(0,1,QtWidgets.QTableWidgetItem(data[1]))
self.table.setItem(0,2,QtWidgets.QTableWidgetItem(data[2]))
color_item = QtWidgets.QTableWidgetItem(data[3])
color_item.setForeground(QtGui.QColor(data[3]))
self.table.setItem(0,3,color_item)
self.table.setItem(0,4,QtWidgets.QTableWidgetItem(data[4]))
pass
def clear_table_contents(self):
# 清空表内容
self.table.clearContents()
self.table.setRowCount(0)
pass
def del_selected_rows(self):
# 删除选中项
selected_items = self.table.selectedItems()
if len(selected_items)<=0:
QtWidgets.QMessageBox.information(
self,
'提示',
'请选择要删除的行',
QtWidgets.QMessageBox.Yes
)
return
col_len = self.table.columnCount()
row_index_list = []
for i in range(0,len(selected_items)-1,col_len):
row_index_list.append(selected_items[i].row())
for i in row_index_list:
self.table.removeRow(i)
pass
def res_name_list(self)->List[str]:
# 返回标题列表
row_count = self.table.rowCount()
if row_count<=0:
return []
else:
res_list = []
for i in range(row_count):
item = self.table.item(i,0)
res_list.append(item.text())
return res_list
pass
def res_data_content(self)->List:
row_count = self.table.rowCount()
if row_count<=0:
return None
res_data = []
col_count = self.table.columnCount()
for i in range(row_count):
node = []
for j in range(col_count):
item_txt = self.table.item(i,j).text()
node.append(item_txt)
pass
res_data.append(node)
pass
return res_data
输入并设置标记数据控件
class SettingMarksWidget(QtWidgets.QWidget):
signal_excute = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.init_data()
self.init_ui()
pass
def init_data(self):
self.please_select_str: str = '-- 请选择 --'
self.rule_map: Dict = {
'点':'数据格式提示:[[x,y],[x,y],...]',
'连线':'数据格式提示:[[x_list,y_list],...]',
'区间':'数据格式提示:[[x0,x1],[x0,x1],...]'
}
self.pointshape_map: Dict = {
'圆':'o',
'三角形':'t',
'星形':'star',
'正方形':'s',
'菱形':'d',
'加号':'+',
'向下箭头':'arrow_down',
'向左箭头':'arrow_left',
'向上箭头':'arrow_up',
'向右箭头':'arrow_right'
}
pass
def init_ui(self):
self.setWindowTitle('输入并设置标记数据')
self.setMinimumWidth(800)
self.setMinimumHeight(600)
submit_btn = QtWidgets.QPushButton('提交')
submit_btn.clicked.connect(self.submit_btn_clicked)
layout_top = QtWidgets.QHBoxLayout()
layout_top.addStretch(1)
layout_top.addWidget(submit_btn)
h_line = QtWidgets.QFrame()
h_line.setFrameShape(QtWidgets.QFrame.HLine)
h_line.setFrameShadow(QtWidgets.QFrame.Sunken)
layout_hline = QtWidgets.QVBoxLayout()
layout_hline.addWidget(h_line)
layout_hline.addSpacing(20)
tip_label = QtWidgets.QLabel('标题')
self.name_lineedit = QtWidgets.QLineEdit()
tip_label2 = QtWidgets.QLabel('规则')
self.rule_combox = QtWidgets.QComboBox()
self.rule_combox.addItem(self.please_select_str)
self.rule_combox.addItems(list(self.rule_map.keys()))
self.rule_combox.currentTextChanged.connect(self.rule_combox_currentTextChanged)
self.rule_label = QtWidgets.QLabel('数据格式提示')
self.rule_label.setWordWrap(True)
tip_label3 = QtWidgets.QLabel('点形状')
self.pointshape_combox = QtWidgets.QComboBox()
self.pointshape_combox.addItem(self.please_select_str)
self.pointshape_combox.addItems(list(self.pointshape_map.keys()))
color_btn = QtWidgets.QPushButton('颜色')
color_btn.clicked.connect(self.color_btn_clicked)
self.color_label = QtWidgets.QLabel()
tip_label4 = QtWidgets.QLabel('数据')
self.data_textedit = QtWidgets.QTextEdit()
check_btn = QtWidgets.QPushButton('确定')
check_btn.clicked.connect(self.check_btn_clicked)
layout_left = QtWidgets.QFormLayout()
layout_left.addRow(tip_label,self.name_lineedit)
layout_left.addRow(tip_label2,self.rule_combox)
layout_left.addRow(self.rule_label)
layout_left.addRow(tip_label3,self.pointshape_combox)
layout_left.addRow(color_btn,self.color_label)
layout_left.addRow(tip_label4,self.data_textedit)
layout_left.addRow(check_btn)
self.results_table = MarkTableWidget()
clear_table_btn = QtWidgets.QPushButton('清空')
clear_table_btn.clicked.connect(self.clear_table_btn_clicked)
del_table_btn = QtWidgets.QPushButton('删除选中项')
del_table_btn.clicked.connect(self.del_table_btn_clicked)
layout_table_btn = QtWidgets.QHBoxLayout()
layout_table_btn.addWidget(clear_table_btn)
layout_table_btn.addWidget(del_table_btn)
layout_right = QtWidgets.QVBoxLayout()
layout_right.addWidget(self.results_table)
layout_right.addLayout(layout_table_btn)
layout_bottom = QtWidgets.QHBoxLayout()
layout_bottom.addLayout(layout_left)
layout_bottom.addLayout(layout_right)
layout = QtWidgets.QVBoxLayout()
layout.addLayout(layout_top)
layout.addLayout(layout_hline)
layout.addLayout(layout_bottom)
self.setLayout(layout)
pass
def rule_combox_currentTextChanged(self,txt:str):
cur_txt = self.rule_combox.currentText()
if not cur_txt or cur_txt == self.please_select_str:
self.rule_label.setText('')
return
self.rule_label.setText(self.rule_map[cur_txt])
if cur_txt == '区间':
self.pointshape_combox.setCurrentText(self.please_select_str)
self.pointshape_combox.setDisabled(True)
else:
self.pointshape_combox.setDisabled(False)
pass
def color_btn_clicked(self):
res = QtWidgets.QColorDialog.getColor()
self.color_label.setText(res.name())
style_str = 'QLabel{font-weight:bold;color:'+res.name()+';}'
self.color_label.setStyleSheet(style_str)
pass
def check_btn_clicked(self):
cur_name = self.name_lineedit.text()
now_name_list = self.results_table.res_name_list()
if now_name_list:
if cur_name in now_name_list:
QtWidgets.QMessageBox.information(
self,
'提示',
'该标题已经设置过',
QtWidgets.QMessageBox.Yes
)
return
rule_str = self.rule_combox.currentText()
if not rule_str or rule_str==self.please_select_str:
QtWidgets.QMessageBox.information(
self,
'提示',
'请选择规则',
QtWidgets.QMessageBox.Yes
)
return
pointshape_str = ''
if rule_str!='区间':
pointshape_str = self.pointshape_combox.currentText()
if not pointshape_str or pointshape_str==self.please_select_str:
QtWidgets.QMessageBox.information(
self,
'提示',
'请选择点形状',
QtWidgets.QMessageBox.Yes
)
return
pass
color_str = self.color_label.text()
if not color_str:
QtWidgets.QMessageBox.information(
self,
'提示',
'请选择颜色',
QtWidgets.QMessageBox.Yes
)
return
data_str = self.data_textedit.toPlainText()
if not data_str:
QtWidgets.QMessageBox.information(
self,
'提示',
'请输入数据',
QtWidgets.QMessageBox.Yes
)
return
# ['标题','规则','形状','颜色','数据']
res_data = [cur_name,rule_str,pointshape_str,color_str,data_str]
self.results_table.insert_one_row(res_data)
pass
def clear_table_btn_clicked(self):
self.results_table.clear_table_contents()
def del_table_btn_clicked(self):
self.results_table.del_selected_rows()
def submit_btn_clicked(self):
res_data = self.results_table.res_data_content()
if not res_data:
QtWidgets.QMessageBox.information(
self,
'提示',
'没有设置相关项,提交的内容为空',
QtWidgets.QMessageBox.Yes
)
return
self.signal_excute.emit(res_data)
self.close()
pass
主界面
class EyeCheckMainWidget(QtWidgets.QWidget):
signal_excute = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.thread_caculate: Thread = None
self.init_data()
self.init_ui()
self.register_event()
self.progress_init()
pass
def init_data(self):
self.single_settingMark_widget: QtWidgets.QWidget = None
self.target_column_list: List[str] = ['xTick', 'open', 'close', 'highest', 'lowest']
pass
def init_ui(self):
self.setWindowTitle('验证K线视觉想法工具')
self.caculate_progress = QtWidgets.QProgressBar()
self.caculate_status_label = QtWidgets.QLabel()
layout_progress = QtWidgets.QHBoxLayout()
layout_progress.addWidget(self.caculate_progress)
layout_progress.addWidget(self.caculate_status_label)
groupbox_one = QtWidgets.QGroupBox('选择',self)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
groupbox_one.setSizePolicy(sizePolicy)
self.radio_group = QtWidgets.QButtonGroup()
single_radio_btn = QtWidgets.QRadioButton('单个验证')
single_radio_btn.setChecked(True)
batch_radio_btn = QtWidgets.QRadioButton('批量验证')
self.radio_group.addButton(single_radio_btn,1)
self.radio_group.addButton(batch_radio_btn,2)
self.radio_group.buttonClicked.connect(self.radio_group_buttonClicked)
layout_one = QtWidgets.QVBoxLayout()
layout_one.addWidget(single_radio_btn)
layout_one.addWidget(batch_radio_btn)
layout_one.addStretch(1)
groupbox_one.setLayout(layout_one)
self.stack_one = QtWidgets.QWidget()
self.stack_two = QtWidgets.QWidget()
self.fill_stack_widget_one()
self.fill_stack_widget_two()
self.stack_widget = QtWidgets.QStackedWidget()
self.stack_widget.setSizePolicy(sizePolicy)
self.stack_widget.addWidget(self.stack_one)
self.stack_widget.addWidget(self.stack_two)
layout_two = QtWidgets.QHBoxLayout()
layout_two.addWidget(groupbox_one,1)
layout_two.addWidget(self.stack_widget,4)
groupbox_two = QtWidgets.QGroupBox('选择与待验证数据相同区间', self)
tip_label = QtWidgets.QLabel('区间')
self.left_point = QtWidgets.QDateEdit()
self.left_point.setDisplayFormat('yyyy-MM-dd')
self.left_point.setCalendarPopup(True)
self.right_point = QtWidgets.QDateEdit()
self.right_point.setDisplayFormat('yyyy-MM-dd')
self.right_point.setCalendarPopup(True)
self.right_point.setDate(QtCore.QDate.currentDate())
tip_label2 = QtWidgets.QLabel('请先设置好对应区间')
tip_label2.setStyleSheet('QLabel{color:red;}')
layout_three = QtWidgets.QHBoxLayout()
layout_three.addWidget(tip_label)
layout_three.addWidget(self.left_point)
layout_three.addWidget(self.right_point)
layout_three.addWidget(tip_label2)
layout_three.addStretch(1)
groupbox_two.setLayout(layout_three)
self.graph_title_label = QtWidgets.QLabel('图标题')
self.graph_title_label.setAlignment(Qt.AlignCenter)
self.graph_title_label.setStyleSheet('QLabel{font-size:18px;font-weight:bold;}')
self.graph_widget = Graph_Widget()
layout_four = QtWidgets.QVBoxLayout()
layout_four.addWidget(self.graph_title_label)
layout_four.addWidget(self.graph_widget)
tip_label3 = QtWidgets.QLabel('标记详情列表')
self.mark_table = MarkTableWidget()
layout_five = QtWidgets.QVBoxLayout()
layout_five.addWidget(tip_label3)
layout_five.addWidget(self.mark_table)
layout_six = QtWidgets.QHBoxLayout()
layout_six.addLayout(layout_four,4)
layout_six.addLayout(layout_five,1)
layout = QtWidgets.QVBoxLayout()
layout.addLayout(layout_progress)
layout.addLayout(layout_two)
layout.addWidget(groupbox_two)
layout.addLayout(layout_six)
self.setLayout(layout)
pass
def register_event(self):
self.signal_excute.connect(self.process_excute_event)
pass
def process_excute_event(self,data:Dict):
pass
def fill_stack_widget_one(self):
single_choice_one_daily_file_path = QtWidgets.QPushButton('选择股票日数据文件')
single_choice_one_daily_file_path.clicked.connect(self.single_choice_one_daily_file_path_clicked)
self.single_choice_one_daily_file_lineedit = QtWidgets.QLineEdit()
single_show_k_btn = QtWidgets.QPushButton('显示K线')
single_show_k_btn.clicked.connect(self.single_show_k_btn_clicked)
single_input_marks_btn = QtWidgets.QPushButton('输入并设置标记数据')
single_input_marks_btn.clicked.connect(self.single_input_marks_btn_clicked)
mark_excute_btn = QtWidgets.QPushButton('执行')
mark_excute_btn.clicked.connect(self.mark_excute_btn_clicked)
single_layout_one = QtWidgets.QHBoxLayout()
single_layout_one.addWidget(single_choice_one_daily_file_path)
single_layout_one.addWidget(self.single_choice_one_daily_file_lineedit)
single_layout_one.addWidget(single_show_k_btn)
single_layout_two = QtWidgets.QHBoxLayout()
single_layout_two.addWidget(single_input_marks_btn)
single_layout_two.addWidget(mark_excute_btn)
single_layout = QtWidgets.QVBoxLayout()
single_layout.addLayout(single_layout_one)
single_layout.addLayout(single_layout_two)
single_layout.addStretch(1)
self.stack_one.setLayout(single_layout)
pass
def fill_stack_widget_two(self):
batch_layout = QtWidgets.QVBoxLayout()
self.stack_two.setLayout(batch_layout)
pass
def radio_group_buttonClicked(self,obj):
radio_id = self.radio_group.checkedId()
self.stack_widget.setCurrentIndex(radio_id - 1)
pass
def single_choice_one_daily_file_path_clicked(self):
path,_ = QtWidgets.QFileDialog.getOpenFileName(
self,
'选择股票日数据文件',
'.',
'CSV(*.csv)'
)
if not path:
return
self.single_choice_one_daily_file_lineedit.setText(path)
pass
def single_show_k_btn_clicked(self):
daily_file = self.single_choice_one_daily_file_lineedit.text()
if not daily_file or not os.path.exists(daily_file):
QtWidgets.QMessageBox.information(
self,
'提示',
'请选择要验证的日数据',
QtWidgets.QMessageBox.Yes
)
return
file_name = os.path.basename(daily_file)
left_point = self.left_point.date().toString('yyyy-MM-dd')
right_point = self.right_point.date().toString('yyyy-MM-dd')
left_datetime = datetime.datetime.strptime(left_point,'%Y-%m-%d')
right_datetime = datetime.datetime.strptime(right_point,'%Y-%m-%d')
if left_datetime >= right_datetime:
QtWidgets.QMessageBox.information(
self,
'提示',
'请选择时间区间',
QtWidgets.QMessageBox.Yes
)
return
df = pd.read_csv(daily_file,encoding='utf-8')
df = df.loc[df['openPrice']>0].copy()
df['o_date'] = pd.to_datetime(df['tradeDate'])
df = df.loc[(df['o_date']>=left_point) & (df['o_date']<=right_point)].copy()
df['open'] = df['openPrice']*df['accumAdjFactor']
df['close'] = df['closePrice']*df['accumAdjFactor']
df['highest'] = df['highestPrice']*df['accumAdjFactor']
df['lowest'] = df['lowestPrice']*df['accumAdjFactor']
df['xTick'] = df['tradeDate']
k_data = {
'whole_df':df,
'whole_header':['日期','开盘','收盘','最高','最低'],
'whole_pd_header': self.target_column_list
}
self.graph_widget.first_setData(k_data)
self.graph_title_label.setText(file_name)
pass
def single_input_marks_btn_clicked(self):
if not self.single_settingMark_widget:
self.single_settingMark_widget = SettingMarksWidget()
self.single_settingMark_widget.signal_excute.connect(self.single_settingMark_widget_signal_emit)
self.single_settingMark_widget.show()
pass
def single_settingMark_widget_signal_emit(self,data:List):
if not data:
return
self.mark_table.set_data(data)
pass
def mark_excute_btn_clicked(self):
mark_data = self.mark_table.res_data_content()
if mark_data:
self.graph_widget.add_marks(mark_data)
pass
def start_caculate_thread(self,mark_str:str,data:Dict[str,Any]):
if self.thread_caculate:
QtWidgets.QMessageBox.information(
self,
'提示',
'线程正在执行任务,请稍后。。。',
QtWidgets.QMessageBox.Yes
)
return
self.thread_caculate = Thread(
target=self.running_caculate_thread,
args=(
mark_str, data,
)
)
self.thread_caculate.start()
self.progress_busy()
pass
def running_caculate_thread(self,mark_str:str,data:Dict[str,Any]):
pass
def progress_init(self) -> None:
self.caculate_progress.setValue(0)
self.caculate_status_label.setText('无任务')
def progress_busy(self) -> None:
self.caculate_progress.setRange(0, 0)
self.caculate_status_label.setText('正在执行')
def progress_finished(self) -> None:
self.caculate_progress.setRange(0, 100)
self.caculate_progress.setValue(100)
self.caculate_status_label.setText('执行完毕')
pass
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
if self.single_settingMark_widget:
self.single_settingMark_widget.close()
self.close()
pass
运行代码
if __name__ == '__main__':
QtCore.QCoreApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
app = QtWidgets.QApplication(sys.argv)
main_window = EyeCheckMainWidget()
main_window.showMaximized()
app.exec()
pass