Ejemplo n.º 1
0
    def __init__(self, parent):
        super().__init__(parent)
        self.client = QWebSocket("", QWebSocketProtocol.Version13, None)        
        self.client.error.connect(self.onError)
        self.client.connected.connect(self.onConnected)
        self.client.disconnected.connect(self.onDisconnected)
        self.ssl_config = QSslConfiguration()
        self.ssl_cert_file = QFile(":certs/server.pem")
        self.ssl_cert_file.open(QIODevice.ReadOnly)
        self.ssl_cert = QSslCertificate(self.ssl_cert_file)
        self.ssl_config.setCaCertificates([self.ssl_cert])
        self.client.setSslConfiguration(self.ssl_config)
        self.message_queue = []
        self.is_connected = False
        self.telemetry_connected = False
        self.websocket_url = "wss://localhost:8081"
        self.websocket_token = "aVerySecureToken"
        
        self.timer_connection_watchdog = QTimer(parent)
        self.timer_connection_watchdog.start(5000)
        self.timer_connection_watchdog.timeout.connect(self.connection_watchdog)

        self.timer_reconnect = QTimer(parent)
        self.timer_reconnect.start(500)
        self.timer_reconnect.timeout.connect(self.send_message)
Ejemplo n.º 2
0
class WSClient(QObject):
    def __init__(self, parent):
        super().__init__(parent)
        self.client = QWebSocket("", QWebSocketProtocol.Version13, None)        
        self.client.error.connect(self.onError)
        self.client.connected.connect(self.onConnected)
        self.client.disconnected.connect(self.onDisconnected)
        self.ssl_config = QSslConfiguration()
        self.ssl_cert_file = QFile(":certs/server.pem")
        self.ssl_cert_file.open(QIODevice.ReadOnly)
        self.ssl_cert = QSslCertificate(self.ssl_cert_file)
        self.ssl_config.setCaCertificates([self.ssl_cert])
        self.client.setSslConfiguration(self.ssl_config)
        self.message_queue = []
        self.is_connected = False
        self.telemetry_connected = False
        self.websocket_url = "wss://localhost:8081"
        self.websocket_token = "aVerySecureToken"
        
        self.timer_connection_watchdog = QTimer(parent)
        self.timer_connection_watchdog.start(5000)
        self.timer_connection_watchdog.timeout.connect(self.connection_watchdog)

        self.timer_reconnect = QTimer(parent)
        self.timer_reconnect.start(500)
        self.timer_reconnect.timeout.connect(self.send_message)

    def onConnected(self):
        # print("WebSocket: connected")
        self.is_connected = True

    def onDisconnected(self):
        # print("WebSocket: disconnected -> " + self.client.errorString())
        self.is_connected = False
        self.client.abort()
    
    def onError(self):
        # print("WebSocket: error -> " + self.client.errorString())
        self.is_connected = False
        self.client.abort()
    

    def queue_message(self, message):
        self.message_queue.append(message)

    def send_message(self):
        if self.is_connected and len(self.message_queue) > 0:
            message = self.message_queue.pop(0)
            message['token'] = self.websocket_token
            if self.client.sendTextMessage(dumps(message, default=json_util.default)) <= 0:
                self.message_queue.append(message)

    def connection_watchdog(self):
        if self.is_connected == False and self.telemetry_connected == True:
            # print("WebSocket: connecting to -> " + self.websocket_url)
            c = self.client.open(QUrl(self.websocket_url))
        if self.is_connected == True and self.telemetry_connected == False:
            # print("WebSocket: disconnecting from -> " + self.websocket_url)
            self.client.close()
            self.is_connected = False
Ejemplo n.º 3
0
class Chat(QObject):

    comment_received = pyqtSignal(dict)

    def __init__(self):
        super(Chat, self).__init__()
        self.websocket = QWebSocket()
        self.websocket.textMessageReceived.connect(self.on_text_message_received)

    def connect(self, broadcast: Broadcast):
        try:
            live_comments_websocket = broadcast.live_comments_websocket()
        except UnsuccessfulRequestException as e:
            logger.error('Could not fetch the video json. Status code: %i. Response: %s', 
                         e.status_code, e.response_content)
            raise
        except KeyError:
            logger.exception('Could not fetch the chat websocket address.')
            raise
            
        self.websocket.open(QUrl(live_comments_websocket))

    @pyqtSlot(str)
    def on_text_message_received(self, response):
        comment = json.loads(response)
        if comment['type'] != 'new_comment':
            return
        self.comment_received.emit(comment)
Ejemplo n.º 4
0
 def __init__(self):
     QWebSocket.__init__(
         self, origin="", version=QWebSocketProtocol.Version13, parent=None
     )
     # Connect base methods to signals
     self.connected.connect(self.__connected)
     self.disconnected.connect(self.__disconnected)
Ejemplo n.º 5
0
    def OpenConnection(self):
        self.CloseConnection()

        if (self.connectionType.lower() == "socket"
                or self.connectionType.lower() == "qtsocket"):
            if "ws:" in self.connectionName:
                connectionName = self.connectionName.replace("ws://", "")
                (ip, port) = connectionName.rsplit(":", 1)
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5679"
                connectionName = "ws://" + ip + ":" + port
                from PyQt5.QtWebSockets import QWebSocket
                from PyQt5.QtCore import QUrl
                self.connection = QWebSocket()
                print("opening websocket " + connectionName)
                self.connection.open(QUrl(connectionName))
                self.connection.binaryMessageReceived.connect(
                    self.processBinaryMessage)
                self.sendBytesFn = self.connection.sendBinaryMessage
            else:
                #print("opening TCP socket " + self.connectionName)
                (ip, port) = self.connectionName.rsplit(":", 1)
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5678"

                port = int(port)

                #print("ip is ", ip, ", port is ", port)
                self.connection = QtNetwork.QTcpSocket(self)
                self.connection.error.connect(self.displayConnectError)
                ret = self.connection.readyRead.connect(self.readRxBuffer)
                self.connection.connectToHost(ip, port)
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
                #print("making connection returned", ret, "for socket", self.connection)
            self.connection.connected.connect(self.onConnected)
            self.connection.disconnected.connect(self.onDisconnect)
        elif (self.connectionType.lower() == "file"):
            try:
                self.connection = open(self.connectionName, 'rb')
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
            except IOError:
                print("\nERROR!\ncan't open file ", self.connectionName)
                sys.exit(1)
        else:
            print("\nERROR!\nneed to specify socket or file")
            sys.exit()

        self.connection
Ejemplo n.º 6
0
    def __init__(self):
        QObject.__init__(self)
        self.websocket = QWebSocket()

        self._connected = False
        self._data = None

        self.websocket.connected.connect(self.slot_connected)
        self.websocket.disconnected.connect(self.slot_disconnected)
        self.websocket.textMessageReceived.connect(self.slot_message_received)
        self.websocket.error.connect(self.slot_error)
Ejemplo n.º 7
0
    def __init__(self, parent=None):
        super().__init__()
        self.setReadOnly(True)
        self.setFont(textfont)

        self.WS = QWebSocket()
        self.WS.textMessageReceived.connect(
            self.signal_textMessageReceived_process)
        self.WS.connected.connect(self.signal_connected_process)
        self.WS.disconnected.connect(self.signal_disconnected_process)
        self.WS.error.connect(self.signal_error_process)
        self.WS.readChannelFinished.connect(
            self.signal_readChannelFinished_process)
Ejemplo n.º 8
0
    def __init__(self, logger, core, config):
        super().__init__(core)
        self.core = core
        self.logger = logger
        disabled = False
        self.config = config
        self.url = config['multichat-url'].rstrip('/') + '/'
        self.key = config['multichat-key']
        self.server_name = config[
            'server-name'] if 'server-name' in config else ''
        self.do_listen = config['listen']
        self.do_post = config['post']
        self.ignore_prefix = config['ignore-prefix']
        self.lang = 'en'  # default value
        if 'lang' in config:
            if config['lang'] not in SUPPORTED_LANGUAGES:
                self.logger.warning('Unsupported language: {}'.format(
                    config['lang']))
            else:
                self.lang = config['lang']

        # load mcBasicLib
        self.utils = core.get_plugin('mcBasicLib')
        if self.utils is None:
            self.logger.error(
                'Failed to load plugin "mcBasicLib", multiChat will be disabled.'
            )
            self.logger.error(
                'Please make sure that "mcBasicLib" has been added to plugins.'
            )
            disabled = True
        if disabled:
            return

        self.ws = QWebSocket()
        self.ws_valid = False
        # connect signals and slots
        self.core.sig_server_start.connect(self.on_server_start)
        self.core.sig_server_stop.connect(self.on_server_stop)
        self.utils.sig_input.connect(self.on_player_input)
        self.utils.sig_login.connect(self.on_player_login)
        self.utils.sig_logout.connect(self.on_player_logout)
        self.utils.sig_advancement.connect(self.on_advancement)
        self.utils.sig_death.connect(self.on_death)
        self.ws.connected.connect(self.on_connected)
        self.ws.textMessageReceived.connect(self.on_recv)
        self.ws.disconnected.connect(self.on_connection_broken)
        self.retry_interval = RETRY_INTERVAL_MIN
        self.retry_timer = QtCore.QTimer()
        self.retry_timer.timeout.connect(self.on_retry_timer)
        self.on_retry_timer()  # open connection using this function
Ejemplo n.º 9
0
class Chat(QObject):

    comment_received = pyqtSignal(str)

    def __init__(self):
        super(Chat, self).__init__()
        self.websocket = QWebSocket()
        self.websocket.textMessageReceived.connect(
            self.on_text_message_received)

    def connect(self):
        live_comments_websocket = reddit.get_live_comments_websocket()
        if live_comments_websocket is None:
            return
        self.websocket.open(QUrl(live_comments_websocket))

    @pyqtSlot(str)
    def on_text_message_received(self, response):
        self.comment_received.emit(response)
Ejemplo n.º 10
0
    def OpenConnection(self):
        self.CloseConnection()

        if(self.connectionType.lower() == "socket" or self.connectionType.lower() == "qtsocket"):
            if "ws:" in self.connectionName:
                connectionName = self.connectionName.replace("ws://","")
                (ip, port) = connectionName.rsplit(":",1)
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5679"
                connectionName = "ws://"+ip+":"+port
                from PyQt5.QtWebSockets import QWebSocket
                from PyQt5.QtCore import QUrl
                self.connection = QWebSocket()
                print("opening websocket " + connectionName)
                self.connection.open(QUrl(connectionName))
                self.connection.binaryMessageReceived.connect(self.processBinaryMessage)
                self.sendBytesFn = self.connection.sendBinaryMessage
            else:
                #print("opening TCP socket " + self.connectionName)
                (ip, port) = self.connectionName.rsplit(":",1)
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5678"
                
                port = int(port)

                #print("ip is ", ip, ", port is ", port)
                self.connection = QtNetwork.QTcpSocket(self)
                self.connection.error.connect(self.displayConnectError)
                ret = self.connection.readyRead.connect(self.readRxBuffer)
                self.connection.connectToHost(ip, port)
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
                #print("making connection returned", ret, "for socket", self.connection)
            self.connection.connected.connect(self.onConnected)
            self.connection.disconnected.connect(self.onDisconnect)
        elif(self.connectionType.lower() == "file"):
            try:
                self.connection = open(self.connectionName, 'rb')
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
            except IOError:
                print("\nERROR!\ncan't open file ", self.connectionName)
                sys.exit(1)
        else:
            print("\nERROR!\nneed to specify socket or file")
            sys.exit()

        self.connection
