admin
2023-05-11 ac3c6e205316848e354ed52967d74f603aa4b3e2
新版看板/PyQt5集成
5个文件已修改
4个文件已添加
3615 ■■■■ 已修改文件
gui_wx.py 1382 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/kpl_api.py 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 1781 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.spec 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
network_util.py 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
setting.py 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test.py 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test.spec 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhibo/tui_liu.py 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gui_wx.py
New file
@@ -0,0 +1,1382 @@
import json
import socket
import sys
import time
import cv2
import win32gui
import ocr_util
import opencv_util
import setting
import ths_util
import win32_util
# freeze_support()
import logging
import multiprocessing
import threading
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import wx
import wx.html2 as webview
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import code_data_manager
import juejin_core
import tool
import requests
APP_TITLE = "卖票看板"
APP_ICON = ""
SERVER_HOST = "192.168.3.252"
SOCKET_PORT = 9001
HTTP_PORT = 9004
def show_warning(content, click):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_WARNING)
    if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
        if click is not None:
            click()
        toastone.Destroy()
def show_info(content, click):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
    if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
        click()
        toastone.Destroy()
def show_sure(content, callback):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_NO | wx.ICON_INFORMATION)
    result = toastone.ShowModal()
    if result == wx.ID_YES:  # 如果点击了提示框的确定按钮
        callback(True)
        toastone.Destroy()
    elif result == wx.ID_NO:
        callback(False)
        toastone.Destroy()
