admin
2023-06-09 c78e5f521a6bd93de2e553cda6d0eee58de46347
看盘页面优化
13个文件已修改
2个文件已添加
560 ■■■■■ 已修改文件
gui_wx.py 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
huaxin/test.py 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
huaxin/trade_server.py 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/codes_list.html 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/css/index23-05-04.css 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/code_list.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/http.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/msg_list.js 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/page.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/msg_list.html 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/gui.py 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/setting.conf 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
setting.py 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ths_util.py 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gui_wx.py
@@ -122,17 +122,23 @@
        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, 0, wx.LEFT | wx.RIGHT, 5)
        self.edit_client = wx.TextCtrl(self)
        boxsier.Add(self.edit_client, 0, wx.LEFT | wx.RIGHT, 5)
        # 代码
        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)
        boxsier.Add(label, 0, wx.LEFT | wx.RIGHT, 5)
        self.edit_codes = wx.TextCtrl(self, size=(150, 150), style=wx.TE_MULTILINE)
        boxsier.Add(self.edit_codes, 0, wx.LEFT | wx.RIGHT, 5)
        # 确定按钮
        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)
        boxsier.Add(self.btn_sure, 0, wx.LEFT | wx.TOP, 5 | 5)
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier.Add(boxsier, 1, wx.TOP | wx.TOP, 10)
        self.SetSizer(root_boxsier)
@@ -144,16 +150,26 @@
    def __init_data(self):
        codes = juejin_core.GPCodeManager().get_codes()
        self.edit_codes.SetValue("\n".join(codes))
        client = setting.get_client()
        if client:
            self.edit_client.SetValue(client)
        else:
            self.edit_client.SetValue("")
        pass
    def __sure_btn(self, event):
        codes_str = self.edit_codes.GetValue()
        client = self.edit_client.GetValue()
        setting.set_client(client)
        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)
@@ -322,12 +338,77 @@
    return code
#
# # 代码属性
# class CodeAttributeFrame(wx.Frame):
#     def __init__(self):
#         wx.Frame.__init__(self, None, -1, "属性",
#                           style=wx.CAPTION ^ wx.RESIZE_BORDER ^ wx.MINIMIZE_BOX ^ wx.CLOSE_BOX ^ wx.STAY_ON_TOP,
#                           size=(200, 300))
#         self.SetBackgroundColour(wx.Colour(224, 224, 224))
#         self.SetTransparent(230)
#         self.Bind(wx.EVT_CLOSE, self.OnExit)
#         # 读取配置信息
#         window_info = setting.get_code_attribute_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])
#
#         bs = wx.BoxSizer(wx.VERTICAL)
#
#         self.label_code_name = wx.StaticText(self, label="")
#
#         self.label_atrribute = wx.StaticText(self, label="")
#
#         self.label_error = wx.StaticText(self, label="")
#         self.label_error.SetForegroundColour(wx.Colour(255, 0, 0))
#
#         bs.Add(self.label_code_name, 0, wx.LEFT | wx.TOP, 10)
#         bs.Add(self.label_atrribute, 0, wx.LEFT | wx.TOP, 10)
#         bs.Add(self.label_error, 0, wx.LEFT | wx.TOP, 10)
#
#         self.SetSizer(bs)
#
#     def __request(self, code):
#         client = socket.socket()  # 生成socket,连接server
#         ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
#         client.connect(ip_port)
#         data = {"type": 430, "data": {"code": code}}
#         client.send(json.dumps(data).encode("utf-8"))
#         result = client.recv(1024)
#         client.close()
#         return result.decode("gbk")
#
#     # 设置代码
#     def setCode(self, code):
#         # 获取代码属性描述
#         try:
#             result = self.__request(code)
#             result = json.loads(result)
#             if result['code'] == 0:
#                 code_info = result['data']['code_info']
#                 desc = result['data']['desc']
#                 self.label_code_name.SetLabelText(f"{code_info[1]}({code_info[0]})")
#                 self.label_atrribute.SetLabelText(desc)
#                 self.label_error.SetLabelText("")
#         except Exception as e:
#             self.label_code_name.SetLabelText("")
#             self.label_atrribute.SetLabelText("")
#             self.label_error.SetLabelText(str(e))
#
#     def OnExit(self, e):
#         try:
#             setting.set_code_attribute_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
#         except Exception as e:
#             print("")
#         self.Hide()
#
# 悬浮框
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=(435, 200))
                          size=(435, 220))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetTransparent(230)
        self.Bind(wx.EVT_CLOSE, self.OnExit)