Ejemplo n.º 11
0
 def __init__(self, event_loop, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.ui: QWidget = uic.loadUi(ui_settings.ui_main_window)
     self.setup_ui()
     self.tray_icon = self.setup_tray_icon()
     self.tray_icon.show()
     self.ensure_addr_not_in_use()
     self.event_loop = event_loop
     self.ws_client = QWebSocket()
     self.ping_daemon_timer = QTimer(self)
     self.ping_daemon_timer.setInterval(1000)
     self.ping_daemon_timer.start()
     self.sync_status_timer = QTimer(self)
     self.sync_status_timer.setInterval(2000)
     self.connect_signals()
     sg = QDesktopWidget().screenGeometry()
     widget = self.ui.geometry()
     self.ui.move(int((sg.width() - widget.width()) / 4),
                  int((sg.height() - widget.height()) / 3))
     self.start_memority_core()
Ejemplo n.º 12
0
    def __init__(self):
        super(aliPayControl, self).__init__()
        self.setupUi(self)
        self.show()
        self.config = ConfigObj(profile.CONFIG_INI_URL,
                                encoding=profile.ENCODING)
        self.websocket = QWebSocket("",
                                    QtWebSockets.QWebSocketProtocol.Version13,
                                    None)  # 与直播服务器的连接
        self.websocket.connected.connect(self.on_connected)
        self.websocket.disconnected.connect(self.on_disconnected)
        self.websocket.textMessageReceived.connect(self.on_textMessageReceived)
        self.websocket.error.connect(self.on_error)
        self.websocket.stateChanged.connect(self.on_stateChanged)

        self.start_work_signal.connect(
            self.on_start_work_signal)  # 接收到服务器开始工作的指令

        self.option = webdriver.ChromeOptions()
        self.title = ""
        self.option.add_argument('disable-infobars')
        self.option.add_argument('--log-level=3')
        self.option.add_experimental_option("excludeSwitches",
                                            ['enable-automation'])
        self.driver = Chrome(chrome_options=self.option,
                             executable_path=profile.WEB_DRIVER_PATH)
        self.driver.implicitly_wait(2)  # 所有的加载页面隐式等待20秒

        self.time = 2
        self.wait_time = 5
        self.exception = (Exception, )
        self.flash_timer = QTimer(self)
        self.flash_timer.timeout.connect(self.on_flash_timer)
        self.flash_timer.setInterval(int(self.config['INTERVAL']) * 1000)
        self.ping_timer = QTimer(self)
        self.ping_timer.timeout.connect(self.on_ping_timer)

        self.ping_timer.setInterval(20 * 1000)
Ejemplo n.º 13
0
class ChatRoom(QTextEdit):
    def __init__(self, parent=None):
        super().__init__()
        self.setReadOnly(True)
        self.setFont(textfont)

        self.WS = QWebSocket()
        self.WS.textMessageReceived.connect(
            self.signal_textMessageReceived_process)
        self.WS.connected.connect(self.signal_connected_process)
        self.WS.disconnected.connect(self.signal_disconnected_process)
        self.WS.error.connect(self.signal_error_process)
        self.WS.readChannelFinished.connect(
            self.signal_readChannelFinished_process)

    def close(self):
        if self.WS:
            self.WS.close()

    def set_roomid(self, roomid):
        self.roomid = roomid
        self.WS.close()

        print(roomid)
        self.WS.open(
            QUrl('ws://mbgows.plu.cn:8805/?room_id=' + str(roomid) +
                 '&batch=1&group=0&connType=1'))

    def signal_textMessageReceived_process(self, message):
        print(message)
        json_msg = json.loads(message)
        print(type(json_msg))
        if type(json_msg) == list:
            msg = json_msg[0]
        else:
            msg = json_msg

        try:
            self.insertHtml(
                '<font color="Blue">{}</font> <font color="Black">{}</font><br>'
                .format(msg['msg']['user']['username'], msg['msg']['content']))
        except:
            print(msg)
        #auto scroll
        sb = self.verticalScrollBar()
        sb.setValue(sb.maximum())

    def signal_connected_process(self):
        print('signal_connected_process')

    def signal_disconnected_process(self):
        print('signal_disconnected_process')

    def signal_error_process(self, error):
        print('signal_error_process')

    def signal_readChannelFinished_process(self):
        print('signal_readChannelFinished_process')
Ejemplo n.º 14
0
    def __init__(self, *args, **kwargs):
        super(WebSocketWindow, self).__init__(*args, **kwargs)
        uic.loadUi('WebSocketTest.ui', self)
        self._param_sep = '\n\n' + '-' * 50
        self._files = []
        self.listWidget.setEnabled(False)

        # 读取配置文件
        setting = QSettings(
            'F:/QtDesigner/Windows/plugins/designer/data/server.ini',
            QSettings.IniFormat)
        port = setting.value('ws_port', 55441)

        self.editAddress.setPlaceholderText('比如:ws://127.0.0.1:%s' % port)
        self.editAddress.setText('ws://127.0.0.1:%s' % port)

        self._socket = QWebSocket(parent=self)
        self._socket.connected.connect(self.onConnected)
        self._socket.disconnected.connect(self.onDisconnected)
        self._socket.binaryMessageReceived.connect(
            self.onBinaryMessageReceived)
        self._socket.textMessageReceived.connect(self.onTextMessageReceived)
        self._socket.error.connect(self.onError)
Ejemplo n.º 15
0
    def __init__(self, parent=None):
        super(Client, self).__init__(parent)

        self.networkSession = None
        self.blockSize = 0
        self.currentFortune = ''
        self.title = "扫码收货PC辅助程序"

        hostLabel = QLabel('IP:')
        hostLabel.setFont(qtawesome.font('fa', 14))

        portLabel = QLabel('端口:')
        portLabel.setFont(qtawesome.font('fa', 14))

        taskCodeLabel = QLabel('任务码:')
        taskCodeLabel.setFont(qtawesome.font('fa', 14))
        self.serverMsgLable = QLabel('来自服务端的消息:')
        self.serverMsgLable.setFont(qtawesome.font('fa', 14))

        self.sendMsgLabel = QLabel('将要发送的消息:')
        self.sendMsgLabel.setFont(qtawesome.font('fa', 14))

        self.hostCombo = QComboBox()
        self.hostCombo.setEditable(True)

        name = QHostInfo.localHostName()
        if name != '':
            self.hostCombo.addItem(name)

            domain = QHostInfo.localDomainName()
            if domain != '':
                self.hostCombo.addItem(name + '.' + domain)

        if name != 'localhost':
            self.hostCombo.addItem('10.286.88.124')

        ipAddressesList = QNetworkInterface.allAddresses()

        for ipAddress in ipAddressesList:
            if not ipAddress.isLoopback():
                self.hostCombo.addItem(ipAddress.toString())

        for ipAddress in ipAddressesList:
            if ipAddress.isLoopback():
                self.hostCombo.addItem(ipAddress.toString())

        self.portLineEdit = QLineEdit()
        self.portLineEdit.setValidator(QIntValidator(1, 65535, self))
        self.portLineEdit.setPlaceholderText("请输入端口")

        self.taskLineEdit = QLineEdit()
        self.taskLineEdit.setPlaceholderText("请向组长询问后输入任务码")
        self.taskLineEdit.setValidator(QIntValidator(1, 9999, self))

        self.serverLineEdit = QLineEdit()
        self.serverLineEdit.setPlaceholderText('服务器发送的消息会显示在这里')

        self.sendTextEdit = QTextEdit()
        self.sendTextEdit.setPlaceholderText('请输入先要发送给服务器的消息')

        hostLabel.setBuddy(self.hostCombo)
        portLabel.setBuddy(self.portLineEdit)
        taskCodeLabel.setBuddy(self.taskLineEdit)
        self.serverMsgLable.setBuddy(self.serverLineEdit)
        self.sendMsgLabel.setBuddy(self.sendTextEdit)

        self.statusLabel = QLabel("状态:尚未连接")
        self.statusLabel.setAutoFillBackground(True)
        self.statusLabel.setAlignment(Qt.AlignCenter)
        palette = QPalette()  # 新建一个调色板
        palette.setColor(QPalette.Window, Qt.red)  # 设置颜色
        self.statusLabel.setPalette(palette)
        self.statusLabel.setStyleSheet('''
                color:#ffffff;
                font-size:18px;
                font-weight:bold;
        ''')

        self.getFortuneButton = QPushButton("启动连接")
        self.getFortuneButton.setDefault(True)
        self.getFortuneButton.setEnabled(False)

        quitButton = QPushButton("退出")
        self.stopButton = QPushButton("中止连接")
        self.stopButton.setDefault(True)
        self.stopButton.setEnabled(False)

        buttonBox = QDialogButtonBox()
        buttonBox.addButton(self.getFortuneButton, QDialogButtonBox.ActionRole)
        buttonBox.addButton(self.stopButton, QDialogButtonBox.AcceptRole)
        buttonBox.addButton(quitButton, QDialogButtonBox.RejectRole)
        self.sendMsgbutton = QPushButton('发送消息')
        self.webSocket = QWebSocket()

        self.hostCombo.editTextChanged.connect(self.enableGetFortuneButton)
        self.portLineEdit.textChanged.connect(self.enableGetFortuneButton)
        self.taskLineEdit.textChanged.connect(self.enableGetFortuneButton)
        self.getFortuneButton.clicked.connect(self.CreateNewConn)
        self.stopButton.clicked.connect(self.stopCurrentConn)
        quitButton.clicked.connect(self.close)
        self.webSocket.connected.connect(self.websocketConnect)
        self.webSocket.disconnected.connect(self.webSocketDisconnect)
        self.webSocket.error.connect(self.displayError)
        self.webSocket.textMessageReceived.connect(
            self.webSocketMessageReceived)
        self.sendTextEdit.textChanged.connect(self.enableSendMessageButton)
        self.sendMsgbutton.clicked.connect(self.sendMsgToServer)

        mainLayout = QGridLayout()
        mainLayout.addWidget(hostLabel, 0, 0)
        mainLayout.addWidget(self.hostCombo, 0, 1)
        mainLayout.addWidget(portLabel, 1, 0)
        mainLayout.addWidget(self.portLineEdit, 1, 1)
        mainLayout.addWidget(taskCodeLabel, 2, 0)
        mainLayout.addWidget(self.taskLineEdit, 2, 1)
        mainLayout.addWidget(self.statusLabel, 3, 0, 1, 2)
        mainLayout.addWidget(buttonBox, 4, 0, 1, 2)
        mainLayout.addWidget(self.serverMsgLable, 5, 0)
        mainLayout.addWidget(self.serverLineEdit, 5, 1, 1, 1)
        mainLayout.addWidget(self.sendMsgLabel, 6, 0)
        mainLayout.addWidget(self.sendTextEdit, 6, 1)
        mainLayout.addWidget(self.sendMsgbutton, 7, 0, 1, 5)
        self.serverLineEdit.setEnabled(False)
        self.serverMsgLable.setVisible(False)
        self.serverLineEdit.setVisible(False)
        self.sendMsgLabel.setVisible(False)
        self.sendTextEdit.setVisible(False)
        self.sendMsgbutton.setEnabled(False)
        self.sendMsgbutton.setVisible(False)
        self.setLayout(mainLayout)
        mainLayout.setSpacing(10)
        self.setWindowTitle(self.title)
        self.portLineEdit.setFocus()
        manager = QNetworkConfigurationManager()
        if manager.capabilities(
        ) & QNetworkConfigurationManager.NetworkSessionRequired:
            settings = QSettings(QSettings.UserScope, 'QtProject')
            settings.beginGroup('QtNetwork')
            id = settings.value('DefaultNetworkConfiguration')
            settings.endGroup()

            config = manager.configurationFromIdentifier(id)
            if config.state() & QNetworkConfiguration.Discovered == 0:
                config = manager.defaultConfiguration()

            self.networkSession = QNetworkSession(config, self)
            self.networkSession.opened.connect(self.sessionOpened)

            self.getFortuneButton.setEnabled(False)
            self.statusLabel.setText("Opening network session.")
            self.networkSession.open()
Ejemplo n.º 16
0
class Client(QDialog):
    def __init__(self, parent=None):
        super(Client, self).__init__(parent)

        self.networkSession = None
        self.blockSize = 0
        self.currentFortune = ''
        self.title = "扫码收货PC辅助程序"

        hostLabel = QLabel('IP:')
        hostLabel.setFont(qtawesome.font('fa', 14))

        portLabel = QLabel('端口:')
        portLabel.setFont(qtawesome.font('fa', 14))

        taskCodeLabel = QLabel('任务码:')
        taskCodeLabel.setFont(qtawesome.font('fa', 14))
        self.serverMsgLable = QLabel('来自服务端的消息:')
        self.serverMsgLable.setFont(qtawesome.font('fa', 14))

        self.sendMsgLabel = QLabel('将要发送的消息:')
        self.sendMsgLabel.setFont(qtawesome.font('fa', 14))

        self.hostCombo = QComboBox()
        self.hostCombo.setEditable(True)

        name = QHostInfo.localHostName()
        if name != '':
            self.hostCombo.addItem(name)

            domain = QHostInfo.localDomainName()
            if domain != '':
                self.hostCombo.addItem(name + '.' + domain)

        if name != 'localhost':
            self.hostCombo.addItem('10.286.88.124')

        ipAddressesList = QNetworkInterface.allAddresses()

        for ipAddress in ipAddressesList:
            if not ipAddress.isLoopback():
                self.hostCombo.addItem(ipAddress.toString())

        for ipAddress in ipAddressesList:
            if ipAddress.isLoopback():
                self.hostCombo.addItem(ipAddress.toString())

        self.portLineEdit = QLineEdit()
        self.portLineEdit.setValidator(QIntValidator(1, 65535, self))
        self.portLineEdit.setPlaceholderText("请输入端口")

        self.taskLineEdit = QLineEdit()
        self.taskLineEdit.setPlaceholderText("请向组长询问后输入任务码")
        self.taskLineEdit.setValidator(QIntValidator(1, 9999, self))

        self.serverLineEdit = QLineEdit()
        self.serverLineEdit.setPlaceholderText('服务器发送的消息会显示在这里')

        self.sendTextEdit = QTextEdit()
        self.sendTextEdit.setPlaceholderText('请输入先要发送给服务器的消息')

        hostLabel.setBuddy(self.hostCombo)
        portLabel.setBuddy(self.portLineEdit)
        taskCodeLabel.setBuddy(self.taskLineEdit)
        self.serverMsgLable.setBuddy(self.serverLineEdit)
        self.sendMsgLabel.setBuddy(self.sendTextEdit)

        self.statusLabel = QLabel("状态:尚未连接")
        self.statusLabel.setAutoFillBackground(True)
        self.statusLabel.setAlignment(Qt.AlignCenter)
        palette = QPalette()  # 新建一个调色板
        palette.setColor(QPalette.Window, Qt.red)  # 设置颜色
        self.statusLabel.setPalette(palette)
        self.statusLabel.setStyleSheet('''
                color:#ffffff;
                font-size:18px;
                font-weight:bold;
        ''')

        self.getFortuneButton = QPushButton("启动连接")
        self.getFortuneButton.setDefault(True)
        self.getFortuneButton.setEnabled(False)

        quitButton = QPushButton("退出")
        self.stopButton = QPushButton("中止连接")
        self.stopButton.setDefault(True)
        self.stopButton.setEnabled(False)

        buttonBox = QDialogButtonBox()
        buttonBox.addButton(self.getFortuneButton, QDialogButtonBox.ActionRole)
        buttonBox.addButton(self.stopButton, QDialogButtonBox.AcceptRole)
        buttonBox.addButton(quitButton, QDialogButtonBox.RejectRole)
        self.sendMsgbutton = QPushButton('发送消息')
        self.webSocket = QWebSocket()

        self.hostCombo.editTextChanged.connect(self.enableGetFortuneButton)
        self.portLineEdit.textChanged.connect(self.enableGetFortuneButton)
        self.taskLineEdit.textChanged.connect(self.enableGetFortuneButton)
        self.getFortuneButton.clicked.connect(self.CreateNewConn)
        self.stopButton.clicked.connect(self.stopCurrentConn)
        quitButton.clicked.connect(self.close)
        self.webSocket.connected.connect(self.websocketConnect)
        self.webSocket.disconnected.connect(self.webSocketDisconnect)
        self.webSocket.error.connect(self.displayError)
        self.webSocket.textMessageReceived.connect(
            self.webSocketMessageReceived)
        self.sendTextEdit.textChanged.connect(self.enableSendMessageButton)
        self.sendMsgbutton.clicked.connect(self.sendMsgToServer)

        mainLayout = QGridLayout()
        mainLayout.addWidget(hostLabel, 0, 0)
        mainLayout.addWidget(self.hostCombo, 0, 1)
        mainLayout.addWidget(portLabel, 1, 0)
        mainLayout.addWidget(self.portLineEdit, 1, 1)
        mainLayout.addWidget(taskCodeLabel, 2, 0)
        mainLayout.addWidget(self.taskLineEdit, 2, 1)
        mainLayout.addWidget(self.statusLabel, 3, 0, 1, 2)
        mainLayout.addWidget(buttonBox, 4, 0, 1, 2)
        mainLayout.addWidget(self.serverMsgLable, 5, 0)
        mainLayout.addWidget(self.serverLineEdit, 5, 1, 1, 1)
        mainLayout.addWidget(self.sendMsgLabel, 6, 0)
        mainLayout.addWidget(self.sendTextEdit, 6, 1)
        mainLayout.addWidget(self.sendMsgbutton, 7, 0, 1, 5)
        self.serverLineEdit.setEnabled(False)
        self.serverMsgLable.setVisible(False)
        self.serverLineEdit.setVisible(False)
        self.sendMsgLabel.setVisible(False)
        self.sendTextEdit.setVisible(False)
        self.sendMsgbutton.setEnabled(False)
        self.sendMsgbutton.setVisible(False)
        self.setLayout(mainLayout)
        mainLayout.setSpacing(10)
        self.setWindowTitle(self.title)
        self.portLineEdit.setFocus()
        manager = QNetworkConfigurationManager()
        if manager.capabilities(
        ) & QNetworkConfigurationManager.NetworkSessionRequired:
            settings = QSettings(QSettings.UserScope, 'QtProject')
            settings.beginGroup('QtNetwork')
            id = settings.value('DefaultNetworkConfiguration')
            settings.endGroup()

            config = manager.configurationFromIdentifier(id)
            if config.state() & QNetworkConfiguration.Discovered == 0:
                config = manager.defaultConfiguration()

            self.networkSession = QNetworkSession(config, self)
            self.networkSession.opened.connect(self.sessionOpened)

            self.getFortuneButton.setEnabled(False)
            self.statusLabel.setText("Opening network session.")
            self.networkSession.open()

    def enableSendMessageButton(self):
        self.sendMsgbutton.setEnabled(self.sendTextEdit.toPlainText() != '')

    def sendMsgToServer(self):
        message = self.sendTextEdit.toPlainText()
        self.webSocket.sendTextMessage(message)

    def CreateNewConn(self):
        '''
        连接按钮点击事件,尝试进行连接 QWebSocket
        :return:
        '''
        self.getFortuneButton.setText("连接中,请等待")
        self.getFortuneButton.setEnabled(False)
        self.stopButton.setEnabled(True)
        self.webSocket.abort()
        host = self.hostCombo.currentText()
        port = self.portLineEdit.text()
        task_code = self.taskLineEdit.text()
        format_url = 'ws://{}:{}/websocket/master/{}/{}'.format(
            host, port, 'xiangjqjngkljjkl12345', task_code)
        print(format_url)
        request_url = QUrl(format_url)
        self.webSocket.open(request_url)

    def stopCurrentConn(self):
        if self.webSocket is not None and self.webSocket.isValid():
            print('终止连接')
            self.getFortuneButton.setEnabled(True)
            self.stopButton.setEnabled(False)
            self.webSocket.close(QWebSocketProtocol.CloseCodeNormal,
                                 reason='终止连接')
            self.webSocket.close()

    def displayError(self, socketError):
        if socketError == QAbstractSocket.RemoteHostClosedError:
            pass
        elif socketError == QAbstractSocket.HostNotFoundError:
            QMessageBox.information(self, self.title, "找不到主机。请检查主机名和端口设置")
        elif socketError == QAbstractSocket.ConnectionRefusedError:
            QMessageBox.information(
                self, self.title, '''
            连接被同伴拒绝了。确保服务器正在运行,并检查主机名端口设置正确
            ''')
        else:
            QMessageBox.information(
                self, self.title, "出现下列错误: %s." % self.webSocket.errorString())

        self.getFortuneButton.setEnabled(True)
        self.getFortuneButton.setText("重新连接")

    def enableGetFortuneButton(self):
        self.getFortuneButton.setEnabled(
            (self.networkSession is None or self.networkSession.isOpen())
            and self.hostCombo.currentText() != ''
            and self.portLineEdit.text() != ''
            and self.taskLineEdit.text() != '')

    def sessionOpened(self):
        config = self.networkSession.configuration()

        if config.type() == QNetworkConfiguration.UserChoice:
            id = self.networkSession.sessionProperty('UserChoiceConfiguration')
        else:
            id = config.identifier()

        settings = QSettings(QSettings.UserScope, 'QtProject')
        settings.beginGroup('QtNetwork')
        settings.setValue('DefaultNetworkConfiguration', id)
        settings.endGroup()

        self.statusLabel.setText("This examples requires that you run the "
                                 "Fortune Server example as well.")
        self.enableGetFortuneButton()

    def websocketConnect(self):
        print("websocketConnect")
        self.statusLabel.setText('连接成功')
        self.serverMsgLable.setVisible(True)
        self.serverLineEdit.setVisible(True)
        self.sendMsgLabel.setVisible(True)
        self.sendTextEdit.setVisible(True)
        self.sendMsgbutton.setVisible(True)

    def webSocketDisconnect(self):
        self.statusLabel.setText('连接退出')
        self.getFortuneButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.serverMsgLable.setVisible(False)
        self.serverLineEdit.setVisible(False)
        self.sendMsgLabel.setVisible(False)
        self.sendTextEdit.setVisible(False)
        self.sendMsgbutton.setVisible(False)
        self.getFortuneButton.setText('重新连接')
        print("disconnected")

    def webSocketMessageReceived(self, p_str):
        self.serverLineEdit.setText('接收到消息' + p_str)
        self.statusLabel.setText('接收到消息' + p_str)
        print("webSocketMessageReceived" + p_str)
Ejemplo n.º 17
0
class WsClient(QObject):
    sig_message = pyqtSignal(str)
    sig_connected = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)
        self.websocket = QWebSocket()

        self._connected = False
        self._data = None

        self.websocket.connected.connect(self.slot_connected)
        self.websocket.disconnected.connect(self.slot_disconnected)
        self.websocket.textMessageReceived.connect(self.slot_message_received)
        self.websocket.error.connect(self.slot_error)

    def is_connected(self):
        return self._connected

    def set_data(self, data):
        self._data = data

    def data(self):
        return self._data

    def json(self):
        try:
            j = json.loads(self.data())
        except JSONDecodeError as e:
            j = {}

        return j

    def send(self, data):
        if isinstance(data, dict):
            d = json.dumps(data)
        else:
            d = str(data)

        self.websocket.sendTextMessage(d)

    def open(self, url):
        self.websocket.open(QUrl(url))

    def close(self):
        self.websocket.close()

    def slot_connected(self):
        self._connected = True
        self.sig_connected.emit()

    def slot_disconnected(self):
        self._connected = False

    def slot_message_received(self, message: str):
        self.sig_message.emit(message)
        self.set_data(message)

    def slot_error(self, error_code):
        print("error code: {}".format(error_code))
        print(self.websocket.errorString())
