admin
2025-06-10 568c763084b926a6f2d632b7ac65b9ec8280752f
main.py
@@ -1,38 +1,40 @@
import base64
import ctypes
import hashlib
import json
import logging
import multiprocessing
import queue
import threading
import time
from multiprocessing import freeze_support
import sys
from urllib.parse import urlparse, parse_qs
from functools import partial
from multiprocessing import Pipe, Process, freeze_support
import torch
import win32api
import win32con
import win32gui
from PyQt5.QtGui import QFont, QPalette, QColor, QTextOption
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMessageBox, QLabel, QDialog, QVBoxLayout, QPushButton, \
    QWidget, QMenu
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings, QWebEnginePage
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMessageBox, QLabel
from PyQt5.QtCore import Qt, pyqtSlot, QObject, pyqtSignal, QTimer, QUrl, QPoint
import constant
import gui_wx
from network_delegate_manager import LocalKanPanNetworkDelegate
from utils import network_util
from utils import network_util, xgb_api, ths_util, ths_ocr_util
import setting
from utils  import tool
from utils import tool
import win32_util
from juejin_core import JueJinApi
from kpl import kpl_util, kpl_api
from kpl.kpl_data_manager import KPLLimitUpDataManager
freeze_support()
from utils.network_delegate_manager import LocalKanPanNetworkDelegate
URL_MSG_LIST = f"http://{constant.WEB_HOST}/kp/msg_list.html"
window_msg_queue = queue.Queue()
class BaseBridgeClass(QObject):
@@ -47,23 +49,25 @@
        self.__webview = webview
        self.signal_request.connect(self.__request_result_callback)
    def __http_request(self, url, callback_info):
        try:
            # 代理请求结果
            result, need_delegate = LocalKanPanNetworkDelegate.http_delegate_request(url)
            if not need_delegate:
                result = network_util.http_get(url)
            print("请求结果:", result)
            print(url, "请求结果:", result)
            self.signal_request.emit(callback_info[0], callback_info[1], result)
            return result
        except Exception as e:
            logging.exception(e)
    def __socket_request(self, text, callback_info):
    def __socket_request(self, text, callback_info, port=None):
        try:
            result = network_util.socket_request(text)
            print("socket請求:", text)
            if port:
                result = network_util.socket_request(text, port=port)
            else:
                result = network_util.socket_request(text)
            print("请求结果:", result)
            self.signal_request.emit(callback_info[0], callback_info[1], result)
            return result
@@ -85,8 +89,56 @@
    @pyqtSlot(str, str)
    def socket_request(self, text, callback_info):
        print("socket_request", text)
        try:
            text_json = json.loads(text)
            params = []
            for k in text_json:
                if k == "sign":
                    continue
                if type(text_json[k]) == dict or type(text_json[k]) == list:
                    params.append(f"{k}={json.dumps(text_json[k], separators=(',', ':'))}")
                else:
                    params.append(f"{k}={text_json[k]}")
            params.sort()
            params.append("%Yeshi2014@#.")
            params_str = "&".join(params)
            md5 = hashlib.md5()
            md5.update(params_str.encode("utf-8"))
            md5_hash = md5.hexdigest()
            text_json["sign"] = md5_hash
            text = json.dumps(text_json)
        except:
            pass
        callback_info = json.loads(callback_info)
        t1 = threading.Thread(target=lambda: self.__socket_request(text, callback_info))
        t1.setDaemon(True)
        t1.start()
    @pyqtSlot(str, str)
    def ls_socket_request(self, text, callback_info):
        print("ls_socket_request", text)
        try:
            text_json = json.loads(text)
            params = []
            for k in text_json:
                if k == "sign":
                    continue
                if type(text_json[k]) == dict or type(text_json[k]) == list:
                    params.append(f"{k}={json.dumps(text_json[k], separators=(',', ':'))}")
                else:
                    params.append(f"{k}={text_json[k]}")
            params.sort()
            params.append("%Yeshi2014@#.")
            params_str = "&".join(params)
            md5 = hashlib.md5()
            md5.update(params_str.encode("utf-8"))
            md5_hash = md5.hexdigest()
            text_json["sign"] = md5_hash
            text = json.dumps(text_json)
        except:
            pass
        callback_info = json.loads(callback_info)
        t1 = threading.Thread(target=lambda: self.__socket_request(text, callback_info, port=14008))
        t1.setDaemon(True)
        t1.start()