@@ -336,6 +417,7 @@
        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, 3, 4, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_remove_black = wx.Button(self, label="移除黑名单", size=(65, 30))
@@ -371,8 +453,10 @@
        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)
        # 加空白
        boxsier.Add(wx.BoxSizer(wx.HORIZONTAL), 0, wx.TOP, 5)
        # 已撤单
        self.btn_already_canceled = wx.Button(self, label="已撤单", size=(65, 25))
        boxsier.Add(self.btn_already_canceled, 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)
@@ -447,6 +531,8 @@
        self.btn_buy_mode_want.Bind(wx.EVT_BUTTON, lambda e: self.__set_trade_mode(e, 1))
        self.btn_buy_mode_all.Bind(wx.EVT_BUTTON, lambda e: self.__set_trade_mode(e, 0))
        self.btn_already_canceled.Bind(wx.EVT_BUTTON, self.cancel_buy)
        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))
@@ -454,8 +540,13 @@
        self.want_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "想要买列表", 403))
        self.pause_buy_list.Bind(wx.EVT_BUTTON, lambda e: self.show_list(e, "暂不买列表", 413))
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        root_boxsier = wx.BoxSizer(wx.VERTICAL)
        root_boxsier.Add(boxsier, 1, wx.LEFT, 10)
        self.label_attribute = wx.StaticText(self, label="")
        self.label_attribute.SetForegroundColour(wx.Colour(255, 0, 0))
        root_boxsier.Add(self.label_attribute, 0, wx.LEFT, 10)
        self.SetSizer(root_boxsier)
        self.__bind_hot_keys()
@@ -590,6 +681,21 @@
            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 cancel_buy(self, event):
        try:
            code = self.__get_code()
            print("撤单", code)
            result = self.__request_cancel_buy(code)
            result = json.loads(result)
            if result["code"] == 0:
                self.show_info(f"{code}撤单上报成功")
            else:
                self.show_warning(result.get("msg"))
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
@@ -754,6 +860,30 @@
        client.close()
        return result.decode("gbk")
    # 查询是否可以撤单
    def __request_can_cancel_buy(self, code):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 420, "data": {"codes": [code]}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    # 撤单
    def __request_cancel_buy(self, code):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 80, "data": {"code": code}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    def __set_trade_mode(self, event, mode):
        try:
            result = self.__request_set_buy_mode(mode)
@@ -789,6 +919,29 @@
                    show_warning(str(e), None)
        show_sure("是否关闭交易", close_buy)
    def __request_attribute(self, code):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 430, "data": {"code": code}}
        client.send(json.dumps(data).encode("utf-8"))
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    # 设置代码,请求代码属性
    def setCode(self, code):
        # 获取代码属性描述
        try:
            result = self.__request_attribute(code)
            result = json.loads(result)
            if result['code'] == 0:
                code_info = result['data']['code_info']
                desc = f"{code_info[1]} {code_info[0]} {result['data']['desc']}"
                wx.CallAfter(lambda : self.label_attribute.SetLabelText(desc))
        except Exception as e:
            wx.CallAfter(lambda: self.label_attribute.SetLabelText(str(e)))
    def OnExit(self, e):
        try:
@@ -1328,9 +1481,9 @@
        self.floatFrame = FloatFrame()
        global_datas["tickFrame"] = self.frame
        global_datas["floatFrame"] = self.floatFrame
        # # 测试
        # self.__show_float_frame()
        # return True
        # global_datas["floatFrame"].Show()
        t1 = threading.Thread(target=lambda: self.__refresh())
        # 后台运行