class JueJinSettingFrame(wx.Frame):
    def __init__(self, position):
        wx.Frame.__init__(self, None, -1, "掘金参数设置", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(450, 200))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer()
        flex = wx.FlexGridSizer(rows=3, cols=2, vgap=10, hgap=10)
        # 策略
        label = wx.StaticText(self, label="策略ID:")
        flex.Add(label, flag=wx.ALIGN_RIGHT)
        self.edit_celue = wx.TextCtrl(self, size=(300, -1))
        flex.Add(self.edit_celue, flag=wx.EXPAND)
        # token
        label = wx.StaticText(self, label="Token:")
        flex.Add(label)
        self.edit_token = wx.TextCtrl(self, size=(300, -1), style=wx.TE_MULTILINE)
        flex.Add(self.edit_token)
        # 占位
        flex.Add(wx.StaticText(self, label=""))
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        flex.Add(self.btn_sure, 1, wx.TOP | wx.LEFT, 20)
        boxsier.Add(flex, 1, wx.TOP | wx.LEFT, 20)
        self.SetSizer(boxsier)
        # 初始化数据
        self.__init_data()
    def __init_data(self):
        strategy_id, token = setting.get_juejin_params()
        self.edit_celue.SetLabelText(strategy_id)
        self.edit_token.SetLabelText(token)
        pass
    def __sure_btn(self, event):
        strategy_id = self.edit_celue.GetValue()
        token = self.edit_token.GetValue()
        setting.set_juejin_params(strategy_id, token)
        toastone = wx.MessageDialog(None, "更改成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
class CodesSettingFrame(wx.Frame):
    def __init__(self, position, callback):
        wx.Frame.__init__(self, None, -1, "代码设置", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(170, 300))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer(wx.VERTICAL)
        # 代码
        label = wx.StaticText(self, label="目标代码:")
        boxsier.Add(label)
        self.edit_codes = wx.TextCtrl(self, size=(150, 200), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes)
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        boxsier.Add(self.btn_sure)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
        # 初始化数据
        self.__init_data()
        self.callback = callback
    def __init_data(self):
        codes = juejin_core.GPCodeManager().get_codes()
        self.edit_codes.SetValue("\n".join(codes))
        pass
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        codes = codes_str.split("\n")
        codes_result = []
        for code in codes:
            if code.strip():
                codes_result.append(code.strip())
        juejin_core.GPCodeManager().set_codes(codes_result)
        # 重新订阅
        self.callback()
        toastone = wx.MessageDialog(None, "更改成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
class FobiddenCodesFrame(wx.Frame):
    def __init__(self, position):
        wx.Frame.__init__(self, None, -1, "添加禁止交易代码", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(170, 200))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer(wx.VERTICAL)
        # 代码
        label = wx.StaticText(self, label="代码:")
        boxsier.Add(label, flag=wx.ALIGN_LEFT, border=10)
        self.edit_codes = wx.TextCtrl(self, size=(150, 100), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes)
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        boxsier.Add(self.btn_sure)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
    def __request(self, codes):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 201, "data": {"codes": codes}}
        client.send(json.dumps(data).encode("utf-8"))
        client.close()
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        if codes_str:
            codes_str = codes_str.strip()
        codes = codes_str.split("\n")
        codes_result = []
        for code in codes:
            if code.strip() and len(code.strip()) == 6:
                codes_result.append(code.strip())
        if len(codes_result) == 0:
            show_warning("请填写正确的代码!", self.Close)
            return
        try:
            self.__request(codes_result)
        except Exception as e:
            show_warning("添加出错:" + str(e), None)
            return
        toastone = wx.MessageDialog(None, "添加成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
class THSPositionSettingFrame(wx.Frame):
    def __init__(self, position):
        wx.Frame.__init__(self, None, -1, "同花顺坐标设置", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(170, 400))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label="交易刷新事件间隔(ms):")
        boxsier.Add(label)
        self.edit_refresh_time_space = wx.TextCtrl(self, size=(150, -1))
        boxsier.Add(self.edit_refresh_time_space)
        label = wx.StaticText(self, label="点击时间间隔(ms):")
        boxsier.Add(label)
        self.edit_time_space = wx.TextCtrl(self, size=(150, -1))
        boxsier.Add(self.edit_time_space)
        # 代码
        label = wx.StaticText(self, label="坐标:")
        boxsier.Add(label)
        self.edit_codes = wx.TextCtrl(self, size=(150, 200), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes)
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        boxsier.Add(self.btn_sure)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
        # 初始化数据
        self.__init_data()
    def __init_data(self):
        pos = setting.get_ths_auto_click_positions()
        self.edit_codes.SetValue("\n".join(pos))
        space = setting.get_ths_auto_click_time_space()
        if space is None:
            space = 500
        self.edit_time_space.SetValue(f"{space}")
        space = setting.get_ths_auto_refresh_time_space()
        if space is None:
            space = 500
        self.edit_refresh_time_space.SetValue(f"{space}")
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        ps = codes_str.split("\n")
        result = []
        for p in ps:
            if p.strip():
                result.append(p.strip())
        setting.set_ths_auto_click_positions(result)
        time_space = self.edit_time_space.GetValue()
        if len(time_space) == 0:
            show_warning("点击时间间隔不正确", None)
            return
        refresh_time_space = self.edit_refresh_time_space.GetValue()
        if len(refresh_time_space) == 0:
            show_warning("刷新时间间隔不正确", None)
            return
        setting.set_ths_auto_click_time_space(time_space)
        setting.set_ths_auto_refresh_time_space(refresh_time_space)
        toastone = wx.MessageDialog(None, "更改成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
last_ocr_code = ["000000"]
import log
def ocr_ths_code():
    hwnd = ths_util.get_ths_main_content_hwnd()
    if not hwnd:
        raise Exception("看盘页面句柄未获取到")
    # 句柄截图
    rect = win32gui.GetWindowRect(hwnd)
    # hwnd_width = (rect[2] - rect[0]) * 15 // 10
    rect_ = setting.get_ths_auto_code_rect()
    img = win32_util.window_capture(hwnd, (0, rect_[0], rect_[1], rect_[0] + rect_[2]))
    clip_img, details = opencv_util.clip_ths_code_area(img)
    start_time = time.time()
    code = ocr_util.recognize_code(clip_img)
    use_time = round((time.time() - start_time) * 1000)
    if code is None:
        code = ocr_util.recognize_code(img)
    if code != last_ocr_code[0]:
        print("保存图片", code)
        log.debug_logger.info(f"代码识别结果:{code} 识别时间:{use_time}ms")
        last_ocr_code[0] = code
        cv2.imwrite(f"datas/test/{code}.png", opencv_util.gray_img(img))
    return code
# 悬浮框
class FloatFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "悬浮盯盘", style=wx.CAPTION ^ wx.MINIMIZE_BOX ^ wx.CLOSE_BOX ^ wx.STAY_ON_TOP,
                          size=(320, 195))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetTransparent(230)
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        # 读取配置信息
        window_info = setting.get_float_watch_window_info()
        if window_info:
            self.SetPosition(wx.Point(window_info[0], window_info[1]))
            self.Size = wx.Size(window_info[2], window_info[3])
        boxsier = wx.FlexGridSizer(5, 2, 2, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_remove_black = wx.Button(self, label="移除黑名单", size=(70, 30))
        self.btn_close_buy = wx.Button(self, label="关闭交易", size=(70, 30))
        self.btn_close_buy.SetForegroundColour("#FF3232")
        bs1.Add(self.btn_remove_black)
        bs1.Add(self.btn_close_buy)
        boxsier.Add(bs1, 0, wx.TOP, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_remove_white = wx.Button(self, label="移除白名单", size=(70, 30))
        self.btn_open_buy = wx.Button(self, label="开启交易", size=(70, 30), )
        self.btn_open_buy.SetForegroundColour("#00e600")
        bs1.Add(self.btn_open_buy, 0, wx.TOP, 5)
        bs1.Add(self.btn_remove_white, 0, wx.TOP, 5)
        boxsier.Add(bs1, 0, wx.LEFT, 0)
        label = wx.StaticText(self, label="交易刷新:")
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        bs1.Add(label)
        self.check_auto_refresh = wx.CheckBox(self, size=(-1, -1))
        bs1.Add(self.check_auto_refresh, 0, wx.LEFT, 10)
        boxsier.Add(bs1, 0, wx.TOP, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.want_list = wx.Button(self, label="想买单", size=(45, 20))
        bs1.Add(self.want_list, 0, wx.TOP, 2)
        self.btn_want_buy = wx.Button(self, label="加入想买单", size=(70, 25))
        bs1.Add(self.btn_want_buy)
        boxsier.Add(bs1, 0, wx.LEFT, 25)
        label = wx.StaticText(self, label="分组刷新:")
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        bs1.Add(label)
        self.check_auto_click = wx.CheckBox(self, size=(-1, -1))
        bs1.Add(self.check_auto_click, 0, wx.LEFT, 10)
        boxsier.Add(bs1)
        self.btn_want_buy_remove = wx.Button(self, label="移除想买单", size=(70, 25))
        boxsier.Add(self.btn_want_buy_remove, 0, wx.LEFT, 70)
        self.edit_code = wx.TextCtrl(self, size=(80, -1))
        boxsier.Add(self.edit_code)
        self.notify_text = wx.StaticText(self, label="", size=(80, -1))
        boxsier.Add(self.notify_text, 0, wx.LEFT, -45)
        # 代码
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_black = wx.Button(self, label="加入黑名单", size=(70, 30))
        bs1.Add(self.btn_black)
        self.black_list = wx.Button(self, label="黑名单", size=(45, 20))
        self.black_list.SetForegroundColour("#00e600")
        bs1.Add(self.black_list, 0, wx.CENTER | wx.ALL, 0)
        boxsier.Add(bs1, 0, wx.LEFT, 0)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_white = wx.Button(self, label="加入白名单", size=(70, 30))
        self.white_list = wx.Button(self, label="白名单", size=(45, 20))
        self.white_list.SetForegroundColour("#FF3232")
        bs1.Add(self.white_list, 0, wx.CENTER | wx.ALL, 0)
        bs1.Add(self.btn_white, 0, wx.LEFT, 2)
        boxsier.Add(bs1, 0, wx.LEFT, 22)
        # 绑定
        self.btn_open_buy.Bind(wx.EVT_BUTTON, self.__open_buy)
        self.btn_close_buy.Bind(wx.EVT_BUTTON, self.__close_buy)
        self.btn_black.Bind(wx.EVT_BUTTON, self.add_black)
        self.btn_white.Bind(wx.EVT_BUTTON, self.add_white)
        self.btn_remove_black.Bind(wx.EVT_BUTTON, self.remove_from_black)
        self.btn_remove_white.Bind(wx.EVT_BUTTON, self.remove_from_white)
        self.btn_want_buy.Bind(wx.EVT_BUTTON, self.add_want)
        self.btn_want_buy_remove.Bind(wx.EVT_BUTTON, self.remove_from_want)
        self.check_auto_click.Bind(wx.EVT_CHECKBOX, self.__auto_click_check)
        self.check_auto_refresh.Bind(wx.EVT_CHECKBOX, self.__auto_refresh_check)
        self.white_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "白名单列表", 302))
        self.black_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "黑名单列表", 301))
        self.want_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "想要买列表", 403))
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.LEFT, 10)
        self.SetSizer(root_boxsier)
        self.__bind_hot_keys()
        # 初始化数据
        self.__init_data()
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.clear_msg, self.timer)
    def clear_msg(self, event):
        self.notify_text.SetLabelText("")
    def __ocr_code(self):
        code = self.edit_code.GetValue()
        if code is not None and len(code.strip()) == 0:
            code = None
        if code is not None:
            if len(code) != 6:
                self.show_warning("请填写正确的代码")
                return
            else:
                return
        code = ocr_ths_code()
        if code is None:
            raise Exception("代码识别出错")
        self.edit_code.SetValue(code)
    def show_warning(self, content):
        self.notify_text.SetLabel(content)
        self.notify_text.SetForegroundColour("#FF7F27")
        self.timer.Stop()
        self.timer.StartOnce(20000)
    def show_info(self, content):
        self.notify_text.SetLabel(content)
        self.notify_text.SetForegroundColour("#008000")
        self.timer.Stop()
        self.timer.StartOnce(20000)
    def __get_code(self):
        self.__ocr_code()
        code = self.edit_code.GetValue()
        if code is None or len(code) != 6:
            raise Exception("请填写正确的代码")
        return code
    def add_black(self, event):
        try:
            code = self.__get_code()
            print("加入黑名单", code)
            self.__request([code], 201)
            self.show_info(f"{code}加入黑名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def add_white(self, event):
        try:
            code = self.__get_code()
            print("加入白名单", code)
            result = self.__request([code], 202)
            result = json.loads(result)
            if result["code"] == 0:
                self.show_info(f"{code}加入白名单成功")
            else:
                self.show_warning(f"加入失败:{result['msg']}")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def add_want(self, event):
        try:
            code = self.__get_code()
            print("加入想要买", code)
            self.__request([code], 401)
            self.show_info(f"{code}加入想要买名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def remove_from_black(self, event):
        try:
            code = self.__get_code()
            print("移除黑名单", code)
            self.__request([code], 203)
            self.show_info(f"{code}移除黑名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def remove_from_white(self, event):
        try:
            code = self.__get_code()
            print("移除白名单", code)
            self.__request([code], 204)
            self.show_info(f"{code}移除白名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def remove_from_want(self, event):
        try:
            code = self.__get_code()
            print("移除想要买名单", code)
            self.__request([code], 402)
            self.show_info(f"{code}移除想要买名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def show_list(self, event, title, type):
        try:
            result = self.__request_list(type)
            result = json.loads(result)
            self.__show_list(title, result["data"])
        except Exception as e:
            show_warning(str(e), None)
    def __show_list(self, title, datas):
        st = ""
        for i in range(0, len(datas)):
            st += datas[i]
            if i % 2 == 1 and i != len(datas) - 1:
                st += "\n"
            elif i != len(datas) - 1:
                st += " , "
        toastone = wx.MessageDialog(None, st, title)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            toastone.Destroy()
    def __bind_hot_keys(self):
        # 快捷键
        setting_ = wx.Menu()
        m_black = wx.MenuItem(setting_, id=101, text='&E', kind=wx.ITEM_NORMAL)
        m_white = wx.MenuItem(setting_, id=102, text='&E', kind=wx.ITEM_NORMAL)
        self.Bind(wx.EVT_MENU, self.add_black, m_black)
        self.Bind(wx.EVT_MENU, self.add_white, m_white)
        entries = [wx.AcceleratorEntry() for i in range(2)]
        entries[0].Set(wx.ACCEL_CTRL, wx.WXK_F4, 101)
        entries[1].Set(wx.ACCEL_CTRL, wx.WXK_F5, 102)
        accel = wx.AcceleratorTable(entries)
        self.SetAcceleratorTable(accel)
    def __init_data(self):
        auto_click = setting.is_ths_auto_click()
        if auto_click:
            self.check_auto_click.SetValue(True)
        else:
            self.check_auto_click.SetValue(False)
        auto_refresh = setting.is_ths_trade_auto_refresh()
        if auto_refresh:
            self.check_auto_refresh.SetValue(True)
        else:
            self.check_auto_refresh.SetValue(False)
        self.__init_trade_state()
    def __init_trade_state(self):
        # 获取交易状态
        try:
            result = self.__request_buy_state()
            result = json.loads(result)
            if result["code"] == 0:
                if result["data"]["can_buy"]:
                    self.btn_open_buy.SetLabelText("开启交易*")
                    self.btn_close_buy.SetLabelText("关闭交易")
                else:
                    self.btn_open_buy.SetLabelText("开启交易")
                    self.btn_close_buy.SetLabelText("关闭交易*")
        except:
            pass
    def __auto_click_check(self, event):
        if event.Selection:
            setting.set_ths_auto_click(True)
        else:
            setting.set_ths_auto_click(False)
    def __auto_refresh_check(self, event):
        if event.Selection:
            setting.set_ths_trade_auto_refresh(True)
        else:
            setting.set_ths_trade_auto_refresh(False)
    def __request(self, codes, type):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {"codes": codes}}
        client.send(json.dumps(data).encode("utf-8"))
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    def __request_list(self, type):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(10240)
        client.close()
        return result.decode("gbk")
    def __request_buy(self, is_open):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 501, "data": {"open": is_open}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(10240)
        client.close()
        return result.decode("gbk")
    # 获取买入状态
    def __request_buy_state(self):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 502, "data": {}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    def __open_buy(self, event):
        def open_buy(sure):
            if sure:
                try:
                    result = self.__request_buy(True)
                    msg = json.loads(result)["msg"]
                    show_info(msg, None)
                    self.__init_trade_state()
                except Exception as e:
                    show_warning(str(e), None)
        show_sure("是否开启交易", open_buy)
    def __close_buy(self, event):
        def close_buy(sure):
            if sure:
                try:
                    result = self.__request_buy(False)
                    msg = json.loads(result)["msg"]
                    show_info(msg, None)
                    self.__init_trade_state()
                except Exception as e:
                    show_warning(str(e), None)
        show_sure("是否关闭交易", close_buy)
    def OnExit(self, e):
        try:
            setting.set_float_watch_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        self.Hide()
class TickFrame(wx.Frame):
    def __init__(self):
        '''构造函数'''
        wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE,
                          size=(800, 500))
        # ^ wx.RESIZE_BORDER ^ wx.STAY_ON_TOP
        # 默认style是下列项的组合:wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
        self.SetBackgroundColour(wx.Colour(0, 0, 0))
        win_info = setting.get_tick_window_info()
        if win_info:
            self.SetPosition(wx.Point(win_info[0], win_info[1]))
            self.Size = wx.Size(win_info[2], win_info[3])
        else:
            self.Center()
        # 以下代码处理图标
        # if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
        #     exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
        #     icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        # else:
        #     icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        # self.SetIcon(icon)
        # 定义窗口关闭
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        self.Bind(wx.EVT_SIZE, self.OnResize)
        self.panels = []
        self.scroll = None
        self.mark_lines = {}
        self.col = 1
        self.ratio = 0.63
        self.__re_draw()
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.post_redraw, self.timer)  # 绑定一个定时器事件
        self.last_size = (self.Size[0], self.Size[1])
        # self.scroll.Layout()
        # self.boxsier.Fit(self.scroll)
        # boxsier.Add(mainBoxsier, 1, wx.EXPAND | wx.ALL, 5)
        # self.SetSizer(boxsier)
        # mainBoxsier.Fit(self)
        if setting.is_stay_on_top():
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.STAY_ON_TOP
    def scrollTo(self, pos):
        self.scroll.Scroll(0, pos)
    def __re_draw(self):
        codes_name = juejin_core.GPCodeManager().get_codes_with_names()
        rows = len(codes_name)
        if rows % self.col == 0:
            rows = rows / self.col
        else:
            rows = rows / self.col + 1
        space = 0
        if self.scroll is None:
            self.scroll = wx.ScrolledWindow(self, -1, size=(800, 1000))
            self.boxsier = wx.FlexGridSizer(rows, self.col, space, space)
            self.scroll.SetSizer(self.boxsier)
        self.scroll.EnableScrolling(False, True)
        if self.panels:
            for p in self.panels:
                p.Destroy()
        self.boxsier.Clear()
        pannel_height = round((self.Size[0] - (self.col - 1) * space) / self.col * self.ratio)
        self.scroll.SetScrollbars(1, 1, 0, pannel_height * rows)
        self.scroll.SetScrollRate(0, pannel_height)
        global drawManager
        axes_list = []
        self.panels = []
        for i in range(0, len(codes_name)):
            # pos=(0, i * pannel_height)
            pannel = wx.Panel(self.scroll, size=(-1, pannel_height))
            pannel.BackgroundColour = wx.Colour(0, 0, 0)
            self.panels.append(pannel)
            self.boxsier.Add(pannel)
            axes = self.__create_canvas(pannel, "{}({})".format(codes_name[i][0], codes_name[i][1]), codes_name[i][1],
                                        codes_name[i][2])
            axes_list.append(axes)
        self.scroll.Layout()
        # self.boxsier.Fit(self.scroll)
        #
        # 初始化数据
        drawManager = DrawManager(axes_list, codes_name)
        t1 = threading.Thread(target=lambda: drawManager.init_code_datas())
        # 后台运行
        t1.setDaemon(True)
        t1.start()
    def __create_canvas(self, pannel, title, name, price, close_callback=None):
        def show_mouse_line(event):
            # 删除之前的线
            if title in self.mark_lines:
                if self.mark_lines.get(title):
                    line = self.mark_lines.get(title).get("mouse")
                    if line is not None:
                        line.remove()
                        self.mark_lines.get(title).pop("mouse")
            else:
                self.mark_lines[title] = {}
            try:
                line = axes2.axhline(event.ydata, linestyle='-', color='white', lw=0.5)
                self.mark_lines[title]["mouse"] = line
                axes2.figure.canvas.draw()
            except:
                pass
        def clear_mouse_line(event):
            print("clear_mouse_line")
            if title in self.mark_lines:
                if self.mark_lines.get(title):
                    line = self.mark_lines.get(title).get("mouse")
                    if line is not None:
                        line.remove()
                        self.mark_lines.get(title).pop("mouse")
                        axes2.figure.canvas.draw()
        def close_canvas(event):
            print("关闭", title)
            close_callback(title)
        dpi = 100
        width_dpi = self.Size[0] / (dpi * self.col)
        figure_score = Figure(figsize=(width_dpi, round(width_dpi * (self.ratio), 2)), dpi=dpi)
        # 设置外边距
        right_padding_px = 85
        right = round((self.Size[0] - right_padding_px) / self.Size[0], 4)
        figure_score.subplots_adjust(left=0.01, bottom=0.15, top=0.92,
                                     right=right)
        # 设置字体颜色
        plt.rcParams["text.color"] = "red"
        plt.rcParams["axes.labelcolor"] = "red"
        # 设置坐标轴数字颜色
        plt.rcParams["xtick.color"] = "white"
        plt.rcParams["ytick.color"] = "white"
        # 设置坐标轴颜色
        plt.rcParams["axes.edgecolor"] = "firebrick"
        # 解决中文乱码问题
        plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体
        plt.rcParams["font.serif"] = ["SimHei"]
        plt.rcParams["axes.unicode_minus"] = False  # 该语句解决图像中的“-”负号的乱码问题
        # 测试
        buttonaxe = plt.axes([0.94, 0.5, 0.1, 0.1])
        button1 = Button(buttonaxe, '关闭', color='white', hovercolor='yellow')
        axes = figure_score.add_subplot(1, 1, 1)
        axes.autoscale(True)
        # axes_score.plot(t_score, s_score, 'ro', t_score, s_score, 'k')
        axes.set_title(title)
        axes.grid(color='firebrick', ls='-', lw=0.5)
        axes.set_xlabel(f'时间({name})')
        axes.dist = 0
        # axes.set_ylabel(u'价格')
        # 获取平开价
        extra = 0  # (tool.get_limit_up_price(price)-decimal.Decimal(price))*decimal.Decimal(0.02)
        axes.set_ylim(tool.get_limit_down_price(price) - extra, tool.get_limit_up_price(price) + extra)
        axes.patch.set_facecolor('black')
        figure_score.patch.set_facecolor('black')
        axes2 = axes.twinx()
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        axes2.grid(False)
        # 鼠标在画布移动
        axes2.figure.canvas.mpl_connect('motion_notify_event', show_mouse_line)
        # 鼠标离开画布
        axes2.figure.canvas.mpl_connect('axes_leave_event', clear_mouse_line)
        # 设置纵坐标轴
        limit_up_price = float(tool.get_limit_up_price(price))
        max_rate = round((limit_up_price - price) / price, 4) * 100
        print("涨停最大比例", max_rate)
        yticks2 = []
        for i in range(0, 41):
            if i >= 20:
                yticks2.append(0 - round(max_rate * (20 - i) / 20, 4))
            else:
                yticks2.append(round(max_rate * (i - 20) / 20, 4))
        yticks2_labels = []
        yticks = []
        yticks_labels = []
        for i in range(0, len(yticks2)):
            if i % 2 == 0:
                yticks2_labels.append("{}%".format(abs(round(yticks2[i], 2))))
            else:
                yticks2_labels.append("")
            price_ = round((1 + yticks2[i] / 100) * price, 2)
            yticks.append(price_)
            if i % 2 == 0:
                yticks_labels.append("")
                # yticks_labels.append(round(yticks[i], 2))
                if i == 20:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=2)
                else:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=1.2)
            else:
                # axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=0.5)
                yticks_labels.append("")
        # 加粗中轴线
        axes2.set_ylabel(u'')
        # 设置纵轴的值的范围
        axes2.set_ylim(0 - max_rate * (1), max_rate * (1))
        axes2.set_yticks(yticks2)
        axes2.set_yticklabels(yticks2_labels)
        axes.set_yticks(yticks)
        axes.set_yticklabels(yticks_labels)
        # 设置纵坐标数值颜色
        for i in range(0, 19):
            axes.get_yticklabels()[i].set_color("green")
            axes2.get_yticklabels()[i].set_color("green")
        for i in range(20, 20):
            axes.get_yticklabels()[i].set_color("white")
            axes2.get_yticklabels()[i].set_color("white")
        for i in range(21, 41):
            axes.get_yticklabels()[i].set_color("red")
            axes2.get_yticklabels()[i].set_color("red")
        line = axes2.plot([], [], color='white', linewidth=1)
        average_line = axes2.plot([], [], color='green', linewidth=1, linestyle='-')
        average_line_1m = axes2.plot([], [], color='yellow', linewidth=1, linestyle='-')
        # axes2.legend(loc='upper left')
        cannvas = FigureCanvas(pannel, -1, figure_score)
        axes2.text(1, 11.5, r'现价:0.0 涨幅:0.00% \n留格局:0%', fontsize=10, color='white')
        axes2.text(-1, -11.5, r'现价:0.0 涨幅:0.00% \n留格局:0%', fontsize=10, color='white')
        axes2.spines['top'].set_visible(False)
        axes.spines['top'].set_visible(False)
        axes2.spines['bottom'].set_visible(False)
        axes.spines['bottom'].set_visible(False)
        return (axes2, line, average_line_1m, average_line)
    def __show_top(self, event):
        if event.Selection:
            setting.set_stay_on_top(1)
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.STAY_ON_TOP
        else:
            setting.set_stay_on_top(0)
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
    def OnExit(self, e):
        try:
            setting.set_tick_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        self.Hide()
    def post_redraw(self, evt):
        if abs(self.last_size[0] - self.Size[0]) > 20:
            print("--------post_redraw--------")
            self.last_size = (self.Size[0], self.Size[1])
            self.__re_draw()
    def OnResize(self, e):
        print("变化后的尺寸", e.Size)
        # 留出滚动条,留出上边距
        if self.scroll:
            self.scroll.Size = (e.Size[0] - 15, e.Size[1] - 60)
            for p in self.panels:
                p_height = round(e.Size[0] * (450 / 800))
                p.Size = (e.Size[0], p_height)
        self.timer.Stop()
        self.timer.StartOnce(1000)
        # 降低重绘频率
        # self.__re_draw()
    def set_codes_success(self):
        print("设置代码成功回调")
        p2.send("resub")
        self.__re_draw()