Ejemplo n.º 18
0
class App(QtWidgets.QMainWindow):
    RxMsg = QtCore.pyqtSignal(object)
    statusUpdate = QtCore.pyqtSignal(str)
    connectionChanged = QtCore.pyqtSignal(bool)

    def __init__(self, name, headerName, argv, options):
        self.name = name

        # persistent settings
        self.settings = QtCore.QSettings("MsgTools", name)

        # rx buffer, to receive a message with multiple signals
        self.rxBuf = bytearray()

        self.allowedMessages = []
        self.keyFields = {}

        # flag that indicates if we're connected
        self.connected = False

        # directory to load messages from.
        msgLoadDir = None

        # need better handling of command line arguments for case when there is only one arg and it's a filename
        if (len(argv) == 3 and argv[2].lower().endswith((".txt", ".log"))):
            self.connectionType = "file"
            self.connectionName = argv[2]
        else:
            allOptions = options
            options += [
                'connectionType=', 'connectionName=', 'msg=', 'ip=', 'port=',
                'msgdir='
            ]
            self.optlist, args = getopt.getopt(sys.argv[1:], '', allOptions)
            # connection modes
            self.connectionType = "qtsocket"
            self.connectionName = "127.0.0.1:5678"
            ip = ""
            port = ""

            for opt in self.optlist:
                if opt[0] == '--connectionType':
                    self.connectionType = opt[1]
                elif opt[0] == '--connectionName':
                    self.connectionName = opt[1]
                elif opt[0] == '--ip':
                    ip = opt[1]
                elif opt[0] == '--port':
                    port = opt[1]
                elif opt[0] == '--msg':
                    option = opt[1].split('/')
                    self.allowedMessages.append(option[0])
                    if len(option) > 1:
                        self.keyFields[option[0]] = option[1]
                    print("only allowing msg " + str(option))
                elif opt[0] == '--msgdir':
                    msgLoadDir = opt[1]

            # if either --ip or --port were used, override connectionName
            if ip or port:
                self.connectionName = str(ip) + ":" + str(port)

        # initialize the read function to None, so it's not accidentally called
        self.readBytesFn = None

        try:
            self.msgLib = Messaging(msgLoadDir, 0, headerName)
        except ImportError:
            print("\nERROR! Auto-generated python code not found!")
            print(
                "cd to a directory downstream from a parent of obj/CodeGenerator/Python"
            )
            print("or specify that directory with --msgdir=PATH\n")
            quit()

        self.OpenConnection()

    def CloseConnection(self):
        if hasattr(self, 'connection') and (
                self.connectionType.lower() == "socket"
                or self.connectionType.lower() == "qtsocket"):
            if "ws:" in self.connectionName:
                if self.connection:
                    self.connection.close()
                    self.connection = None
            else:
                if self.connection:
                    self.connection.disconnectFromHost()
                    self.connection = None

    # this function opens a connection, and returns the connection object.
    def OpenConnection(self):
        self.CloseConnection()

        if (self.connectionType.lower() == "socket"
                or self.connectionType.lower() == "qtsocket"):
            if "ws:" in self.connectionName:
                connectionName = self.connectionName.replace("ws://", "")
                (ip, port) = connectionName.split(":")
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5679"
                connectionName = "ws://" + ip + ":" + port
                from PyQt5.QtWebSockets import QWebSocket
                from PyQt5.QtCore import QUrl
                self.connection = QWebSocket()
                print("opening websocket " + connectionName)
                self.connection.open(QUrl(connectionName))
                self.connection.binaryMessageReceived.connect(
                    self.processBinaryMessage)
                self.sendBytesFn = self.connection.sendBinaryMessage
            else:
                #print("opening TCP socket " + self.connectionName)
                (ip, port) = self.connectionName.split(":")
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5678"

                port = int(port)

                #print("ip is ", ip, ", port is ", port)
                self.connection = QtNetwork.QTcpSocket(self)
                self.connection.error.connect(self.displayConnectError)
                ret = self.connection.readyRead.connect(self.readRxBuffer)
                self.connection.connectToHost(ip, port)
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
                #print("making connection returned", ret, "for socket", self.connection)
            self.connection.connected.connect(self.onConnected)
            self.connection.disconnected.connect(self.onDisconnect)
        elif (self.connectionType.lower() == "file"):
            try:
                self.connection = open(self.connectionName, 'rb')
            except IOError:
                print("\nERROR!\ncan't open file ", self.connectionName)
            self.readBytesFn = self.connection.read
            self.sendBytesFn = self.connection.write
        else:
            print("\nERROR!\nneed to specify socket or file")
            sys.exit()

        self.connection

    def onConnected(self):
        self.connected = True
        self.connectionChanged.emit(True)
        # send a connect message
        connectMsg = self.msgLib.Messages.Network.Connect()
        connectMsg.SetName(self.name)
        self.SendMsg(connectMsg)
        # if the app has it's own function to happen after connection, assume it will set subscriptions to what it wants.
        try:
            fn = self.onAppConnected
        except AttributeError:
            # send a subscription message
            subscribeMsg = self.msgLib.Messages.Network.MaskedSubscription()
            self.SendMsg(subscribeMsg)
            #self.statusUpdate.emit('Connected')
        else:
            self.onAppConnected()

    def onDisconnect(self):
        self.connected = False
        self.connectionChanged.emit(False)
        #self.statusUpdate.emit('NOT Connected')

    def displayConnectError(self, socketError):
        self.connected = False
        self.connectionChanged.emit(False)
        self.statusUpdate.emit('Not Connected(' + str(socketError) + '), ' +
                               self.connection.errorString())

    # Qt signal/slot based reading of websocket
    def processBinaryMessage(self, bytes):
        hdr = Messaging.hdr(bytes.data())
        # if we got this far, we have a whole message! So, emit the signal
        self.RxMsg.emit(Messaging.MsgFactory(hdr))

    # Qt signal/slot based reading of TCP socket
    def readRxBuffer(self):
        input_stream = QtCore.QDataStream(self.connection)
        while (self.connection.bytesAvailable() > 0):
            # read the header, unless we have the header
            if (len(self.rxBuf) < Messaging.hdrSize):
                #print("reading", Messaging.hdrSize - len(self.rxBuf), "bytes for header")
                self.rxBuf += input_stream.readRawData(Messaging.hdrSize -
                                                       len(self.rxBuf))
                #print("have", len(self.rxBuf), "bytes")

            # if we still don't have the header, break
            if (len(self.rxBuf) < Messaging.hdrSize):
                print("don't have full header, quitting")
                return

            hdr = Messaging.hdr(self.rxBuf)

            # need to decode body len to read the body
            bodyLen = hdr.GetDataLength()

            # read the body, unless we have the body
            if (len(self.rxBuf) < Messaging.hdrSize + bodyLen):
                #print("reading", Messaging.hdrSize + bodyLen - len(self.rxBuf), "bytes for body")
                self.rxBuf += input_stream.readRawData(Messaging.hdrSize +
                                                       bodyLen -
                                                       len(self.rxBuf))

            # if we still don't have the body, break
            if (len(self.rxBuf) < Messaging.hdrSize + bodyLen):
                print("don't have full body, quitting")
                return

            # create a new header object with the appended body
            hdr = Messaging.hdr(self.rxBuf)

            # if we got this far, we have a whole message! So, emit the signal
            self.RxMsg.emit(Messaging.MsgFactory(hdr))
            # then clear the buffer, so we start over on the next message
            self.rxBuf = bytearray()

    def SendMsg(self, msg):
        bufferSize = len(msg.rawBuffer().raw)
        hdr = msg.hdr
        computedSize = Messaging.hdrSize + hdr.GetDataLength()
        if (computedSize > bufferSize):
            hdr.SetDataLength(bufferSize - Messaging.hdrSize)
            print("Truncating message to " + str(computedSize) + " bytes")
        if (computedSize < bufferSize):
            # don't send the *whole* message, just a section of it up to the specified length
            self.sendBytesFn(msg.rawBuffer().raw[0:computedSize])
        else:
            self.sendBytesFn(msg.rawBuffer().raw)

    # this function reads messages (perhaps from a file, like in LumberJack), and calls the message handler.
    # unclear if it ever makes sense to use this in a application that talks to a socket or UART, because
    # it exits when there's no more data.  Perhaps it does in a CLI that's very procedural, like an automated
    # system test script?
    def MessageLoop(self):
        msgCount = 0
        startSeqField = Messaging.findFieldInfo(Messaging.hdr.fields,
                                                "StartSequence")
        if startSeqField == None:
            print("header contains no StartSequence")
        else:
            startSequence = int(Messaging.hdr.GetStartSequence.default)
            print("header contains StartSequence " + hex(startSequence))
        try:
            while (1):
                msgCount += 1
                self.rxBuf = self.readBytesFn(Messaging.hdrSize)

                if (len(self.rxBuf) != Messaging.hdrSize):
                    raise StopIteration

                hdr = Messaging.hdr(self.rxBuf)
                try:
                    start = hdr.GetStartSequence()
                    if (start != startSequence):
                        print("Error on message " + str(msgCount) +
                              ". Start sequence invalid: 0x" +
                              format(start, '02X'))
                        # resync on start sequence
                        bytesThrownAway = 0
                        while (1):
                            self.rxBuf += self.readBytesFn(1)
                            self.rxBuf = self.rxBuf[1:]
                            if (len(self.rxBuf) != Messaging.hdrSize):
                                raise StopIteration
                            bytesThrownAway += 1
                            start = hdr.GetStartSequence()
                            if (start == startSequence):
                                print("Resynced after " +
                                      str(bytesThrownAway) + " bytes")
                                break
                    headerChecksum = hdr.GetHeaderChecksum()
                    bodyChecksum = hdr.GetBodyChecksum(self)
                except AttributeError:
                    pass

                # need to decode body len to read the body
                bodyLen = hdr.GetDataLength()

                # read the body
                self.rxBuf += self.readBytesFn(bodyLen)
                if (len(self.rxBuf) != Messaging.hdrSize + bodyLen): break

                # create a new header object with the appended body
                hdr = Messaging.hdr(self.rxBuf)

                # got a complete message, call the callback to process it
                self.ProcessMessage(Messaging.MsgFactory(hdr))
        except StopIteration:
            print("found end of file, exited")
