| | |
| | | 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): |
| | |
| | | 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 |
| | |
| | | @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() |
| | | |
| | |
| | | @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): |
| | |
| | | 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) |
| | |
| | | # 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)) |
| | |
| | | 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) |
| | | |
| | |
| | | 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(): |
| | |
| | | |
| | | 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: |
| | |
| | | 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() |
| | |
| | | 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) |
| | |
| | | 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__() |
| | |
| | | self.center() |
| | | self.setWindowFlag(Qt.WindowStaysOnTopHint, True) |
| | | self.webview = QWebEngineView() |
| | | self.webview.setPage(WebEnginePage()) |
| | | self.webview.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True) |
| | | self.__menu() |
| | | # JS桥设置 |
| | |
| | | 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) |
| | |
| | | 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() |
| | |
| | | except Exception as e: |
| | | print("") |
| | | self.wx_pipe.send(json.dumps({"type": "exit"})) |
| | | wxGuiProcess.terminate() |
| | | wxGuiProcess.join() |
| | | sys.exit(0) |
| | | |
| | | # 读取消息 |
| | |
| | | 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: |
| | |
| | | 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) |