# 绘图管理器
class DrawManager:
    X_RANGE_MINIUTES = 60
    X_DATA_MINIUTES = 56
    def __load_lack_datas(self, code, time_ranges):
        codeDataManager = code_data_manager.CodeDataManager()
        day = tool.get_now_date_str()
        for time_range in time_ranges:
            results = juejin_core.GPCodeManager().get_history_tick(code, day + " " + time_range[0],
                                                                   day + " " + time_range[1])
            datas = []
            for data in results:
                datas.append(juejin_core.parse_tick(data))
            # 保存数据
            for data in datas:
                # 09:25:00之前的数据不保存
                created_at = data["created_at"].strftime("%Y-%m-%d %H:%M:%S")
                time_ = created_at[-8:]
                if tool.trade_time_sub(time_, "09:25:00") < 0:
                    continue
                if tool.trade_time_sub(time_, "15:00:00") > 0:
                    continue
                if tool.trade_time_sub(time_, "11:30:00") > 0 and tool.trade_time_sub(time_, "13:00:00") < 0:
                    continue
                # 不是今天的数据不保存
                if day != created_at[:10]:
                    continue
                codeDataManager.save_data(data)
    def init_code_datas(self):
        global code_datas
        global max_min_prices
        codeDataManager = code_data_manager.CodeDataManager()
        gpCodeManager = juejin_core.GPCodeManager()
        code_datas = {}
        max_min_prices = {}
        codes = gpCodeManager.get_codes()
        if codes:
            # 获取当日的最高价最低价
            res = juejin_core.GPCodeManager().get_min_and_max_price(codes)
            for data in res:
                max_min_prices[data[0]] = (data[1], data[2])
            for code in codes:
                # 加载历史数据
                code_datas[code] = []
                old_datas = codeDataManager.get_datas(code)
                # 获取缺失的数据
                min_time = tool.get_now_time_str()
                if int(min_time.replace(":", "")) > int("150000"):
                    min_time = "15:00:00"
                elif int("113000") < int(min_time.replace(":", "")) < int("130000"):
                    min_time = "11:30:00"
                min_time = tool.trade_time_add_second(min_time, 0 - DrawManager.X_DATA_MINIUTES * 60)
                ranges = codeDataManager.get_lack_datas_time_range(old_datas, min_time)
                if len(ranges) > 0:
                    self.__load_lack_datas(code, ranges)
                    old_datas = codeDataManager.get_datas(code)
                if old_datas:
                    code_datas[code].extend(old_datas)
                    # self.update(code, code_datas[code])
                    wx.CallAfter(self.update, code, code_datas[code])
    # 更新数据
    def __update_data(self, code, axes, datas, min_rate, max_rate):
        def get_time_as_seconds(created_at):
            time_ = created_at[-8:]
            if tool.get_time_as_second("13:00:00") > tool.get_time_as_second(time_) > tool.get_time_as_second(
                    "11:30:00"):
                time_ = "11:30:00"
            time_s = int(time_.split(":")[0]) * 3600 + int(time_.split(":")[1]) * 60 + int(
                time_.split(":")[2]) - 9 * 3600 - 60 * 30
            if int(time_.replace(":", "")) > int("11:30:00".replace(":", "")):
                time_s -= 90 * 60
            return time_s
        def seconds_2_time_str(seconds):
            seconds += 9 * 3600 + 60 * 30
            if seconds > 11 * 3600 + 60 * 30:
                seconds += 90 * 60
            h = seconds // 3600
            m = seconds % 3600 // 60
            s = seconds % 60
            return "{0:0>2}:{1:0>2}:{2:0>2}".format(h, m, s)
        # 删除9:30以前的数据
        for i in range(0, len(datas)):
            time_ = datas[i]["created_at"][-8:]
            if int(time_.replace(":", "")) >= int("092600"):
                datas = datas[i:]
                break
        # 取最近14分钟的数据
        for i in range(len(datas) - 1, -1, -1):
            time_ = datas[i]["created_at"][-8:]
            if tool.trade_time_sub(datas[-1]["created_at"][-8:], time_) >= self.X_DATA_MINIUTES * 60:
                datas = datas[i:]
                break
        xs = []
        ys_rate = []
        ys_average_rate_1m = []
        ys_average_rate = []
        for data in datas:
            xs.append(get_time_as_seconds(data["created_at"]))
            ys_rate.append(data["rate"] * 100)
            ys_average_rate_1m.append(data["average_rate"] * 100 + 1.5)
            ys_average_rate.append(data["average_rate"] * 100)
        xticks = []
        xticklabels = []
        # 设置X轴范围为09:30:00 到15:00:00
        # x轴范围为0-15分钟
        end_x = "0000-00-00 " + tool.trade_time_add_second(datas[0]["created_at"][-8:], self.X_RANGE_MINIUTES * 60)
        axes[0].set_xlim(get_time_as_seconds(datas[0]["created_at"]), get_time_as_seconds(end_x))
        # if len(xs) < 2 or xs[-1] - xs[0] < 30 * 60:
        #     axes[0].set_xlim(xs[0], xs[0] + 30 * 60)
        # else:
        #     axes[0].set_xlim(xs[0], xs[-1])
        xms = axes[0].get_xlim()
        yms = axes[0].get_ylim()
        step = (int(xms[1]) - int(xms[0])) // 30
        for i in range(int(xms[0]), int(xms[1] + 1), step):
            xticks.append(i)
            xticklabels.append("")
        axes[0].set_xticks(xticks)
        axes[0].set_xticklabels(xticklabels)
        axes[1][0].set_data(xs, ys_rate)
        axes[2][0].set_data(xs, ys_average_rate_1m)
        if axes[3]:
            axes[3][0].set_data(xs, ys_average_rate)
        texts = axes[0].texts
        texts[0].set_text("{}% \n留格局:0%".format(round(datas[-1]["rate"] * 100, 2)))
        texts[1].set_text("{}".format(datas[-1]["created_at"].split(" ")[1]))
        texts[0].set_x(xms[1] - 80)
        texts[0].set_y(yms[1] + 0.5)
        texts[1].set_x(xms[0])
        texts[1].set_y(yms[0] - 1.5)
        # 删除之前
        if code in self.lines:
            for key in self.lines[code]:
                line = self.lines[code][key]
                line.remove()
            self.lines.pop(code)
        # 绘制最大最小坐标
        line_min = axes[0].axhline(min_rate, linestyle='--', color='yellow', lw=0.5)
        line_max = axes[0].axhline(max_rate, linestyle='--', color='yellow', lw=0.5)
        self.lines[code] = {"min": line_min, "max": line_max}
        axes[0].figure.canvas.draw()
        axes[0].figure.canvas.flush_events()
    def __init__(self, axes_list, codes_info):
        self.axes_list = axes_list
        self.codes_info = codes_info
        self.lines = {}
    def update(self, code, datas):
        __start_time = time.time()
        # 获取前高和前低
        max_rate = None
        min_rate = None
        if code in max_min_prices:
            pre_price = juejin_core.GPCodeManager().get_pre_prices(code)
            min_rate = round((max_min_prices[code][0] - pre_price) / pre_price, 4)
            max_rate = round((max_min_prices[code][1] - pre_price) / pre_price, 4)
        for data in datas:
            rate = data["rate"] * 100
            created_at = data["created_at"][-8:]
            if tool.get_time_as_second("15:00:00") >= tool.get_time_as_second(created_at) >= tool.get_time_as_second(
                    "09:30:00"):
                if max_rate is None:
                    max_rate = rate
                if min_rate is None:
                    min_rate = rate
                if rate > max_rate:
                    max_rate = rate
                if rate < min_rate:
                    min_rate = rate
        # 展示最近的600个
        datas = datas[0 - self.X_DATA_MINIUTES * 20:]
        # 修正量数据
        last_info = None
        for i in range(1, len(datas)):
            # 如果在
            if not last_info:
                last_info = datas[i]
            if int(datas[i]["created_at"][-2:]) - int(datas[i - 1]["created_at"][-2:]) < 0:
                last_info = datas[i]
            datas[i]['average_rate_1m'] = last_info['average_rate']
        datas[0]['average_rate_1m'] = datas[0]['average_rate']
        for i in range(0, len(self.codes_info)):
            if self.codes_info[i][0] == code:
                try:
                    self.__update_data(code, self.axes_list[i], datas, min_rate, max_rate)
                except Exception as e:
                    logging.exception(e)
        print("绘图花费时间:", time.time() - __start_time)
class mainApp(wx.App):
    def __refresh(self):
        codes = juejin_core.GPCodeManager().get_codes()
        last_time = round(time.time())
        while True:
            try:
                code = ocr_ths_code()
                if not code:
                    time.sleep(0.1)
                    continue
                global_datas["pipe"].send(json.dumps({"code": code, "type": "code"}))
                # 1s更新一次
                if round(time.time()) - last_time > 5:
                    codes = juejin_core.GPCodeManager().get_codes()
                    last_time = round(time.time())
                for index in range(0, len(codes)):
                    if codes[index] == code:
                        self.frame.scrollTo(index)
                        break
            except Exception as e:
                # print(str(e))
                pass
            time.sleep(0.005)
    def __show_float_frame(self):
        self.floatFrame.Show()
    def __show_main_frame(self):
        self.frame.Show()
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.frame = TickFrame()
        self.floatFrame = FloatFrame()
        global_datas["tickFrame"] = self.frame
        global_datas["floatFrame"] = self.floatFrame
        t1 = threading.Thread(target=lambda: self.__refresh())
        # 后台运行
        t1.setDaemon(True)
        t1.start()
        return True
def recieve_tick(pipe):
    codeDataManager = code_data_manager.CodeDataManager()
    while True:
        data = pipe.recv()
        if data:
            type = data["type"]
            if type == 0:
                # tick数据
                data = data["data"]
                code = data["code"]
                if code not in code_datas:
                    code_datas[code] = []
                code_datas[code].append(data)
                codeDataManager.save_data(data)
                # 更新数据
                try:
                    drawManager.update(code, code_datas[code])
                    print("接受到的tick数据:", data)
                except:
                    pass
def ths_auto_click():
    hwnd = ths_util.get_ths_second_screen_menu_hwnd()
    while True:
        try:
            if hwnd is None or not win32gui.IsWindowVisible(hwnd):
                # print("未找到同花顺副屏句柄")
                hwnd = ths_util.get_ths_second_screen_menu_hwnd()
            if hwnd is None:
                continue
            if not setting.is_ths_auto_click():
                continue
            ps = setting.get_ths_auto_click_positions()
            if not ps:
                continue
            ps_new = []
            for p in ps:
                p = eval(p)
                ps_new.append(p)
            space = setting.get_ths_auto_click_time_space()
            if space is None:
                space = 500
            ths_util.betch_click(hwnd, ps_new, round(space / 1000, 4))
        except Exception as e:
            pass
        finally:
            time.sleep(0.02)
def ths_auto_refresh():
    hwnd = ths_util.get_trade_refesh_hwnd()
    while True:
        try:
            if hwnd is None or not win32gui.IsWindowVisible(hwnd):
                # print("未找到同花顺交易刷新句柄")
                hwnd = ths_util.get_trade_refesh_hwnd()
            if hwnd is None:
                continue
            if not setting.is_ths_trade_auto_refresh():
                continue
            rect = win32gui.GetWindowRect(hwnd)
            win32_util.visual_click(hwnd, (160, (rect[3] - rect[1]) // 2))
            time_space = setting.get_ths_auto_refresh_time_space()
            if time_space is None:
                time_space = 500
            time.sleep(round(time_space / 1000, 4))
        except Exception as e:
            pass
        finally:
            time.sleep(0.02)
if __name__ == "__main__1":
    print(webview.WebView.IsBackendAvailable(webview.WebViewBackendEdge))
global_datas = {
}
# 接受来自其他进程的数据
def recieve_data(pipe):
    while True:
        try:
            value = pipe.recv()
            data = json.loads(value)
            print("接受到数据:", data)
            type_ = data["type"]
            if type_ == "codes_setting":
                wx.CallAfter(lambda: CodesSettingFrame(data["pos"], global_datas["tickFrame"].set_codes_success).Show())
            elif type_ == "juejin_setting":
                wx.CallAfter(lambda: JueJinSettingFrame(data["pos"]).Show())
            elif type_ == "manage_ths_pos":
                wx.CallAfter(lambda: THSPositionSettingFrame(data["pos"]).Show())
            elif type_ == "show_float_callback":
                wx.CallAfter(lambda: global_datas["floatFrame"].Show())
            elif type_ == "show_main_callback":
                wx.CallAfter(lambda: global_datas["tickFrame"].Show())
            elif type_ == "exit":
                try:
                   jueJinProcess.terminate()
                except:
                   pass
                wx.CallAfter(lambda: sys.exit())
        except Exception as e:
            logging.exception(e)
def run(pipe):
    global_datas["pipe"] = pipe
    global p2
    p1, p2 = multiprocessing.Pipe()
    global jueJinProcess
    jueJinProcess = multiprocessing.Process(target=juejin_core.run, args=(p1,))
    jueJinProcess.start()
    t1 = threading.Thread(target=lambda: recieve_data(pipe))
    # 后台运行
    t1.setDaemon(True)
    t1.start()
    t1 = threading.Thread(target=lambda: recieve_tick(p2))
    # 后台运行
    t1.setDaemon(True)
    t1.start()
    t2 = threading.Thread(target=lambda: ths_auto_click())
    # 后台运行
    t2.setDaemon(True)
    t2.start()
    t3 = threading.Thread(target=lambda: ths_auto_refresh())
    # 后台运行
    t3.setDaemon(True)
    t3.start()
    app = mainApp()
    global_datas["app"] = app
    app.MainLoop()
if __name__ == "__main__":
    pass
kpl/kpl_api.py
@@ -79,6 +79,27 @@
        raise Exception("请求出错")
    return response.text
# 市场行情-行业
def getMarketIndustryRealRankingInfo(orderJingE_DESC=True):
    data = f"Order={ 1 if orderJingE_DESC else 0}&a=RealRankingInfo&st=80&apiv=w32&Type=5&c=ZhiShuRanking&PhoneOSNew=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&Index=0&ZSType=4&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
# 市场行情-精选
def getMarketJingXuanRealRankingInfo(orderJingE_DESC=True):
    data = f"Order={ 1 if orderJingE_DESC else 0}&a=RealRankingInfo&st=80&apiv=w32&Type=5&c=ZhiShuRanking&PhoneOSNew=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&Index=0&ZSType=7&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
# 获取代码的概念
def getStockIDPlate(code):
    data = f"a=GetStockIDPlate_New&apiv=w32&c=StockL2Data&StockID={code}&PhoneOSNew=1&UserID=0&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&Token=0&"
@@ -87,6 +108,26 @@
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
# 获取概念代码
def getCodesByPlate(plate):
    data = f"Order=1&a=ZhiShuStockList_W8&st=100&c=ZhiShuRanking&PhoneOSNew=1&old=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&IsZZ=0&Token=0&Index=0&apiv=w32&Type=6&IsKZZType=0&UserID=0&PlateID={plate}&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
# 获取概念中的板块强度
def getSonPlate(plate):
    data = f"a=SonPlate_Info&apiv=w32&c=ZhiShuRanking&PhoneOSNew=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&PlateID={plate}&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php", data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
def test_l2():
    code = "600981"
@@ -102,10 +143,15 @@
if __name__ == '__main__':
    result = (daBanList(3))
    result = (getCodesByPlate("801085"))
    result = json.loads(result)
    print(result["list"])
    result = (daBanList(DABAN_TYPE_LIMIT_UP))
    result = json.loads(result)
    print(len(result["list"]))
    tool.get_now_time_str()
    for d in result["list"]:
        print(d)
    print("行业涨幅", getStockIDPlate("000333"))
    # print("强度", getSonPlate("801270"))
    print(getMarketJingXuanRealRankingInfo(True))
main.py
@@ -1,1581 +1,316 @@
import json
import os
import socket
import sys
import time
from multiprocessing import freeze_support
import cv2
import torch
import win32con
import win32gui
from matplotlib.lines import Line2D
import ocr_util
import opencv_util
import setting
import ths_util
import win32_util
freeze_support()
import logging
import multiprocessing
import threading
import time
from multiprocessing import freeze_support
import sys
import torch
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMessageBox
import wx
import wx.html2 as webview
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtCore import pyqtSlot, QObject, pyqtSignal, QTimer, QUrl, QPoint
import code_data_manager
import juejin_core
import tool
import requests
import gui_wx
import network_util
import setting
APP_TITLE = "卖票看板"
APP_ICON = ""
SERVER_HOST = "192.168.3.252"
SOCKET_PORT = 9001
HTTP_PORT = 9004
freeze_support()
def show_warning(content, click):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_WARNING)
    if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
        if click is not None:
            click()
        toastone.Destroy()
class JSBridgeClass(QObject):
    signal_request = pyqtSignal(str, str, str)
    def __request_result_callback(self, method, key, result):
        self.webview.page().runJavaScript(f"{method}('{key}','{result}')")
def show_info(content, click):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
    if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
        click()
        toastone.Destroy()
    def __init__(self, window, webview):
        super().__init__()
        self.window = window
        self.webview = webview
        self.signal_request.connect(self.__request_result_callback)
def show_sure(content, callback):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_NO | wx.ICON_INFORMATION)
    result = toastone.ShowModal()
    if result == wx.ID_YES:  # 如果点击了提示框的确定按钮
        callback(True)
        toastone.Destroy()
    elif result == wx.ID_NO:
        callback(False)
        toastone.Destroy()