Ejemplo n.º 19
0
class MainWindow(QMainWindow):
    memority_core = None
    daemon_started = pyqtSignal()
    synced = pyqtSignal()
    request_pool = []

    def __init__(self, event_loop, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ui: QWidget = uic.loadUi(ui_settings.ui_main_window)
        self.setup_ui()
        self.tray_icon = self.setup_tray_icon()
        self.tray_icon.show()
        self.ensure_addr_not_in_use()
        self.event_loop = event_loop
        self.ws_client = QWebSocket()
        self.ping_daemon_timer = QTimer(self)
        self.ping_daemon_timer.setInterval(1000)
        self.ping_daemon_timer.start()
        self.sync_status_timer = QTimer(self)
        self.sync_status_timer.setInterval(2000)
        self.connect_signals()
        sg = QDesktopWidget().screenGeometry()
        widget = self.ui.geometry()
        self.ui.move(int((sg.width() - widget.width()) / 4),
                     int((sg.height() - widget.height()) / 3))
        self.start_memority_core()

    def start_memority_core(self):
        self.memority_core = MemorityCore(event_loop=self.event_loop)
        if utils.check_first_run():
            self.memority_core.prepare()
            return

        password = settings.load_locals().get('password')
        if password:
            try:
                self.memority_core.set_password(password)
                self.memority_core.prepare()
                return
            except Settings.InvalidPassword:
                self.error('Invalid password in settings file!')
        while True:
            try:
                password_dialog: QDialog = uic.loadUi(
                    ui_settings.ui_enter_password)
                password_dialog.password_input.setFocus()
                if not password_dialog.exec_():
                    self.shutdown()
                    return
                password = password_dialog.password_input.text()
                self.memority_core.set_password(password)
                break
            except Settings.InvalidPassword:
                self.error('Invalid password!')
                continue
        self.memority_core.prepare()

    def setup_ui(self):
        self.ui.closeEvent = self.closeEvent
        self.ui.buy_mmr_btn.hide()
        self.ui.transfer_mmr_btn.hide()

        self.ui.transaction_history_lbl.hide()
        self.ui.transaction_history_table.hide()
        self.ui.sync_display_widget.hide()
        self.ui.copy_address_btn.setDisabled(True)
        header = self.ui.transaction_history_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        header.setSectionResizeMode(1, QHeaderView.Stretch)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.Stretch)

        self.ui.refresh_btn.clicked.connect(self.refresh)
        self.ui.copy_address_btn.clicked.connect(
            self.copy_address_to_clipboard)
        self.ui.create_account_btn.clicked.connect(self.create_account)
        self.ui.import_account_btn.clicked.connect(self.import_account)
        self.ui.export_account_btn.clicked.connect(self.export_account)
        self.ui.become_hoster_btn.clicked.connect(self.become_a_hoster)
        self.ui.become_miner_btn.clicked.connect(self.become_a_miner)
        self.ui.check_miner_request_status_btn.clicked.connect(
            self.check_miner_request_status)
        self.ui.directory_change_btn.clicked.connect(self.change_directory)
        self.ui.settings_apply_btn.clicked.connect(self.apply_settings)
        self.ui.settings_reset_btn.clicked.connect(self.reset_settings)
        self.ui.disk_space_input.valueChanged.connect(
            self.enable_hosting_settings_controls)
        self.ui.directory_input.textChanged.connect(
            self.enable_hosting_settings_controls)
        self.ui.upload_file_btn.clicked.connect(self.upload_file)
        self.ui.bulk_prolong_deposit_btn.clicked.connect(
            self.prolong_deposit_for_all_files)

    def setup_tray_icon(self):
        tray_icon = QSystemTrayIcon(self)
        tray_icon.setIcon(
            QIcon(os.path.join(daemon_settings.base_dir, 'icon.ico')))

        show_action = QAction("Show", self)
        hide_action = QAction("Hide", self)
        quit_action = QAction("Exit", self)
        show_action.triggered.connect(self.ui.show)
        hide_action.triggered.connect(self.ui.hide)
        quit_action.triggered.connect(self.shutdown)

        tray_menu = QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(hide_action)
        tray_menu.addAction(quit_action)
        tray_icon.setContextMenu(tray_menu)
        return tray_icon

    def connect_signals(self):
        self.ws_client.error.connect(self.ws_error)
        self.ws_client.textMessageReceived.connect(self.ws_on_msg_received)
        self.daemon_started.connect(self.on_daemon_started)
        self.synced.connect(self.on_synced)
        self.ping_daemon_timer.timeout.connect(self.ping_daemon)
        self.sync_status_timer.timeout.connect(self.update_sync_status)

    def ensure_addr_not_in_use(self):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind(('127.0.0.1', daemon_settings.renter_app_port))
            s.close()
            del s
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind(('127.0.0.1', daemon_settings.hoster_app_port))
            s.close()
            del s
        except OSError as err:
            if err.errno == errno.EADDRINUSE:
                self.error(
                    'Ports are already in use!\n'
                    'Seems like Memority is already running or another application uses them.'
                )
                sys.exit(0)
            else:
                raven_client.captureException()

    def ping_daemon(self):
        @del_from_pool
        def done(started: bool):
            if started:
                self.daemon_started.emit()

        r = PingRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(done, self.request_pool, r))
        r.send()

    def update_sync_status(self):
        @del_from_pool
        @pyqtSlot()
        def got_sync_status(syncing: bool, percent: int):
            if syncing:
                self.ui.setDisabled(True)
                self.ui.sync_display_widget.show()
                self.ui.sync_progressbar.setValue(percent)
            else:
                self.sync_status_timer.stop()
                self.ui.setEnabled(True)
                self.ui.sync_display_widget.hide()
                self.synced.emit()

        r = GetSyncStatusRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_sync_status, self.request_pool, r))
        r.send()

    @pyqtSlot()
    def on_daemon_started(self):
        self.ping_daemon_timer.stop()
        self.sync_status_timer.start()
        self.refresh_wallet_tab()
        self.ws_client.open(QUrl(f'ws://{daemon_settings.daemon_address}/ws/'))
        self.ui.show()
        self.check_app_updates()

    @pyqtSlot()
    def on_synced(self):
        self.check_contract_updates()
        self.refresh()

    def check_contract_updates(self):
        @del_from_pool
        @pyqtSlot()
        def got_response(client_update_available: bool):
            if client_update_available:
                self.notify(
                    'Smart Contract needs update.\n'
                    'Please do not close the application while your data is transferring.'
                )
                self.ui.setDisabled(True)
                self.update_client_contract()

        r = CheckClientContractUpdatesRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_response, self.request_pool, r))
        r.send()

    def check_app_updates(self):
        @del_from_pool
        @pyqtSlot()
        def got_response(update_available: bool, download_url):
            if update_available:
                self.notify(
                    f'<html><body>'
                    f'<p>New version is available!</p>'
                    f'<p>You can download it on <a href={download_url}>{download_url}</a></p>'
                    f'</html></body>')

        r = CheckAppUpdatesRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_response, self.request_pool, r))
        r.send()

    def clear_layout(self, layout):
        if layout is not None:
            while layout.count():
                item = layout.takeAt(0)
                widget = item.widget()
                if widget is not None:
                    widget.deleteLater()
                else:
                    self.clear_layout(item.layout())

    def log(self, msg):
        self.ui.log_widget.appendHtml(msg)
        self.ui.log_widget.moveCursor(QTextCursor.End)
        self.ui.log_widget.repaint()

    def error(self, msg: str):
        self.log(msg)

        msg = msg.replace('\n', '<br/>')

        for url in re.findall(
                '(?:(?:https?|ftp)://)?[\w/\-?=%.]+\.[\w/\-?=%.]+', msg):
            msg = msg.replace(
                url,
                f'<a href="{url}" style="font-weight: bold; color: #fff">{url}</a>'
            )

        msg = f'<html><body>{msg}</html></body>'

        dialog: QDialog = uic.loadUi(ui_settings.ui_error_msg)
        dialog.msg.setText(msg)
        dialog.adjustSize()
        dialog.exec_()

    def notify(self, msg):
        self.log(msg)
        msg = msg.replace('\n', '<br/>')
        dialog: QDialog = uic.loadUi(ui_settings.ui_info_msg)
        dialog.msg.setText(f'<html><body>{msg}</html></body>')
        dialog.adjustSize()
        dialog.exec_()

    def ws_send(self, data: dict):
        self.ws_client.sendTextMessage(json.dumps(data))

    @pyqtSlot(QAbstractSocket.SocketError)
    def ws_error(self, error_code):
        self.log(f'Error {error_code}: {self.ws_client.errorString()}')
        self.error(self.ws_client.errorString())

    @pyqtSlot(str)
    def ws_on_msg_received(self, payload: str):
        try:
            data = json.loads(payload)
        except json.JSONDecodeError as err:
            self.error(str(err))
            return
        status = data.get('status')
        if status == 'info':
            self.log(data.get('message'))
        elif status == 'action_needed':
            if data.get('details') == 'tokens_to_deposit':
                self.choose_tokens_for_deposit(**data.get('result', {}))
        elif status == 'success':
            if data.get('details') == 'uploaded':
                self.notify('File successfully uploaded!')
                self.refresh_files_tab()
            elif data.get('details') == 'downloaded':
                self.notify('File successfully downloaded!')
                self.refresh_files_tab()
            elif data.get('details') == 'client_contract_updated':
                self.notify(
                    'Your data successfully transferred to new Smart Contract!'
                    'In order to work correctly with the new contract, the application needs to be restarted.'
                )
                self.shutdown()
        elif status == 'error':
            self.error(data.get('message'))

    @pyqtSlot()
    def refresh(self):
        @del_from_pool
        @pyqtSlot()
        def done(roles: list):
            self.ui.tabWidget.removeTab(
                self.ui.tabWidget.indexOf(self.ui.My_files))
            self.ui.tabWidget.removeTab(
                self.ui.tabWidget.indexOf(self.ui.Hosting_statistics))
            self.ui.tabWidget.removeTab(
                self.ui.tabWidget.indexOf(self.ui.Settings))
            if 'renter' in roles:
                self.ui.tabWidget.addTab(self.ui.My_files, "My files")
            if 'hoster' in roles:
                self.ui.tabWidget.addTab(self.ui.Hosting_statistics,
                                         "Hosting statistics")
            self.ui.tabWidget.addTab(self.ui.Settings, "Settings")
            self.refresh_wallet_tab()
            self.refresh_files_tab()
            self.refresh_hosting_tab()
            self.refresh_settings_tab()

        r = GetUserRoleRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(done, self.request_pool, r))
        r.send()

    def refresh_wallet_tab(self):
        @del_from_pool
        @pyqtSlot()
        def got_address(address: str):
            if address:
                self.ui.copy_address_btn.setEnabled(True)
            else:
                address = 'Please go to "Settings" - "Create account"'
            self.ui.address_display.setText(address)

        @del_from_pool
        @pyqtSlot()
        def got_balance(balance: float):
            self.ui.balance_display.setText(f'{balance} MMR')

        @del_from_pool
        @pyqtSlot()
        def got_transactions(transactions: list):
            self.ui.transaction_history_table: QTableWidget
            self.ui.transaction_history_table.setRowCount(0)  # clear table
            if transactions:
                self.ui.transaction_history_lbl.show()
                self.ui.transaction_history_table.show()
            for tx in transactions:
                from_item = QTableWidgetItem(tx['from'] or '-')
                to_item = QTableWidgetItem(tx['to'])
                date_item = QTableWidgetItem(tx['date'])
                value_item = QTableWidgetItem(str(tx['value']))
                comment_item = QTableWidgetItem(tx['comment'])
                for item in [
                        from_item, to_item, date_item, value_item, comment_item
                ]:
                    item.setToolTip(item.text())
                    item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                    item.setTextAlignment(Qt.AlignCenter)
                row_position = self.ui.transaction_history_table.rowCount()
                self.ui.transaction_history_table.insertRow(row_position)
                self.ui.transaction_history_table.setItem(
                    row_position, 0, from_item)
                self.ui.transaction_history_table.setItem(
                    row_position, 1, to_item)
                self.ui.transaction_history_table.setItem(
                    row_position, 2, date_item)
                self.ui.transaction_history_table.setItem(
                    row_position, 3, value_item)
                self.ui.transaction_history_table.setItem(
                    row_position, 4, comment_item)

        # region Address
        r = GetUserAddressRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_address, self.request_pool, r))
        r.send()
        # endregion

        # region Balance
        r = GetUserBalanceRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_balance, self.request_pool, r))
        r.send()
        # endregion

        # region TX History
        r = GetUserTransactionsRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_transactions, self.request_pool, r))
        r.send()
        # endregion

    def refresh_files_tab(self):
        @del_from_pool
        @pyqtSlot()
        def got_files(ok: bool, error: str, files: list):
            if not ok:
                self.error(error)
                return
            self.ui.file_list_scrollarea_layout: QVBoxLayout
            self.ui.file_list_spacer: QSpacerItem
            if not files:
                return
            self.clear_layout(self.ui.file_list_scrollarea_layout)
            for file in files:
                file_list_item = uic.loadUi(ui_settings.ui_file_list_item)
                file_list_item.uploaded_on_display.setText(
                    file.get('timestamp'))
                file_list_item.name_display.setText(file.get('name'))
                file_list_item.hash_display.setText(file.get('hash'))
                file_list_item.size_display.setText(
                    utils.file_size_human_readable(file.get('size')))
                file_list_item.deposit_end_display.setText(
                    file.get('deposit_ends_on'))
                file_list_item.download_btn.clicked.connect(
                    partial(self.download_file, file.get('hash')))
                file_list_item.prolong_deposit_btn.clicked.connect(
                    partial(self.prolong_deposit, file.get('hash')))
                self.ui.file_list_scrollarea_layout.addWidget(file_list_item)
            self.ui.file_list_scrollarea_layout.addItem(
                QSpacerItem(QSizePolicy.Expanding, QSizePolicy.Expanding, 0,
                            0))

        r = GetUserFilesRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_files, self.request_pool, r))
        r.send()

    def refresh_hosting_tab(self):
        @del_from_pool
        @pyqtSlot()
        def got_address(address: str):
            self.ui.hosting_addr_display.setText(address)

        @del_from_pool
        @pyqtSlot()
        def got_ip(ip: str):
            self.ui.hosting_ip_display.setText(ip or 'Not in host list.')

        @del_from_pool
        @pyqtSlot()
        def got_space_used(space_used: str):
            self.ui.hosting_space_display.setText(space_used)

        # region Address
        r = GetUserAddressRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_address, self.request_pool, r))
        r.send()
        # endregion

        # region IP
        r = GetUserIPRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_ip, self.request_pool, r))
        r.send()
        # endregion

        # region Space used
        r = GetSpaceUsedRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_space_used, self.request_pool, r))
        r.send()
        # endregion

    def refresh_settings_tab(self):
        @del_from_pool
        @pyqtSlot()
        def got_user_role(roles: list):
            for element in [
                    self.ui.create_account_btn, self.ui.import_account_btn,
                    self.ui.export_account_btn, self.ui.become_hoster_btn,
                    self.ui.become_miner_btn,
                    self.ui.check_miner_request_status_btn,
                    self.ui.hosting_settings_widget
            ]:
                element.hide()
            self.ui.import_account_btn.show()
            if roles:
                self.ui.export_account_btn.show()
                if 'hoster' in roles:
                    self.ui.hosting_settings_widget.show()
                else:
                    self.ui.become_hoster_btn.show()
                if 'miner' not in roles:
                    if 'pending_miner' in roles:
                        self.ui.check_miner_request_status_btn.show()
                    else:
                        self.ui.become_miner_btn.show()
            else:
                self.ui.create_account_btn.show()

        @del_from_pool
        @pyqtSlot()
        def got_disk_space_for_hosting(disk_space_for_hosting: float):
            self.ui.disk_space_input.setValue(disk_space_for_hosting)

        @del_from_pool
        @pyqtSlot()
        def got_box_dir(box_dir: str):
            self.ui.directory_input.setText(box_dir)

        # region Role
        r = GetUserRoleRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_user_role, self.request_pool, r))
        r.send()
        # endregion

        # region Disk Space
        r = GetDiskSpaceForHostingRequest()
        self.request_pool.append(r)
        r.finished.connect(
            partial(got_disk_space_for_hosting, self.request_pool, r))
        r.send()
        # endregion

        # region Box Dir
        r = GetBoxDirRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_box_dir, self.request_pool, r))
        r.send()
        # endregion

        self.ui.settings_apply_btn.setDisabled(True)
        self.ui.settings_reset_btn.setDisabled(True)

    @pyqtSlot()
    def copy_address_to_clipboard(self):
        self.ui.address_display: QLineEdit
        QApplication.clipboard().setText(self.ui.address_display.text())

    @pyqtSlot()
    def create_account(self):
        def _request_mmr():
            add_key_dialog: QDialog = uic.loadUi(ui_settings.ui_add_key)
            if not add_key_dialog.exec_():
                return
            self.log(
                'Please wait while we’ll send you MMR tokens for testing, it may take a few minutes. '
                'Do not close the application.')
            r = RequestMMRRequest(key=add_key_dialog.key_input.text())
            self.request_pool.append(r)
            r.finished.connect(
                partial(got_request_mmr_result, self.request_pool, r))
            r.send()

        def _create_account():
            self.log(
                f'Creating renter account...\n'
                f'This can take some time, as transaction is being written in blockchain.\n'
                f'When finished, the "My Files" tab appears.')
            r = CreateRenterAccountRequest()
            self.request_pool.append(r)
            r.finished.connect(
                partial(got_create_client_result, self.request_pool, r))
            r.send()

        @del_from_pool
        @pyqtSlot()
        def got_address_gen_result(ok: bool, result: str):
            if ok:
                self.log(f'Your address: {result}')
                self.refresh()
                nonlocal _request_mmr
                _request_mmr()
            else:
                self.error(result)

        @del_from_pool
        @pyqtSlot()
        def got_request_mmr_result(ok: bool, result: str):
            if ok:
                self.log(f'Tokens received. Your balance: {result} MMR')
                self.refresh()
                nonlocal _create_account
                _create_account()
            else:
                self.error(result)

        @del_from_pool
        @pyqtSlot()
        def got_create_client_result(ok: bool, result: str):
            if ok:
                self.notify('Renter account successfully created!')
                self.refresh()
            else:
                self.error(result)

        # region Ask for password and generate address
        generate_address_dialog: QDialog = uic.loadUi(
            ui_settings.ui_generate_address)
        if not generate_address_dialog.exec_():
            return
        password1 = generate_address_dialog.password1.text()
        password2 = generate_address_dialog.password2.text()

        if password1 != password2:
            self.error('Passwords don`t match!')
            return

        self.log(f'Generating address...')
        r = GenerateAddressRequest(password=password1)
        self.request_pool.append(r)
        r.finished.connect(
            partial(got_address_gen_result, self.request_pool, r))
        r.send()
        # endregion

    @pyqtSlot()
    def import_account(self):
        @del_from_pool
        @pyqtSlot()
        def got_importing_result(ok: bool, result: str):
            if ok:
                self.log('Account successfully imported!')
                self.notify(
                    'In order to work correctly with the new account, the application needs to be restarted.'
                )
                self.shutdown()
            else:
                self.error(result)

        filename, _ = QFileDialog.getOpenFileName(
            None,
            "Select account file",
            os.path.join(
                os.getenv('HOME', None) or os.getenv('HOMEPATH', None),
                'memority_account.bin'),
        )
        if filename:
            password_dialog: QDialog = uic.loadUi(
                ui_settings.ui_enter_password)
            password_dialog.password_input.setFocus()
            if not password_dialog.exec_():
                self.shutdown()
                return
            password = password_dialog.password_input.text()
            self.log('Importing account...')
            r = ImportAccountRequest(filename=filename, password=password)
            self.request_pool.append(r)
            r.finished.connect(
                partial(got_importing_result, self.request_pool, r))
            r.send()

    @pyqtSlot()
    def export_account(self):
        @del_from_pool
        @pyqtSlot()
        def got_exporting_result(ok: bool, result: str):
            if ok:
                self.log('Account successfully exported!')
            else:
                self.error(result)

        filename, _ = QFileDialog.getSaveFileName(
            None, "Select a location to save your account file.",
            os.path.join(
                os.getenv('HOME', None) or os.getenv('HOMEPATH', None),
                'memority_account.bin'), "*.bin")
        if filename:
            self.log('Exporting account...')
            r = ExportAccountRequest(filename=filename)
            self.request_pool.append(r)
            r.finished.connect(
                partial(got_exporting_result, self.request_pool, r))
            r.send()

    @pyqtSlot()
    def become_a_hoster(self):
        @del_from_pool
        @pyqtSlot()
        def got_create_host_result(ok: bool, result: str):
            if ok:
                self.log('Successfully added to hoster list!')
            else:
                self.error(result)
            self.refresh()

        self.log(
            'Adding your address and IP to contract...\n'
            'This can take some time, as transaction is being written in blockchain.\n'
            'When finished, the "Hosting statistics" tab appears.')

        r = CreateHosterAccountRequest()
        self.request_pool.append(r)
        r.finished.connect(
            partial(got_create_host_result, self.request_pool, r))
        r.send()

    @pyqtSlot()
    def become_a_miner(self):
        @del_from_pool
        @pyqtSlot()
        def got_response(ok: bool, result: str):
            if ok:
                self.log(
                    f'The request was successfully sent. Request status: {result}.'
                )
            else:
                self.error(result)
            self.refresh()

        self.log('Sending request for adding you to miner list...')

        r = MinerStatusRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_response, self.request_pool, r))
        r.send()

    @pyqtSlot()
    def check_miner_request_status(self):
        @del_from_pool
        @pyqtSlot()
        def got_response(ok: bool, result: str):
            if ok:
                self.log(f'Request status: {result}.')
                if result == 'active':
                    self.notify(
                        'Please restart the application to start mining.')
            else:
                self.error(result)
            self.refresh()

        self.log('Sending request for miner request status...')

        r = MinerStatusRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_response, self.request_pool, r))
        r.send()

    @pyqtSlot()
    def upload_file(self):
        filename, _ = QFileDialog.getOpenFileName(
            None,
            "Select file",
            directory=os.getenv('HOME', None) or os.getenv('HOMEPATH', None),
        )
        if filename:
            self.ws_send({"command": "upload", "kwargs": {"path": filename}})

    @pyqtSlot()
    def download_file(self, hash_):
        directory = QFileDialog.getExistingDirectory(
            None,
            "Select Directory",
            directory=os.getenv('HOME', None) or os.getenv('HOMEPATH', None),
        )
        if not directory:
            self.log('Downloading cancelled.')
            return
        self.ws_send({
            "command": "download",
            "kwargs": {
                "destination": directory,
                "file_hash": hash_
            }
        })

    @pyqtSlot()
    def update_client_contract(self):
        self.ws_send({"command": "update_client_contract"})

    @pyqtSlot()
    def prolong_deposit(self, _hash):
        @del_from_pool
        @pyqtSlot()
        def got_file_metadata(file_metadata: dict):
            @pyqtSlot(float)
            def upd_date(value: float):
                if value < price_per_hour * 24 * 14:
                    dialog.deposit_size_input.setValue(price_per_hour * 24 *
                                                       14)
                    return
                try:
                    deposit_end = datetime.now() + timedelta(hours=value //
                                                             price_per_hour)
                except OverflowError:
                    deposit_end = datetime.max
                deposit_end = QDate.fromString(
                    deposit_end.strftime('%Y-%m-%d'), 'yyyy-MM-dd')
                dialog.calendarWidget.setSelectedDate(deposit_end)

            @pyqtSlot(QDate)
            def upd_value(date: QDate):
                deposit_end = date.toPyDate()
                hours = (deposit_end - deposit_ends_on.date()).days * 24
                _value = hours * price_per_hour
                dialog.deposit_size_input.setValue(_value)

            def paint_cell(painter, rect, date):
                QCalendarWidget.paintCell(dialog.calendarWidget, painter, rect,
                                          date)
                if date == QDate(deposit_ends_on):
                    color = dialog.calendarWidget.palette().color(
                        QPalette.Highlight)
                    color.setAlpha(128)
                    painter.fillRect(rect, color)

            size = file_metadata.get('size')
            price_per_hour = file_metadata.get('price_per_hour')
            deposit_ends_on = datetime.strptime(
                file_metadata.get('deposit_ends_on', [])[:-4],
                '%Y-%m-%d %H:%M')

            dialog: QDialog = uic.loadUi(
                ui_settings.ui_create_deposit_for_file)
            dialog.calendarWidget: QCalendarWidget
            dialog.deposit_size_input: QDoubleSpinBox
            dialog.calendarWidget.setMinimumDate(
                (deposit_ends_on + timedelta(weeks=2)).date())
            dialog.deposit_size_input.setValue(price_per_hour * 24 * 14)
            dialog.calendarWidget.paintCell = paint_cell
            dialog.calendarWidget.updateCells()

            size = utils.file_size_human_readable(size)
            dialog.file_size_display.setText(size)
            dialog.calendarWidget.clicked[QDate].connect(upd_value)
            dialog.deposit_size_input.valueChanged.connect(upd_date)

            if dialog.exec_():
                input_value = dialog.deposit_size_input.value()
                self.log(
                    f'Adding {input_value:.18f} MMR to deposit | file: {_hash}.\n'
                    f'Please wait...')
                r = ProlongDepositForFileRequest(file_hash=_hash,
                                                 value=input_value)
                self.request_pool.append(r)
                r.finished.connect(
                    partial(got_prolong_deposit_resp, self.request_pool, r))
                r.send()

        @del_from_pool
        @pyqtSlot()
        def got_prolong_deposit_resp(ok, result):
            if ok:
                self.refresh_files_tab()
                self.notify('File deposit successfully updated.')
            else:
                self.error(result)

        r = GetFileMetadataRequest(file_hash=_hash)
        self.request_pool.append(r)
        r.finished.connect(partial(got_file_metadata, self.request_pool, r))
        r.send()

    @pyqtSlot()
    def prolong_deposit_for_all_files(self):
        @del_from_pool
        @pyqtSlot()
        def got_file_list(ok: bool, error: str, files: list):
            @pyqtSlot(QDate)
            def upd_value(date: QDate):
                deposit_end_ = date.toPyDate()
                self.clear_layout(dialog.labels_layout)
                total_val = 0
                i = 0
                for i, file_ in enumerate(files):
                    val_ = file_['price_per_hour'] \
                           * (deposit_end_ - parse_date_from_string(file_['deposit_ends_on'])).days * 24
                    total_val += val_
                    dialog.labels_layout.addWidget(
                        QLabel(
                            f'to {file_["name"] if len(file_["name"]) < 64 else file_["name"][:64] + "..."}:'
                        ), i, 0)
                    dialog.labels_layout.addWidget(QLabel(f'{val_:.18f} MMR'),
                                                   i, 1)
                dialog.total_val_display.setText(f'{total_val:.18f} MMR')
                dialog.labels_layout.addItem(
                    QSpacerItem(QSizePolicy.Expanding, QSizePolicy.Expanding,
                                0, 0), i + 2, 0)

            def paint_cell(painter, rect, date: QDate):
                QCalendarWidget.paintCell(dialog.calendarWidget, painter, rect,
                                          date)
                for f in files:
                    if date.toPyDate() == parse_date_from_string(
                            f['deposit_ends_on']):
                        color = dialog.calendarWidget.palette().color(
                            QPalette.Highlight)
                        color.setAlpha(128)
                        painter.fillRect(rect, color)
                        painter.drawText(rect.bottomLeft(), f['name'])

            if not ok:
                self.error(error)
                return

            if not files:
                self.error('You don`t have files to prolong deposit.')
                return

            min_date = min(
                [parse_date_from_string(f['deposit_ends_on'])
                 for f in files]) + timedelta(weeks=2)

            dialog: QDialog = uic.loadUi(ui_settings.ui_bulk_prolong_deposit)
            dialog.calendarWidget: QCalendarWidget
            dialog.labels_layout: QGridLayout
            dialog.calendarWidget.setMinimumDate(min_date)
            upd_value(QDate(min_date))
            dialog.calendarWidget.paintCell = paint_cell
            dialog.calendarWidget.updateCells()
            dialog.calendarWidget.clicked[QDate].connect(upd_value)

            if dialog.exec_():
                deposits = []
                for file in files:
                    deposit_end = dialog.calendarWidget.selectedDate(
                    ).toPyDate()
                    val = file['price_per_hour'] * (
                        deposit_end - parse_date_from_string(
                            file['deposit_ends_on'])).days * 24
                    if val:
                        deposits.append((file['hash'], val))
                self.log(
                    f'Adding {sum([d[1] for d in deposits]):.18f} MMR to deposits'
                )
                for d in deposits:
                    self.log(
                        f'Adding {d[1]:.18f} MMR to deposit | file: {d[0]}. Please wait...'
                    )
                    r = ProlongDepositForFileRequest(file_hash=d[0],
                                                     value=d[1])
                    self.request_pool.append(r)
                    r.finished.connect(
                        partial(got_prolong_deposit_resp, self.request_pool,
                                r))
                    r.send()

        @del_from_pool
        @pyqtSlot()
        def got_prolong_deposit_resp(ok, result):
            if ok:
                self.refresh_files_tab()
                self.notify('File deposit successfully updated.')
            else:
                self.error(result)

        r = GetUserFilesRequest()
        self.request_pool.append(r)
        r.finished.connect(partial(got_file_list, self.request_pool, r))
        r.send()

    def choose_tokens_for_deposit(self, size, price_per_hour):
        dialog: QDialog = uic.loadUi(ui_settings.ui_create_deposit_for_file)
        dialog.calendarWidget: QCalendarWidget
        dialog.deposit_size_input: QDoubleSpinBox
        dialog.calendarWidget.setMinimumDate(
            (datetime.now() + timedelta(weeks=2)).date())

        @pyqtSlot(float)
        def upd_date(value: float):
            if value < price_per_hour * 24 * 14:
                dialog.deposit_size_input.setValue(price_per_hour * 24 * 14)
                return
            try:
                deposit_end = datetime.now() + timedelta(hours=value //
                                                         price_per_hour)
            except OverflowError:
                deposit_end = datetime.max
            deposit_end = QDate.fromString(deposit_end.strftime('%Y-%m-%d'),
                                           'yyyy-MM-dd')
            dialog.calendarWidget.setSelectedDate(deposit_end)

        @pyqtSlot(QDate)
        def upd_value(date: QDate):
            deposit_end = date.toPyDate()
            hours = (deposit_end - datetime.now().date()).days * 24
            value = hours * price_per_hour
            dialog.deposit_size_input.setValue(value)

        size = utils.file_size_human_readable(size)
        dialog.file_size_display.setText(size)
        dialog.deposit_size_input.setValue(price_per_hour * 24 * 14)
        dialog.calendarWidget.clicked[QDate].connect(upd_value)
        dialog.deposit_size_input.valueChanged.connect(upd_date)

        if dialog.exec_():
            result = dialog.deposit_size_input.value()
            if not result:
                result = -1
        else:
            result = -1
        return self.ws_send({'status': 'success', 'result': result})

    @pyqtSlot()
    def enable_hosting_settings_controls(self):
        self.ui.settings_apply_btn.setEnabled(True)
        self.ui.settings_reset_btn.setEnabled(True)

    @pyqtSlot()
    def change_directory(self):
        directory = QFileDialog.getExistingDirectory(
            None,
            "Select Directory",
            directory=os.getenv('HOME', None) or os.getenv('HOMEPATH', None),
        )
        if not directory:
            return
        self.ui.directory_input.setText(directory)

    @pyqtSlot()
    def apply_settings(self):
        @del_from_pool
        @pyqtSlot()
        def got_resp(ok, result):
            if ok:
                self.refresh_settings_tab()
            else:
                self.error(result)

        # region Disk space for hosting
        disk_space = self.ui.disk_space_input.value()
        r = SetDiskSpaceForHostingRequest(disk_space=disk_space)
        self.request_pool.append(r)
        r.finished.connect(partial(got_resp, self.request_pool, r))
        r.send()
        # endregion

        # region Box dir
        box_dir = self.ui.directory_input.text()
        r = ChangeBoxDirRequest(box_dir=box_dir)
        self.request_pool.append(r)
        r.finished.connect(partial(got_resp, self.request_pool, r))
        r.send()
        # endregion

    @pyqtSlot()
    def reset_settings(self):
        self.refresh_settings_tab()

    @pyqtSlot(QEvent)
    def closeEvent(self, event):
        dialog: QDialog = uic.loadUi(ui_settings.ui_submit_exit)
        if dialog.exec_():  # submitted
            dialog.minimize_to_tray_cb: QCheckBox
            if dialog.minimize_to_tray_cb.isChecked():
                self.ui.hide()
                event.ignore()
            else:
                self.shutdown()
                event.accept()
        else:
            event.ignore()

    def shutdown(self):
        self.ui.hide()
        if hasattr(self, 'sync_status_timer'):
            self.sync_status_timer.stop()
        self.memority_core.cleanup()
        for task in asyncio.Task.all_tasks():
            task.cancel()
        qApp.quit()
        self.event_loop.stop()
        self.event_loop.close()
        sys.exit(0)