@@ -1393,14 +1546,16 @@
def ths_auto_refresh():
    hwnd = ths_util.get_trade_refesh_hwnd()
    count = 0
    while True:
        count += 1
        if count > 10:
            count = 0
            hwnd = ths_util.get_trade_refesh_hwnd()
        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)
@@ -1441,6 +1596,13 @@
                wx.CallAfter(lambda: global_datas["floatFrame"].Show())
            elif type_ == "show_main_callback":
                wx.CallAfter(lambda: global_datas["tickFrame"].Show())
            elif type_ == "set_code":
                code = data["code"]
                t1 = threading.Thread(target=lambda: global_datas["floatFrame"].setCode(code))
                # 后台运行
                t1.setDaemon(True)
                t1.start()
            elif type_ == "exit":
                try:
                    jueJinProcess.terminate()
@@ -1489,3 +1651,4 @@
if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()
    # ths_auto_refresh()
huaxin/test.py
@@ -176,7 +176,7 @@
                if ret != 0:
                    print('ReqQryShareholderAccount fail, ret[%d]' % ret)
            if 1:
            if 0:
                # 查询资金账号
                req_field = traderapi.CTORATstpQryTradingAccountField()
@@ -226,7 +226,7 @@
                if ret != 0:
                    raise Exception('ReqQryTrade fail, ret[%d]' % ret)
            if 1:
            if 0:
                # 请求报单
                req_field = traderapi.CTORATstpInputOrderField()
@@ -535,14 +535,14 @@
def __buy():
    data = {"type": 0, "req_id": f"test-{random.randint(0, 10000)}",
            "data": json.dumps({"code": "600706", "count": 1500, "price": 17.42})}
            "data": json.dumps({"code": "002146", "count": 1000, "price": 1.29})}
    result = __send_msg(data)
    print("下单结果", json.loads(result))
def __cancel_buy():
    data = {"type": 1, "req_id": f"test-{random.randint(0, 10000)}",
            "data": json.dumps({"code": "601611", "order_sys_id": "110018100025424"})}
            "data": json.dumps({"code": "002146", "order_sys_id": "12002P900020732"})}
    result = __send_msg(data)
    print("取消订单", json.loads(result))
@@ -567,12 +567,21 @@
    for item in result["data"]:
        print(item)
def __list_position():
    data = {"type": 4, "req_id": f"test-{random.randint(0, 10000)}"}
    result = __send_msg(data)
    result = json.loads(result)
    print("持仓列表")
    for item in result["data"]:
        print(item)
if __name__ == '__main__':
    # __buy()
    # # __cancel_buy()
    # __cancel_buy()
    __list_delegate()
    __list_traded()
    __list_position()
    # if 1>0:
    #     return;
huaxin/trade_server.py
@@ -43,6 +43,7 @@
TYPE_CANCEL_BUY = 1
TYPE_LIST_DELEGATE = 2
TYPE_LIST_TRADED = 3
TYPE_LIST_POSITION = 4
class TradeSimpleApi:
@@ -176,6 +177,16 @@
            raise Exception('ReqQryTrade fail, ret[%d]' % ret)
        return req_id
    # 查询持仓
    def list_positions(self):
        self.req_id += 1
        req_id = self.req_id
        req_field = traderapi.CTORATstpQryPositionField()
        ret = api.ReqQryPosition(req_field, req_id)
        if ret != 0:
            print('ReqQryPosition fail, ret[%d]' % ret)
        return req_id
    def login(self):
        # 请求登录
        login_req = traderapi.CTORATstpReqUserLoginField()
@@ -229,6 +240,7 @@
        self.__session_id = 0
        self.__data_callback = callback
        self.__temp_order_list_dict = {}
        self.__temp_position_list_dict = {}
    def OnFrontConnected(self) -> "void":
        logger.info('OnFrontConnected')