class JueJinSettingFrame(wx.Frame):
    def __init__(self, position):
        wx.Frame.__init__(self, None, -1, "掘金参数设置", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(450, 200))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer()
        flex = wx.FlexGridSizer(rows=3, cols=2, vgap=10, hgap=10)
        # 策略
        label = wx.StaticText(self, label="策略ID:")
        flex.Add(label, flag=wx.ALIGN_RIGHT)
        self.edit_celue = wx.TextCtrl(self, size=(300, -1))
        flex.Add(self.edit_celue, flag=wx.EXPAND)
        # token
        label = wx.StaticText(self, label="Token:")
        flex.Add(label)
        self.edit_token = wx.TextCtrl(self, size=(300, -1), style=wx.TE_MULTILINE)
        flex.Add(self.edit_token)
        # 占位
        flex.Add(wx.StaticText(self, label=""))
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        flex.Add(self.btn_sure, 1, wx.TOP | wx.LEFT, 20)
        boxsier.Add(flex, 1, wx.TOP | wx.LEFT, 20)
        self.SetSizer(boxsier)
        # 初始化数据
        self.__init_data()
    def __init_data(self):
        strategy_id, token = setting.get_juejin_params()
        self.edit_celue.SetLabelText(strategy_id)
        self.edit_token.SetLabelText(token)
        pass
    def __sure_btn(self, event):
        strategy_id = self.edit_celue.GetValue()
        token = self.edit_token.GetValue()
        setting.set_juejin_params(strategy_id, token)
        toastone = wx.MessageDialog(None, "更改成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
class CodesSettingFrame(wx.Frame):
    def __init__(self, position, callback):
        wx.Frame.__init__(self, None, -1, "代码设置", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(170, 300))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer(wx.VERTICAL)
        # 代码
        label = wx.StaticText(self, label="目标代码:")
        boxsier.Add(label)
        self.edit_codes = wx.TextCtrl(self, size=(150, 200), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes)
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        boxsier.Add(self.btn_sure)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
        # 初始化数据
        self.__init_data()
        self.callback = callback
    def __init_data(self):
        codes = juejin_core.GPCodeManager().get_codes()
        self.edit_codes.SetValue("\n".join(codes))
        pass
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        codes = codes_str.split("\n")
        codes_result = []
        for code in codes:
            if code.strip():
                codes_result.append(code.strip())
        juejin_core.GPCodeManager().set_codes(codes_result)
        # 重新订阅
        self.callback()
        toastone = wx.MessageDialog(None, "更改成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
class FobiddenCodesFrame(wx.Frame):
    def __init__(self, position):
        wx.Frame.__init__(self, None, -1, "添加禁止交易代码", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(170, 200))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer(wx.VERTICAL)
        # 代码
        label = wx.StaticText(self, label="代码:")
        boxsier.Add(label, flag=wx.ALIGN_LEFT, border=10)
        self.edit_codes = wx.TextCtrl(self, size=(150, 100), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes)
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        boxsier.Add(self.btn_sure)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
    def __request(self, codes):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 201, "data": {"codes": codes}}
        client.send(json.dumps(data).encode("utf-8"))
        client.close()
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        if codes_str:
            codes_str = codes_str.strip()
        codes = codes_str.split("\n")
        codes_result = []
        for code in codes:
            if code.strip() and len(code.strip()) == 6:
                codes_result.append(code.strip())
        if len(codes_result) == 0:
            show_warning("请填写正确的代码!", self.Close)
            return
    def __http_request(self, url,callback_info):
        try:
            self.__request(codes_result)
            result = network_util.http_get(url)
            print("请求结果:", result)
            self.signal_request.emit(callback_info[0],callback_info[1],result)
            return result
        except Exception as e:
            show_warning("添加出错:" + str(e), None)
            return
            logging.exception(e)
        toastone = wx.MessageDialog(None, "添加成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
class THSPositionSettingFrame(wx.Frame):
    def __init__(self, position):
        wx.Frame.__init__(self, None, -1, "同花顺坐标设置", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(170, 400))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetPosition(wx.Point(position[0] - self.GetSize()[0] / 2, position[1] - self.GetSize()[1] / 2))
        boxsier = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label="交易刷新事件间隔(ms):")
        boxsier.Add(label)
        self.edit_refresh_time_space = wx.TextCtrl(self, size=(150, -1))
        boxsier.Add(self.edit_refresh_time_space)
        label = wx.StaticText(self, label="点击时间间隔(ms):")
        boxsier.Add(label)
        self.edit_time_space = wx.TextCtrl(self, size=(150, -1))
        boxsier.Add(self.edit_time_space)
        # 代码
        label = wx.StaticText(self, label="坐标:")
        boxsier.Add(label)
        self.edit_codes = wx.TextCtrl(self, size=(150, 200), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes)
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        boxsier.Add(self.btn_sure)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
        # 初始化数据
        self.__init_data()
    def __init_data(self):
        pos = setting.get_ths_auto_click_positions()
        self.edit_codes.SetValue("\n".join(pos))
        space = setting.get_ths_auto_click_time_space()
        if space is None:
            space = 500
        self.edit_time_space.SetValue(f"{space}")
        space = setting.get_ths_auto_refresh_time_space()
        if space is None:
            space = 500
        self.edit_refresh_time_space.SetValue(f"{space}")
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        ps = codes_str.split("\n")
        result = []
        for p in ps:
            if p.strip():
                result.append(p.strip())
        setting.set_ths_auto_click_positions(result)
        time_space = self.edit_time_space.GetValue()
        if len(time_space) == 0:
            show_warning("点击时间间隔不正确", None)
            return
        refresh_time_space = self.edit_refresh_time_space.GetValue()
        if len(refresh_time_space) == 0:
            show_warning("刷新时间间隔不正确", None)
            return
        setting.set_ths_auto_click_time_space(time_space)
        setting.set_ths_auto_refresh_time_space(refresh_time_space)
        toastone = wx.MessageDialog(None, "更改成功", "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            self.Close()
            toastone.Destroy()
last_ocr_code = ["000000"]
import log
def ocr_ths_code():
    hwnd = ths_util.get_ths_main_content_hwnd()
    if not hwnd:
        raise Exception("看盘页面句柄未获取到")
    # 句柄截图
    rect = win32gui.GetWindowRect(hwnd)
    # hwnd_width = (rect[2] - rect[0]) * 15 // 10
    rect_ = setting.get_ths_auto_code_rect()
    img = win32_util.window_capture(hwnd, (0, rect_[0], rect_[1], rect_[0] + rect_[2]))
    clip_img, details = opencv_util.clip_ths_code_area(img)
    start_time = time.time()
    code = ocr_util.recognize_code(clip_img)
    use_time =round( (time.time()-start_time)*1000)
    if code is None:
        code = ocr_util.recognize_code(img)
    if code != last_ocr_code[0]:
        print("保存图片", code)
        log.debug_logger.info(f"代码识别结果:{code} 识别时间:{use_time}ms")
        last_ocr_code[0] = code
        cv2.imwrite(f"datas/test/{code}.png", opencv_util.gray_img(img))
    return code
# 悬浮框
class FloatFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "悬浮盯盘", style=wx.CAPTION ^ wx.MINIMIZE_BOX ^ wx.CLOSE_BOX ^ wx.STAY_ON_TOP,
                          size=(320, 195))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetTransparent(230)
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        # 读取配置信息
        window_info = setting.get_float_watch_window_info()
        if window_info:
            self.SetPosition(wx.Point(window_info[0], window_info[1]))
            self.Size = wx.Size(window_info[2], window_info[3])
        boxsier = wx.FlexGridSizer(5, 2, 2, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_remove_black = wx.Button(self, label="移除黑名单", size=(70, 30))
        self.btn_close_buy = wx.Button(self, label="关闭交易", size=(70, 30))
        self.btn_close_buy.SetForegroundColour("#FF3232")
        bs1.Add(self.btn_remove_black)
        bs1.Add(self.btn_close_buy)
        boxsier.Add(bs1, 0, wx.TOP, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_remove_white = wx.Button(self, label="移除白名单", size=(70, 30))
        self.btn_open_buy = wx.Button(self, label="开启交易", size=(70, 30), )
        self.btn_open_buy.SetForegroundColour("#00e600")
        bs1.Add(self.btn_open_buy, 0, wx.TOP, 5)
        bs1.Add(self.btn_remove_white, 0, wx.TOP, 5)
        boxsier.Add(bs1, 0, wx.LEFT, 0)
        label = wx.StaticText(self, label="交易刷新:")
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        bs1.Add(label)
        self.check_auto_refresh = wx.CheckBox(self, size=(-1, -1))
        bs1.Add(self.check_auto_refresh, 0, wx.LEFT, 10)
        boxsier.Add(bs1, 0, wx.TOP, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.want_list = wx.Button(self, label="想买单", size=(45, 20))
        bs1.Add(self.want_list, 0, wx.TOP, 2)
        self.btn_want_buy = wx.Button(self, label="加入想买单", size=(70, 25))
        bs1.Add(self.btn_want_buy)
        boxsier.Add(bs1, 0, wx.LEFT, 25)
        label = wx.StaticText(self, label="分组刷新:")
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        bs1.Add(label)
        self.check_auto_click = wx.CheckBox(self, size=(-1, -1))
        bs1.Add(self.check_auto_click, 0, wx.LEFT, 10)
        boxsier.Add(bs1)
        self.btn_want_buy_remove = wx.Button(self, label="移除想买单", size=(70, 25))
        boxsier.Add(self.btn_want_buy_remove, 0, wx.LEFT, 70)
        self.edit_code = wx.TextCtrl(self, size=(80, -1))
        boxsier.Add(self.edit_code)
        self.notify_text = wx.StaticText(self, label="", size=(80, -1))
        boxsier.Add(self.notify_text, 0, wx.LEFT, -45)
        # 代码
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_black = wx.Button(self, label="加入黑名单", size=(70, 30))
        bs1.Add(self.btn_black)
        self.black_list = wx.Button(self, label="黑名单", size=(45, 20))
        self.black_list.SetForegroundColour("#00e600")
        bs1.Add(self.black_list, 0, wx.CENTER | wx.ALL, 0)
        boxsier.Add(bs1, 0, wx.LEFT, 0)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_white = wx.Button(self, label="加入白名单", size=(70, 30))
        self.white_list = wx.Button(self, label="白名单", size=(45, 20))
        self.white_list.SetForegroundColour("#FF3232")
        bs1.Add(self.white_list, 0, wx.CENTER | wx.ALL, 0)
        bs1.Add(self.btn_white, 0, wx.LEFT, 2)
        boxsier.Add(bs1, 0, wx.LEFT, 22)
        # 绑定
        self.btn_open_buy.Bind(wx.EVT_BUTTON, self.__open_buy)
        self.btn_close_buy.Bind(wx.EVT_BUTTON, self.__close_buy)
        self.btn_black.Bind(wx.EVT_BUTTON, self.add_black)
        self.btn_white.Bind(wx.EVT_BUTTON, self.add_white)
        self.btn_remove_black.Bind(wx.EVT_BUTTON, self.remove_from_black)
        self.btn_remove_white.Bind(wx.EVT_BUTTON, self.remove_from_white)
        self.btn_want_buy.Bind(wx.EVT_BUTTON, self.add_want)
        self.btn_want_buy_remove.Bind(wx.EVT_BUTTON, self.remove_from_want)
        self.check_auto_click.Bind(wx.EVT_CHECKBOX, self.__auto_click_check)
        self.check_auto_refresh.Bind(wx.EVT_CHECKBOX, self.__auto_refresh_check)
        self.white_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "白名单列表", 302))
        self.black_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "黑名单列表", 301))
        self.want_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "想要买列表", 403))
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.LEFT, 10)
        self.SetSizer(root_boxsier)
        self.__bind_hot_keys()
        # 初始化数据
        self.__init_data()
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.clear_msg, self.timer)
    def clear_msg(self, event):
        self.notify_text.SetLabelText("")
    def __ocr_code(self):
        code = self.edit_code.GetValue()
        if code is not None and len(code.strip()) == 0:
            code = None
        if code is not None:
            if len(code) != 6:
                self.show_warning("请填写正确的代码")
                return
            else:
                return
        code = ocr_ths_code()
        if code is None:
            raise Exception("代码识别出错")
        self.edit_code.SetValue(code)
    def show_warning(self, content):
        self.notify_text.SetLabel(content)
        self.notify_text.SetForegroundColour("#FF7F27")
        self.timer.Stop()
        self.timer.StartOnce(20000)
    def show_info(self, content):
        self.notify_text.SetLabel(content)
        self.notify_text.SetForegroundColour("#008000")
        self.timer.Stop()
        self.timer.StartOnce(20000)
    def __get_code(self):
        self.__ocr_code()
        code = self.edit_code.GetValue()
        if code is None or len(code) != 6:
            raise Exception("请填写正确的代码")
        return code
    def add_black(self, event):
        try:
            code = self.__get_code()
            print("加入黑名单", code)
            self.__request([code], 201)
            self.show_info(f"{code}加入黑名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def add_white(self, event):
        try:
            code = self.__get_code()
            print("加入白名单", code)
            result = self.__request([code], 202)
            result = json.loads(result)
            if result["code"] == 0:
                self.show_info(f"{code}加入白名单成功")
            else:
                self.show_warning(f"加入失败:{result['msg']}")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def add_want(self, event):
        try:
            code = self.__get_code()
            print("加入想要买", code)
            self.__request([code], 401)
            self.show_info(f"{code}加入想要买名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def remove_from_black(self, event):
        try:
            code = self.__get_code()
            print("移除黑名单", code)
            self.__request([code], 203)
            self.show_info(f"{code}移除黑名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def remove_from_white(self, event):
        try:
            code = self.__get_code()
            print("移除白名单", code)
            self.__request([code], 204)
            self.show_info(f"{code}移除白名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def remove_from_want(self, event):
        try:
            code = self.__get_code()
            print("移除想要买名单", code)
            self.__request([code], 402)
            self.show_info(f"{code}移除想要买名单成功")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
            return
    def show_list(self, event, title, type):
        try:
            result = self.__request_list(type)
            result = json.loads(result)
            self.__show_list(title, result["data"])
        except Exception as e:
            show_warning(str(e), None)
    def __show_list(self, title, datas):
        st = ""
        for i in range(0, len(datas)):
            st += datas[i]
            if i % 2 == 1 and i != len(datas) - 1:
                st += "\n"
            elif i != len(datas) - 1:
                st += " , "
        toastone = wx.MessageDialog(None, st, title)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            toastone.Destroy()
    def __bind_hot_keys(self):
        # 快捷键
        setting_ = wx.Menu()
        m_black = wx.MenuItem(setting_, id=101, text='&E', kind=wx.ITEM_NORMAL)
        m_white = wx.MenuItem(setting_, id=102, text='&E', kind=wx.ITEM_NORMAL)
        self.Bind(wx.EVT_MENU, self.add_black, m_black)
        self.Bind(wx.EVT_MENU, self.add_white, m_white)
        entries = [wx.AcceleratorEntry() for i in range(2)]
        entries[0].Set(wx.ACCEL_CTRL, wx.WXK_F4, 101)
        entries[1].Set(wx.ACCEL_CTRL, wx.WXK_F5, 102)
        accel = wx.AcceleratorTable(entries)
        self.SetAcceleratorTable(accel)
    def __init_data(self):
        auto_click = setting.is_ths_auto_click()
        if auto_click:
            self.check_auto_click.SetValue(True)
        else:
            self.check_auto_click.SetValue(False)
        auto_refresh = setting.is_ths_trade_auto_refresh()
        if auto_refresh:
            self.check_auto_refresh.SetValue(True)
        else:
            self.check_auto_refresh.SetValue(False)
        self.__init_trade_state()
    def __init_trade_state(self):
        # 获取交易状态
        try:
            result = self.__request_buy_state()
            result = json.loads(result)
            if result["code"] == 0:
                if result["data"]["can_buy"]:
                    self.btn_open_buy.SetLabelText("开启交易*")
                    self.btn_close_buy.SetLabelText("关闭交易")
                else:
                    self.btn_open_buy.SetLabelText("开启交易")
                    self.btn_close_buy.SetLabelText("关闭交易*")
        except:
            pass
    def __auto_click_check(self, event):
        if event.Selection:
            setting.set_ths_auto_click(True)
        else:
            setting.set_ths_auto_click(False)
    def __auto_refresh_check(self, event):
        if event.Selection:
            setting.set_ths_trade_auto_refresh(True)
        else:
            setting.set_ths_trade_auto_refresh(False)
    def __request(self, codes, type):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {"codes": codes}}
        client.send(json.dumps(data).encode("utf-8"))
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    def __request_list(self, type):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(10240)
        client.close()
        return result.decode("gbk")
    def __request_buy(self, is_open):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 501, "data": {"open": is_open}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(10240)
        client.close()
        return result.decode("gbk")
    # 获取买入状态
    def __request_buy_state(self):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 502, "data": {}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    def __open_buy(self, event):
        def open_buy(sure):
            if sure:
                try:
                    result = self.__request_buy(True)
                    msg = json.loads(result)["msg"]
                    show_info(msg, None)
                    self.__init_trade_state()
                except Exception as e:
                    show_warning(str(e), None)
        show_sure("是否开启交易", open_buy)
    def __close_buy(self, event):
        def close_buy(sure):
            if sure:
                try:
                    result = self.__request_buy(False)
                    msg = json.loads(result)["msg"]
                    show_info(msg, None)
                    self.__init_trade_state()
                except Exception as e:
                    show_warning(str(e), None)
        show_sure("是否关闭交易", close_buy)
    def OnExit(self, e):
        try:
            setting.set_float_watch_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        self.Hide()
# 代码数据
class CodeDataFrame(wx.Frame):
    def __init__(self, set_codes_success_callback, show_float_callback, show_main_callback):
        # style=wx.CAPTION |
        wx.Frame.__init__(self, None, -1, "数据",
                          style=wx.CAPTION | wx.STAY_ON_TOP | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.CLOSE_BOX | wx.RESIZE_BORDER,
                          size=(320, 195))
        self.set_codes_success = set_codes_success_callback
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        # self.SetTransparent(230)
        boxsier = wx.BoxSizer(wx.VERTICAL)
        # ---配置浏览器----
        webview.WebView.MSWSetEmulationLevel()
        backend = None
        backends = [
            (webview.WebViewBackendEdge, 'WebViewBackendEdge'),
            (webview.WebViewBackendIE, 'WebViewBackendIE'),
            (webview.WebViewBackendWebKit, 'WebViewBackendWebKit'),
            (webview.WebViewBackendDefault, 'WebViewBackendDefault'),
        ]
        for id, name in backends:
            available = webview.WebView.IsBackendAvailable(id)
            if available and backend is None:
                backend = id
        self.wv = webview.WebView.New(self, size=wx.Size(self.Size[0], self.Size[
            1]), backend=backend)
        self.wv.SetZoomType(wx.html2.WEBVIEW_ZOOM_TYPE_TEXT)
        self.Bind(webview.EVT_WEBVIEW_NAVIGATING, self.OnWebViewNavigating, self.wv)
        self.Bind(webview.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.wv)
        # 执行定时更新
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.__update_kpl, self.timer)
        self.timer.Start(3000)
        self.count = 0
        self.wv.SetBackgroundColour(wx.Colour(224, 224, 224))
        if "gtk2" in wx.PlatformInfo:
            self.wv.SetStandardFonts()
        boxsier.Add(self.wv, 1, wx.EXPAND)
        self.latest_code = ''
        self.SetSizer(boxsier)
        window_info = setting.get_xgb_window_info()
        if window_info:
            self.SetPosition(wx.Point(window_info[0], window_info[1]))
            self.Size = wx.Size(window_info[2], window_info[3])
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        self.Bind(wx.EVT_SIZE, self.OnResize)
        self.wv.SetPage("", "")
        self.__menu()
        self.show_float_callback = show_float_callback
        self.show_main_callback = show_main_callback
    def OnWebViewNavigating(self, evt):
        # this event happens prior to trying to get a resource
        if evt.GetURL().find("cancel_order://") >= 0:
            code__ = evt.GetURL().split("cancel_order://")[1]
            # if wx.MessageBox("是否要撤单?",
            #                  style=wx.YES_NO | wx.ICON_QUESTION) == wx.YES:
            self.__cancel_order(code__)
            evt.Veto()
    def OnWebViewLoaded(self, evt):
        # pass
        # 加载开盘啦数据
        self.__update_kpl(None)
    # 更新开盘啦数据
    def __update_kpl(self, evt):
        response = requests.get(f"http://{SERVER_HOST}:{HTTP_PORT}/get_kpl_data")
        if response.status_code == 200:
            print(response.text)
            with open("res/kpl.js", mode="r", encoding="utf-8") as f:
                lines = f.readlines()
                script = "\n".join(lines)
                script = script.replace("{}", response.text)
                self.wv.RunScript(script)
    def __cancel_order(self, code):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        try:
            client.connect(ip_port)
            data = {"type": 80, "data": {"code": code}}
            client.send(json.dumps(data).encode("utf-8"))
            result = client.recv(1024)
            result = result.decode("utf-8")
            result = json.loads(result)
            if result["code"] == 0:
                # show_info("撤单处理成功", None)
                # 刷新数据
                self.__init_data(self.latest_code)
            else:
                show_warning(result["msg"], None)
            client.close()
        except Exception as e:
            show_warning(str(e), None)
    def OnWebViewError(self, evt):
        # The full document has loaded
        print("网页出错")
    def handleMessage(self, event):
        print("数据回调")
    def set_data(self, code, data):
        self.wv.SetPage(data, "")
    # 设置代码
    def set_code(self, code):
        if self.latest_code == code:
            return
        self.latest_code = code
        t1 = threading.Thread(target=lambda: self.__init_data(code))
    @pyqtSlot(str, str, str)
    def http_request(self, path, params, callback_info):
        url = path + "?"
        if params:
            params = json.loads(params)
            url += "&".join([f"{key}={params[key]}" for key in params])
        print("http请求", url)
        callback_info = json.loads(callback_info)
        t1 = threading.Thread(target=lambda: self.__http_request(url, callback_info))
        t1.setDaemon(True)
        t1.start()
    def __init_data(self, code):
        if code:
            # 请求网络
            result_data = self.__request(code)
            if result_data:
                result_json = json.loads(result_data)
                if result_json['code'] == 0:
                    code = result_json['data']['code']
                    data = result_json['data']['data']
                    wx.CallAfter(self.set_data, code, data)
    @pyqtSlot(str, result=str)
    def socket_request(self, text):
        print("socket_request", text)
        return network_util.socket_request(json.loads(text))
    def __request(self, code, type=71):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {"code": code}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(102400)
        client.close()
        return result.decode("gbk")
    @pyqtSlot(str)
    def show_info(self, msg):
        QMessageBox.information(self.window, "提示", msg, QMessageBox.Yes)
    def OnExit(self, e):
        try:
            setting.set_xgb_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        jueJinProcess.terminate()
        sys.exit(0)
    @pyqtSlot(str, str, str, str)
    def set_trade_info(self, code, name, trade_data, trade_record):
        self.window.set_trade_data(code, name, trade_data, trade_record)
    def OnResize(self, e):
        print("变化后的尺寸", e.Size)
        self.wv.Size = e.Size
    def __menu(self):
        self.mainmenu = wx.MenuBar()
        setting_ = wx.Menu()
        # setting_.Append(104, '&钉住', '', kind=wx.ITEM_CHECK)
        # setting_.Check(104, setting.is_stay_on_top())
        setting_.AppendSeparator()
        setting_.Append(101, '&掘金参数配置', 'Open a new document')
        setting_.AppendSeparator()
        setting_.Append(103, '&代码管理', 'Open a new document')
        setting_.Append(102, '&下载涨停代码', 'Open a new document')
        setting_.AppendSeparator()
        setting_.Append(104, '&GPU检测', 'Open a new document')
        self.mainmenu.Append(setting_, '&设置')
        auto_help = wx.Menu()
        auto_help.Append(202, '&同花顺设置', '')
        self.mainmenu.Append(auto_help, '&自动化')
        view = wx.Menu()
        view.Append(301, '&打开悬浮盯盘', '')
        view.Append(302, '&打开分时看盘', '')
        self.mainmenu.Append(view, '&视图')
        # 设置事件
        self.Bind(wx.EVT_MENU, self.__set_juejin_params, id=101)
        self.Bind(wx.EVT_MENU, self.__download_codes, id=102)
        self.Bind(wx.EVT_MENU, self.__manage_code, id=103)
        self.Bind(wx.EVT_MENU, self.__gpu_is_avaiable, id=104)
        self.Bind(wx.EVT_MENU, self.__manage_ths_pos, id=202)
        self.Bind(wx.EVT_MENU, self.__show_float_callback, id=301)
        self.Bind(wx.EVT_MENU, self.__show_main_callback, id=302)
        self.SetMenuBar(self.mainmenu)
    def __gpu_is_avaiable(self,event):
        if torch.cuda.is_available():
            show_info("GPU可用", None)
        else:
            show_warning("GPU不可用", None)
    def __set_juejin_params(self, event):
        ps = self.GetPosition()
        size = self.GetSize()
        JueJinSettingFrame((ps[0] + size[0] / 2, ps[1] + size[1] / 2)).Show()
    def __download_codes(self, event):
        try:
            result = self.__request("", 72)
            result = json.loads(result)
            if result['code'] == 0:
                codes_dict = result['data']
                if codes_dict:
                    for key in codes_dict:
                        # 将代码保存到桌面
                        path = f"C:\\Users\\Administrator\\Desktop\\涨停代码{key}.txt"
                        with open(path, mode='w') as f:
                            for c in codes_dict[key]:
                                f.write(c)
                                f.write("\n")
                    show_info("下载成功", None)
            else:
                show_warning(result['msg'], None)
        except Exception as e:
            show_warning(str(e), None)
    def __manage_code(self, event):
        ps = self.GetPosition()
        size = self.GetSize()
        CodesSettingFrame((ps[0] + size[0] / 2, ps[1] + size[1] / 2), self.set_codes_success).Show()
    def __manage_ths_pos(self, event):
        ps = self.GetPosition()
        size = self.GetSize()
        THSPositionSettingFrame((ps[0] + size[0] / 2, ps[1] + size[1] / 2)).Show()
    def __show_float_callback(self, event):
        self.show_float_callback()
    def __show_main_callback(self, event):
        self.show_main_callback()
    @pyqtSlot(str, str)
    def show_want_codes(self, plate, codes):
        WantBuyWindow(plate, json.loads(codes)).show()
class TickFrame(wx.Frame):
    def __init__(self):
        '''构造函数'''
        wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE,
                          size=(800, 500))
        # ^ wx.RESIZE_BORDER ^ wx.STAY_ON_TOP
        # 默认style是下列项的组合:wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
        self.SetBackgroundColour(wx.Colour(0, 0, 0))
        win_info = setting.get_tick_window_info()
        if win_info:
            self.SetPosition(wx.Point(win_info[0], win_info[1]))
            self.Size = wx.Size(win_info[2], win_info[3])
        else:
            self.Center()
class WantBuyWindow(QMainWindow):
    def __init__(self, plate, codes):
        super(WantBuyWindow, self).__init__()
        self.setWindowTitle(f'想买单({plate})')
        self.resize(300, 200)
        # 以下代码处理图标
        # if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
        #     exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
        #     icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        # else:
        #     icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        # self.SetIcon(icon)
        # 定义窗口关闭
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        self.Bind(wx.EVT_SIZE, self.OnResize)
        self.panels = []
        self.scroll = None
        self.mark_lines = {}
        self.col = 1
        self.ratio = 0.63
        self.__re_draw()
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.post_redraw, self.timer)  # 绑定一个定时器事件
        self.last_size = (self.Size[0], self.Size[1])
