class Window(QWidget, Ui_FormFrameworkTools): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.setupUi(self) self._passwordError = False self.imageWidget = ImageView() self._initSocket() self._initCodeEdit() # 配置 self._setting = QSettings('PsConnectFrameworkTools', 'Settings') # ip地址 self.lineEditAddress.setText( self._setting.value('lineEditAddress', '127.0.0.1', str)) # ps端口 self.spinBoxPsPort.setValue( self._setting.value('spinBoxPsPort', 49494, int)) # ps密码 self.lineEditPassword.setText( self._setting.value('lineEditPassword', '', str)) # 设置参数 self.argsEdit.setPlainText( self._setting.value('argsEdit', 'test2();', str)) # 设置编辑器里的代码 self.codeEdit.setText(self._setting.value('codeEdit', CodeDefault, str)) def _initSocket(self): # 初始化client(用于连接ps) self._client = TcpSocket(self) self._client.messageReceived.connect(self.onMessageReceived) self._client.connected.connect(self.onConnectSuccessed) self._client.connectClosed.connect(self.onConnectClosed) def _initCodeEdit(self): # 初始化编辑器的工作 self.codeEdit.setUtf8(True) self.codeEdit.linesChanged.connect(self.onLinesChanged) # 行改变 # 代码高亮 self.codeEdit.setLexer(QsciLexerJavaScript(self)) # 自动折叠 self.codeEdit.setMarginType(3, QsciScintilla.SymbolMargin) self.codeEdit.setMarginLineNumbers(3, False) self.codeEdit.setMarginWidth(3, 15) self.codeEdit.setMarginSensitivity(3, True) # 显示行号 #self.codeEdit.setMarginType(0, QsciScintilla.NumberMargin) self.codeEdit.setMarginLineNumbers(0, True) self.onLinesChanged() # 代码提示 sciApi = QsciAPIs(self.codeEdit.lexer()) sciApi.prepare() self.codeEdit.setAutoCompletionSource(QsciScintilla.AcsAll) # 设置源 self.codeEdit.setAutoCompletionCaseSensitivity(True) # 设置自动补全大小写敏感 self.codeEdit.setAutoCompletionThreshold(1) # 设置每输入一个字符就会出现自动补全的提示 # 设置字体 self.codeEdit.setFont(QFont('Consolas', 16)) self.codeEdit.setMarginsFont(self.codeEdit.font()) # 设置编码 self.codeEdit.SendScintilla(QsciScintilla.SCI_SETCODEPAGE, QsciScintilla.SC_CP_UTF8) self.codeEdit.setBraceMatching(QsciScintilla.StrictBraceMatch) # 设置当前行高亮 self.codeEdit.setCaretLineVisible(True) self.codeEdit.setCaretLineBackgroundColor(Qt.lightGray) self.codeEdit.setCaretForegroundColor(Qt.white) # tab # table relative self.codeEdit.setIndentationsUseTabs(True) self.codeEdit.setIndentationWidth(4) self.codeEdit.setTabIndents(True) self.codeEdit.setAutoIndent(True) self.codeEdit.setBackspaceUnindents(True) self.codeEdit.setTabWidth(4) # indentation guides self.codeEdit.setIndentationGuides(True) # folding margin self.codeEdit.setFolding(QsciScintilla.PlainFoldStyle) self.codeEdit.setMarginWidth(2, 12) # 自动换行 self.codeEdit.setWrapMode(QsciScintilla.WrapWord) def onLinesChanged(self): # 动态设置左边的边距 self.codeEdit.setMarginWidth( 0, self.codeEdit.fontMetrics().width(str(self.codeEdit.lines())) + 5) def onMessageReceived(self, data): # 接收到ps返回的结果就关闭进度条 self.closeWait() if data.find(b'disconnecting') > -1: self._passwordError = True # 密码错误 QMessageBox.critical(self, '错误', '密码错误') return self._passwordError = False # 解码数据 try: message = Protocol.unpack(data) print('message:', message) # print('data:', data) except Exception as e: self.resultEdit.append('解码数据错误: ' + str(e)) return if len(data) < 6: return if message.isImg(): self.analysisImage(message.data) return if message.isText(): self.analysisText(message) def analysisText(self, message): # 尝试解析文本消息 try: data = message.data.decode() self.resultEdit.append(data) if not data.startswith('{') and not data.endswith('}'): return data = json.loads(data) # 获取版本 version = data.get('version', '0.') if not version or int(version.split('.')[0]) < 18: QMessageBox.information(self, '提示', '未能获取到版本或者版本号小于18(PS 2017)') return # 获取可能需要调用的函数和参数 method = data.get('method', 'nomethod') params = data.get('params', []) if hasattr(self, method): # 调用函数 getattr(self, method)(*params) except Exception as e: print('解析数据错误:', e) traceback.print_exc() def analysisImage(self, data): # 解析图片消息 pixmap = QPixmap() pixmap.loadFromData(data) print('image is null:', pixmap.isNull()) if not pixmap.isNull(): self.imageWidget.setPixmap(pixmap) self.imageWidget.show() def onConnectSuccessed(self): # 连接成功发送代码验证密码是否正确 self.closeWait() # 关闭正在连接中的进度条 self._client.write( Protocol.pack(CodeGetVersion, Protocol.JAVASCRIPT_TYPE)) self._client.flush() Protocol.increase() self.showWait('正在验证密码...') def onConnectClosed(self, message=None): # 连接被断开 self.closeWait() if self._passwordError: return if QMessageBox.question(self, '错误', message + '\n是否重连?', QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: # 重连 self.on_buttonConnect_clicked() def showMessage(self, message): # 显示消息 if message: # unquote 尝试对消息反编码 QMessageBox.information(self, '提示', unquote(message)) @pyqtSlot() def on_buttonResetCode_clicked(self): # 重置代码区域为默认 self.codeEdit.setText(CodeDefault) @pyqtSlot() def on_buttonGetImage_clicked(self): # 获取ps中的图片过来显示 self.codeEdit.setText(CodeGetImage) @pyqtSlot() def on_buttonSendImage_clicked(self): # 发送图片到ps中 file, _ = QFileDialog.getOpenFileName( self, '选择图片', QStandardPaths.writableLocation(QStandardPaths.DesktopLocation), '*.jpg') if not file: return if self._client.state( ) == self._client.ConnectedState and self._client.isWritable(): # 这里必须要有一个1表示图片类型 data = b'\x01' + open(file, 'rb').read() self._client.write(Protocol.pack(data, Protocol.IMAGE_TYPE)) # 发送图片数据 self._client.flush() Protocol.increase() # 自增1 self.showWait('正在发送中...') # 显示进度条 else: QMessageBox.critical(self, '错误', '未连接或者不能发送数据') @pyqtSlot() def on_buttonConnect_clicked(self): # 连接ps的按钮 if self._client.state() == self._client.ConnectedState: # 如果已经连接上了则跳过 return # 保存ps的地址端口和密码 self._setting.setValue('lineEditAddress', self.lineEditAddress.text().strip()) self._setting.setValue('spinBoxPsPort', self.spinBoxPsPort.value()) self._setting.setValue('lineEditPassword', self.lineEditPassword.text().strip()) self._setting.sync() # 构建协议类(用于传递数据的加密) Protocol.init(self.lineEditPassword.text().strip()) # 开始连接ps self._client.connectToHost(self.lineEditAddress.text().strip(), self.spinBoxPsPort.value()) # 显示进度条 self.showWait('正在连接中') @pyqtSlot() def on_runButton_clicked(self): # 运行按钮 code = self.codeEdit.text().strip() if not code: return if self._client.state( ) == self._client.ConnectedState and self._client.isWritable(): # 保存参数和代码 self._setting.setValue('argsEdit', self.argsEdit.toPlainText().strip()) self._setting.setValue('codeEdit', self.codeEdit.text().strip()) self._setting.sync() self._client.write( Protocol.pack(self.formatCode(code), Protocol.JAVASCRIPT_TYPE)) # 发送数据 self._client.flush() Protocol.increase() # 自增1 self.showWait('正在处理中...') # 显示进度条 else: QMessageBox.critical(self, '错误', '未连接或者不能发送数据') def formatCode(self, code): # 格式化code code = code.replace('// #Function#', self.argsEdit.toPlainText().strip()) print('格式化code, 长度: ', len(code)) return code def showWait(self, text): # 显示等待进度条 self._wdialog = QProgressDialog(text, '', 0, 0, self) self._wdialog.setWindowFlags(self._wdialog.windowFlags() | Qt.FramelessWindowHint) self._wdialog.setWindowModality(Qt.WindowModal) self._wdialog.setWindowTitle('请稍候') self._wdialog.setCancelButton(None) self._wdialog.show() def closeWait(self): # 隐藏或者关闭等待进度条 self._wdialog.accept() def closeEvent(self, event): self.imageWidget.close() # 断开和ps的连接 self._client.blockSignals(True) self._client.abort() self._client.deleteLater() super(Window, self).closeEvent(event)
class Window(QWidget, Ui_FormFrameworkTools): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.setupUi(self) self.groupBoxPs.setEnabled(False) self._initSocket() self._initCodeEdit() # 配置 self._setting = QSettings('PsConnectFrameworkTools', 'Settings') # 本地端口 self.spinBoxPort.setValue( self._setting.value('spinBoxPort', 59595, int)) # ip地址 self.lineEditAddress.setText(self._setting.value( 'lineEditAddress', '127.0.0.1', str)) # ps端口 self.spinBoxPsPort.setValue( self._setting.value('spinBoxPsPort', 49494, int)) # ps密码 self.lineEditPassword.setText(self._setting.value( 'lineEditPassword', '', str)) # 设置参数 self.argsEdit.setPlainText(self._setting.value( 'argsEdit', 'test1();\ntest2();', str)) # 设置编辑器里的代码 self.codeEdit.setText(self._setting.value('codeEdit', Template, str)) def _initSocket(self): # 初始化server(用于接收ps返回消息) self._server = TcpServer(self) self._server.messageReceived.connect(self.onMessageReceived) # 初始化client(用于连接ps) self._client = TcpSocket(self) self._client.connected.connect(self.onConnectSuccessed) # 连接成功 self._client.connectClosed.connect(self.onConnectClosed) def _initCodeEdit(self): # 初始化编辑器的工作 self.codeEdit.setUtf8(True) self.codeEdit.linesChanged.connect(self.onLinesChanged) # 行改变 # 代码高亮 self.codeEdit.setLexer(QsciLexerJavaScript(self)) # 自动折叠 self.codeEdit.setMarginType(3, QsciScintilla.SymbolMargin) self.codeEdit.setMarginLineNumbers(3, False) self.codeEdit.setMarginWidth(3, 15) self.codeEdit.setMarginSensitivity(3, True) # 显示行号 #self.codeEdit.setMarginType(0, QsciScintilla.NumberMargin) self.codeEdit.setMarginLineNumbers(0, True) self.onLinesChanged() # 代码提示 sciApi = QsciAPIs(self.codeEdit.lexer()) sciApi.prepare() self.codeEdit.setAutoCompletionSource(QsciScintilla.AcsAll) # 设置源 self.codeEdit.setAutoCompletionCaseSensitivity(True) # 设置自动补全大小写敏感 self.codeEdit.setAutoCompletionThreshold(1) # 设置每输入一个字符就会出现自动补全的提示 # 设置字体 self.codeEdit.setFont(QFont('Consolas', 16)) self.codeEdit.setMarginsFont(self.codeEdit.font()) # 设置编码 self.codeEdit.SendScintilla( QsciScintilla.SCI_SETCODEPAGE, QsciScintilla.SC_CP_UTF8) self.codeEdit.setBraceMatching(QsciScintilla.StrictBraceMatch) # 设置当前行高亮 self.codeEdit.setCaretLineVisible(True) self.codeEdit.setCaretLineBackgroundColor(Qt.lightGray) self.codeEdit.setCaretForegroundColor(Qt.white) # tab # table relative self.codeEdit.setIndentationsUseTabs(True) self.codeEdit.setIndentationWidth(4) self.codeEdit.setTabIndents(True) self.codeEdit.setAutoIndent(True) self.codeEdit.setBackspaceUnindents(True) self.codeEdit.setTabWidth(4) # indentation guides self.codeEdit.setIndentationGuides(True) # folding margin self.codeEdit.setFolding(QsciScintilla.PlainFoldStyle) self.codeEdit.setMarginWidth(2, 12) # 自动换行 self.codeEdit.setWrapMode(QsciScintilla.WrapWord) def onLinesChanged(self): # 动态设置左边的边距 self.codeEdit.setMarginWidth( 0, self.codeEdit.fontMetrics().width(str(self.codeEdit.lines())) + 5) def onMessageReceived(self, data): # 接收到ps中js发送的结果 self.closeWait() # 解码数据 try: data = data.decode() except Exception as e: self.resultEdit.append('解码数据错误: ' + str(e)) return self.resultEdit.append(data) # 尝试解析消息 try: data = json.loads(data) # 获取版本 version = data.get('version', '0.') if not version or int(version.split('.')[0]) < 18: QMessageBox.information( self, '提示', '未能获取到版本或者版本号小于18(PS 2017)') return # 获取可能需要调用的函数和参数 method = data.get('method', 'nomethod') params = data.get('params', []) if hasattr(self, method): # 调用函数 getattr(self, method)(*params) except Exception as e: print('解析数据错误:', e) traceback.print_exc() def onConnectSuccessed(self): self.closeWait() QMessageBox.information(self, '恭喜', '连接成功') def onConnectClosed(self, message=None): # 连接被断开 self.closeWait() if QMessageBox.question( self, '错误', message + '\n是否重连?', QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: # 重连 self.on_buttonConnect_clicked() def showMessage(self, message): # 显示消息 if message: # unquote 尝试对消息反编码 QMessageBox.information(self, '提示', unquote(message)) @pyqtSlot() def on_buttonResetCode_clicked(self): # 重置代码区域为默认 self.codeEdit.setText(Template) @pyqtSlot() def on_buttonStartServer_clicked(self): # 开启本地服务的按钮 if not self._server.listen(QHostAddress.LocalHost, self.spinBoxPort.value()): QMessageBox.critical( self, '错误', '监听本地端口{}失败,请尝试更换端口试试'.format(self.spinBoxPort.value())) return # 保存本地端口到配置 self._setting.setValue('spinBoxPort', self.spinBoxPort.value()) self._setting.sync() # 本地服务器开启成功则禁用 self.buttonStartServer.setEnabled(False) self.spinBoxPort.setEnabled(False) self.groupBoxPs.setEnabled(True) @pyqtSlot() def on_buttonConnect_clicked(self): # 连接ps的按钮 if self._client.state() == self._client.ConnectedState: # 如果已经连接上了则跳过 return # 保存ps的地址端口和密码 self._setting.setValue( 'lineEditAddress', self.lineEditAddress.text().strip()) self._setting.setValue('spinBoxPsPort', self.spinBoxPsPort.value()) self._setting.setValue( 'lineEditPassword', self.lineEditPassword.text().strip()) self._setting.sync() # 构建协议类(用于传递数据的加密) Protocol.init(self.lineEditPassword.text().strip()) # 开始连接ps self._client.connectToHost( self.lineEditAddress.text().strip(), self.spinBoxPsPort.value()) # 显示进度条 self.showWait('正在连接中') @pyqtSlot() def on_runButton_clicked(self): # 运行按钮 code = self.codeEdit.text().strip() if not code: return if self._client.state() == self._client.ConnectedState and self._client.isWritable(): # 保存参数和代码 self._setting.setValue( 'argsEdit', self.argsEdit.toPlainText().strip()) self._setting.setValue( 'codeEdit', self.codeEdit.text().strip()) self._setting.sync() self._client.write(Protocol.pack(self.formatCode(code), 2)) # 发送数据 Protocol.increase() # 自增1 self.showWait('正在处理中...') # 显示进度条 else: QMessageBox.critical(self, '错误', '未连接或者不能发送数据') def formatCode(self, code): # 格式化code code = code.replace( '#HOST#', '127.0.0.1').replace( '#PORT#', str(self.spinBoxPort.value())).replace( '//#Function#', self.argsEdit.toPlainText().strip()) print('格式化code, 长度: ', len(code)) return code def showWait(self, text): # 显示等待进度条 self._wdialog = QProgressDialog(text, '', 0, 0, self) self._wdialog.setWindowFlags( self._wdialog.windowFlags() | Qt.FramelessWindowHint) self._wdialog.setWindowTitle('请稍候') self._wdialog.setCancelButton(None) self._wdialog.exec_() def closeWait(self): # 隐藏或者关闭等待进度条 self._wdialog.accept() def closeEvent(self, event): if self._server.isListening(): # 关闭本地服务器 self._server.close() self._server.deleteLater() # 断开和ps的连接 self._client.blockSignals(True) self._client.abort() self._client.deleteLater() super(Window, self).closeEvent(event)