Ejemplo n.º 20
0
class aliPayControl(QMainWindow, Ui_MainWindow):
    start_work_signal = pyqtSignal(dict)

    def __init__(self):
        super(aliPayControl, self).__init__()
        self.setupUi(self)
        self.show()
        self.config = ConfigObj(profile.CONFIG_INI_URL,
                                encoding=profile.ENCODING)
        self.websocket = QWebSocket("",
                                    QtWebSockets.QWebSocketProtocol.Version13,
                                    None)  # 与直播服务器的连接
        self.websocket.connected.connect(self.on_connected)
        self.websocket.disconnected.connect(self.on_disconnected)
        self.websocket.textMessageReceived.connect(self.on_textMessageReceived)
        self.websocket.error.connect(self.on_error)
        self.websocket.stateChanged.connect(self.on_stateChanged)

        self.start_work_signal.connect(
            self.on_start_work_signal)  # 接收到服务器开始工作的指令

        self.option = webdriver.ChromeOptions()
        self.title = ""
        self.option.add_argument('disable-infobars')
        self.option.add_argument('--log-level=3')
        self.option.add_experimental_option("excludeSwitches",
                                            ['enable-automation'])
        self.driver = Chrome(chrome_options=self.option,
                             executable_path=profile.WEB_DRIVER_PATH)
        self.driver.implicitly_wait(2)  # 所有的加载页面隐式等待20秒

        self.time = 2
        self.wait_time = 5
        self.exception = (Exception, )
        self.flash_timer = QTimer(self)
        self.flash_timer.timeout.connect(self.on_flash_timer)
        self.flash_timer.setInterval(int(self.config['INTERVAL']) * 1000)
        self.ping_timer = QTimer(self)
        self.ping_timer.timeout.connect(self.on_ping_timer)

        self.ping_timer.setInterval(20 * 1000)

    @pyqtSlot()
    def on_ping_timer(self):
        if self.websocket.isValid():
            self.websocket.sendBinaryMessage(b"2")

    @pyqtSlot()
    def on_flash_timer(self):
        # 定时刷新订单页面

        original_window_handle = self.driver.current_window_handle  # 拿到当前的窗口句柄
        for hande in self.driver.window_handles:
            self.driver.switch_to.window(hande)
            if self.driver.current_url == profile.WAIT_PAGE_PATH:
                self.driver.get(profile.MAIN_PATH_PATH)  # 操作完一边 回到用户主页
            if str(self.driver.current_url) == profile.ORDER_PAGE_PATH:
                self.driver.refresh()
                with open(profile.WORK_JS, "r") as f:

                    self.driver.execute_script("".join(f.readlines()))
            if str(self.driver.current_url).startswith(
                    "https://consumeprod.alipay.com/errorSecurity.htm"):
                self.driver.get(profile.ORDER_PAGE_PATH)

            if str(self.driver.current_url).startswith(
                    profile.ERROR_LOGIN_PATH):
                QMessageBox.warning(self, "错误", "检测到需要用户登录,请按要求登录")
            self.delayMsec(5 * 1000)
            ## TODO 安全检查
        if self.driver.current_window_handle != original_window_handle:
            self.driver.switch_to.window(original_window_handle)  # 并切回来

    def delayMsec(self, msec):
        dieTime = QTime.currentTime().addMSecs(msec)
        while QTime.currentTime() < dieTime:
            QApplication.processEvents()

    def retries(func):
        # 类中定义一个重试的装饰器

        @functools.wraps(func)
        def warpper(self, *args, **kwargs):
            for i in range(self.time + 1):

                try:
                    result = func(self, *args, **kwargs)
                except self.exception as e:
                    logger.info("启用重试器")
                    logger.error(str(func))
                    logger.error(e)
                    self.delayMsec(5 * 1000)
                    continue
                else:
                    return result
            # 如果重试几次还没有得到结果,那么可能是cookies失效了,回到主页
            self.driver.get("https://my.alipay.com/portal/i.htm")

        return warpper

    def in_ali_login_page(self):
        '''判断是否在阿里支付宝登录页面'''
        return self.driver.current_url == profile.ALI_LOGIN_PATH

    @pyqtSlot(dict)
    def on_start_work_signal(self, recv_json):
        '''开始工作流程'''

        if not self.driver.get_cookies():  # 如果cookies失效,重回到登录界面
            self.driver.get(profile.ALI_LOGIN_PATH)

        content_dict = ast.literal_eval(
            recv_json.get("content"))  # 开始工作,拿到服务器传来的消息
        logger.info("content_dict->" + str(content_dict))
        self.click_work(content_dict.get("bankcode"),
                        content_dict.get("money"), recv_json.get("id"),
                        content_dict.get("mark"))

    @retries
    def go_to_charge_page(self):
        # 进入到充值页面
        self.driver.get(
            "https://shenghuo.alipay.com/transfer/deposit/depositPreprocessGw.htm"
        )

    @retries
    def click_charge_to_left(self):
        # 点击充值到余额
        self.driver.find_element_by_xpath(
            "//*[@id='container']/div[1]/ul/li[2]/a").click()

    @retries
    def go_to_next(self):
        # 点击选择其他,进入到选择银行页面
        href = self.driver.find_element_by_xpath(
            "//*[@id='J-DEbank']/div/form/div[2]/div/ul/li/a").get_attribute(
                "href")
        self.driver.get(href)

    @retries
    def chose_bank(self, bank_code):
        self.driver.find_element_by_xpath(
            f"//input[@value='{bank_code}']").click()  # 选择银行

    @retries
    def click_chose_bank_button(self):
        self.driver.find_element_by_xpath(
            "//*[@id='bankCardForm']/div/input").click()  # 选择下一步

    @retries
    def input_money(self, money):
        self.driver.find_element_by_xpath(
            "//*[@id='J-depositAmount']").send_keys(money)

    @retries
    def click_submit(self):
        # 点击登陆到网上银行
        self.driver.find_element_by_xpath(
            '//input[@id="J-deposit-submit"]').click()

    @retries
    def get_ua(self):
        # 得到UAID
        return self.driver.find_element_by_xpath(
            "//*[@id='UA_InputId']").get_attribute("value")

    @retries
    def get_order_id(self):
        return self.driver.find_element_by_xpath(
            "//*[@id='orderId']").get_attribute("value")

    @retries
    def get_form_token(self):
        return self.driver.find_element_by_xpath(
            '//*[@id="ebankDepositForm"]/input[1]').get_attribute("value")

    @retries
    def get_securityId(self):
        return self.driver.find_element_by_xpath(
            "//*[@id='securityId']").get_attribute("value")

    @retries
    def click_work(self, bank_code, money, msgid, mark):
        logger.info("开始")
        if self.in_ali_login_page(
        ):  # 每次任务开始前都要检查自己是不是在登陆页面,如果在登录界面,则直接提示用户登录,并结束本次流程
            QMessageBox.warning(self, "错误", "请登录支付宝")
            return
        if self.driver.current_url == profile.WAIT_PAGE_PATH:
            self.driver.get(profile.MAIN_PATH_PATH)
        if self.driver.current_url == profile.MAIN_PATH_PATH:  # 如果在登录后的主页中,下一步期待进入充值页面
            self.driver.execute_script(
                f"document.title='{self.key_name_lineEdit.text()}'")
            # self.driver.find_element_by_xpath("//*[@id='J-assets-balance']/div[1]/div/div[2]/ul/li[1]/a").click()  # 点击充值按钮
            logger.info("当前在个人用户主页:" + str(self.driver.current_url))

            if len(self.driver.window_handles) == 1:
                original_window_handle = self.driver.current_window_handle  # 拿到当前的窗口句柄

                self.driver.execute_script(
                    f"window.open('{profile.ORDER_PAGE_PATH}');")

                self.driver.switch_to.window(original_window_handle)  # 并切回来
                self.flash_timer.start()
            self.go_to_charge_page()

        if str(self.driver.current_url).startswith(
                "https://cashiersu18.alipay.com/standard/deposit/cashier.htm"):
            logger.info("当前在支付宝充值主页是:" + str(self.driver.current_url))
            self.driver.execute_script(
                f"document.title='{self.key_name_lineEdit.text()}'")
            try:
                self.driver.find_element_by_xpath(
                    "//*[@id='content']/div[1]/ul/li[1]/a")
                # 尝试获取这个元素,如果获取到了,则说明已经选择了充实到余额
                logger.info("已经选择了充值到余额")
            except Exception as e:
                # 如果出现了异常,则说明,没选,需要选一下
                self.click_charge_to_left()

            self.go_to_next()

        if str(self.driver.current_url).startswith(
                "https://cashiersu18.alipay.com/standard/deposit/chooseBank.htm"
        ):
            logger.info("当前在选择银行主页:" + str(self.driver.current_url))
            self.driver.execute_script(
                f"document.title='{self.key_name_lineEdit.text()}'")
            self.chose_bank(bank_code)  # 选择银行
            # self.driver.execute_script(f"document.getElementById('{profile.BAND_CODE_ID.get(bank_code)}').click()")
            self.click_chose_bank_button()  # 选择下一步

        if str(self.driver.current_url).startswith(
                "https://cashiersu18.alipay.com/standard/gateway/ebankDeposit.htm"
        ):
            # 在这个页面操作完了,一定要回到登录后的主页页面 https://my.alipay.com/portal/i.htm
            logger.info("当前在充值金额页面")
            self.driver.execute_script(
                f"document.title='{self.key_name_lineEdit.text()}'")
            self.input_money(money)  # 填写金额
            self.driver.execute_script(
                "window.onbeforeunload = function() { return 'NUL'; }")
            self.click_submit()  # 点击登录到网上银行

            wait = WebDriverWait(self.driver, 5)  # 等待告警出现
            alert = wait.until(expected_conditions.alert_is_present())
            if alert:
                alert.dismiss()
            ua = self.get_ua()
            self.driver.execute_script(
                "window.onbeforeunload = function() { return null; }")
            self.driver.refresh()  # 刷新
            orderId = self.get_order_id()  # 拿到orderId
            form_token = self.get_form_token()
            securityId = self.get_securityId()  # 拿到securityId
            ctoken = self.driver.get_cookie("ctoken").get("value")
            data = {
                "_form_token": form_token,
                "orderId": orderId,
                "securityId": securityId,
                "depositAmount": float(money),
                "depositType": "hasAmount",
                "ua": ua,
                "_input_charset": "utf-8",
                "ctoken": ctoken
            }
            response = self.driver.request(
                "POST",
                "https://cashiersu18.alipay.com/standard/gateway/ebankDeposit.json",
                data=data,
                find_window_handle_timeout=5,
                page_load_timeout=5)

            url = json.loads(response.text).get("url").replace("\\", "")
            outBizNo = get_query_dict(urlparse(url).query).get("outBizNo")
            result = self.driver.request(
                "GET",
                json.loads(response.text).get("url").replace("\\", ""))
            html = result.text
            soup = BeautifulSoup(html, features="html.parser")
            form = soup.select("form[id='ebankDepositForm']")[0]
            self.send_to_websocket({
                "code": 0,
                "msg": "获取成功",
                "payurl": str(form),
                "mark": str(mark),
                "money": float(money),
                "type": "alibank",
                "msgId": str(msgid),
                "batchNo": outBizNo,
                "bankCode": bank_code
            })
            self.driver.get(
                "https://my.alipay.com/portal/i.htm")  # 操作完一边 回到用户主页
            self.driver.execute_script(
                f"document.title='{self.key_name_lineEdit.text()}'")

    @pyqtSlot()
    def on_close_driver_signal(self):
        self.driver.quit()

    @pyqtSlot(str)
    def on_set_title_signal(self, title):

        self.driver.execute_script(f"document.title='{title}'")

    def closeEvent(self, QCloseEvent):
        self.driver.quit()
        self.websocket.close()
        QCloseEvent.accept()

    def send_to_websocket(self, data: dict):
        '''向CTP服务器发送数据'''
        logger.info("向服务器发送数据" + str(data))
        if isinstance(data, dict):
            d = json.dumps(data)
        else:
            d = str(data)
        logger.info(d)
        b_message = EngineIoPacket(
            MESSAGE,
            SocketIoPacket(EVENT, data=["message"] + [d]).encode()).encode()
        logger.info(b_message)
        self.websocket.sendBinaryMessage(b_message)

    @pyqtSlot()
    def on_disconnected(self):
        '''与CTP服务器断开连接'''
        logger.info("与服务器断开连接")

    @pyqtSlot(str)
    def on_textMessageReceived(self, message: str):
        '''直播服务器收到数据'''

        logger.info("接收服务器消息" + str(message))
        if message.startswith("0"):
            logger.info("engine.io is OPENED")
            logger.info(json.loads(message[1:]))
        elif message == "40":
            pass
        elif message.startswith("42"):
            msg = json.loads(ast.literal_eval(message[2:])[1])
            logger.info("接收服务器有效数据包" + str(msg))
            self.start_work_signal.emit(msg)

    @pyqtSlot(QAbstractSocket.SocketError)
    def on_error(self, error_code):
        '''CTP连接错误码'''
        if error_code == 0:
            logger.error("服务器,连接被拒绝(或者超时)")
        elif error_code == 1:
            logger.error("服务器,连接被远程服务器断开,请注意,在发送远程关闭通知后,将关闭客户端套接字(即此套接字)")
            # self.change_loginDialog_lineedit_empty_label_text("连接被远程服务器断开,请注意,在发送远程关闭通知后,将关闭客户端套接字(即此套接字)")
        elif error_code == 2:
            logger.error("服务器,找不到主机地址")
            # self.change_loginDialog_lineedit_empty_label_text("找不到主机地址")
        elif error_code == 3:
            logger.error("服务器,套接字操作失败,因为应用程序缺少必需的权限。")
            # self.change_loginDialog_lineedit_empty_label_text("套接字操作失败,因为应用程序缺少必需的权限。")
        elif error_code == 4:
            logger.error("服务器,本地系统耗尽资源(例如,套接字太多)。")
            # self.change_loginDialog_lineedit_empty_label_text("本地系统耗尽资源(例如,套接字太多)。")
        elif error_code == 5:
            logger.error("服务器,套接字操作超时。")
            # self.change_loginDialog_lineedit_empty_label_text("套接字操作超时。")
        elif error_code == 6:
            logger.error("服务器,数据报大于操作系统的限制(可以低至8192字节)。")
            # self.change_loginDialog_lineedit_empty_label_text("数据报大于操作系统的限制(可以低至8192字节)。")
        elif error_code == 7:
            logger.error("服务器,网络发生错误(例如,网络电缆意外拔出)。")
            # self.change_loginDialog_lineedit_empty_label_text("网络发生错误(例如,网络电缆意外拔出)。")
        elif error_code == 8:
            logger.error("服务器,为QAbstractSocket :: bind()指定的地址已被使用,并被设置为独占。")
            # self.change_loginDialog_lineedit_empty_label_text("为QAbstractSocket :: bind()指定的地址已被使用,并被设置为独占。")
        elif error_code == 9:
            logger.error("服务器,指定给QAbstractSocket :: bind()的地址不属于主机。")
            # self.change_loginDialog_lineedit_empty_label_text("指定给QAbstractSocket :: bind()的地址不属于主机。")
        elif error_code == 10:
            logger.error("服务器,本地操作系统不支持请求的套接字操作(例如,缺乏IPv6支持)。")
            # self.change_loginDialog_lineedit_empty_label_text("本地操作系统不支持请求的套接字操作(例如,缺乏IPv6支持)。")
        elif error_code == 11:
            logger.error(
                "服务器,仅由QAbstractSocketEngine使用,最后一次尝试操作尚未完成(后台仍在进行中)。")
            # self.change_loginDialog_lineedit_empty_label_text("仅由QAbstractSocketEngine使用,最后一次尝试操作尚未完成(后台仍在进行中)。")
        elif error_code == 12:
            logger.error("服务器,套接字使用代理,代理需要身份验证。")
            # self.change_loginDialog_lineedit_empty_label_text("套接字使用代理,代理需要身份验证。")
        elif error_code == 13:
            logger.error("服务器,SSL / TLS握手失败,因此连接已关闭(仅在QSslSocket中使用)")
            # self.change_loginDialog_lineedit_empty_label_text("SSL / TLS握手失败,因此连接已关闭(仅在QSslSocket中使用)")
        elif error_code == 14:
            logger.error("服务器,无法联系代理服务器,因为与该服务器的连接被拒绝")
            # self.change_loginDialog_lineedit_empty_label_text("无法联系代理服务器,因为与该服务器的连接被拒绝")
        elif error_code == 15:
            logger.error("服务器,与代理服务器的连接意外关闭(在建立与最终对等体的连接之前)")
            # self.change_loginDialog_lineedit_empty_label_text("与代理服务器的连接意外关闭(在建立与最终对等体的连接之前)")
        elif error_code == 16:
            logger.error("服务器,与代理服务器的连接超时或代理服务器在身份验证阶段停止响应。")
            # self.change_loginDialog_lineedit_empty_label_text("与代理服务器的连接超时或代理服务器在身份验证阶段停止响应。")
        elif error_code == 17:
            logger.error("服务器,未找到使用setProxy()(或应用程序代理)设置的代理地址。")
            # self.change_loginDialog_lineedit_empty_label_text("未找到使用setProxy()(或应用程序代理)设置的代理地址。")
        elif error_code == 18:
            logger.error("服务器,与代理服务器的连接协商失败,因为无法理解代理服务器的响应。")
            # self.change_loginDialog_lineedit_empty_label_text("与代理服务器的连接协商失败,因为无法理解代理服务器的响应。")
        elif error_code == 19:
            logger.error("服务器,当套接字处于不允许它的状态时,尝试进行操作。")
            # self.change_loginDialog_lineedit_empty_label_text("当套接字处于不允许它的状态时,尝试进行操作。")
        elif error_code == 20:
            logger.error("服务器,正在使用的SSL库报告了内部错误。这可能是由于安装错误或库配置错误造成的。")
            # self.change_loginDialog_lineedit_empty_label_text("正在使用的SSL库报告了内部错误。这可能是由于安装错误或库配置错误造成的。")
        elif error_code == 21:
            logger.error("服务器,提供了无效数据(证书,密钥,密码等),并且其使用导致SSL库中的错误。")

        elif error_code == 22:
            logger.error("服务器,发生临时错误(例如,操作将阻塞并且套接字是非阻塞的)。")

        elif error_code == -1:
            logger.error("服务器,发生了一个未识别的错误。")

    @pyqtSlot(QAbstractSocket.SocketState)
    def on_stateChanged(self, state_code):
        '''内网服务器状态码'''
        if state_code == 0:
            # 连接未建立
            logger.info("服务器,连接未建立")
            # self.change_loginDialog_lineedit_empty_label_text("连接未建立")
        elif state_code == 1:
            logger.info("服务器,套接字正在执行主机名查找")
            # self.change_loginDialog_lineedit_empty_label_text("套接字正在执行主机名查找")
        elif state_code == 2:
            logger.info("服务器,套接字已经开始建立连接")
            # self.change_loginDialog_lineedit_empty_label_text("套接字已经开始建立连接")
        elif state_code == 3:
            logger.info("服务器连接已经建立")
            # self.change_loginDialog_lineedit_empty_label_text("连接已经建立")
        elif state_code == 4:
            logger.info("服务器,套接字绑定到地址和端口")
            # self.change_loginDialog_lineedit_empty_label_text("套接字绑定到地址和端口")
        elif state_code == 6:
            logger.info("服务器,套接字即将关闭(数据可能仍在等待写入)")
            # self.change_loginDialog_lineedit_empty_label_text("套接字即将关闭(数据可能仍在等待写入)")

    @pyqtSlot()
    def on_connected(self):
        '''与服务器连接上以后,判断一下使用的是不是本地服务器'''
        logger.info("与服务器建立连接")
        self.ping_timer.start()

    @pyqtSlot()
    def on_start_pushButton_clicked(self):
        '''开始'''
        if not self.websocket.isValid():
            key_name = self.key_name_lineEdit.text()
            token_value = self.token_value_lineEdit.text()
            if not key_name or not token_value:
                QMessageBox.warning(self, "错误", "请输入")
                return
            address = f"ws://{self.config['SOCKET']['IP']}:{self.config['SOCKET']['PORT']}/socket.io/?EIO=3&transport=websocket&token={token_value}"
            logger.info(f"尝试与{address}建立连接")
            self.websocket.open(QUrl(address))  # 建立连接
            self.title = key_name
            self.driver.get(profile.ALI_LOGIN_PATH)
            self.driver.execute_script(
                f"document.title='{self.key_name_lineEdit.text()}'")

    @pyqtSlot()
    def on_set_title_pushButton_clicked(self):
        '''设置页面title'''
        self.driver.execute_script(
            f"document.title='{self.key_name_lineEdit.text()}'")