@@ -295,7 +307,6 @@
            ret = api.ReqUserLogin(login_req, TradeSimpleApi.req_id)
            if ret != 0:
                logger.info('ReqUserLogin fail, ret[%d]' % ret)
            print(log.log_records)
        else:
            logger.info('GetConnectionInfo fail, [%d] [%d] [%s]!!!' % (
                nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
@@ -484,14 +495,17 @@
    def OnRspQryPosition(self, pPositionField: "CTORATstpPositionField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        if nRequestID not in self.__temp_position_list_dict:
            self.__temp_position_list_dict[nRequestID] = []
        if bIsLast != 1:
            logger.info(
                'OnRspQryPosition[%d]: InvestorID[%s] SecurityID[%s] HistoryPos[%d] TodayBSPos[%d] TodayPRPos[%d]'
                % (nRequestID, pPositionField.InvestorID, pPositionField.SecurityID, pPositionField.HistoryPos,
                   pPositionField.TodayBSPos, pPositionField.TodayPRPos))
            self.__temp_position_list_dict[nRequestID].append(
                {"securityID": pPositionField.SecurityID, "totalPosCost": pPositionField.TotalPosCost,
                 "availablePosition": pPositionField.AvailablePosition})
        else:
            logger.info('查询持仓结束[%d] ErrorID[%d] ErrorMsg[%s]'
                        % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
            self.__data_callback(TYPE_LIST_POSITION, nRequestID, self.__temp_position_list_dict[nRequestID])
            self.__temp_position_list_dict.pop(nRequestID)
    # 成交回报,参数pTradeField是一个CTORATstpTradeField类对象
    def OnRtnTrade(self, pTradeField: "CTORATstpTradeField") -> "void":
@@ -542,7 +556,6 @@
            logging.exception(e)
    def setup(self):
        logger.info("----setup方法被执行-----")
        self.__init()
    @classmethod
@@ -616,6 +629,11 @@
                        local_req_id = self.__tradeSimpleApi.list_traded_orders()
                        self.__req_socket_dict[local_req_id] = sk
                        time.sleep(3)
                    elif _type == 4:
                        # 成交列表
                        local_req_id = self.__tradeSimpleApi.list_positions()
                        self.__req_socket_dict[local_req_id] = sk
                        time.sleep(3)
                    elif _type == 100:
                        # 活动日志
                        _data = json.loads(_data)
kp_html/kp/codes_list.html
@@ -55,6 +55,26 @@
                    </tbody>
                </table>
                
                <div v-if="same_reason_codes">
                    <table class="half-width">
                        <caption class="table-name">昨日相同板块代码({{same_reason_codes.reason}})</caption>
                    <tbody>
                        <tr>
                            <td style="padding: 0;border: none">
                                <div class="code-table-container">
                                    <div v-for="(item,i) in same_reason_codes.data"
                                        :style="{'border-left-style': i%3==0?'solid':'none','border-top-style': i<3?'solid':'none'}">
                                        <span class="num-style">{{item[1]}}</span>
                                        <span class="num-style">{{item[0]}}</span>
                                    </div>
                                </div>
                            </td>
                        </tr>
                    </tbody>
                    </table>
                </div>
                
                
kp_html/kp/css/index23-05-04.css
@@ -511,7 +511,7 @@
.market-container {
    width: 520px;
    height: 190px;
    height: 198px;
    word-wrap: break-word;
    display: flex;
    flex-wrap: wrap;
kp_html/kp/js/code_list.js
@@ -49,14 +49,28 @@
                open_limit_up: "",
                records: []
            },
            same_reason_codes: null
        },
        methods: {
            get_last_trade_day_reasons: function(code) {
                http_util.get_last_trade_day_reasons(code, function(res) {
                       res= JSON.parse(res);
                       console.log("返回内容",res);
                       if(res.code==0){
                           app.same_reason_codes = res.data;
                       }else{
                           app.same_reason_codes = null;
                       }
                });
            },
            set_trade_info: function(code, code_name, trade_data, trade_record, initiative_buy_codes,
                passive_buy_codes) {
                console.log("交易数据", code, code_name, trade_data, trade_record)
                console.log("主动买入", initiative_buy_codes)
                console.log("被动买入", passive_buy_codes)
                app.code = code;
                app.get_last_trade_day_reasons(code);
                app.code_name = code_name;
                if (trade_data) {
                    var trade_data = JSON.parse(trade_data);
@@ -109,6 +123,7 @@
            }
        }
    })
kp_html/kp/js/http.js
@@ -167,5 +167,16 @@
        http_util.http_request("/get_h_cancel_data", {
            code: code
        }, callback);
    }
    },
    // 获取消息列表
    list_msg:function(callback){
        http_util.http_request("/list_kp_client_msg", {
        }, callback);
    },
    //上个交易日相同涨停原因的代码列表
    get_last_trade_day_reasons:function(code,callback){
        http_util.http_request("/get_last_trade_day_reasons", {code:code
        }, callback);
    },
};
kp_html/kp/js/msg_list.js
New file
@@ -0,0 +1,35 @@
document.addEventListener("DOMContentLoaded", function() {
    //把对象赋值到JS中
    try {
        new QWebChannel(qt.webChannelTransport, function(channel) {
            window.pyjs = channel.objects.Bridge;
            console.log("回调成功");
            app.get_msg_list();
        });
    } catch (e) {
    }
});
var app;
$(function() {
    new VConsole();
    app = new Vue({
        el: "#app",
        data: {
            msg_list: [],
        },
        methods: {
            get_msg_list: function() {
              http_util.list_msg(function(res){
                console.log(res)
                res = JSON.parse(res);
                console.log(res)
                if(res.code==0){
                    app.msg_list =    res.data;
                }
              });
            }
        }
    });
});
kp_html/kp/js/page.js
@@ -576,11 +576,6 @@
                                content: $("#want_code_dialog"),
                            })
                        }, 100);
                        // pyjs.show_want_codes(plate,JSON.stringify(datas));
                    });
                },