class SecondWindow(QMainWindow):
    signal_update_kpl = pyqtSignal(str)
        # self.scroll.Layout()
        # self.boxsier.Fit(self.scroll)
        # boxsier.Add(mainBoxsier, 1, wx.EXPAND | wx.ALL, 5)
        # self.SetSizer(boxsier)
        # mainBoxsier.Fit(self)
        if setting.is_stay_on_top():
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.STAY_ON_TOP
    def scrollTo(self, pos):
        self.scroll.Scroll(0, pos)
    def __re_draw(self):
        codes_name = juejin_core.GPCodeManager().get_codes_with_names()
        rows = len(codes_name)
        if rows % self.col == 0:
            rows = rows / self.col
        else:
            rows = rows / self.col + 1
        space = 0
        if self.scroll is None:
            self.scroll = wx.ScrolledWindow(self, -1, size=(800, 1000))
            self.boxsier = wx.FlexGridSizer(rows, self.col, space, space)
            self.scroll.SetSizer(self.boxsier)
        self.scroll.EnableScrolling(False, True)
        if self.panels:
            for p in self.panels:
                p.Destroy()
        self.boxsier.Clear()
        pannel_height = round((self.Size[0] - (self.col - 1) * space) / self.col * self.ratio)
        self.scroll.SetScrollbars(1, 1, 0, pannel_height * rows)
        self.scroll.SetScrollRate(0, pannel_height)
        global drawManager
        axes_list = []
        self.panels = []
        for i in range(0, len(codes_name)):
            # pos=(0, i * pannel_height)
            pannel = wx.Panel(self.scroll, size=(-1, pannel_height))
            pannel.BackgroundColour = wx.Colour(0, 0, 0)
            self.panels.append(pannel)
            self.boxsier.Add(pannel)
            axes = self.__create_canvas(pannel, "{}({})".format(codes_name[i][0], codes_name[i][1]), codes_name[i][1],
                                        codes_name[i][2])
            axes_list.append(axes)
        self.scroll.Layout()
        # self.boxsier.Fit(self.scroll)
        #
        # 初始化数据
        drawManager = DrawManager(axes_list, codes_name)
        t1 = threading.Thread(target=lambda: drawManager.init_code_datas())
        # 后台运行
        t1.setDaemon(True)
        t1.start()
    def __create_canvas(self, pannel, title, name, price, close_callback=None):
        def show_mouse_line(event):
            # 删除之前的线
            if title in self.mark_lines:
                if self.mark_lines.get(title):
                    line = self.mark_lines.get(title).get("mouse")
                    if line is not None:
                        line.remove()
                        self.mark_lines.get(title).pop("mouse")
            else:
                self.mark_lines[title] = {}
    def update_kpl_func(self):
        while True:
            time.sleep(3)
            try:
                line = axes2.axhline(event.ydata, linestyle='-', color='white', lw=0.5)
                self.mark_lines[title]["mouse"] = line
                axes2.figure.canvas.draw()
                self.__update_kpl()
            except:
                pass
        def clear_mouse_line(event):
            print("clear_mouse_line")
            if title in self.mark_lines:
                if self.mark_lines.get(title):
                    line = self.mark_lines.get(title).get("mouse")
                    if line is not None:
                        line.remove()
                        self.mark_lines.get(title).pop("mouse")
                        axes2.figure.canvas.draw()
        def close_canvas(event):
            print("关闭", title)
            close_callback(title)
        dpi = 100
        width_dpi = self.Size[0] / (dpi * self.col)
        figure_score = Figure(figsize=(width_dpi, round(width_dpi * (self.ratio), 2)), dpi=dpi)
        # 设置外边距
        right_padding_px = 85
        right = round((self.Size[0] - right_padding_px) / self.Size[0], 4)
        figure_score.subplots_adjust(left=0.01, bottom=0.15, top=0.92,
                                     right=right)
        # 设置字体颜色
        plt.rcParams["text.color"] = "red"
        plt.rcParams["axes.labelcolor"] = "red"
        # 设置坐标轴数字颜色
        plt.rcParams["xtick.color"] = "white"
        plt.rcParams["ytick.color"] = "white"
        # 设置坐标轴颜色
        plt.rcParams["axes.edgecolor"] = "firebrick"
        # 解决中文乱码问题
        plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体
        plt.rcParams["font.serif"] = ["SimHei"]
        plt.rcParams["axes.unicode_minus"] = False  # 该语句解决图像中的“-”负号的乱码问题
        # 测试
        buttonaxe = plt.axes([0.94, 0.5, 0.1, 0.1])
        button1 = Button(buttonaxe, '关闭', color='white', hovercolor='yellow')
        axes = figure_score.add_subplot(1, 1, 1)
        axes.autoscale(True)
        # axes_score.plot(t_score, s_score, 'ro', t_score, s_score, 'k')
        axes.set_title(title)
        axes.grid(color='firebrick', ls='-', lw=0.5)
        axes.set_xlabel(f'时间({name})')
        axes.dist = 0
        # axes.set_ylabel(u'价格')
        # 获取平开价
        extra = 0  # (tool.get_limit_up_price(price)-decimal.Decimal(price))*decimal.Decimal(0.02)
        axes.set_ylim(tool.get_limit_down_price(price) - extra, tool.get_limit_up_price(price) + extra)
        axes.patch.set_facecolor('black')
        figure_score.patch.set_facecolor('black')
        axes2 = axes.twinx()
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        axes2.grid(False)
        # 鼠标在画布移动
        axes2.figure.canvas.mpl_connect('motion_notify_event', show_mouse_line)
        # 鼠标离开画布
        axes2.figure.canvas.mpl_connect('axes_leave_event', clear_mouse_line)
        # 设置纵坐标轴
        limit_up_price = float(tool.get_limit_up_price(price))
        max_rate = round((limit_up_price - price) / price, 4) * 100
        print("涨停最大比例", max_rate)
        yticks2 = []
        for i in range(0, 41):
            if i >= 20:
                yticks2.append(0 - round(max_rate * (20 - i) / 20, 4))
            else:
                yticks2.append(round(max_rate * (i - 20) / 20, 4))
        yticks2_labels = []
        yticks = []
        yticks_labels = []
        for i in range(0, len(yticks2)):
            if i % 2 == 0:
                yticks2_labels.append("{}%".format(abs(round(yticks2[i], 2))))
            else:
                yticks2_labels.append("")
            price_ = round((1 + yticks2[i] / 100) * price, 2)
            yticks.append(price_)
            if i % 2 == 0:
                yticks_labels.append("")
                # yticks_labels.append(round(yticks[i], 2))
                if i == 20:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=2)
                else:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=1.2)
            else:
                # axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=0.5)
                yticks_labels.append("")
        # 加粗中轴线
        axes2.set_ylabel(u'')
        # 设置纵轴的值的范围
        axes2.set_ylim(0 - max_rate * (1), max_rate * (1))
        axes2.set_yticks(yticks2)
        axes2.set_yticklabels(yticks2_labels)
        axes.set_yticks(yticks)
        axes.set_yticklabels(yticks_labels)
        # 设置纵坐标数值颜色
        for i in range(0, 19):
            axes.get_yticklabels()[i].set_color("green")
            axes2.get_yticklabels()[i].set_color("green")
        for i in range(20, 20):
            axes.get_yticklabels()[i].set_color("white")
            axes2.get_yticklabels()[i].set_color("white")
        for i in range(21, 41):
            axes.get_yticklabels()[i].set_color("red")
            axes2.get_yticklabels()[i].set_color("red")
        line = axes2.plot([], [], color='white', linewidth=1)
        average_line = axes2.plot([], [], color='green', linewidth=1, linestyle='-')
        average_line_1m = axes2.plot([], [], color='yellow', linewidth=1, linestyle='-')
        # axes2.legend(loc='upper left')
        cannvas = FigureCanvas(pannel, -1, figure_score)
        axes2.text(1, 11.5, r'现价:0.0 涨幅:0.00% \n留格局:0%', fontsize=10, color='white')
        axes2.text(-1, -11.5, r'现价:0.0 涨幅:0.00% \n留格局:0%', fontsize=10, color='white')
        axes2.spines['top'].set_visible(False)
        axes.spines['top'].set_visible(False)
        axes2.spines['bottom'].set_visible(False)
        axes.spines['bottom'].set_visible(False)
        return (axes2, line, average_line_1m, average_line)
    def __show_top(self, event):
        if event.Selection:
            setting.set_stay_on_top(1)
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.STAY_ON_TOP
    def __init__(self, parent=None):
        super(SecondWindow, self).__init__(parent)
        self.setWindowTitle('看盘副屏')
        window_info = setting.get_kp_second_window_info()
        if window_info:
            self.move(window_info[0], window_info[1])
            self.resize(window_info[2], window_info[3])
        else:
            setting.set_stay_on_top(0)
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
            self.resize(500, 800)
    def OnExit(self, e):
        try:
            setting.set_tick_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        self.Hide()
        self.webview = QWebEngineView()
        self.webview.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
        self.webview.page().setZoomFactor(1)
        self.setCentralWidget(self.webview)
    def post_redraw(self, evt):
        self.signal_update_kpl.connect(self.set_kpl_data)
        if abs(self.last_size[0] - self.Size[0]) > 20:
            print("--------post_redraw--------")
            self.last_size = (self.Size[0], self.Size[1])
            self.__re_draw()
    def OnResize(self, e):
        print("变化后的尺寸", e.Size)
        # 留出滚动条,留出上边距
        if self.scroll:
            self.scroll.Size = (e.Size[0] - 15, e.Size[1] - 60)
            for p in self.panels:
                p_height = round(e.Size[0] * (450 / 800))
                p.Size = (e.Size[0], p_height)
        self.timer.Stop()
        self.timer.StartOnce(1000)
        # 降低重绘频率
        # self.__re_draw()
    def set_codes_success(self):
        print("设置代码成功回调")
        p2.send("resub")
        self.__re_draw()