Ejemplo n.º 21
0
class WebSocketWindow(QWidget):
    def __init__(self, *args, **kwargs):
        super(WebSocketWindow, self).__init__(*args, **kwargs)
        uic.loadUi('WebSocketTest.ui', self)
        self._param_sep = '\n\n' + '-' * 50
        self._files = []
        self.listWidget.setEnabled(False)

        # 读取配置文件
        setting = QSettings(
            'F:/QtDesigner/Windows/plugins/designer/data/server.ini',
            QSettings.IniFormat)
        port = setting.value('ws_port', 55441)

        self.editAddress.setPlaceholderText('比如:ws://127.0.0.1:%s' % port)
        self.editAddress.setText('ws://127.0.0.1:%s' % port)

        self._socket = QWebSocket(parent=self)
        self._socket.connected.connect(self.onConnected)
        self._socket.disconnected.connect(self.onDisconnected)
        self._socket.binaryMessageReceived.connect(
            self.onBinaryMessageReceived)
        self._socket.textMessageReceived.connect(self.onTextMessageReceived)
        self._socket.error.connect(self.onError)

    @pyqtSlot()
    def on_buttonConnect_clicked(self):
        # 连接
        url = self.editAddress.text().strip()
        if not url or not url.startswith('ws://'):
            return
        self.editAddress.setEnabled(False)
        self.buttonConnect.setEnabled(False)
        self._socket.open(QUrl(url))

    @pyqtSlot()
    def on_buttonSend_clicked(self):
        # 执行命令
        code = self.textEditParams.toPlainText().split(
            self._param_sep)[0].strip().strip('\n')
        if not code:
            return
        try:
            code = json.loads(code)
            self._sendCode(code)
        except Exception as e:
            self.textBrowserApi.append('<font color=red>{0}</font>'.format(
                str(e)))

    def on_textBrowserApi_textChanged(self):
        self.textBrowserApi.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)

    def on_checkBoxWrap_toggled(self, toggled):
        # 自动换行
        self.textBrowserApi.setLineWrapMode(
            QTextEdit.WidgetWidth if toggled else QTextEdit.NoWrap)

    def on_listWidget_itemClicked(self, item):
        index = self.listWidget.row(item)
        if index == 0:
            self.onOpenFiles()
        elif index == 1:
            self.onCloseFiles()
        elif index == 2:
            self.onWindowInfo()
        elif index == 3:
            self.onWindowCode()
        elif index == 4:
            self.onWindowScreenShot()
        elif index == 5:
            self.onSetStyleSheet()
        elif index == 6:
            self.onSetProperties()
        elif index == 7:
            self.onSendLogs()

    def setParams(self, param, doc=''):
        # 设置参数显示
        self.textEditParams.clear()
        self.textEditParams.appendPlainText(json.dumps(param, indent=4))
        if doc:
            self.textEditParams.appendPlainText(self._param_sep)
            self.textEditParams.appendPlainText(doc)
        self.textEditParams.moveCursor(QTextCursor.Start,
                                       QTextCursor.MoveAnchor)

    def onOpenFiles(self):
        # 打开文件
        files, _ = QFileDialog.getOpenFileNames(self, self.tr('打开'), '',
                                                self.tr('设计师UI文件(*.ui)'))
        if not files:
            return
        # 一定要把\\转为/
        self._files = [f.replace('\\', '/') for f in files]
        self.setParams({
            'method': 'openFiles',
            'files': self._files
        }, '\n\nfiles=[...] 需要打开的UI文件路径数组,必须把路径中的\\替换为/')

    def onCloseFiles(self):
        # 关闭文件
        item, ok = QInputDialog.getItem(
            self, self.tr('关闭方式'),
            self.tr('all=true关闭所有打开的文件,忽略files参数\n否则只关闭files参数指定的文件'),
            ['True', 'Files'], 0, False)
        if not ok:
            return
        if item == 'True':
            self.setParams({
                'method': 'closeFiles',
                'all': True
            }, '\n\nall=true 关闭所有打开的文件并忽略 files参数\nfiles=[...] 只关闭files参数指定的文件'
                           )
            return
        if self._files:
            self.setParams({
                'method': 'closeFiles',
                'files': self._files
            }, '\n\nall=true 关闭所有打开的文件并忽略 files参数\nfiles=[...] 只关闭files参数指定的文件'
                           )

    def onWindowInfo(self):
        # 获取窗口信息
        item, ok = QInputDialog.getItem(self, self.tr('获取类型'),
                                        self.tr('all=true所有属性和信号槽连接(包括继承)'),
                                        ['True', 'False'], 0, False)
        if not ok:
            return
        self.setParams({
            'method': 'getObjectInfos',
            'all': item == 'True'
        }, '\n\nall=true 获取所有属性和信号槽连接(包括继承)\nall=false 获取该类自身的属性和信号槽连接')

    def onWindowCode(self):
        # 获取窗口代码
        item, ok = QInputDialog.getItem(
            self, self.tr('代码类型'), self.tr('xml\nc++,cpp,c\npyqt5\npyside2'),
            ['xml', 'cpp', 'pyqt5', 'pyside2'], 0, False)
        if not ok:
            return
        self.setParams({
            'method': 'getUiCode',
            'type': item
        }, '\n\nxml cpp pyqt5 pyside2')

    def onWindowScreenShot(self):
        # 获取窗口截图
        item, ok = QInputDialog.getItem(
            self, self.tr('截取类型'),
            self.tr('all=所有打开的窗口\nmain=整个设计师窗口\ncurrent或其他值则为当前窗口'),
            ['all', 'main', 'current'], 0, False)
        if not ok:
            return
        self.setParams(
            {
                'method': 'getScreenShot',
                'type': item,
                'callback': 'doViewImages'
            },
            '\n\ntype=all 截取所有打开的UI文件的界面图\ntype=main 截取整个设计师窗口\ntype=current或其他值则截取当前窗口\ncallback=function '
            '可选回调函数跟随服务端返回值里附带(用户客户端自己调用对应的回调函数)\n\n注意: 返回的是Base64编码的PNG图片')

    def onSetStyleSheet(self):
        # 打开测试文件
        path = os.path.abspath('TestUi.ui').replace('\\', '/')
        self._sendCode({'method': 'openFiles', 'files': [path]})
        self.setParams(
            {
                'method': 'setStyleSheet',
                'files': {
                    path: {
                        'Form': {
                            'styleSheet': '#Form {background: black;}'
                        }  # 修改样式
                    }
                }
            },
            '\n\n设置对应控件的样式')

    def onSetProperties(self):
        # 打开测试文件
        path = os.path.abspath('TestUi.ui').replace('\\', '/')
        self._sendCode({'method': 'openFiles', 'files': [path]})
        self.setParams(
            {
                'method': 'setProperties',
                'files': {
                    path: {
                        'pushButton': {
                            'text': '测试文字',  # 修改文字
                            'geometry': [100, 100, 30, 30],  # 修改位置和大小
                        }
                    }
                }
            },
            '\n\n设置对应控件的样式')

    def onSendLogs(self):
        # 发送日志
        self.setParams(
            {
                'method': 'sendLog',
                'level': logging.INFO,
                'msg': 'log message'
            },
            '\n\n发送日志\nlevel: 0=NOTSET\nlevel: 10=DEBUG\nlevel: 20=INFO\nlevel: 30=WARNING\nlevel: '
            '40=ERROR\nlevel: 50=CRITICAL')

    def doViewImages(self, info):
        # 截图回调函数
        for name, value in info.get('data', {}).items():
            try:
                pixmap = QPixmap()
                # 从base64加载图
                pixmap.loadFromData(QByteArray.fromBase64(value.encode()))
                # 显示小图
                self.textBrowserApi.document().addResource(
                    QTextDocument.ImageResource,
                    QUrl('dynamic:/{0}'.format(name)),
                    pixmap.scaledToHeight(512))
                self.textBrowserApi.append(
                    '<img src="dynamic:/{0}">'.format(name))
            except Exception as e:
                self.textBrowserApi.append('<font color=red>{0}</font>'.format(
                    str(e)))

    def _sendCode(self, code):
        code = json.dumps(code)
        if self._socket.isValid() and self._socket.state(
        ) == QAbstractSocket.ConnectedState:
            # 服务端返回的数据经过zlib压缩
            self._socket.sendTextMessage(code)
        else:
            self.textBrowserApi.append('<font color=red>{0}</font>'.format(
                self.tr('错误:无法发送网络数据')))

    def onConnected(self):
        self.buttonSend.setEnabled(True)
        self.listWidget.setEnabled(True)
        self.textBrowserApi.append('<font color=black>{0}</font>'.format(
            self.tr('连接成功')))

    def onDisconnected(self):
        self.editAddress.setEnabled(True)
        self.buttonConnect.setEnabled(True)
        self.listWidget.setEnabled(False)
        self.buttonSend.setEnabled(False)
        self.textBrowserApi.append('<font color=red>{0}</font>'.format(
            self.tr('连接断开')))

    def onBinaryMessageReceived(self, message):
        try:
            # zlib压缩过后的内容(为了减小带宽传送)
            message = zlib.decompress(message).decode()
        except Exception as e:
            self.textBrowserApi.append(
                '<font color=red>uncompress binary message failed:{0}</font>'.
                format(e))
            return
        self.onTextMessageReceived(message)

    def onTextMessageReceived(self, message):
        try:
            info = json.loads(message)
            text = json.dumps(
                info, indent=4 if self.checkBoxFormat.isChecked() else None)
            time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            if info.get('type', '') == 'event':
                self.textBrowserEvent.append(time)
                self.textBrowserEvent.append(text)
                self.textBrowserEvent.append('')
            else:
                self.textBrowserApi.append(time)
                self.textBrowserApi.append(text)
                self.textBrowserApi.append('')
            # 调用回调
            callback = info.get('callback', '')
            if callback and hasattr(self, callback) and callable(
                    getattr(self, callback)):
                try:
                    getattr(self, callback)(info)
                except Exception as e:
                    self.textBrowserApi.append(
                        '<font color=red>callback({0}) failed:{1}</font>'.
                        format(callback, e))
        except Exception as e:
            self.textBrowserApi.append(
                '<font color=red>json load message failed:{0}</font>'.format(
                    e))
            return

    def onError(self, error):
        self.editAddress.setEnabled(True)
        self.buttonConnect.setEnabled(True)
        self.listWidget.setEnabled(False)
        self.buttonSend.setEnabled(False)
        self.textBrowserApi.append(
            '<font color=red>websocket error:{0} {1}</font>'.format(
                error, self._socket.errorString()))