kp_html/kp/msg_list.html
New file
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>消息列表</title>
        <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <script src="http://cdn.yeshitv.com/js/vue.min.js"></script>
        <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
        <script src="js/msg_list.js"></script>
        <script src="js/qwebchannel.js"></script>
        <script src="js/kpl.js"></script>
        <script src="js/http.js"></script>
        <style>
            body {
                max-width: 1060px;
                font-size: 13px;
                color: #000000;
                background: #FFFFFF;
                font-family: Microsfot YaHei,微软雅黑;
            }
            .msg{
                word-wrap: break-word;
            }
        </style>
    </head>
    <body>
        <div id="app">
         <div class="container">
             <div class="msg" v-for="(item,index) in msg_list">
                 {{item}}
             </div>
         </div>
        </div>
    </body>
</html>
kpl/gui.py
@@ -161,11 +161,10 @@
        # 开启定时器,在9点26时读取竞价数据
        # schedule.every().day.at("17:54:30").do(test) #lambda: self.__capture_bidding(None)
        def schedule_fun():
            self.__capture_bidding(None)
        schedule.every().day.at("18:02:30").do(schedule_fun) #lambda: self.__capture_bidding(None)
        schedule.every().day.at("09:26:00").do(schedule_fun) #lambda: self.__capture_bidding(None)
        t1 = threading.Thread(target=self.run_schedule)
        t1.setDaemon(True)
        t1.start()
main.py
@@ -6,18 +6,23 @@
from multiprocessing import freeze_support
import sys
import torch
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
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMessageBox, QLabel, QDialog, QVBoxLayout, QPushButton, \
    QWidget
from PyQt5.QtCore import Qt, pyqtSlot, QObject, pyqtSignal, QTimer, QUrl, QPoint
import gui_wx
import network_util
import setting
import tool
freeze_support()
URL_MSG_LIST = "http://192.168.3.252/kp/msg_list.html"
class BaseBridgeClass(QObject):
@@ -57,6 +62,11 @@
        print("socket_request", text)
        return network_util.socket_request(json.loads(text))
    # 获取客户端ID
    @pyqtSlot(result=str)
    def get_client(self):
        return setting.get_client()
