class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowIcon(QIcon(":/network.svg")) # fix stuff imposible to do in qtdesigner # remove dock titlebar for addressbar w = QWidget() self.ui.addrDockWidget.setTitleBarWidget(w) # tabify some docks self.tabifyDockWidget(self.ui.evDockWidget, self.ui.subDockWidget) self.tabifyDockWidget(self.ui.subDockWidget, self.ui.refDockWidget) self.tabifyDockWidget(self.ui.refDockWidget, self.ui.graphDockWidget) # we only show statusbar in case of errors self.ui.statusBar.hide() # setup QSettings for application and get a settings object QCoreApplication.setOrganizationName("FreeOpcUa") QCoreApplication.setApplicationName("OpcUaClient") self.settings = QSettings() self._address_list = self.settings.value("address_list", [ "opc.tcp://localhost:4840", "opc.tcp://localhost:53530/OPCUA/SimulationServer/" ]) self._address_list_max_count = int( self.settings.value("address_list_max_count", 10)) # init widgets for addr in self._address_list: self.ui.addrComboBox.insertItem(-1, addr) self.uaclient = UaClient() self.tree_ui = TreeWidget(self.ui.treeView) self.tree_ui.error.connect(self.show_error) self.setup_context_menu_tree() self.ui.treeView.selectionModel().currentChanged.connect( self._update_actions_state) self.refs_ui = RefsWidget(self.ui.refView) self.refs_ui.error.connect(self.show_error) self.attrs_ui = AttrsWidget(self.ui.attrView) self.attrs_ui.error.connect(self.show_error) self.datachange_ui = DataChangeUI(self, self.uaclient) self.event_ui = EventUI(self, self.uaclient) self.graph_ui = GraphUI(self, self.uaclient) self.ui.addrComboBox.currentTextChanged.connect(self._uri_changed) self._uri_changed(self.ui.addrComboBox.currentText() ) # force update for current value at startup self.ui.treeView.selectionModel().selectionChanged.connect( self.show_refs) self.ui.actionCopyPath.triggered.connect(self.tree_ui.copy_path) self.ui.actionCopyNodeId.triggered.connect(self.tree_ui.copy_nodeid) self.ui.actionCall.triggered.connect(self.call_method) self.ui.treeView.selectionModel().selectionChanged.connect( self.show_attrs) self.ui.attrRefreshButton.clicked.connect(self.show_attrs) self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600))) data = self.settings.value("main_window_state", None) if data: self.restoreState(data) self.ui.connectButton.clicked.connect(self.connect) self.ui.disconnectButton.clicked.connect(self.disconnect) # self.ui.treeView.expanded.connect(self._fit) self.ui.actionConnect.triggered.connect(self.connect) self.ui.actionDisconnect.triggered.connect(self.disconnect) self.ui.connectOptionButton.clicked.connect( self.show_connection_dialog) def _uri_changed(self, uri): self.uaclient.load_security_settings(uri) def show_connection_dialog(self): dia = ConnectionDialog(self, self.ui.addrComboBox.currentText()) dia.security_mode = self.uaclient.security_mode dia.security_policy = self.uaclient.security_policy dia.certificate_path = self.uaclient.certificate_path dia.private_key_path = self.uaclient.private_key_path ret = dia.exec_() if ret: self.uaclient.security_mode = dia.security_mode self.uaclient.security_policy = dia.security_policy self.uaclient.certificate_path = dia.certificate_path self.uaclient.private_key_path = dia.private_key_path @trycatchslot def show_refs(self, selection): if isinstance(selection, QItemSelection): if not selection.indexes(): # no selection return node = self.get_current_node() if node: self.refs_ui.show_refs(node) @trycatchslot def show_attrs(self, selection): if isinstance(selection, QItemSelection): if not selection.indexes(): # no selection return node = self.get_current_node() if node: self.attrs_ui.show_attrs(node) def show_error(self, msg): logger.warning("showing error: %s") self.ui.statusBar.show() self.ui.statusBar.setStyleSheet( "QStatusBar { background-color : red; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(1500, self.ui.statusBar.hide) def get_current_node(self, idx=None): return self.tree_ui.get_current_node(idx) def get_uaclient(self): return self.uaclient @trycatchslot def connect(self): uri = self.ui.addrComboBox.currentText() try: self.uaclient.connect(uri) except Exception as ex: self.show_error(ex) raise self._update_address_list(uri) self.tree_ui.set_root_node(self.uaclient.client.get_root_node()) self.ui.treeView.setFocus() self.load_current_node() def _update_address_list(self, uri): if uri == self._address_list[0]: return if uri in self._address_list: self._address_list.remove(uri) self._address_list.insert(0, uri) if len(self._address_list) > self._address_list_max_count: self._address_list.pop(-1) def disconnect(self): try: self.uaclient.disconnect() except Exception as ex: self.show_error(ex) raise finally: self.save_current_node() self.tree_ui.clear() self.refs_ui.clear() self.attrs_ui.clear() self.datachange_ui.clear() self.event_ui.clear() def closeEvent(self, event): self.tree_ui.save_state() self.attrs_ui.save_state() self.refs_ui.save_state() self.settings.setValue("main_window_width", self.size().width()) self.settings.setValue("main_window_height", self.size().height()) self.settings.setValue("main_window_state", self.saveState()) self.settings.setValue("address_list", self._address_list) self.disconnect() event.accept() def save_current_node(self): current_node = self.tree_ui.get_current_node() if current_node: mysettings = self.settings.value("current_node", None) if mysettings is None: mysettings = {} uri = self.ui.addrComboBox.currentText() mysettings[uri] = current_node.nodeid.to_string() self.settings.setValue("current_node", mysettings) def load_current_node(self): mysettings = self.settings.value("current_node", None) if mysettings is None: return uri = self.ui.addrComboBox.currentText() if uri in mysettings: nodeid = ua.NodeId.from_string(mysettings[uri]) node = self.uaclient.client.get_node(nodeid) self.tree_ui.expand_to_node(node) def setup_context_menu_tree(self): self.ui.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeView.customContextMenuRequested.connect( self._show_context_menu_tree) self._contextMenu = QMenu() self.addAction(self.ui.actionCopyPath) self.addAction(self.ui.actionCopyNodeId) self._contextMenu.addSeparator() self._contextMenu.addAction(self.ui.actionCall) self._contextMenu.addSeparator() def addAction(self, action): self._contextMenu.addAction(action) @trycatchslot def _update_actions_state(self, current, previous): node = self.get_current_node(current) self.ui.actionCall.setEnabled(False) if node: if node.get_node_class() == ua.NodeClass.Method: self.ui.actionCall.setEnabled(True) def _show_context_menu_tree(self, position): node = self.tree_ui.get_current_node() if node: self._contextMenu.exec_( self.ui.treeView.viewport().mapToGlobal(position)) def call_method(self): node = self.get_current_node() dia = CallMethodDialog(self, self.uaclient.client, node) dia.show()
class UaModeler(QMainWindow): """ Main class of modeler. Should be as simple as possible, try to push things to other classes or even better python-opcua """ def __init__(self): QMainWindow.__init__(self) self.ui = Ui_UaModeler() self.ui.setupUi(self) self.setWindowIcon(QIcon(":/network.svg")) # we only show statusbar in case of errors self.ui.statusBar.hide() # setup QSettings for application and get a settings object QCoreApplication.setOrganizationName("FreeOpcUa") QCoreApplication.setApplicationName("OpcUaModeler") self.settings = QSettings() self._restore_ui_geometri() self.tree_ui = TreeWidget(self.ui.treeView) self.tree_ui.error.connect(self.show_error) self.refs_ui = RefsWidget(self.ui.refView) self.refs_ui.error.connect(self.show_error) self.refs_ui.reference_changed.connect(self.tree_ui.reload_current) # FIXME: shoudl reload a specific node self.attrs_ui = AttrsWidget(self.ui.attrView, show_timestamps=False) self.attrs_ui.error.connect(self.show_error) self.idx_ui = NamespaceWidget(self.ui.namespaceView) self.nodesets_ui = RefNodeSetsWidget(self.ui.refNodeSetsView) self.nodesets_ui.error.connect(self.show_error) self.nodesets_ui.nodeset_added.connect(self.nodesets_change) self.nodesets_ui.nodeset_removed.connect(self.nodesets_change) self.ui.treeView.activated.connect(self.show_refs) self.ui.treeView.clicked.connect(self.show_refs) self.ui.treeView.activated.connect(self.show_attrs) self.ui.treeView.clicked.connect(self.show_attrs) self.model_mgr = ModelManagerUI(self) self.model_mgr.error.connect(self.show_error) self.model_mgr.titleChanged.connect(self.update_title) self.actions = ActionsManager(self, self.ui, self.model_mgr) self.setup_context_menu_tree() delegate = BoldDelegate(self, self.tree_ui.model, self.model_mgr.get_new_nodes()) self.ui.treeView.setItemDelegate(delegate) self.ui.treeView.selectionModel().currentChanged.connect(self._update_actions_state) self._recent_files = self.settings.value("recent_files", []) self._recent_files_max_count = int(self.settings.value("recent_files_max_count", 10)) self._recent_files_acts = [QAction(self, visible=False, triggered=self.open_recent_files) for _ in range(self._recent_files_max_count)] for act in self._recent_files_acts: self.ui.menuRecentFiles.addAction(act) self._update_recent_files_ui() def open_recent_files(self): if not self.model_mgr.try_close_model(): return action = self.sender() if action: path = action.data() self.model_mgr.open_file(path) self.update_recent_files(path) def update_recent_files(self, path): if self._recent_files and path == self._recent_files[0]: return if self._recent_files is not None: if path in self._recent_files: self._recent_files.remove(path) self._recent_files.insert(0, path) self._recent_files = self._recent_files[:self._recent_files_max_count] self._update_recent_files_ui() def _update_recent_files_ui(self): if self._recent_files is not None: for idx, path in enumerate(self._recent_files): self._recent_files_acts[idx].setText(path) self._recent_files_acts[idx].setData(path) self._recent_files_acts[idx].setVisible(True) def get_current_node(self, idx=None): return self.tree_ui.get_current_node(idx) def get_current_server(self): """ Used by tests """ return self.model_mgr.get_current_server() def clear_all_widgets(self): self.tree_ui.clear() self.refs_ui.clear() self.attrs_ui.clear() self.idx_ui.clear() self.nodesets_ui.clear() @trycatchslot def _update_actions_state(self, current, previous): node = self.get_current_node(current) self.actions.update_actions_states(node) def setup_context_menu_tree(self): self.ui.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeView.customContextMenuRequested.connect(self._show_context_menu_tree) self._contextMenu = QMenu() # tree view menu self._contextMenu.addAction(self.ui.actionCopy) self._contextMenu.addAction(self.ui.actionPaste) self._contextMenu.addAction(self.ui.actionDelete) self._contextMenu.addSeparator() self._contextMenu.addAction(self.tree_ui.actionReload) self._contextMenu.addSeparator() self._contextMenu.addAction(self.ui.actionAddFolder) self._contextMenu.addAction(self.ui.actionAddObject) self._contextMenu.addAction(self.ui.actionAddVariable) self._contextMenu.addAction(self.ui.actionAddProperty) self._contextMenu.addAction(self.ui.actionAddMethod) self._contextMenu.addAction(self.ui.actionAddObjectType) self._contextMenu.addAction(self.ui.actionAddVariableType) self._contextMenu.addAction(self.ui.actionAddDataType) def _show_context_menu_tree(self, position): node = self.tree_ui.get_current_node() if node: self._contextMenu.exec_(self.ui.treeView.viewport().mapToGlobal(position)) def _restore_ui_geometri(self): self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600))) #self.restoreState(self.settings.value("main_window_state", b"", type="QByteArray")) self.restoreState(self.settings.value("main_window_state", bytearray())) self.ui.splitterLeft.restoreState(self.settings.value("splitter_left", bytearray())) self.ui.splitterRight.restoreState(self.settings.value("splitter_right", bytearray())) self.ui.splitterCenter.restoreState(self.settings.value("splitter_center", bytearray())) def update_title(self, path): self.setWindowTitle("FreeOpcUa Modeler " + str(path)) def show_error(self, msg): self.ui.statusBar.show() self.ui.statusBar.setStyleSheet("QStatusBar { background-color : red; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(2500, self.ui.statusBar.hide) def show_msg(self, msg): self.ui.statusBar.show() self.ui.statusBar.setStyleSheet("QStatusBar { background-color : green; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(1500, self.ui.statusBar.hide) @trycatchslot def show_refs(self, idx=None): node = self.get_current_node(idx) if node: self.refs_ui.show_refs(node) @trycatchslot def show_attrs(self, idx=None): if not isinstance(idx, QModelIndex): idx = None node = self.get_current_node(idx) if node: self.attrs_ui.show_attrs(node) def nodesets_change(self, data): self.idx_ui.reload() self.tree_ui.reload() self.refs_ui.clear() self.attrs_ui.clear() self.model_mgr.setModified(True) def closeEvent(self, event): if not self.model_mgr.try_close_model(): event.ignore() return self.attrs_ui.save_state() self.refs_ui.save_state() self.tree_ui.save_state() self.settings.setValue("main_window_width", self.size().width()) self.settings.setValue("main_window_height", self.size().height()) self.settings.setValue("main_window_state", self.saveState()) self.settings.setValue("splitter_left", self.ui.splitterLeft.saveState()) self.settings.setValue("splitter_right", self.ui.splitterRight.saveState()) self.settings.setValue("splitter_center", self.ui.splitterCenter.saveState()) self.settings.setValue("recent_files", self._recent_files) event.accept()
class UaModeler(QMainWindow): """ Main class of modeler. Should be as simple as possible, try to push things to other classes or even better python-opcua """ def __init__(self): QMainWindow.__init__(self) self.ui = Ui_UaModeler() self.ui.setupUi(self) self.setWindowIcon(QIcon(":/network.svg")) # we only show statusbar in case of errors self.ui.statusBar.hide() # setup QSettings for application and get a settings object QCoreApplication.setOrganizationName("FreeOpcUa") QCoreApplication.setApplicationName("OpcUaModeler") self.settings = QSettings() self._restore_ui_geometri() self.tree_ui = TreeWidget(self.ui.treeView) self.tree_ui.error.connect(self.show_error) self.refs_ui = RefsWidget(self.ui.refView) self.refs_ui.error.connect(self.show_error) self.refs_ui.reference_changed.connect(self.tree_ui.reload_current) # FIXME: shoudl reload a specific node self.attrs_ui = AttrsWidget(self.ui.attrView, show_timestamps=False) self.attrs_ui.error.connect(self.show_error) self.idx_ui = NamespaceWidget(self.ui.namespaceView) self.nodesets_ui = RefNodeSetsWidget(self.ui.refNodeSetsView) self.nodesets_ui.error.connect(self.show_error) self.nodesets_ui.nodeset_added.connect(self.nodesets_change) self.nodesets_ui.nodeset_removed.connect(self.nodesets_change) self.ui.treeView.activated.connect(self.show_refs) self.ui.treeView.clicked.connect(self.show_refs) self.ui.treeView.activated.connect(self.show_attrs) self.ui.treeView.clicked.connect(self.show_attrs) self.model_mgr = ModelManagerUI(self) self.model_mgr.error.connect(self.show_error) self.model_mgr.titleChanged.connect(self.update_title) self.actions = ActionsManager(self.ui, self.model_mgr) self.setup_context_menu_tree() delegate = BoldDelegate(self, self.tree_ui.model, self.model_mgr.get_new_nodes()) self.ui.treeView.setItemDelegate(delegate) self.ui.treeView.selectionModel().currentChanged.connect(self._update_actions_state) self._recent_files = self.settings.value("recent_files", []) self._recent_files_max_count = int(self.settings.value("recent_files_max_count", 10)) self._recent_files_acts = [QAction(self, visible=False, triggered=self.open_recent_files) for _ in range(self._recent_files_max_count)] for act in self._recent_files_acts: self.ui.menuRecentFiles.addAction(act) self._update_recent_files_ui() def open_recent_files(self): if not self.model_mgr.try_close_model(): return action = self.sender() if action: path = action.data() self.model_mgr.open_file(path) self.update_recent_files(path) def update_recent_files(self, path): if self._recent_files and path == self._recent_files[0]: return if path in self._recent_files: self._recent_files.remove(path) self._recent_files.insert(0, path) self._recent_files = self._recent_files[:self._recent_files_max_count] self._update_recent_files_ui() def _update_recent_files_ui(self): if not self._recent_files: return for idx, path in enumerate(self._recent_files): self._recent_files_acts[idx].setText(path) self._recent_files_acts[idx].setData(path) self._recent_files_acts[idx].setVisible(True) def get_current_node(self, idx=None): return self.tree_ui.get_current_node(idx) def get_current_server(self): """ Used by tests """ return self.model_mgr.get_current_server() def clear_all_widgets(self): self.tree_ui.clear() self.refs_ui.clear() self.attrs_ui.clear() self.idx_ui.clear() self.nodesets_ui.clear() @trycatchslot def _update_actions_state(self, current, previous): node = self.get_current_node(current) self.actions.update_actions_states(node) def setup_context_menu_tree(self): self.ui.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeView.customContextMenuRequested.connect(self._show_context_menu_tree) self._contextMenu = QMenu() # tree view menu self._contextMenu.addAction(self.ui.actionCopy) self._contextMenu.addAction(self.ui.actionPaste) self._contextMenu.addAction(self.ui.actionDelete) self._contextMenu.addSeparator() self._contextMenu.addAction(self.tree_ui.actionReload) self._contextMenu.addSeparator() self._contextMenu.addAction(self.ui.actionAddFolder) self._contextMenu.addAction(self.ui.actionAddObject) self._contextMenu.addAction(self.ui.actionAddVariable) self._contextMenu.addAction(self.ui.actionAddProperty) self._contextMenu.addAction(self.ui.actionAddMethod) self._contextMenu.addAction(self.ui.actionAddObjectType) self._contextMenu.addAction(self.ui.actionAddVariableType) self._contextMenu.addAction(self.ui.actionAddDataType) def _show_context_menu_tree(self, position): node = self.tree_ui.get_current_node() if node: self._contextMenu.exec_(self.ui.treeView.viewport().mapToGlobal(position)) def _restore_ui_geometri(self): self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600))) #self.restoreState(self.settings.value("main_window_state", b"", type="QByteArray")) self.restoreState(self.settings.value("main_window_state", bytearray())) self.ui.splitterLeft.restoreState(self.settings.value("splitter_left", bytearray())) self.ui.splitterRight.restoreState(self.settings.value("splitter_right", bytearray())) self.ui.splitterCenter.restoreState(self.settings.value("splitter_center", bytearray())) def update_title(self, path): self.setWindowTitle("FreeOpcUa Modeler " + str(path)) def show_error(self, msg): self.ui.statusBar.show() self.ui.statusBar.setStyleSheet("QStatusBar { background-color : red; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(2500, self.ui.statusBar.hide) def show_msg(self, msg): self.ui.statusBar.show() self.ui.statusBar.setStyleSheet("QStatusBar { background-color : green; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(1500, self.ui.statusBar.hide) @trycatchslot def show_refs(self, idx=None): node = self.get_current_node(idx) if node: self.refs_ui.show_refs(node) @trycatchslot def show_attrs(self, idx=None): if not isinstance(idx, QModelIndex): idx = None node = self.get_current_node(idx) if node: self.attrs_ui.show_attrs(node) def nodesets_change(self, data): self.idx_ui.reload() self.tree_ui.reload() self.refs_ui.clear() self.attrs_ui.clear() self.model_mgr.setModified(True) def closeEvent(self, event): if not self.model_mgr.try_close_model(): event.ignore() return self.attrs_ui.save_state() self.refs_ui.save_state() self.tree_ui.save_state() self.settings.setValue("main_window_width", self.size().width()) self.settings.setValue("main_window_height", self.size().height()) self.settings.setValue("main_window_state", self.saveState()) self.settings.setValue("splitter_left", self.ui.splitterLeft.saveState()) self.settings.setValue("splitter_right", self.ui.splitterRight.saveState()) self.settings.setValue("splitter_center", self.ui.splitterCenter.saveState()) self.settings.setValue("recent_files", self._recent_files) event.accept()
class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowIcon(QIcon(":/network.svg")) # fix stuff imposible to do in qtdesigner # remove dock titlebar for addressbar w = QWidget() self.ui.addrDockWidget.setTitleBarWidget(w) # tabify some docks self.tabifyDockWidget(self.ui.evDockWidget, self.ui.subDockWidget) self.tabifyDockWidget(self.ui.subDockWidget, self.ui.refDockWidget) # we only show statusbar in case of errors self.ui.statusBar.hide() # setup QSettings for application and get a settings object QCoreApplication.setOrganizationName("FreeOpcUa") QCoreApplication.setApplicationName("OpcUaClient") self.settings = QSettings() self._address_list = self.settings.value("address_list", [ "opc.tcp://localhost:4840", "opc.tcp://localhost:53530/OPCUA/SimulationServer/" ]) self._address_list_max_count = int( self.settings.value("address_list_max_count", 10)) # init widgets for addr in self._address_list: self.ui.addrComboBox.insertItem(-1, addr) self.uaclient = UaClient() self.tree_ui = TreeWidget(self.ui.treeView) self.tree_ui.error.connect(self.show_error) self.refs_ui = RefsWidget(self.ui.refView) self.refs_ui.error.connect(self.show_error) self.attrs_ui = AttrsWidget(self.ui.attrView) self.attrs_ui.error.connect(self.show_error) self.datachange_ui = DataChangeUI(self, self.uaclient) self.event_ui = EventUI(self, self.uaclient) self.ui.addrComboBox.currentTextChanged.connect(self._uri_changed) self._uri_changed(self.ui.addrComboBox.currentText() ) # force update for current value at startup self.ui.treeView.activated.connect(self.show_refs) self.ui.treeView.clicked.connect(self.show_refs) self.ui.actionCopyPath.triggered.connect(self.tree_ui.copy_path) self.ui.actionCopyNodeId.triggered.connect(self.tree_ui.copy_nodeid) # add items to context menu self.ui.treeView.addAction(self.ui.actionCopyPath) self.ui.treeView.addAction(self.ui.actionCopyNodeId) self.ui.treeView.activated.connect(self.show_attrs) self.ui.treeView.clicked.connect(self.show_attrs) self.ui.attrRefreshButton.clicked.connect(self.show_attrs) self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600))) data = self.settings.value("main_window_state", None) if data: self.restoreState(data) self.ui.connectButton.clicked.connect(self.connect) self.ui.disconnectButton.clicked.connect(self.disconnect) # self.ui.treeView.expanded.connect(self._fit) self.ui.actionConnect.triggered.connect(self.connect) self.ui.actionDisconnect.triggered.connect(self.disconnect) self.ui.connectOptionButton.clicked.connect( self.show_connection_dialog) def _uri_changed(self, uri): self.uaclient.load_security_settings(uri) def show_connection_dialog(self): dia = ConnectionDialog(self, self.ui.addrComboBox.currentText()) dia.security_mode = self.uaclient.security_mode dia.security_policy = self.uaclient.security_policy dia.certificate_path = self.uaclient.certificate_path dia.private_key_path = self.uaclient.private_key_path ret = dia.exec_() if ret: self.uaclient.security_mode = dia.security_mode self.uaclient.security_policy = dia.security_policy self.uaclient.certificate_path = dia.certificate_path self.uaclient.private_key_path = dia.private_key_path @trycatchslot def show_refs(self, idx): node = self.get_current_node(idx) if node: self.refs_ui.show_refs(node) @trycatchslot def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None node = self.get_current_node(idx) if node: self.attrs_ui.show_attrs(node) def show_error(self, msg): logger.warning("showing error: %s") self.ui.statusBar.show() self.ui.statusBar.setStyleSheet( "QStatusBar { background-color : red; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(1500, self.ui.statusBar.hide) def get_current_node(self, idx=None): return self.tree_ui.get_current_node(idx) def get_uaclient(self): return self.uaclient @trycatchslot def connect(self): uri = self.ui.addrComboBox.currentText() try: self.uaclient.connect(uri) except Exception as ex: self.show_error(ex) raise self._update_address_list(uri) self.tree_ui.set_root_node(self.uaclient.client.get_root_node()) def _update_address_list(self, uri): if uri == self._address_list[0]: return if uri in self._address_list: self._address_list.remove(uri) self._address_list.insert(0, uri) if len(self._address_list) > self._address_list_max_count: self._address_list.pop(-1) def disconnect(self): try: self.uaclient.disconnect() except Exception as ex: self.show_error(ex) raise finally: self.tree_ui.clear() self.refs_ui.clear() self.attrs_ui.clear() self.datachange_ui.clear() self.event_ui.clear() def closeEvent(self, event): self.tree_ui.save_state() self.attrs_ui.save_state() self.refs_ui.save_state() self.settings.setValue("main_window_width", self.size().width()) self.settings.setValue("main_window_height", self.size().height()) self.settings.setValue("main_window_state", self.saveState()) self.settings.setValue("address_list", self._address_list) self.disconnect() event.accept()
class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowIcon(QIcon(":/network.svg")) # fix stuff imposible to do in qtdesigner # remove dock titlebar for addressbar w = QWidget() self.ui.addrDockWidget.setTitleBarWidget(w) # tabify some docks self.tabifyDockWidget(self.ui.evDockWidget, self.ui.subDockWidget) self.tabifyDockWidget(self.ui.subDockWidget, self.ui.refDockWidget) # we only show statusbar in case of errors self.ui.statusBar.hide() # setup QSettings for application and get a settings object QCoreApplication.setOrganizationName("FreeOpcUa") QCoreApplication.setApplicationName("OpcUaClient") self.settings = QSettings() self._address_list = self.settings.value("address_list", ["opc.tcp://localhost:4840", "opc.tcp://localhost:53530/OPCUA/SimulationServer/"]) self._address_list_max_count = int(self.settings.value("address_list_max_count", 10)) # init widgets for addr in self._address_list: self.ui.addrComboBox.insertItem(-1, addr) self.uaclient = UaClient() self.tree_ui = TreeWidget(self.ui.treeView) self.tree_ui.error.connect(self.show_error) self.refs_ui = RefsWidget(self.ui.refView) self.refs_ui.error.connect(self.show_error) self.attrs_ui = AttrsWidget(self.ui.attrView) self.attrs_ui.error.connect(self.show_error) self.datachange_ui = DataChangeUI(self, self.uaclient) self.event_ui = EventUI(self, self.uaclient) self.ui.addrComboBox.currentTextChanged.connect(self._uri_changed) self._uri_changed(self.ui.addrComboBox.currentText()) # force update for current value at startup self.ui.treeView.activated.connect(self.show_refs) self.ui.treeView.clicked.connect(self.show_refs) self.ui.actionCopyPath.triggered.connect(self.tree_ui.copy_path) self.ui.actionCopyNodeId.triggered.connect(self.tree_ui.copy_nodeid) # add items to context menu self.ui.treeView.addAction(self.ui.actionCopyPath) self.ui.treeView.addAction(self.ui.actionCopyNodeId) self.ui.treeView.activated.connect(self.show_attrs) self.ui.treeView.clicked.connect(self.show_attrs) self.ui.attrRefreshButton.clicked.connect(self.show_attrs) self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600))) data = self.settings.value("main_window_state", None) if data: self.restoreState(data) self.ui.connectButton.clicked.connect(self.connect) self.ui.disconnectButton.clicked.connect(self.disconnect) # self.ui.treeView.expanded.connect(self._fit) self.ui.actionConnect.triggered.connect(self.connect) self.ui.actionDisconnect.triggered.connect(self.disconnect) self.ui.connectOptionButton.clicked.connect(self.show_connection_dialog) def _uri_changed(self, uri): self.uaclient.load_security_settings(uri) def show_connection_dialog(self): dia = ConnectionDialog(self, self.ui.addrComboBox.currentText()) dia.security_mode = self.uaclient.security_mode dia.security_policy = self.uaclient.security_policy dia.certificate_path = self.uaclient.certificate_path dia.private_key_path = self.uaclient.private_key_path ret = dia.exec_() if ret: self.uaclient.security_mode = dia.security_mode self.uaclient.security_policy = dia.security_policy self.uaclient.certificate_path = dia.certificate_path self.uaclient.private_key_path = dia.private_key_path @trycatchslot def show_refs(self, idx): node = self.get_current_node(idx) if node: self.refs_ui.show_refs(node) @trycatchslot def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None node = self.get_current_node(idx) if node: self.attrs_ui.show_attrs(node) def show_error(self, msg): logger.warning("showing error: %s") self.ui.statusBar.show() self.ui.statusBar.setStyleSheet("QStatusBar { background-color : red; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(1500, self.ui.statusBar.hide) def get_current_node(self, idx=None): return self.tree_ui.get_current_node(idx) def get_uaclient(self): return self.uaclient @trycatchslot def connect(self): uri = self.ui.addrComboBox.currentText() try: self.uaclient.connect(uri) except Exception as ex: self.show_error(ex) raise self._update_address_list(uri) self.tree_ui.set_root_node(self.uaclient.client.get_root_node()) def _update_address_list(self, uri): if uri == self._address_list[0]: return if uri in self._address_list: self._address_list.remove(uri) self._address_list.insert(0, uri) if len(self._address_list) > self._address_list_max_count: self._address_list.pop(-1) def disconnect(self): try: self.uaclient.disconnect() except Exception as ex: self.show_error(ex) raise finally: self.tree_ui.clear() self.refs_ui.clear() self.attrs_ui.clear() self.datachange_ui.clear() self.event_ui.clear() def closeEvent(self, event): self.attrs_ui.save_state() self.refs_ui.save_state() self.settings.setValue("main_window_width", self.size().width()) self.settings.setValue("main_window_height", self.size().height()) self.settings.setValue("main_window_state", self.saveState()) self.settings.setValue("address_list", self._address_list) self.disconnect() event.accept()
class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowIcon(QIcon(":/network.svg")) # fix stuff imposible to do in qtdesigner # remove dock titlebar for addressbar w = QWidget() self.ui.addrDockWidget.setTitleBarWidget(w) # tabify some docks self.tabifyDockWidget(self.ui.evDockWidget, self.ui.subDockWidget) self.tabifyDockWidget(self.ui.subDockWidget, self.ui.refDockWidget) self.tabifyDockWidget(self.ui.refDockWidget, self.ui.graphDockWidget) # we only show statusbar in case of errors self.ui.statusBar.hide() # setup QSettings for application and get a settings object QCoreApplication.setOrganizationName("FreeOpcUa") QCoreApplication.setApplicationName("OpcUaClient") self.settings = QSettings() self._address_list = self.settings.value("address_list", ["opc.tcp://localhost:4840", "opc.tcp://localhost:53530/OPCUA/SimulationServer/"]) print("ADR", self._address_list) self._address_list_max_count = int(self.settings.value("address_list_max_count", 10)) # init widgets for addr in self._address_list: self.ui.addrComboBox.insertItem(100, addr) self.uaclient = UaClient() self.tree_ui = TreeWidget(self.ui.treeView) self.tree_ui.error.connect(self.show_error) self.setup_context_menu_tree() self.ui.treeView.selectionModel().currentChanged.connect(self._update_actions_state) self.refs_ui = RefsWidget(self.ui.refView) self.refs_ui.error.connect(self.show_error) self.attrs_ui = AttrsWidget(self.ui.attrView) self.attrs_ui.error.connect(self.show_error) self.datachange_ui = DataChangeUI(self, self.uaclient) self.event_ui = EventUI(self, self.uaclient) self.graph_ui = GraphUI(self, self.uaclient) self.ui.addrComboBox.currentTextChanged.connect(self._uri_changed) self._uri_changed(self.ui.addrComboBox.currentText()) # force update for current value at startup self.ui.treeView.selectionModel().selectionChanged.connect(self.show_refs) self.ui.actionCopyPath.triggered.connect(self.tree_ui.copy_path) self.ui.actionCopyNodeId.triggered.connect(self.tree_ui.copy_nodeid) self.ui.actionCall.triggered.connect(self.call_method) self.ui.treeView.selectionModel().selectionChanged.connect(self.show_attrs) self.ui.attrRefreshButton.clicked.connect(self.show_attrs) self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600))) data = self.settings.value("main_window_state", None) if data: self.restoreState(data) self.ui.connectButton.clicked.connect(self.connect) self.ui.disconnectButton.clicked.connect(self.disconnect) # self.ui.treeView.expanded.connect(self._fit) self.ui.actionConnect.triggered.connect(self.connect) self.ui.actionDisconnect.triggered.connect(self.disconnect) self.ui.connectOptionButton.clicked.connect(self.show_connection_dialog) def _uri_changed(self, uri): self.uaclient.load_security_settings(uri) def show_connection_dialog(self): dia = ConnectionDialog(self, self.ui.addrComboBox.currentText()) dia.security_mode = self.uaclient.security_mode dia.security_policy = self.uaclient.security_policy dia.certificate_path = self.uaclient.certificate_path dia.private_key_path = self.uaclient.private_key_path ret = dia.exec_() if ret: self.uaclient.security_mode = dia.security_mode self.uaclient.security_policy = dia.security_policy self.uaclient.certificate_path = dia.certificate_path self.uaclient.private_key_path = dia.private_key_path @trycatchslot def show_refs(self, selection): if isinstance(selection, QItemSelection): if not selection.indexes(): # no selection return node = self.get_current_node() if node: self.refs_ui.show_refs(node) @trycatchslot def show_attrs(self, selection): if isinstance(selection, QItemSelection): if not selection.indexes(): # no selection return node = self.get_current_node() if node: self.attrs_ui.show_attrs(node) def show_error(self, msg): logger.warning("showing error: %s") self.ui.statusBar.show() self.ui.statusBar.setStyleSheet("QStatusBar { background-color : red; color : black; }") self.ui.statusBar.showMessage(str(msg)) QTimer.singleShot(1500, self.ui.statusBar.hide) def get_current_node(self, idx=None): return self.tree_ui.get_current_node(idx) def get_uaclient(self): return self.uaclient @trycatchslot def connect(self): uri = self.ui.addrComboBox.currentText() try: self.uaclient.connect(uri) except Exception as ex: self.show_error(ex) raise self._update_address_list(uri) self.tree_ui.set_root_node(self.uaclient.client.get_root_node()) self.ui.treeView.setFocus() self.load_current_node() def _update_address_list(self, uri): if uri == self._address_list[0]: return if uri in self._address_list: self._address_list.remove(uri) self._address_list.insert(0, uri) if len(self._address_list) > self._address_list_max_count: self._address_list.pop(-1) def disconnect(self): try: self.uaclient.disconnect() except Exception as ex: self.show_error(ex) raise finally: self.save_current_node() self.tree_ui.clear() self.refs_ui.clear() self.attrs_ui.clear() self.datachange_ui.clear() self.event_ui.clear() def closeEvent(self, event): self.tree_ui.save_state() self.attrs_ui.save_state() self.refs_ui.save_state() self.settings.setValue("main_window_width", self.size().width()) self.settings.setValue("main_window_height", self.size().height()) self.settings.setValue("main_window_state", self.saveState()) self.settings.setValue("address_list", self._address_list) self.disconnect() event.accept() def save_current_node(self): current_node = self.tree_ui.get_current_node() if current_node: mysettings = self.settings.value("current_node", None) if mysettings is None: mysettings = {} uri = self.ui.addrComboBox.currentText() mysettings[uri] = current_node.nodeid.to_string() self.settings.setValue("current_node", mysettings) def load_current_node(self): mysettings = self.settings.value("current_node", None) if mysettings is None: return uri = self.ui.addrComboBox.currentText() if uri in mysettings: nodeid = ua.NodeId.from_string(mysettings[uri]) node = self.uaclient.client.get_node(nodeid) self.tree_ui.expand_to_node(node) def setup_context_menu_tree(self): self.ui.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeView.customContextMenuRequested.connect(self._show_context_menu_tree) self._contextMenu = QMenu() self.addAction(self.ui.actionCopyPath) self.addAction(self.ui.actionCopyNodeId) self._contextMenu.addSeparator() self._contextMenu.addAction(self.ui.actionCall) self._contextMenu.addSeparator() def addAction(self, action): self._contextMenu.addAction(action) @trycatchslot def _update_actions_state(self, current, previous): node = self.get_current_node(current) self.ui.actionCall.setEnabled(False) if node: if node.get_node_class() == ua.NodeClass.Method: self.ui.actionCall.setEnabled(True) def _show_context_menu_tree(self, position): node = self.tree_ui.get_current_node() if node: self._contextMenu.exec_(self.ui.treeView.viewport().mapToGlobal(position)) def call_method(self): node = self.get_current_node() dia = CallMethodDialog(self, self.uaclient.client, node) dia.show()