Ejemplo n.º 22
0
class App(QtWidgets.QMainWindow):
    RxMsg = QtCore.pyqtSignal(object)
    statusUpdate = QtCore.pyqtSignal(str)
    connectionChanged = QtCore.pyqtSignal(bool)

    @classmethod
    def addBaseArguments(cls, parser):
        '''
        Adds base app arguments to the provided ArgParser
        skipFiles - if True we won't provide an agument for a files list
        returns the parser
        '''
        parser.add_argument('--connectionType', choices=['socket', 'qtsocket', 'file'], default='qtsocket',
            help='Specify the type of connection we are establishing as a message pipe/source.')
        parser.add_argument('--connectionName', default='127.0.0.1:5678',
            help='''The connection name.  For socket connections this is an IP and port e.g. 127.0.0.1:5678.  
                    You may prepend ws:// to indicate you want to use a Websocket instead.  For file 
                    connection types, this is the filename to use as a message source.
                    This parameter is overridden by the --ip and --port options.''')
        parser.add_argument('--ip', help='The IP address for a socket connection.   Overrides connectionName.')
        parser.add_argument('--port', type=int, help='The port for a socket connection. Overrides connectionName.')
        parser.add_argument('--msg', nargs='+', default=set(), help='''A space delimited list of white list messages to process.
                    All messages outside of this list will be ignored.  For example: --msg TestCase1 Network.Note 
                    Taxonomy/Candidae.AFox''')
        parser.add_argument('--msgdir', help=''''The directory to load Python message source from.''')
        parser.add_argument('--serial', action='store_true', help='Set if you want to use a SerialHeader instead of a NetworkHeader.')
        parser.add_argument('--log', help='The log file type (csv/json/bin) or complete log file name.')

        return parser
    
    def __init__(self, name, args):
        '''Initializer - this is used more as a decorator in the MsgTools suite than an actual
        standalone class.

        name - the name of the application - usually displayed in the UI window header
        args - argparse.Namespace of command line options.  See getArgParser for what we provide
               by default as part of this base app framework.  A command line tool may act as a 
               parent ArgumentParser and provide additional options.
        '''

        # If the caller skips adding base arguments we need to patch up args
        args.serial = None if hasattr(args, 'serial') == False else args.serial
        args.msg = None if hasattr(args, 'msg') == False else args.msg
        args.msgdir = None if hasattr(args, 'msgdir') == False else args.msgdir

        # default to Network, unless we have a input filename that contains .txt
        headerName = "NetworkHeader"
        if args.serial or (args.connectionType=='file' and os.path.splitext(args.connectionType)[1].lower() == '.txt'):
            headerName = "SerialHeader"

        self.name = name
        
        # persistent settings
        self.settings = QtCore.QSettings("MsgTools", name)
        
        # rx buffer, to receive a message with multiple signals
        self.rxBuf = bytearray()
        
        self.allowedMessages = set(args.msg)
        
        # flag that indicates if we're connected
        self.connected = False
        
        # directory to load messages from.
        msgLoadDir = None

        # connection modes
        ip = ""
        port = ""

        if args.connectionType is not None:
            self.connectionType = args.connectionType
        if args.connectionName is not None:
            self.connectionName = args.connectionName
        if args.ip is not None:
            ip = args.ip
        if args.msgdir:
            msgLoadDir = args.msgdir
        
        # if either --ip or --port were used, override connectionName
        if args.ip is not None or args.port is not None:
            self.connectionName = str(ip)+":"+str(port)
        
        # initialize the read function to None, so it's not accidentally called
        self.readBytesFn = None

        try:
            Messaging.LoadAllMessages(searchdir=msgLoadDir, headerName=headerName)
        except RuntimeError as e:
            print(e)
            quit()
        except:
            import traceback
            print(traceback.format_exc())
            quit()

        if args.msg is not None:
            # Validate all message names are valid
            for msg in args.msg:
                if msg not in Messaging.MsgIDFromName:
                    print('{0} is not a valid message name!'.format(msg))
                    sys.exit(1)

        self.logFileType = None
        self.logFile = None
        if args.log is not None:
            self.startLog(args.log)

        port = args.port
        
        self.OpenConnection()

    def CloseConnection(self):
        if hasattr(self, 'connection') and (self.connectionType.lower() == "socket" or self.connectionType.lower() == "qtsocket"):
            if "ws:" in self.connectionName:
                if self.connection:
                    self.connection.close()
                    self.connection = None
            else:
                if self.connection:
                    self.connection.disconnectFromHost()
                    self.connection = None

    # this function opens a connection, and returns the connection object.
    def OpenConnection(self):
        self.CloseConnection()

        if(self.connectionType.lower() == "socket" or self.connectionType.lower() == "qtsocket"):
            if "ws:" in self.connectionName:
                connectionName = self.connectionName.replace("ws://","")
                (ip, port) = connectionName.rsplit(":",1)
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5679"
                connectionName = "ws://"+ip+":"+port
                from PyQt5.QtWebSockets import QWebSocket
                from PyQt5.QtCore import QUrl
                self.connection = QWebSocket()
                print("opening websocket " + connectionName)
                self.connection.open(QUrl(connectionName))
                self.connection.binaryMessageReceived.connect(self.processBinaryMessage)
                self.sendBytesFn = self.connection.sendBinaryMessage
            else:
                #print("opening TCP socket " + self.connectionName)
                (ip, port) = self.connectionName.rsplit(":",1)
                if ip == None or ip == "":
                    ip = "127.0.0.1"

                if port == None or port == "":
                    port = "5678"
                
                port = int(port)

                #print("ip is ", ip, ", port is ", port)
                self.connection = QtNetwork.QTcpSocket(self)
                self.connection.error.connect(self.displayConnectError)
                ret = self.connection.readyRead.connect(self.readRxBuffer)
                self.connection.connectToHost(ip, port)
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
                #print("making connection returned", ret, "for socket", self.connection)
            self.connection.connected.connect(self.onConnected)
            self.connection.disconnected.connect(self.onDisconnect)
        elif(self.connectionType.lower() == "file"):
            try:
                self.connection = open(self.connectionName, 'rb')
                self.readBytesFn = self.connection.read
                self.sendBytesFn = self.connection.write
            except IOError:
                print("\nERROR!\ncan't open file ", self.connectionName)
                sys.exit(1)
        else:
            print("\nERROR!\nneed to specify socket or file")
            sys.exit()

        self.connection

    def startLog(self, log_name, log_suffix=''):
        if log_name.endswith('csv'):
            self.logFileType = "csv"
            # hash table of booleans for if each type of message has had it's header
            # put into this log file already.
            self.loggedMsgHeaderRow = {}
        elif log_name.endswith('json'):
            self.logFileType = "json"
        elif log_name.endswith('bin') or log_name.endswith('log'):
            self.logFileType = "bin"
        else:
            print("ERROR!  Invalid log type " + log_name)
        if "." in log_name:
            # if there's a ., assume they specified an exact filename to use
            logFileName = log_type
        else:
            # if not, generate a filename based on current date/time
            currentDateTime = QtCore.QDateTime.currentDateTime()
            logFileName = currentDateTime.toString("yyyyMMdd-hhmmss") + log_suffix + "." + self.logFileType
        self.logFile = QtCore.QFile(logFileName)
        self.logFile.open(QtCore.QIODevice.Append)

    def stopLog(self):
        if self.logFile is not None:
            self.logFile.close()
            self.logFile = None

    def onConnected(self):
        self.connected = True
        self.connectionChanged.emit(True)
        # send a connect message
        connectMsg = Messaging.Messages.Network.Connect()
        connectMsg.SetName(self.name)
        self.SendMsg(connectMsg)
        # if the app has it's own function to happen after connection, assume it will set subscriptions to what it wants.
        try:
            fn = self.onAppConnected
        except AttributeError:
            # send a subscription message
            subscribeMsg = Messaging.Messages.Network.MaskedSubscription()
            self.SendMsg(subscribeMsg)
            #self.statusUpdate.emit('Connected')
        else:
            self.onAppConnected()
    
    def onDisconnect(self):
        self.connected = False
        self.connectionChanged.emit(False)
        #self.statusUpdate.emit('NOT Connected')
    
    def displayConnectError(self, socketError):
        self.connected = False
        self.connectionChanged.emit(False)
        self.statusUpdate.emit('Not Connected('+str(socketError)+'), '+self.connection.errorString())

    # Qt signal/slot based reading of websocket
    def processBinaryMessage(self, bytes):
        if isinstance(bytes, bytearray):
            hdr = Messaging.hdr(bytes)
        else:
            # Assume this is a QByteArray from a websocket
            hdr = Messaging.hdr(bytes.data())

        # if we got this far, we have a whole message! Emit the signal
        # if the message is whitelisted
        msg = Messaging.MsgFactory(hdr)
        if self._messageAllowed(msg):
            self.RxMsg.emit(msg)
            self.logMsg(msg)

    def _messageAllowed(self, msg):
        '''Check msg against the list of allowed messages.

        return True if all messages are allowed, or the message is
        in the message white list.'''
        retVal = True
        if len(self.allowedMessages) > 0:
            if not msg.MsgName() in self.allowedMessages:
                retVal = False

        return retVal

    # Qt signal/slot based reading of TCP socket
    def readRxBuffer(self):
        input_stream = QtCore.QDataStream(self.connection)
        while(self.connection.bytesAvailable() > 0):
            # read the header, unless we have the header
            if(len(self.rxBuf) < Messaging.hdrSize):
                #print("reading", Messaging.hdrSize - len(self.rxBuf), "bytes for header")
                self.rxBuf += input_stream.readRawData(Messaging.hdrSize - len(self.rxBuf))
                #print("have", len(self.rxBuf), "bytes")
            
            # if we still don't have the header, break
            if(len(self.rxBuf) < Messaging.hdrSize):
                print("don't have full header, quitting")
                return
            
            hdr = Messaging.hdr(self.rxBuf)
            
            # need to decode body len to read the body
            bodyLen = hdr.GetDataLength()
            
            # read the body, unless we have the body
            if(len(self.rxBuf) < Messaging.hdrSize + bodyLen):
                #print("reading", Messaging.hdrSize + bodyLen - len(self.rxBuf), "bytes for body")
                self.rxBuf += input_stream.readRawData(Messaging.hdrSize + bodyLen - len(self.rxBuf))
            
            # if we still don't have the body, break
            if(len(self.rxBuf) < Messaging.hdrSize + bodyLen):
                print("don't have full body, quitting")
                return

            # Process what we assume to be a full message...            
            self.processBinaryMessage(self.rxBuf)

            # then clear the buffer, so we start over on the next message
            self.rxBuf = bytearray()
    
    def SendMsg(self, msg):
        bufferSize = len(msg.rawBuffer().raw)
        computedSize = Messaging.hdrSize + msg.hdr.GetDataLength()
        if(computedSize > bufferSize):
            msg.hdr.SetDataLength(bufferSize - Messaging.hdrSize)
            print("Truncating message to "+str(computedSize)+" bytes")
        if(computedSize < bufferSize):
            # don't send the *whole* message, just a section of it up to the specified length
            self.sendBytesFn(msg.rawBuffer().raw[0:computedSize])
        else:
            self.sendBytesFn(msg.rawBuffer().raw)

    def logMsg(self, msg):
        if self.logFile:
            log = ''
            if self.logFileType == "csv":
                if not msg.MsgName() in self.loggedMsgHeaderRow:
                    self.loggedMsgHeaderRow[msg.MsgName()] = True
                    log = msg.csvHeader(timeColumn=True)+'\n'
                log += msg.toCsv(timeColumn=True)+'\n'
                log = log.encode('utf-8')
            elif self.logFileType == "json":
                log = msg.toJson(includeHeader=True)+'\n'
                log = log.encode('utf-8')
            elif self.logFileType == "bin":
                log = msg.rawBuffer().raw
            self.logFile.write(log)
            self.logFile.flush()

    # this function reads messages (perhaps from a file, like in LumberJack), and calls the message handler.
    # unclear if it ever makes sense to use this in a application that talks to a socket or UART, because
    # it exits when there's no more data.  Perhaps it does in a CLI that's very procedural, like an automated
    # system test script?
    #
    # Note: This loop won't work with Qt socket signals.  For signals to work you have to exeucute the
    # application event loop.  To make this method work with QtSockets you would need to layer in 
    # the various waitFor* methods on the connection within this loop.
    def MessageLoop(self):
        msgCount=0
        startSeqField = Messaging.findFieldInfo(Messaging.hdr.fields, "StartSequence")
        if startSeqField == None:
            print("header contains no StartSequence")
        else:
            startSequence = int(Messaging.hdr.GetStartSequence.default)
            print("header contains StartSequence " + hex(startSequence))
        try:
            while (1):
                msgCount+=1
                self.rxBuf = self.readBytesFn(Messaging.hdrSize)
                
                if(len(self.rxBuf) != Messaging.hdrSize):
                    raise StopIteration
                
                hdr = Messaging.hdr(self.rxBuf)
                try:
                    start = hdr.GetStartSequence()
                    if(start != startSequence):
                        print("Error on message " + str(msgCount) + ". Start sequence invalid: 0x" + format(start, '02X'))
                        # resync on start sequence
                        bytesThrownAway = 0
                        while (1):
                            self.rxBuf += self.readBytesFn(1)
                            self.rxBuf = self.rxBuf[1:]
                            if(len(self.rxBuf) != Messaging.hdrSize):
                                raise StopIteration
                            bytesThrownAway += 1
                            start = hdr.GetStartSequence()
                            if(start == startSequence):
                                print("Resynced after " + str(bytesThrownAway) + " bytes")
                                break
                    headerChecksum = hdr.GetHeaderChecksum()
                    bodyChecksum = hdr.GetBodyChecksum(self)
                except AttributeError:
                    pass

                # need to decode body len to read the body
                bodyLen = hdr.GetDataLength()
                
                # read the body
                self.rxBuf += self.readBytesFn(bodyLen)
                if(len(self.rxBuf) != Messaging.hdrSize + bodyLen): break
                
                # create a new header object with the appended body
                hdr = Messaging.hdr(self.rxBuf)

                # got a complete message, call the callback to process it if
                # the message is on our white list
                msg = Messaging.MsgFactory(hdr)
                if self._messageAllowed(msg):
                    self.ProcessMessage(msg)
        except StopIteration:
            print("found end of file, exited")