class JSBridgeClass(BaseBridgeClass):
@@ -73,16 +83,80 @@
    def set_trade_info(self, code, name, trade_data, trade_record, initiative_buy_codes, passive_buy_codes):
        self.window.set_trade_data(code, name, trade_data, trade_record, initiative_buy_codes, passive_buy_codes)
    @pyqtSlot(str, str)
    def show_want_codes(self, plate, codes):
        WantBuyWindow(plate, json.loads(codes)).show()
class MessageWindow(QMainWindow):
    msgChange = pyqtSignal(str)
    def __setMsg(self, msg):
        print("收到信息:", msg)
        self.label.setText(msg)
        self.show()
        self.timer.stop()
        self.timer.start(5000)
    # 设置信息
    def setMsg(self, msg):
        self.msgChange.emit(msg)
    def __init__(self):
        super().__init__()
        window_height = 80
        padding = 10
        window_info = setting.get_float_watch_window_info()
        if window_info:
            self.move(window_info[0] + padding - 3, window_info[1] - window_height - 30)
            self.resize(window_info[2] - (padding - 2) * 2, window_height)
        else:
            self.resize(300, 50)
        self.setWindowTitle("消息提示")
        self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.setWindowOpacity(0.9)
        # 去掉标题栏
        # self.setWindowFlags(Qt.FramelessWindowHint)
        # 删除最大最小化按钮
        # self.setWindowFlags(self.windowFlags() & ~Qt.WindowMaximizeButtonHint & ~Qt.WindowMinimizeButtonHint)
        font = QFont('微软雅黑', 12)  # 设置字体为微软雅黑,字号为12
        palette = QPalette()  # 创建一个调色板
        palette.setColor(QPalette.WindowText, QColor(255, 0, 0))  # 设置字体颜色为红色
        self.label = QLabel(self)
        self.label.setStyleSheet(f"padding: {padding}px;")
        self.label.setFont(font)
        self.label.setPalette(palette)  # 将该调色板应用到QLabel上
        self.label.setAlignment(Qt.AlignTop)
        self.label.resize(self.width(), self.height())
        # 设置多行显示
        self.label.setWordWrap(True)  # 设置Label的自动换行
        self.timer = QTimer()
        self.timer.timeout.connect(self.close)
        # 只运行1次定时器
        self.timer.setSingleShot(True)
        self.msgChange.connect(self.__setMsg)
class WantBuyWindow(QMainWindow):
    def __init__(self, plate, codes):
        super(WantBuyWindow, self).__init__()
        self.setWindowTitle(f'想买单({plate})')
        self.resize(300, 200)
class MsgListWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('历史消息')
        # 窗口置顶
        self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.resize(500, 800)
        self.webview = QWebEngineView()
        self.webview.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
        self.webview.page().setZoomFactor(1)
        self.setCentralWidget(self.webview)
        # JS桥设置
        channel = QWebChannel(self.webview.page())
        self.webview.page().setWebChannel(channel)
        self.python_bridge = BaseBridgeClass(self.webview)
        channel.registerObject("Bridge", self.python_bridge)
    def loadUrl(self, url):
        self.webview.load(QUrl(url))
class SecondWindow(QMainWindow):
@@ -164,6 +238,7 @@
    def set_target_code(self, code):
        print("set_target_code")
        self.webview.page().runJavaScript(f"app.set_target_code('{code}')")
        self.wx_pipe.send(json.dumps({"type": "set_code", "code": code}))
    # 菜单及菜单点击事件
    def __menu(self):
@@ -217,6 +292,10 @@
        def __show_second_window():
            self.secondWindow.show()
        def __show_his_msg_window():
            self.msgListWindow.loadUrl(URL_MSG_LIST)
            self.msgListWindow.show()
        # 清除浏览器缓存
        def __clear_webview_cache():
            self.webview.page().profile().clearHttpCache()
@@ -263,6 +342,10 @@
        action.triggered.connect(__show_main_callback)
        view_.addAction(action)
        action = QAction("&打开历史消息", self)
        action.triggered.connect(__show_his_msg_window)
        view_.addAction(action)
    def __init__(self, wx_pipe):
        super().__init__()
        self.wx_pipe = wx_pipe