# 绘图管理器
class DrawManager:
    X_RANGE_MINIUTES = 60
    X_DATA_MINIUTES = 56
    def __load_lack_datas(self, code, time_ranges):
        codeDataManager = code_data_manager.CodeDataManager()
        day = tool.get_now_date_str()
        for time_range in time_ranges:
            results = juejin_core.GPCodeManager().get_history_tick(code, day + " " + time_range[0],
                                                                   day + " " + time_range[1])
            datas = []
            for data in results:
                datas.append(juejin_core.parse_tick(data))
            # 保存数据
            for data in datas:
                # 09:25:00之前的数据不保存
                created_at = data["created_at"].strftime("%Y-%m-%d %H:%M:%S")
                time_ = created_at[-8:]
                if tool.trade_time_sub(time_, "09:25:00") < 0:
                    continue
                if tool.trade_time_sub(time_, "15:00:00") > 0:
                    continue
                if tool.trade_time_sub(time_, "11:30:00") > 0 and tool.trade_time_sub(time_, "13:00:00") < 0:
                    continue
                # 不是今天的数据不保存
                if day != created_at[:10]:
                    continue
                codeDataManager.save_data(data)
    def init_code_datas(self):
        global code_datas
        global max_min_prices
        codeDataManager = code_data_manager.CodeDataManager()
        gpCodeManager = juejin_core.GPCodeManager()
        code_datas = {}
        max_min_prices = {}
        codes = gpCodeManager.get_codes()
        if codes:
            # 获取当日的最高价最低价
            res = juejin_core.GPCodeManager().get_min_and_max_price(codes)
            for data in res:
                max_min_prices[data[0]] = (data[1], data[2])
            for code in codes:
                # 加载历史数据
                code_datas[code] = []
                old_datas = codeDataManager.get_datas(code)
                # 获取缺失的数据
                min_time = tool.get_now_time_str()
                if int(min_time.replace(":", "")) > int("150000"):
                    min_time = "15:00:00"
                elif int("113000") < int(min_time.replace(":", "")) < int("130000"):
                    min_time = "11:30:00"
                min_time = tool.trade_time_add_second(min_time, 0 - DrawManager.X_DATA_MINIUTES * 60)
                ranges = codeDataManager.get_lack_datas_time_range(old_datas, min_time)
                if len(ranges) > 0:
                    self.__load_lack_datas(code, ranges)
                    old_datas = codeDataManager.get_datas(code)
                if old_datas:
                    code_datas[code].extend(old_datas)
                    # self.update(code, code_datas[code])
                    wx.CallAfter(self.update, code, code_datas[code])
    # 更新数据
    def __update_data(self, code, axes, datas, min_rate, max_rate):
        def get_time_as_seconds(created_at):
            time_ = created_at[-8:]
            if tool.get_time_as_second("13:00:00") > tool.get_time_as_second(time_) > tool.get_time_as_second(
                    "11:30:00"):
                time_ = "11:30:00"
            time_s = int(time_.split(":")[0]) * 3600 + int(time_.split(":")[1]) * 60 + int(
                time_.split(":")[2]) - 9 * 3600 - 60 * 30
            if int(time_.replace(":", "")) > int("11:30:00".replace(":", "")):
                time_s -= 90 * 60
            return time_s
        def seconds_2_time_str(seconds):
            seconds += 9 * 3600 + 60 * 30
            if seconds > 11 * 3600 + 60 * 30:
                seconds += 90 * 60
            h = seconds // 3600
            m = seconds % 3600 // 60
            s = seconds % 60
            return "{0:0>2}:{1:0>2}:{2:0>2}".format(h, m, s)
        # 删除9:30以前的数据
        for i in range(0, len(datas)):
            time_ = datas[i]["created_at"][-8:]
            if int(time_.replace(":", "")) >= int("092600"):
                datas = datas[i:]
                break
        # 取最近14分钟的数据
        for i in range(len(datas) - 1, -1, -1):
            time_ = datas[i]["created_at"][-8:]
            if tool.trade_time_sub(datas[-1]["created_at"][-8:], time_) >= self.X_DATA_MINIUTES * 60:
                datas = datas[i:]
                break
        xs = []
        ys_rate = []
        ys_average_rate_1m = []
        ys_average_rate = []
        for data in datas:
            xs.append(get_time_as_seconds(data["created_at"]))
            ys_rate.append(data["rate"] * 100)
            ys_average_rate_1m.append(data["average_rate"] * 100 + 1.5)
            ys_average_rate.append(data["average_rate"] * 100)
        xticks = []
        xticklabels = []
        # 设置X轴范围为09:30:00 到15:00:00
        # x轴范围为0-15分钟
        end_x = "0000-00-00 " + tool.trade_time_add_second(datas[0]["created_at"][-8:], self.X_RANGE_MINIUTES * 60)
        axes[0].set_xlim(get_time_as_seconds(datas[0]["created_at"]), get_time_as_seconds(end_x))
        # if len(xs) < 2 or xs[-1] - xs[0] < 30 * 60:
        #     axes[0].set_xlim(xs[0], xs[0] + 30 * 60)
        # else:
        #     axes[0].set_xlim(xs[0], xs[-1])
        xms = axes[0].get_xlim()
        yms = axes[0].get_ylim()
        step = (int(xms[1]) - int(xms[0])) // 30
        for i in range(int(xms[0]), int(xms[1] + 1), step):
            xticks.append(i)
            xticklabels.append("")
        axes[0].set_xticks(xticks)
        axes[0].set_xticklabels(xticklabels)
        axes[1][0].set_data(xs, ys_rate)
        axes[2][0].set_data(xs, ys_average_rate_1m)
        if axes[3]:
            axes[3][0].set_data(xs, ys_average_rate)
        texts = axes[0].texts
        texts[0].set_text("{}% \n留格局:0%".format(round(datas[-1]["rate"] * 100, 2)))
        texts[1].set_text("{}".format(datas[-1]["created_at"].split(" ")[1]))
        texts[0].set_x(xms[1] - 80)
        texts[0].set_y(yms[1] + 0.5)
        texts[1].set_x(xms[0])
        texts[1].set_y(yms[0] - 1.5)
        # 删除之前
        if code in self.lines:
            for key in self.lines[code]:
                line = self.lines[code][key]
                line.remove()
            self.lines.pop(code)
        # 绘制最大最小坐标
        line_min = axes[0].axhline(min_rate, linestyle='--', color='yellow', lw=0.5)
        line_max = axes[0].axhline(max_rate, linestyle='--', color='yellow', lw=0.5)
        self.lines[code] = {"min": line_min, "max": line_max}
        axes[0].figure.canvas.draw()
        axes[0].figure.canvas.flush_events()
    def __init__(self, axes_list, codes_info):
        self.axes_list = axes_list
        self.codes_info = codes_info
        self.lines = {}
    def update(self, code, datas):
        __start_time = time.time()
        # 获取前高和前低
        max_rate = None
        min_rate = None
        if code in max_min_prices:
            pre_price = juejin_core.GPCodeManager().get_pre_prices(code)
            min_rate = round((max_min_prices[code][0] - pre_price) / pre_price, 4)
            max_rate = round((max_min_prices[code][1] - pre_price) / pre_price, 4)
        for data in datas:
            rate = data["rate"] * 100
            created_at = data["created_at"][-8:]
            if tool.get_time_as_second("15:00:00") >= tool.get_time_as_second(created_at) >= tool.get_time_as_second(
                    "09:30:00"):
                if max_rate is None:
                    max_rate = rate
                if min_rate is None:
                    min_rate = rate
                if rate > max_rate:
                    max_rate = rate
                if rate < min_rate:
                    min_rate = rate
        # 展示最近的600个
        datas = datas[0 - self.X_DATA_MINIUTES * 20:]
        # 修正量数据
        last_info = None
        for i in range(1, len(datas)):
            # 如果在
            if not last_info:
                last_info = datas[i]
            if int(datas[i]["created_at"][-2:]) - int(datas[i - 1]["created_at"][-2:]) < 0:
                last_info = datas[i]
            datas[i]['average_rate_1m'] = last_info['average_rate']
        datas[0]['average_rate_1m'] = datas[0]['average_rate']
        for i in range(0, len(self.codes_info)):
            if self.codes_info[i][0] == code:
                try:
                    self.__update_data(code, self.axes_list[i], datas, min_rate, max_rate)
                except Exception as e:
                    logging.exception(e)
        print("绘图花费时间:", time.time() - __start_time)