Ejemplo n.º 23
0
class MultiChatWS(QtCore.QObject):
    """
    Websocket connected to MultiChat-Server.
    """
    def __init__(self, logger, core, config):
        super().__init__(core)
        self.core = core
        self.logger = logger
        disabled = False
        self.config = config
        self.url = config['multichat-url'].rstrip('/') + '/'
        self.key = config['multichat-key']
        self.server_name = config[
            'server-name'] if 'server-name' in config else ''
        self.do_listen = config['listen']
        self.do_post = config['post']
        self.ignore_prefix = config['ignore-prefix']
        self.lang = 'en'  # default value
        if 'lang' in config:
            if config['lang'] not in SUPPORTED_LANGUAGES:
                self.logger.warning('Unsupported language: {}'.format(
                    config['lang']))
            else:
                self.lang = config['lang']

        # load mcBasicLib
        self.utils = core.get_plugin('mcBasicLib')
        if self.utils is None:
            self.logger.error(
                'Failed to load plugin "mcBasicLib", multiChat will be disabled.'
            )
            self.logger.error(
                'Please make sure that "mcBasicLib" has been added to plugins.'
            )
            disabled = True
        if disabled:
            return

        self.ws = QWebSocket()
        self.ws_valid = False
        # connect signals and slots
        self.core.sig_server_start.connect(self.on_server_start)
        self.core.sig_server_stop.connect(self.on_server_stop)
        self.utils.sig_input.connect(self.on_player_input)
        self.utils.sig_login.connect(self.on_player_login)
        self.utils.sig_logout.connect(self.on_player_logout)
        self.utils.sig_advancement.connect(self.on_advancement)
        self.utils.sig_death.connect(self.on_death)
        self.ws.connected.connect(self.on_connected)
        self.ws.textMessageReceived.connect(self.on_recv)
        self.ws.disconnected.connect(self.on_connection_broken)
        self.retry_interval = RETRY_INTERVAL_MIN
        self.retry_timer = QtCore.QTimer()
        self.retry_timer.timeout.connect(self.on_retry_timer)
        self.on_retry_timer()  # open connection using this function

    def post(self, message):
        self.utils.tell('@a', message, color='#777777')

    def send_msg(self, message):
        """
        A simple wrap for sending message to multichat-server.
        """
        if self.ws_valid:
            obj = {
                'action': 'client-message',
                'content': message,
            }
            data = json.dumps(obj)
            self.logger.debug('WebSocket sending: ' + data)
            self.ws.sendTextMessage(data)
        else:
            self.logger.warning('Tried to write websocket when not available')

    @QtCore.pyqtSlot()
    def on_connected(self):
        self.logger.info('Successfully connected to: ' + self.url)
        # register
        register_obj = {
            'action': 'register',
            'client-name':
            'MC-' + self.server_name if self.server_name else 'MC',
            'secret-key': self.key
        }
        register_str = json.dumps(register_obj)
        self.logger.debug('WebSocket sending:  ' + register_str)
        self.ws.sendTextMessage(register_str)

    @QtCore.pyqtSlot()
    def on_connection_broken(self):
        self.logger.info('Connection broken, retry after ' +
                         str(self.retry_interval) + 'ms')
        self.utils.tell(
            '@a', 'multichat connection broken. Retry after ' +
            str(self.retry_interval) + 'ms')
        self.ws_valid = False
        self.retry_timer.setInterval(self.retry_interval)
        self.retry_timer.start()
        self.retry_interval = min(RETRY_INTERVAL_MAX, self.retry_interval * 2)

    @QtCore.pyqtSlot()
    def on_retry_timer(self):
        self.retry_timer.stop()
        self.logger.info('Connecting to multichat server')
        self.utils.tell('@a', 'multichat: connecting to server')
        self.ws.open(QtCore.QUrl(self.url))

    @QtCore.pyqtSlot(str)
    def on_recv(self, message):
        self.logger.debug('WebSocket received: ' + message)
        data = json.loads(message)
        if data['action'] == 'register-ack':
            self.logger.info('Successfully registered.')
            self.utils.tell('@a', 'multichat: server connected')
            self.ws_valid = True
            self.retry_interval = RETRY_INTERVAL_MIN
        if data['action'] == 'forwarding-message' and self.do_post:
            source = data['source-client-name']
            content = data['content']
            post_str = '[{}] {}'.format(source, content)
            # post to mc server
            self.post(post_str)

    @QtCore.pyqtSlot()
    def on_server_start(self):
        self.logger.debug('MultiChat.on_server_start called')
        if self.lang == 'en':
            msg = 'Server starting up...'
        elif self.lang == 'zh-cn':
            msg = '服务器正在开启...'
        self.send_msg(msg)

    @QtCore.pyqtSlot()
    def on_server_stop(self):
        self.logger.debug('MultiChat.on_server_stop called')
        if self.lang == 'en':
            msg = 'Server closed'
        elif self.lang == 'zh-cn':
            msg = '服务器已关闭'
        self.send_msg(msg)

    @QtCore.pyqtSlot(tuple)
    def on_player_input(self, pair):
        self.logger.debug('MultiChat.on_player_input called')
        player, text = pair
        if text == '!multichat connect':
            if self.ws_valid:
                self.utils.tell(player,
                                'multichat is already connected to server')
            else:
                self.on_retry_timer()
            return
        if self.do_listen:
            if player.is_console():
                # we should not listen from console
                return
            for prefix in self.ignore_prefix:
                if text.startswith(prefix):
                    return
            msg = '<{}> {}'.format(player.name, text)
            self.send_msg(msg)

    @QtCore.pyqtSlot(object)
    def on_player_login(self, player):
        self.logger.debug('MultiChat.on_player_login called')
        # player should never be console
        player = player.name
        if self.lang == 'en':
            msg = '{} joined the game'.format(player)
        elif self.lang == 'zh-cn':
            msg = '{}加入了游戏'.format(player)
        self.send_msg(msg)

    @QtCore.pyqtSlot(object)
    def on_player_logout(self, player):
        self.logger.debug('MultiChat.on_player_logout called')
        # player should never be console
        player = player.name
        if self.lang == 'en':
            msg = '{} left the game'.format(player)
        elif self.lang == 'zh-cn':
            msg = '{}退出了游戏'.format(player)
        self.send_msg(msg)

    @QtCore.pyqtSlot(object)
    def on_advancement(self, advc):
        self.logger.debug('MultiChat.on_advancement called')
        msg = advc.format(lang=self.lang)
        self.send_msg(msg)

    @QtCore.pyqtSlot(object)
    def on_death(self, death_obj):
        self.logger.debug('MultiChat.on_death called')
        msg = death_obj.format(lang=self.lang)
        self.send_msg(msg)
Ejemplo n.º 24
0
 def __init__(self):
     super(Chat, self).__init__()
     self.websocket = QWebSocket()
     self.websocket.textMessageReceived.connect(self.on_text_message_received)