@@ -297,6 +380,16 @@
        # 绑定槽函数
        self.signal_update_code.connect(self.set_target_code)
        # self.statusBar().showMessage("这是条测试数据额", 10000)
        self.messageWindow = MessageWindow()
        self.msgListWindow = MsgListWindow()
        # 开启消息监听
        t1 = threading.Thread(target=self.read_msg)
        # 后台运行
        t1.setDaemon(True)
        t1.start()
    def closeEvent(self, event):
        event.accept()
@@ -307,6 +400,24 @@
        self.wx_pipe.send(json.dumps({"type": "exit"}))
        sys.exit(0)
    # 读取消息
    def read_msg(self):
        client = setting.get_client()
        if not client:
            client = 'hxh'
        while True:
            if tool.is_trade_time():
                try:
                    res = network_util.http_get(f"/pull_kp_client_msg?client=" + client.strip())
                    if res:
                        res = json.loads(res)
                        if res["code"] == 0:
                            print("拉取到消息", res)
                            self.messageWindow.setMsg(res["data"])
                except:
                    pass
            time.sleep(0.5)
# 打包命令
# cd D:\workspace\GP\trade_desk
res/setting.conf
@@ -1,10 +1,12 @@
[config]
stay_on_top = 1
window_info = [[-1711, 194, 1280, 800], [1473, 621, 320, 195]]
xgb_window_info = [-1921, -8, 1920, 1017]
window_watch_float_info = [294, 497, 435, 200]
xgb_window_info = [-2161, 77, 1920, 1017]
window_watch_float_info = [-1075, 374, 435, 220]
window_tick_info = [-1919, 1, 1918, 1038]
kp_second_window_info = [627, 93, 738, 890]
kp_second_window_info = [-961, 751, 738, 890]
code_attribute_window_info = [-650, 315, 291, 278]
client = hxh
[juejin]
strategy_id = 95a982ce-fc2d-11ec-8ff3-0a0027000010
setting.py
@@ -35,6 +35,21 @@
    cp = __read_setting()
    return int(cp.get("config", "stay_on_top"))
# -------------------------------客户端标识--------------------------
# 设置是否置顶
def set_client(client):
    # 设置是否置顶
    cp = __read_setting()
    cp.set("config", "client", f"{client}")
    __write_setting(cp)
# 获取是否置顶
def get_client():
    cp = __read_setting()
    val = __get_setting(cp, "config", "client")
    return val
# --------------------------------掘金--------------------------------
@@ -223,6 +238,23 @@
        return val
# 设置代码属性框信息
def set_code_attribute_window_info(window):
    cp = __read_setting()
    cp.set("config", "code_attribute_window_info", json.dumps(window))
    __write_setting(cp)
def get_code_attribute_window_info():
    cp = __read_setting()
    val = __get_setting(cp, "config", "code_attribute_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)
ths_util.py
@@ -33,13 +33,25 @@
    hwnds = win32_util.search_window("网上股票交易系统")
    if hwnds:
        for hwnd in hwnds:
            hwnd = win32gui.FindWindowEx(hwnd, None, "ToolbarWindow32", None)
            temp = win32gui.FindWindowEx(hwnd, None, "#32770", None)
            content_hwnd =  win32gui.FindWindowEx(hwnd, None, "AfxMDIFrame140s", None)
            temp = win32gui.FindWindowEx(content_hwnd, None, "#32770", None)
            for i in range(10):
                if win32gui.IsWindowVisible(temp):
                    break
                temp = win32gui.FindWindowEx(content_hwnd, temp, "#32770", None)
            if not temp:
                continue
            t1 = win32gui.FindWindowEx(temp, None, None, "过滤")
            if not win32gui.IsWindowVisible(t1):
                continue
            tool_hwnd = win32gui.FindWindowEx(hwnd, None, "ToolbarWindow32", None)
            temp = win32gui.FindWindowEx(tool_hwnd, None, "#32770", None)
            if temp:
                nq = win32gui.FindWindowEx(temp, None, None, "内嵌")
                if nq and win32gui.IsWindowVisible(nq):
                    continue
            return hwnd
            return tool_hwnd
    return None