class mainApp(wx.App):
    def __refresh(self):
        codes = juejin_core.GPCodeManager().get_codes()
        last_time = round(time.time())
        while True:
            try:
                code = ocr_ths_code()
                if not code:
                    time.sleep(0.1)
                    continue
                self.xgb.set_code(code)
                # 1s更新一次
                if round(time.time()) - last_time > 5:
                    codes = juejin_core.GPCodeManager().get_codes()
                    last_time = round(time.time())
                for index in range(0, len(codes)):
                    if codes[index] == code:
                        self.frame.scrollTo(index)
                        break
            except Exception as e:
                # print(str(e))
                pass
            time.sleep(0.005)
    def __show_float_frame(self):
        self.floatFrame.Show()
    def __show_main_frame(self):
        self.frame.Show()
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.frame = TickFrame()
        self.floatFrame = FloatFrame()
        # 传递代码设置成功的回调
        self.xgb = CodeDataFrame(self.frame.set_codes_success, self.__show_float_frame, self.__show_main_frame)
        self.xgb.Show()
        t1 = threading.Thread(target=lambda: self.__refresh())
        # 后台运行
        t1 = threading.Thread(target=self.update_kpl_func)
        t1.setDaemon(True)
        t1.start()
        return True
    def loadUrl(self, url):
        self.webview.load(QUrl(url))
    # 设置交易数据
    def set_trade_data(self, code, code_name, trade_data, trade_record):
        self.webview.page().runJavaScript(f"app.set_trade_info('{code}','{code_name}','{trade_data}','{trade_record}')")
def recieve_tick(pipe):
    codeDataManager = code_data_manager.CodeDataManager()
    while True:
        data = pipe.recv()
        if data:
            type = data["type"]
            if type == 0:
                # tick数据
                data = data["data"]
                code = data["code"]
                if code not in code_datas:
                    code_datas[code] = []
                code_datas[code].append(data)
                codeDataManager.save_data(data)
                # 更新数据
                try:
                    drawManager.update(code, code_datas[code])
                    print("接受到的tick数据:", data)
                except:
                    pass
    def set_kpl_data(self, data):
        self.webview.page().runJavaScript(f"fill_kpl_data('{data}')")
    # 更新开盘啦数据
    def __update_kpl(self):
        datas = network_util.http_get("/get_kpl_data")
        self.signal_update_kpl.emit(datas)
def ths_auto_click():
    hwnd = ths_util.get_ths_second_screen_menu_hwnd()
    while True:
    def closeEvent(self, event):
        try:
            if hwnd is None or not win32gui.IsWindowVisible(hwnd):
                # print("未找到同花顺副屏句柄")
                hwnd = ths_util.get_ths_second_screen_menu_hwnd()
            if hwnd is None:
                continue
            if not setting.is_ths_auto_click():
                continue
            ps = setting.get_ths_auto_click_positions()
            if not ps:
                continue
            ps_new = []
            for p in ps:
                p = eval(p)
                ps_new.append(p)
            space = setting.get_ths_auto_click_time_space()
            if space is None:
                space = 500
            ths_util.betch_click(hwnd, ps_new, round(space / 1000, 4))
            setting.set_kp_second_window_info(
                (self.pos().x(), self.pos().y(), self.size().width(), self.size().height()))
        except Exception as e:
            pass
        finally:
            time.sleep(0.02)
            print("")
def ths_auto_refresh():
    hwnd = ths_util.get_trade_refesh_hwnd()
    while True:
class MainWindow(QMainWindow):
    signal_update_code = pyqtSignal(str)
    def show_info(self, msg):
        QMessageBox.information(self, "提示", msg, QMessageBox.Yes)
    def show_warning(self, msg):
        QMessageBox.warning(self, "提示", msg, QMessageBox.Yes)
    def set_trade_data(self, code, code_name, trade_data, trade_record):
        self.secondWindow.set_trade_data(code, code_name, trade_data, trade_record)
    # 设置目标代码
    def set_target_code(self, code):
        print("set_target_code")
        self.webview.page().runJavaScript(f"app.set_target_code('{code}')")
    # 菜单及菜单点击事件
    def __menu(self):
        def __gpu_is_avaiable():
            if torch.cuda.is_available():
                self.show_info("GPU可用")
            else:
                self.show_warning("GPU不可用")
        def __set_juejin_params():
            ps = (self.x(), self.y())
            size = (self.width(), self.height())
            self.wx_pipe.send(json.dumps({"type": "juejin_setting", "pos": (ps[0] + size[0] / 2, ps[1] + size[1] / 2)}))
        def __download_codes():
            try:
                result = self.__request("", 72)
                result = json.loads(result)
                if result['code'] == 0:
                    codes_dict = result['data']
                    if codes_dict:
                        for key in codes_dict:
                            # 将代码保存到桌面
                            path = f"C:\\Users\\Administrator\\Desktop\\涨停代码{key}.txt"
                            with open(path, mode='w') as f:
                                for c in codes_dict[key]:
                                    f.write(c)
                                    f.write("\n")
                        self.show_info("下载成功")
                else:
                    self.show_warning(result['msg'])
            except Exception as e:
                self.show_warning(str(e))
        def __manage_code():
            ps = (self.x(), self.y())
            size = (self.width(), self.height())
            self.wx_pipe.send(json.dumps({"type": "codes_setting", "pos": (ps[0] + size[0] / 2, ps[1] + size[1] / 2)}))
        def __manage_ths_pos():
            ps = (self.x(), self.y())
            size = (self.width(), self.height())
            self.wx_pipe.send(json.dumps({"type": "manage_ths_pos", "pos": (ps[0] + size[0] / 2, ps[1] + size[1] / 2)}))
        def __show_float_callback():
            self.wx_pipe.send(json.dumps({"type": "show_float_callback"}))
        def __show_main_callback():
            self.wx_pipe.send(json.dumps({"type": "show_main_callback"}))
        def __show_second_window():
            self.secondWindow.show()
        menubar = self.menuBar()
        setting_ = menubar.addMenu('&设置')
        juejin_action = QAction("&掘金参数配置", self)
        juejin_action.triggered.connect(__set_juejin_params)
        setting_.addAction(juejin_action)
        action = QAction("&代码管理", self)
        action.triggered.connect(__manage_code)
        setting_.addAction(action)
        action = QAction("&下载涨停代码", self)
        action.triggered.connect(__download_codes)
        setting_.addAction(action)
        action = QAction("&GPU检测", self)
        action.triggered.connect(__gpu_is_avaiable)
        setting_.addAction(action)
        auto_ = menubar.addMenu('&自动化')
        action = QAction("&同花顺设置", self)
        action.triggered.connect(__manage_ths_pos)
        auto_.addAction(action)
        view_ = menubar.addMenu('&视图')
        action = QAction("&打开副屏", self)
        action.triggered.connect(__show_second_window)
        view_.addAction(action)
        action = QAction("&打开悬浮盯盘", self)
        action.triggered.connect(__show_float_callback)
        view_.addAction(action)
        action = QAction("&打开分时看盘", self)
        action.triggered.connect(__show_main_callback)
        view_.addAction(action)
    def __init__(self, wx_pipe):
        super().__init__()
        self.wx_pipe = wx_pipe
        self.setWindowTitle('看盘页面')
        window_info = setting.get_kp_window_info()
        if window_info:
            self.move(window_info[0], window_info[1])
            self.resize(window_info[2], window_info[3])
        else:
            self.resize(1100, 1000)
            self.center()
        self.webview = QWebEngineView()
        self.webview.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
        self.__menu()
        # JS桥设置
        channel = QWebChannel(self.webview.page())
        self.webview.page().setWebChannel(channel)
        self.python_bridge = JSBridgeClass(self,self.webview)
        channel.registerObject("Bridge", self.python_bridge)
        # 设置副屏
        self.secondWindow = SecondWindow(self)
        self.setCentralWidget(self.webview)
        self.show()
        self.webview.load(QUrl("http://192.168.3.122:8848/kp/index23-05-04.html"))
        self.secondWindow.show()
        self.secondWindow.loadUrl("http://192.168.3.122:8848/kp/banshuping.html")
        # 绑定槽函数
        self.signal_update_code.connect(self.set_target_code)
    def closeEvent(self, event):
        event.accept()
        try:
            if hwnd is None or not win32gui.IsWindowVisible(hwnd):
                # print("未找到同花顺交易刷新句柄")
                hwnd = ths_util.get_trade_refesh_hwnd()
            if hwnd is None:
                continue
            if not setting.is_ths_trade_auto_refresh():
                continue
            rect = win32gui.GetWindowRect(hwnd)
            win32_util.visual_click(hwnd, (160, (rect[3] - rect[1]) // 2))
            time_space = setting.get_ths_auto_refresh_time_space()
            if time_space is None:
                time_space = 500
            time.sleep(round(time_space / 1000, 4))
            setting.set_kp_window_info((self.pos().x(), self.pos().y(), self.size().width(), self.size().height()))
        except Exception as e:
            pass
        finally:
            time.sleep(0.02)
            print("")
        self.wx_pipe.send(json.dumps({"type": "exit"}))
        sys.exit(0)
if __name__ == "__main__1":
    print(webview.WebView.IsBackendAvailable(webview.WebViewBackendEdge))
# 打包命令
# cd D:\workspace\GP\trade_desk
# D:\workspace\GP\trade_desk\dist\env\pk_env\Scripts\pyinstaller.exe main.spec
if __name__ == "__main__":
    print(pow(3, 1))
    global p2
    p1, p2 = multiprocessing.Pipe()
    global jueJinProcess
    jueJinProcess = multiprocessing.Process(target=juejin_core.run, args=(p1,))
    jueJinProcess.start()
    t1 = threading.Thread(target=lambda: recieve_tick(p2))
def recieve_code(pipe, mainWindow):
    latest_code = ''
    while True:
        try:
            data = pipe.recv()
            if data:
                data = json.loads(data)
                if data["type"] == "code":
                    if latest_code != data["code"]:
                        latest_code = data["code"]
                        mainWindow.signal_update_code.emit(latest_code)
        except Exception as e:
            logging.exception(e)
if __name__ == "__main__":
    p1, p2 = multiprocessing.Pipe()
    wxGuiProcess = multiprocessing.Process(target=gui_wx.run, args=(p1,))
    wxGuiProcess.start()
    app = QApplication(sys.argv)
    browser = MainWindow(p2)
    t1 = threading.Thread(target=lambda: recieve_code(p2, browser))
    # 后台运行
    t1.setDaemon(True)
    t1.start()
    t2 = threading.Thread(target=lambda: ths_auto_click())
    # 后台运行
    t2.setDaemon(True)
    t2.start()
    t3 = threading.Thread(target=lambda: ths_auto_refresh())
    # 后台运行
    t3.setDaemon(True)
    t3.start()
    app = mainApp(redirect=False)
    app.MainLoop()
    sys.exit(app.exec_())
main.spec
@@ -8,7 +8,7 @@
    ['main.py'],
    pathex=[],
    binaries=[],
    datas=[('res','res'),('datas','datas'),('venv/Lib/site-packages/dateutil','dateutil')],
    datas=[('res','res'),('datas','datas'),('logs','logs'),('venv/Lib/site-packages/dateutil','dateutil')],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
@@ -47,5 +47,5 @@
    strip=False,
    upx=True,
    upx_exclude=[],
    name='分时1',
    name='kp',
)
network_util.py
New file
@@ -0,0 +1,26 @@
import json
import socket
import requests
SERVER_HOST = "192.168.3.252"
SOCKET_PORT = 9001
HTTP_PORT = 9004
def socket_request(data):
    client = socket.socket()  # 生成socket,连接server
    ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
    client.connect(ip_port)
    client.send(json.dumps(data).encode("utf-8"))
    # 读取内容
    result = client.recv(102400)
    client.close()
    return result.decode("gbk")
def http_get(path):
    response = requests.get(f"http://{SERVER_HOST}:{HTTP_PORT}{path}")
    if response.status_code == 200:
        return response.text
    return None
setting.py
@@ -191,13 +191,13 @@
        return val
def set_xgb_window_info(window):
def set_kp_window_info(window):
    cp = __read_setting()
    cp.set("config", "xgb_window_info", json.dumps(window))
    __write_setting(cp)
def get_xgb_window_info():
def get_kp_window_info():
    cp = __read_setting()
    val = __get_setting(cp, "config", "xgb_window_info")
    if val is None:
@@ -207,6 +207,22 @@
        return val
def set_kp_second_window_info(window):
    cp = __read_setting()
    cp.set("config", "kp_second_window_info", json.dumps(window))
    __write_setting(cp)
def get_kp_second_window_info():
    cp = __read_setting()
    val = __get_setting(cp, "config", "kp_second_window_info")
    if val is None:
        return None
    else:
        val = json.loads(val)
        return val
if __name__ == "__main__":
    print(get_ths_auto_click_time_space())
    set_ths_auto_click_time_space(1000)
test.py
@@ -1,258 +1,27 @@
import sys
import json
import random
from PyQt5 import QtWidgets
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
import xlwt
# k_form=(15个交易日是否涨幅24.9%,是否破前高,是否超跌,是否接近前高,是否N)
# code_nature = (是否有涨停,是否有溢价)
# hot_block(板块中涨停票个数(包含自己),板块炸板票个数)
# zyltgb自由流通市值是否大于250亿
# limit_price 涨停价是否大于100块
# limit_up_time 是否10点之前涨停
def getscore(bidding_money, big_money, volume, k_form, code_nature, hot_block, zyltgb, limit_price, limit_up_time):
    score = 0
    score_list = []
    # 开盘前竞价
    if bidding_money:
        score += 200
        score_list.append(200)
    else:
        score_list.append(0)
    # 大单成交
    if big_money:
        score += 150
        score_list.append(150)
    else:
        score_list.append(0)
    # 量
    volume_score = [0, 200, 250, 200, 150, 100, -500, -1000]
    score += volume_score[volume]
    score_list.append(volume_score[volume])
    # 15个交易日是否涨幅24.9%
    if k_form[0]:
        score += -1000
        score_list.append(-1000)
    else:
        score_list.append(0)
    # 是否破前高
    if k_form[1]:
        score += 150
        score_list.append(150)
    else:
        score_list.append(0)
    # 是否超跌
    if k_form[2]:
        score += 170
        score_list.append(170)
    else:
        score_list.append(0)
class MainWindow(QtWidgets.QMainWindow):
    # 是否接近前高
    if k_form[3]:
        score += -1000
        score_list.append(-1000)
    else:
        score_list.append(0)
    # 是否N
    if k_form[4]:
        score += 160
        score_list.append(160)
    else:
        score_list.append(0)
    def __init__(self):
        super().__init__()
    if not code_nature[0]:
        score += 50
        score_list.append(50)
    else:
        score_list.append(0)
    if code_nature[1]:
        score += 100
        score_list.append(100)
    else:
        score_list.append(0)
        self.setWindowTitle('看盘页面')
        self.resize(1100, 1000)
        self.webview = QWebEngineView()
        self.webview.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
    if hot_block[0] >= 2:
        score += 200
        score_list.append(200)
    else:
        score += 150
        score_list.append(150)
    if hot_block[1] > 0:
        score += 50
        score_list.append(50)
    else:
        score_list.append(0)
    if zyltgb:
        score += -500
        score_list.append(-500)
    else:
        score_list.append(0)
    if limit_price:
        score += -1000
        score_list.append(-1000)
    else:
        score_list.append(0)
    if limit_up_time:
        score += 150
        score_list.append(150)
    else:
        score_list.append(0)
    return score, score_list
        self.setCentralWidget(self.webview)
        self.show()
        self.webview.load(QUrl("http://192.168.3.122:8848/%E7%9C%8B%E7%9B%98%E7%AB%AF/index23-05-04.html"))
if __name__ == "__main__":
    bidding_money = [True, False]
    bidding_money_desc = ["Y", "N"]
    big_money = [True, False]
    big_money_desc = ["Y", "N"]
    volume = [0, 1, 2, 3, 4, 5, 6, 7]
    volume_desc = ["换手量<49.9%。", "64.9%>换手量≥49.9%。", "79.9%>换手量≥64.9%。", "94.9%>换手量≥79.9%。", "109.9%>换手量≥94.9%。",
                   "124.9%>换手量≥109.9%。", "139.9%>换手量≥124.9%。", "换手量≥139.9%。"]
    k_form = []
    k_form_str = []
    for i in range(0, 5):
        k_form.append([True, False])
        k_form_str.append(["Y", "N"])
    code_nature = []
    code_nature_str = []
    for i in range(0, 2):
        code_nature.append([True, False])
        code_nature_str.append(["Y", "N"])
    hot_block = []
    hot_block.append([1, 2])
    hot_block.append([0, 1])
    zyltgb = [True, False]
    limit_price = [True, False]
    limit_up_time = [True, False]
    total_count = 0
    params = {}
    for i0 in range(0, len(bidding_money)):
        for i1 in range(0, len(big_money)):
            for i2 in range(0, len(volume)):
                for i3 in range(0, len(k_form)):
                    ks = []
                    for i in range(0, len(k_form)):
                        ks.append(0)
                    for n in range(0, 2):
                        ks[0] = k_form[0][n]
                        for n1 in range(0, 2):
                            ks[1] = k_form[1][n1]
                            for n2 in range(0, 2):
                                ks[2] = k_form[2][n2]
                                for n3 in range(0, 2):
                                    ks[3] = k_form[3][n3]
                                    for n4 in range(0, 2):
                                        ks[4] = k_form[4][n4]
                                        cns = []
                                        for i in range(0, len(code_nature)):
                                            cns.append(0)
                                        for j in range(0, 2):
                                            cns[0] = code_nature[0][j]
                                            for j1 in range(0, 2):
                                                cns[1] = code_nature[1][j1]
                                                hbs = []
                                                for i in range(0, len(hot_block)):
                                                    hbs.append(0)
                                                for h in range(0, 2):
                                                    hbs[0] = hot_block[0][h]
                                                    for h1 in range(0, 2):
                                                        hbs[1] = hot_block[1][h1]
                                                        for i10 in range(0, len(zyltgb)):
                                                            for i11 in range(0, len(limit_price)):
                                                                for i12 in range(0, len(limit_up_time)):
                                                                    param = (bidding_money[i0], big_money[i1],
                                                                             volume[i2], ks, cns, hbs,
                                                                             zyltgb[i10], limit_price[i11],
                                                                             limit_up_time[i12])
                                                                    score, score_list = getscore(bidding_money[i0],
                                                                                                 big_money[i1],
                                                                                                 volume[i2], ks, cns,
                                                                                                 hbs,
                                                                                                 zyltgb[i10],
                                                                                                 limit_price[i11],
                                                                                                 limit_up_time[i12])
                                                                    params[json.dumps(param)] = (score, score_list)
    titles = ["竞价强度", "资金力度", "换手量能", ["K线-涨幅24.9%", "K线-是否破前高", "K线-是否超跌", "K线-是否接近前高", "K线-是否N"],
              ["股性-无涨停", "股性-有溢价"],
              ["板块-涨停票数", "板块-炸板票数"], "股价>100", "自由流通市值>250亿", "10点前涨停", "得分"]
    # 写入excel
    datas = []
    for key in params:
        param = json.loads(key)
        score = params[key]
        # 15个交易日是否涨幅24.9 %, 是否破前高,是否超跌,是否接近前高,是否N
        if param[3][1:2] == [True, True]:
            continue
        if param[3][1] == True and param[3][3] == True:
            continue
        if param[4][0] == True and param[4][1] == True:
            continue
        datas.append((param, score))
    print("数量", len(datas))
    random.shuffle(datas)
    datas = datas[:1000]
    if True:
        print("总数", len(datas))
        file_name = "D:/test__.xls"
        wb = xlwt.Workbook()
        ws = wb.add_sheet('sheet1')
        ws.write(0, 0, '序号')
        index = 1
        for title in titles:
            if type(title) != str:
                for t in title:
                    ws.write(0, index, t)
                    index += 1
            else:
                ws.write(0, index, title)
                index += 1
        index = 0
        for data in datas:
            index += 1
            col = 1
            ws.write(index, 0, index)
            for d in data[0]:
                col_data = ""
                if type(d) == list:
                    for dd in d:
                        if type(dd) == bool:
                            col_data = "Y" if dd else "N"
                        else:
                            col_data = str(dd)
                        ws.write(index, col, f"{col_data}({data[1][1][col - 1]})")
                        col += 1
                else:
                    if type(d) == bool:
                        col_data = "Y" if d else "N"
                    else:
                        if col == 3:
                            col_data = volume_desc[d]
                        else:
                            col_data = str(d)
                    print(col)
                    ws.write(index, col, f"{col_data}({data[1][1][col - 1]})")
                    col += 1
            ws.write(index, col, data[1][0])
        wb.save(file_name)
    app = QtWidgets.QApplication(sys.argv)
    browser = MainWindow()
    sys.exit(app.exec())
test.spec
New file
@@ -0,0 +1,51 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
    ['test.py'],
    pathex=['D:\\workspace\\GP\\trade_desk\\dist\\env\\pk_env\\Lib\\site-packages\\PyQt5','D:\\workspace\\GP\\trade_desk\\dist\\env\\pk_env\\Lib\\site-packages\\PyQt5\\Qt5\\bin','D:\\workspace\\GP\\trade_desk\\dist\\env\\pk_env\\Lib\\site-packages\\PyQt5\\Qt5\\plugins'],
    binaries=[],
    datas=[('res','res'),('datas','datas'),('venv/Lib/site-packages/dateutil','dateutil')],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='分时',
    debug=True,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon=['icon.ico'],
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='test',
)
zhibo/tui_liu.py
New file
@@ -0,0 +1,36 @@
import os
import threading
import time
def __push(video_path, roomId):
    print(video_path, roomId)
    cmd = f"ffmpeg -re -i {video_path} -c copy -f flv rtmp://127.0.0.1/live/{roomId}"
    print(cmd)
    os.system(cmd)
def __get_video_file(path, file_set):
    if os.path.isfile(path):
        file_set.add(path)
    else:
        files = os.listdir(path)
        for f in files:
            __get_video_file(f"{path}/{f}", file_set)
# 初始化数据
def __start_push():
    base_dir = "/root/videos"
    files = set()
    __get_video_file(base_dir, files)
    for file in files:
        t1 = threading.Thread(target=lambda xf=file: __push(xf, xf.split('_')[-1].split('.')[0]))
        t1.setDaemon(True)
        t1.start()
if __name__ == "__main__":
    __start_push()
    while True:
        time.sleep(1)