@@ -94,6 +146,18 @@
    @pyqtSlot(result=str)
    def get_client(self):
        return setting.get_client()
    @pyqtSlot(str)
    def add_code_to_ths(self, code):
        # 添加到同花顺
        threading.Thread(target=lambda: ths_util.add_code_to_zixuan(code), daemon=True).start()
class SecondWindowBridgeClass(BaseBridgeClass):
    @pyqtSlot(str)
    def set_target_code(self, code):
        # 设置目标代码
        window_msg_queue.put_nowait({"type": "set_target_code", "data": {"code": code}})
class JSBridgeClass(BaseBridgeClass):
@@ -140,12 +204,12 @@
        window_height = 80
        padding = 10
        self.resize(300, 50)
        hwnds = win32_util.search_window("悬浮盯盘")
        if hwnds:
            rect = win32gui.GetWindowRect(hwnds[0])
            self.move(rect[0] + padding - 3, rect[1] - window_height - 30)
            if rect[2] - rect[0] > 0:
                self.resize((rect[2] - rect[0]) - (padding - 2) * 2, window_height)
        # hwnds = win32_util.search_window("悬浮盯盘", is_one_result=True)
        # if hwnds:
        #     rect = win32gui.GetWindowRect(hwnds[0])
        #     self.move(rect[0] + padding - 3, rect[1] - window_height - 30)
        #     if rect[2] - rect[0] > 0:
        #         self.resize((rect[2] - rect[0]) - (padding - 2) * 2, window_height)
        self.setWindowTitle("消息提示")
        self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.setWindowOpacity(0.9)
@@ -226,14 +290,32 @@
        # JS桥设置
        channel = QWebChannel(self.webview.page())
        self.webview.page().setWebChannel(channel)
        self.python_bridge = BaseBridgeClass(self.webview)
        self.python_bridge = SecondWindowBridgeClass(self.webview)
        channel.registerObject("Bridge", self.python_bridge)
        # win32gui.SetWindowLong(self.winId(), win32con.GWL_WNDPROC, self.handleCustomMessage)
        # self.signal_update_kpl.connect(self.set_kpl_data)
        # t1 = threading.Thread(target=self.update_kpl_func)
        # t1.setDaemon(True)
        # t1.start()
    # 自定义消息处理函数
    def handleCustomMessage(self, hwnd, msg, wparam, lparam):
        if msg == win32con.WM_USER + 1024:
            # 解析数据
            # try:
            #     code = ctypes.cast(lparam, ctypes.py_object).value
            #     print(code)
            # except:
            #     pass
            pass
            # 处理数据
            # self.dataReceived.emit(intValue, stringValue)
        return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
    def loadUrl(self, url):
        self.webview.load(QUrl(url))
@@ -271,6 +353,16 @@
            print("")
class WebEnginePage(QWebEnginePage):
    def javaScriptConsoleMessage(self, level, message, lineNumber, sourceID):
        """处理 JavaScript 控制台消息"""
        print(f"JavaScript Console Error: {message} at line {lineNumber} of {sourceID}")
    def onLoadFailed(self, errorCode, failingUrl, errorDescription):
        """处理加载失败的情况"""
        print(f"Failed to load URL: {failingUrl}, Error: {errorDescription}, Code: {errorCode}")
class MainWindow(QMainWindow):
    signal_update_code = pyqtSignal(str)
@@ -291,6 +383,18 @@
        self.webview.page().runJavaScript(f"app.set_target_code('{code}')")
        self.secondWindow.set_target_code(code)
    def read_window_msg(self):
        while True:
            try:
                data = window_msg_queue.get()
                if data["type"] == "set_target_code":
                    code = data["data"]["code"]
                    self.signal_update_code.emit(code)
            except:
                pass
            finally:
                pass
    # 菜单及菜单点击事件
    def __menu(self):
        def __gpu_is_avaiable():
@@ -306,6 +410,13 @@
        def __juejin_tick_download():
            self.wx_pipe.send(json.dumps({"type": "juejin_tick_download"}))
        def __ths_ocr_code():
            try:
                code = ths_ocr_util.ocr_ths_code(always_save=True)
                self.show_info(f"识别到的代码:{code}")
            except Exception as e:
                self.show_warning(f"识别出错:{str(e)}")
        def __download_codes():
            try:
@@ -332,16 +443,13 @@
            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 __show_dead_hwnds():
            self.wx_pipe.send(json.dumps({"type": "show_dead_hwnds"}))
        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()
