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)
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
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)
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)
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 __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 __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 __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
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)
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 __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 __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)
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')
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)
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()
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)
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())
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")
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)
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()}'")
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()))
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")
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)
def __init__(self): super(Chat, self).__init__() self.websocket = QWebSocket() self.websocket.textMessageReceived.connect(self.on_text_message_received)