@@ -354,21 +462,16 @@
        def __clear_webview_cache():
            self.webview.page().profile().clearHttpCache()
        def __show_ths_flash_trade():
            hwnds = ths_util.get_flash_trade_hwnds()
            for hwnd in hwnds:
                if not win32gui.IsWindowVisible(hwnd):
                    win32gui.BringWindowToTop(hwnd)
                win32_util.move_window(hwnd, 0, 0)
        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)
@@ -378,33 +481,25 @@
        action.triggered.connect(__clear_webview_cache)
        setting_.addAction(action)
        action = QAction("&掘金分时数据下载", self)
        action.triggered.connect(__juejin_tick_download)
        action = QAction("&同花顺代码识别", self)
        action.triggered.connect(__ths_ocr_code)
        setting_.addAction(action)
        auto_ = menubar.addMenu('&自动化')
        action = QAction("&同花顺设置", self)
        action.triggered.connect(__manage_ths_pos)
        auto_.addAction(action)
        action = QAction("&死句柄管理", self)
        action.triggered.connect(__show_dead_hwnds)
        setting_.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_his_msg_window)
        view_.addAction(action)
        view_ = menubar.addMenu('&代码管理')
        view_.aboutToShow.connect(__manage_code)
        view_ = menubar.addMenu('&打开分时')
        view_.aboutToShow.connect(__show_main_callback)
        view_ = menubar.addMenu('&显示一键卖出')
        view_.aboutToShow.connect(__show_ths_flash_trade)
    def __init__(self, wx_pipe):
        super().__init__()
@@ -419,6 +514,7 @@
            self.center()
        self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.webview = QWebEngineView()
        self.webview.setPage(WebEnginePage())
        self.webview.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
        self.__menu()
        # JS桥设置
@@ -436,11 +532,12 @@
        else:
            self.webview.load(QUrl("http://127.0.0.1:8848/kp/index23-05-04.html"))
        self.secondWindow.show()
        if not constant.IS_TEST:
            self.secondWindow.loadUrl(f"http://{constant.WEB_HOST}/kp/codes_list.html")
        else:
            self.secondWindow.loadUrl("http://127.0.0.1:8848/kp/codes_list.html")
        if not setting.is_only_convertible_bonds():
            self.secondWindow.show()
            if not constant.IS_TEST:
                self.secondWindow.loadUrl(f"http://{constant.WEB_HOST}/kp/codes_list.html")
            else:
                self.secondWindow.loadUrl("http://127.0.0.1:8848/kp/codes_list.html")
        # 绑定槽函数
        self.signal_update_code.connect(self.set_target_code)
@@ -449,12 +546,9 @@
        self.messageWindow = MessageWindow()
        self.msgListWindow = MsgListWindow()
        # 开启消息监听
        t1 = threading.Thread(target=self.read_msg)
        # 后台运行
        t1.setDaemon(True)
        t1.start()
        threading.Thread(target=KPLLimitUpDataManager().run, daemon=True).start()
        threading.Thread(target=xgb_api.run, daemon=True).start()
        threading.Thread(target=self.read_window_msg, daemon=True).start()
    def closeEvent(self, event):
        event.accept()
@@ -463,6 +557,8 @@
        except Exception as e:
            print("")
        self.wx_pipe.send(json.dumps({"type": "exit"}))
        wxGuiProcess.terminate()
        wxGuiProcess.join()
        sys.exit(0)
    # 读取消息
@@ -484,11 +580,6 @@
            time.sleep(0.5)
# 打包命令
# cd D:\workspace\GP\trade_desk
# D:\workspace\GP\trade_desk\dist\env\pk_env\Scripts\pyinstaller.exe main.spec
def recieve_code(pipe, mainWindow):
    latest_code = ''
    while True:
@@ -504,9 +595,16 @@
            logging.exception(e)
# 打包命令
# cd D:\workspace\GP\trade_desk
# D:\workspace\GP\trade_desk\dist\env\pk_env\Scripts\pyinstaller.exe main.spec
# 为了不出现意外的bug,运行时请将目录放在英文路径
if __name__ == "__main__":
    freeze_support()
    p1, p2 = multiprocessing.Pipe()
    wxGuiProcess = multiprocessing.Process(target=gui_wx.run, args=(p1,))
    global wxGuiProcess
    wxGuiProcess = Process(target=gui_wx.run, args=(p1,))
    wxGuiProcess.start()
    app = QApplication(sys.argv)