def loadClueAlbum(self, filename): # keys with ',' are read as list and will be joined back if os.path.isfile(filename): album = QSettings(filename, QSettings.IniFormat) album.setIniCodec("UTF-8"); for group in album.childGroups(): album.beginGroup(group) key = group if not 'fr' in album.childKeys(): self._logger.warning( "{} [{}] : {}".format(self.tr("Clue"), group, self.tr("ignored because 'fr' is missing"))) continue if not 'en' in album.childKeys(): self._logger.warning( "{} [{}] : {}".format(self.tr("Clue"), group, self.tr("ignored because 'en' is missing"))) continue try: title = album.value('en') if '|' in title: en = re.compile(r'\s*\|\s*').split(title) else: en = (title, '') text = album.value('fr') if '|' in text: fr = re.compile(r'\s*\|\s*').split(text) else: fr = (text, '') self._clues[key] = Clue(title, fr, en) except: self._logger.warning("{} {}".format(self.tr("Failed to load clue : "), key)) album.endGroup()
def applyFilterSettings(self, name): if name == self._observation: reg = QSettings() reg.beginGroup(self._session) reg.beginGroup(self._observation) self._displayFormat = reg.value( "display", int(TopicDisplay.EMPHASIZEDCORRESPONDENT), type=int) self._filter1 = reg.value("filter 1", '') self._filter2 = reg.value("filter 2", '') self._filter3 = reg.value("filter 1", '') self._filter4 = reg.value("filter 4", '') self._bufferSize = reg.value("buffer size", 50000, type=int) self._hiddenCorrespondents = [] if "Hidden correspondents" in reg.childGroups(): reg.beginGroup("Hidden correspondents") for p in reg.childKeys(): self._hiddenCorrespondents.append(p.replace('\\', '/')) reg.endGroup() self._hiddenTopics = [] if "Hidden topics" in reg.childGroups(): reg.beginGroup("Hidden topics") for t in reg.childKeys(): self._hiddenTopics.append(t.replace('\\', '/')) reg.endGroup() reg.endGroup() reg.endGroup()
def apply(self): reg = QSettings() if self._mqttPortInput.text().isdecimal(): port = int(self._mqttPortInput.text()) else: port = 1883 try: port_in_reg = reg.value("port", 1883, type=int) except: port_in_reg = 1883 reg.beginGroup(self._session) self._logger.info(self.tr("Current session : ") + self._session) self._logger.info("{0} : {1} -> {2}".format( self.tr("Apply settings for server host"), reg.value("host", 'localhost'), self._mqttServerInput.text().replace('/', ''))) self._logger.info("{0} : {1} -> {2}".format( self.tr("Apply settings for server port"), str(port_in_reg), str(port))) self._logger.info("{0} : {1} -> {2}".format( self.tr("Apply settings for room topic"), reg.value("root topic", ''), self._mqttTopicInput.text())) reg.endGroup() reg.beginGroup(self._session) reg.setValue("host", self._mqttServerInput.text().replace('/', '')) reg.setValue("port", port) reg.setValue("root topic", self._mqttTopicInput.text()) reg.endGroup() reg.sync() new_session = self._sessionNameInput.text().strip() if new_session and new_session != self._session: reg_from = QSettings() reg_from.beginGroup(self._session) reg_to = QSettings() reg_to.beginGroup(new_session) for k in reg_from.childKeys(): reg_to.setValue(k, reg_from.value(k)) for g in reg_from.childGroups(): reg_from.beginGroup(g) reg_to.beginGroup(g) for k in reg_from.childKeys(): reg_to.setValue(k, reg_from.value(k)) reg_from.endGroup() reg_to.endGroup() reg_from.endGroup() reg_to.endGroup() reg.setValue("current session", new_session) Timer(0, self.reloadSession.emit).start() self.accept()
def retrieveAllStoredServerInfo(self): """Retrieves a list of ThreddsServerInfo objects stored in the underlying settings object for this application. Uses the Group definition "self.ThreddsServerGroup" to know under which 'namespace' are these elements stored. This allows us to store other configuration settings under other 'namespaces' using the same QSettings object. :returns A list of objects which contain information about the thredds servers previously stored in the settings object. :rtype [ThreddsServerInfoObject] """ serverList = [] settings = QSettings() settings.beginGroup(self.ThreddsServerGroup) for key in settings.childKeys(): ret = settings.value(key) if type(ret) in [str, unicode]: name, url = key, ret else: try: name, url = ret except: continue serverList.append(ThreddsServerInfoObject(name, url)) settings.endGroup() return serverList
class qTorObject(object): """ The configuration of the machine's qBittorrent with a human-readable data structure """ def __init__(self, path: str): if not isfile(path): raise FileNotFoundError( f"The configuration file {sp(path)[-1]} does not exist.") self.settings = QSettings(path, QSettings.IniFormat) self._data = {} # type: dict self._goDeep(self._data) def _goDeep(self, conf: dict) -> dict: for key in self.settings.childKeys(): conf[key] = self.settings.value(key) if self.settings.childGroups(): for group in self.settings.childGroups(): self.settings.beginGroup(group) conf[group] = {} self._goDeep(conf[group]) self.settings.endGroup() else: pass return conf @property def data(self) -> dict: return self._data
class MySettings: def __init__(self): self.user_valueList = [] self.user_value = 0 def ReadSettings(self, path, user_key, settingType=0): if path is None: return False else: self.settings = QSettings(path, QSettings.IniFormat) # ini文件格式 if settingType == 1: self.settings.beginGroup(user_key) # 读取所有键 user_childKey = self.settings.childKeys() self.user_valueList = [] # 清空之前的列表 for i in user_childKey: # 读取所有值 self.user_valueList.append(self.settings.value(i)) self.settings.endGroup() return self.user_valueList else: self.user_value = self.settings.value(user_key) return self.user_value # 将设置写入ini文件 def WriteSettings(self, path, user_key, user_value): settings = QSettings(path, QSettings.IniFormat) settings.setValue(user_key, user_value)
def layout_childkeys_only(group): filename = read_settings("User_Settings", 'current_layout_file') filename = os.path.join(profiles_folder, filename) settings = QSettings(filename, QSettings.NativeFormat) settings.beginGroup(group) keys = settings.childKeys() settings.endGroup() return keys
def delete_layout_key(group, key): filename = read_settings("User_Settings", 'current_layout_file') filename = os.path.join(profiles_folder, filename) settings = QSettings(filename, QSettings.NativeFormat) settings.beginGroup(group) keys = settings.childKeys() if key in keys: settings.remove(key) settings.endGroup() return keys
def declare_settings(): settings = QSettings(full_config_file_path, QSettings.NativeFormat) settings.beginGroup("User_Settings") keys = settings.childKeys() for s in settings_variables(): for k in keys: if s[0] == k: value = settings.value(k, s[1], type=s[2]) globals()[k] = value settings.endGroup()
def updateLocationsTable(self): self.locationsTable.setUpdatesEnabled(False) self.locationsTable.setRowCount(0) for i in range(2): if i == 0: if self.scope() == QSettings.SystemScope: continue actualScope = QSettings.UserScope else: actualScope = QSettings.SystemScope for j in range(2): if j == 0: if not self.application(): continue actualApplication = self.application() else: actualApplication = "" settings = QSettings( self.format(), actualScope, self.organization(), actualApplication ) row = self.locationsTable.rowCount() self.locationsTable.setRowCount(row + 1) item0 = QTableWidgetItem() item0.setText(settings.fileName()) item1 = QTableWidgetItem() disable = not (settings.childKeys() or settings.childGroups()) if row == 0: if settings.isWritable(): item1.setText("Read-write") disable = False else: item1.setText("Read-only") self.buttonBox.button(QDialogButtonBox.Ok).setDisabled(disable) else: item1.setText("Read-only fallback") if disable: item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled) item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled) self.locationsTable.setItem(row, 0, item0) self.locationsTable.setItem(row, 1, item1) self.locationsTable.setUpdatesEnabled(True)
def updateLocationsTable(self): self.locationsTable.setUpdatesEnabled(False) self.locationsTable.setRowCount(0) for i in range(2): if i == 0: if self.scope() == QSettings.SystemScope: continue actualScope = QSettings.UserScope else: actualScope = QSettings.SystemScope for j in range(2): if j == 0: if not self.application(): continue actualApplication = self.application() else: actualApplication = '' settings = QSettings(self.format(), actualScope, self.organization(), actualApplication) row = self.locationsTable.rowCount() self.locationsTable.setRowCount(row + 1) item0 = QTableWidgetItem() item0.setText(settings.fileName()) item1 = QTableWidgetItem() disable = not (settings.childKeys() or settings.childGroups()) if row == 0: if settings.isWritable(): item1.setText("Read-write") disable = False else: item1.setText("Read-only") self.buttonBox.button(QDialogButtonBox.Ok).setDisabled(disable) else: item1.setText("Read-only fallback") if disable: item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled) item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled) self.locationsTable.setItem(row, 0, item0) self.locationsTable.setItem(row, 1, item1) self.locationsTable.setUpdatesEnabled(True)
def showEvent(self, event: QtGui.QShowEvent) -> None: domains = [] setting = QSettings() # MacOS: ~/Library/Preferences/QCoreApplication.organizationDomain().QCoreApplication.applicationName().plist # Linux: ~/.config/QCoreApplication.organizationName()/QCoreApplication.applicationName().conf # print(f"Setting storage: {setting.fileName()}") if "domains" in setting.childGroups(): setting.beginGroup("domains") domains = setting.childKeys() setting.endGroup() if not domains: domains = configurations.get("domains") self.ui.textEdit.setPlainText(f"{os.linesep}".join(list(domains)))
def read_settings(group, setting='all'): settings = QSettings(full_config_file_path, QSettings.NativeFormat) value = None pairs = [] if setting != 'all': settings.beginGroup(group) for s in settings_variables(): if s[0] == setting: value = settings.value(setting, s[1], type=s[2]) settings.endGroup() return value if setting == 'all': settings.beginGroup(group) keys = settings.childKeys() for s in settings_variables(): for k in keys: if s[0] == k: value = settings.value(k, s[1], type=s[2]) pairs.append((k, value, s[2])) settings.endGroup() return pairs
class FMIConfig: def __init__(self): self.config = QSettings('FMI.ini', QSettings.IniFormat) pass def saveNtripValue(self, group, key, value): self.config.beginGroup(group) self.config.setValue(key, value) self.config.endGroup() pass def getValue(self, group, key): self.config.beginGroup(group) value = self.config.value(key, "") self.config.endGroup() return value def getGroupKeys(self, group): try: self.config.beginGroup(group) keys = self.config.childKeys() self.config.endGroup() return keys except Exception as e: print(e) return None def clear(self): self.config.clear() pass def close(self): pass def getCmdComb(self, CMD): if "VEH_MODE" in CMD: return VEH_MODE elif "UAV_MODE" in CMD: return UAV_MODE return CMD
class Config(object): def load_config(self): # 读取配置文件 self.setting = QSettings('./setting.ini', QSettings.IniFormat) # self.setting.setIniCodec("UTF8") # 加载落子音效 self.sound_piece = QSound("sound/move.wav") self.background_music = QSound("sound/bgm.wav") self.background_music.setLoops(-1) # 加载配置 self.config = {} self.setting.beginGroup("setting") ck = self.setting.childKeys() for k in ck: self.config[k] = self.setting.value(k) self.setting.endGroup() # print('配置字典', self.config) # 读取样式文件 # print('qss/', type(self.config['qss'])) style_file = self.setting.value('qss/' + self.config['qss']) # print("common:49", style_file) self.QSS = self.read_qss(style_file) # 设置程序图标 icon = QIcon('img/logo.png') self.setWindowIcon(icon) @staticmethod def read_qss(style_file): with open(style_file, 'r') as f: return f.read() @staticmethod def get_time(): tm = "{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}"\ .format(*localtime()[:6]) return tm pass
def loadSettings(self, currentKey, namesGroup): # don't mark schemes for removal anymore self._schemesToRemove = set() s = QSettings() cur = s.value(currentKey, "default", str) # load the names for the shortcut schemes s.beginGroup(namesGroup) block = self.scheme.blockSignals(True) self.scheme.clear() self.scheme.addItem(_("Default"), "default") lst = [(s.value(key, key, str), key) for key in s.childKeys()] for name, key in sorted(lst, key=lambda f: f[0].lower()): self.scheme.addItem(name, key) # find out index index = self.scheme.findData(cur) self.disableDefault(cur == 'default') self.scheme.setCurrentIndex(index) self.scheme.blockSignals(block) self.currentChanged.emit()
def load_layout(): filename = read_settings("User_Settings", 'current_layout_file') filename = os.path.join(profiles_folder, filename) settings = QSettings(filename, QSettings.NativeFormat) settings.beginGroup("Layout") keys = settings.childKeys() button_layout = {} for ck in keys: for k, v in default_button_layout.items(): if ck == k: v[0], v[1], v[2], v[3] = int(v[0]), int(v[1]), int(v[2]), int( v[3]) value = settings.value(ck, v, type=str) value[0], value[1], value[2], value[3] = int(value[0]), int( value[1]), int(value[2]), int(value[3]) button_layout[ck] = value if not ck in default_button_layout.keys(): value = settings.value(ck, type=str) value[0], value[1], value[2], value[3] = int(value[0]), int( value[1]), int(value[2]), int(value[3]) button_layout[ck] = value globals()['button_layout'] = button_layout settings.endGroup()
def _buildUi(self): self._options = {} if os.path.isfile('definitions.ini'): definitions = QSettings('definitions.ini', QSettings.IniFormat) for group in definitions.childGroups(): definitions.beginGroup(group) if group == "options": for key in definitions.childKeys(): self._options[key] = definitions.value(key) definitions.endGroup() main_layout = QVBoxLayout() main_layout.setSpacing(12) self._led = LedWidget(self.tr("Props name"), QSize(40, 20)) self._led.setRedAsBold(True) self._led.setRedAsRed(True) self._led.switchOn('gray') settings_button = QPushButton() settings_button.setIcon(QIcon("./images/settings.svg")) settings_button.setFlat(True) settings_button.setToolTip(self.tr("Configuration")) settings_button.setIconSize(QSize(16, 16)) settings_button.setFixedSize(QSize(24, 24)) header_layout = QHBoxLayout() header_layout.addWidget(self._led) header_layout.addWidget(settings_button, Qt.AlignRight) main_layout.addLayout(header_layout) main_layout.addStretch(0) self.setLayout(main_layout) settings_button.pressed.connect(self.settings) self.switchLed.connect(self._led.switchOn)
def read_layout(group, setting='all'): filename = read_settings("User_Settings", 'current_layout_file') filename = os.path.join(profiles_folder, filename) settings = QSettings(filename, QSettings.NativeFormat) value = None pairs = [] if setting != 'all': settings.beginGroup(group) for k, v in default_button_layout.items(): if k == setting: value = settings.value(setting, v, type=str) value[0], value[1], value[2], value[3] = int(value[0]), int( value[1]), int(value[2]), int(value[3]) if not setting in default_button_layout.keys(): value = settings.value(setting, type=str) value[0], value[1], value[2], value[3] = int(value[0]), int( value[1]), int(value[2]), int(value[3]) settings.endGroup() return value if setting == 'all': settings.beginGroup(group) keys = settings.childKeys() for s in keys: for k, v in default_button_layout.items(): if s == k: value = settings.value(s, v, type=str) value[0], value[1], value[2], value[3] = int( value[0]), int(value[1]), int(value[2]), int(value[3]) pairs.append((s, value)) if not s in default_button_layout.keys(): value = settings.value(s, type=str) value[0], value[1], value[2], value[3] = int(value[0]), int( value[1]), int(value[2]), int(value[3]) pairs.append((s, value)) settings.endGroup() return pairs
def reload(self): reg = QSettings() if "current session" in reg.childKeys() and reg.value( "current session", '').strip() and reg.value( "current session", '').strip() in reg.childGroups(): self._session = reg.value("current session") self._logger.info(self.tr("Current session : ") + self._session) self.setWindowTitle(self._session) if self._session not in reg.childGroups(): reg.beginGroup(self._session) reg.setValue("host", self._host) reg.setValue("port", self._port) reg.setValue("root topic", self._rootTopic) reg.endGroup() else: reg.beginGroup(self._session) self._host = reg.value("host", 'localhost') try: self._port = reg.value("port", 1883, type=int) except: pass self._rootTopic = reg.value("root topic", '') reg.endGroup() if "current session" in reg.childKeys() and not reg.value( "current session", '') in reg.childGroups(): reg.remove("current session") self._mqttSwitchingConnection = False self._mqttSwitchingSubscription = False if self._host != self._mqttServerHost or self._port != self._mqttServerPort: self._mqttSwitchingConnection = True elif self._rootTopic != self._mqttRootTopic: self._mqttSwitchingSubscription = True self._mqttServerHost = self._host self._mqttServerPort = self._port self._mqttRootTopic = self._rootTopic self._mqttSubTopic = '#' if self._rootTopic: self._mqttSubTopic = self._rootTopic + '/#' reg.beginGroup(self._session) inbox = reg.value("param inbox", 'inbox') outbox = reg.value("param outbox", 'outbox') regexInbox = reg.value("regex inbox", r'^%ROOT%/(?P<correspondent>.+)/%INBOX%$') regexOutbox = reg.value("regex outbox", r'^%ROOT%/(?P<correspondent>.+)/%OUTBOX%$') regexDefault = reg.value("regex default", r'.*/(?P<correspondent>[^/]+)/[^/]+$') reg.endGroup() regexInbox = regexInbox.replace("%ROOT%", self._rootTopic).replace( "%INBOX%", inbox) regexOutbox = regexOutbox.replace("%ROOT%", self._rootTopic).replace( "%OUTBOX%", outbox) self._topicRegexInbox = None try: self._topicRegexInbox = re.compile(regexInbox) except Exception as e: self._logger.error(self.tr("Failed to compile inbox regex")) self._logger.debug(e) self._topicRegexOutbox = None try: self._topicRegexOutbox = re.compile(regexOutbox) except Exception as e: self._logger.error(self.tr("Failed to compile outbox regex")) self._logger.debug(e) self._topicRegexDefault = None try: self._topicRegexDefault = re.compile(regexDefault) except Exception as e: self._logger.error( self.tr("Failed to compile topic default regex")) self._logger.debug(e) index = self._tabWidget.currentIndex() current = self._tabWidget.currentWidget() for index in (1, self._tabWidget.count()): try: self._displays.remove(self._tabWidget.widget(index)) self._tabWidget.widget(index).deleteLater() self._tabWidget.removeTab(index) except Exception as e: self._logger.error( self. tr("Failed to remove observation : not in display list (index=" ) + str(index) + self.tr(")")) self._logger.debug(e) dspmsg = '' for d in self._displays: if dspmsg: dspmsg += ' | ' else: dspmsg = 'Displays : ' dspmsg += d.title() self._logger.debug(dspmsg) reg.beginGroup(self._session) for i in reg.childGroups(): self.addDisplay(i) reg.endGroup() QCoreApplication.processEvents() if self._mqttSwitchingConnection: self.switchConnection() elif self._mqttSwitchingSubscription: self.switchSubscription()
class ConnectionPanel(QWidget): def __init__(self, parent=None, settings=None): QWidget.__init__(self, parent) self.main_layout = QVBoxLayout() self.setLayout(self.main_layout) # settings self.ini = QSettings(settings, QSettings.IniFormat) self.ini.setIniCodec("utf-8") self.last_login = "" self.ALIAS_DICT = {} self.VPN_SERVERS = {} self.load_settings() vpn_layout_1 = QHBoxLayout() vpn_layout_2 = QHBoxLayout() self.line_login = QLineEdit() if self.last_login != "": self.line_login.setText(self.last_login) else: self.line_login.setPlaceholderText("Login") self.line_login.setStyleSheet(""" QLineEdit { border: 0.5px solid grey; border-radius: 2px; background: #f3f3f3; } QLineEdit:focus { border: 0.5px solid grey; border-radius: 2px; background:white; }""") self.line_password = QLineEdit() self.line_password.setEchoMode(QLineEdit.Password) self.line_password.setPlaceholderText("Password") self.line_password.setStyleSheet(""" QLineEdit { border: 0.5px solid grey; border-radius: 2px; background: #f3f3f3; } QLineEdit:focus { border: 0.5px solid grey; border-radius: 2px; background:white; }""") self.line_password.returnPressed.connect(self.enable_vpn) vpn_layout_1.addWidget(self.line_login) vpn_layout_1.addWidget(self.line_password) checkbox_layout = QHBoxLayout() self.checkbox = QCheckBox("Remember login") self.checkbox.setChecked(True) checkbox_layout.addWidget(self.checkbox) self.vpn_button = Button("Enable VPN", self.enable_vpn) # check that vpn is not started proc = [] for p in psutil.process_iter(attrs=['name']): proc.append(p.info['name']) if "dsSamProxy.exe" in proc: self.vpn_button.setDisabled(True) else: self.vpn_button.setDisabled(False) self.vpn_servers = QComboBox() self.vpn_servers.addItems(self.VPN_SERVERS.values()) vpn_layout_2.addWidget(self.vpn_servers) vpn_layout_2.addWidget(self.vpn_button) # RDP conection line servers_layout = QHBoxLayout() self.servers_box = QComboBox() self.servers_box.addItems(self.ALIAS_DICT.keys()) self.servers_box.currentIndexChanged.connect(self.server_property) self.servers_connect = Button("RDP connection", self.get_RDP_conn) servers_layout.addWidget(self.servers_box) servers_layout.addWidget(self.servers_connect) # Information group self.group_box = QGroupBox() self.group_box_layout = QVBoxLayout() self.label = QLabel( f"Server adress: {self.ALIAS_DICT[self.servers_box.currentText()]}" ) self.group_box.setLayout(self.group_box_layout) self.group_box_layout.addWidget(self.label) self.group_box.setTitle("Server information") self.main_layout.addLayout(vpn_layout_1) self.main_layout.addLayout(checkbox_layout) self.main_layout.addLayout(vpn_layout_2) self.main_layout.addSpacing(10) self.main_layout.addLayout(servers_layout) self.main_layout.addWidget(self.group_box) self.main_layout.addStretch(2) self.show() def load_settings(self): """ load settings :return: """ self.ini.beginGroup("Login") self.last_login = self.ini.value("last_login") self.ini.endGroup() self.ini.beginGroup("Servers") for key in self.ini.childKeys(): self.ALIAS_DICT.update({key: self.ini.value(key)}) self.ini.endGroup() self.ini.beginGroup('Extended_VPN') for key in self.ini.childKeys(): self.VPN_SERVERS.update({key: self.ini.value(key)}) self.ini.endGroup() def enable_vpn(self): """ Activate VPN :return: """ login = self.line_login.text() password = self.line_password.text() if self.checkbox.isChecked(): self.ini.setValue("last_login", login) else: self.settings.setValue("last_login", "") response = startVPN(login, password, self.vpn_servers.currentText()) if response: self.vpn_button.setDisabled(True) else: print("ERRROOOOORRRR!!!!!!") def reload_servers(self): """ Update Combobox with servers names when you back from "Servers" tab :return: """ old_keys = [ self.servers_box.itemText(i) for i in range(0, self.servers_box.count()) ] self.ini.beginGroup("Servers") self.ALIAS_DICT = {} for key in self.ini.childKeys(): self.ALIAS_DICT.update({key: self.ini.value(key)}) for key in self.ALIAS_DICT.keys(): if key not in old_keys: self.servers_box.addItem(key) for key in old_keys: if key not in self.ini.childKeys(): self.servers_box.removeItem(self.servers_box.findText(key)) self.ini.endGroup() def get_RDP_conn(self): """ connect by RDP :return: """ Popen( f"C:\Windows\System32\mstsc.exe /v {self.ALIAS_DICT[self.servers_box.currentText()]}", shell=True) def server_property(self): """ Just show servers property :return: """ self.label.setText( f"Server adress: {self.ALIAS_DICT[self.servers_box.currentText()]}" )
def __init__(self, client, logger): super(ObserverWindow, self).__init__() self._connectionState = None self._countUntitledDisplay = 0 self._displays = [] self._session = 'Default session' self._host = 'localhost' self._port = 1883 self._rootTopic = '' self._logger = logger self._logger.info(self.tr("Started")) reg = QSettings() if "current session" in reg.childKeys() and reg.value( "current session", '').strip() and reg.value( "current session", '').strip() in reg.childGroups(): self._session = reg.value("current session") self._logger.info(self.tr("Current session : ") + self._session) if self._session not in reg.childGroups(): reg.beginGroup(self._session) reg.setValue("host", self._host) reg.setValue("port", self._port) reg.setValue("root topic", self._rootTopic) reg.endGroup() else: reg.beginGroup(self._session) self._host = reg.value("host", 'localhost') try: self._port = reg.value("port", 1883, type=int) except: pass self._rootTopic = reg.value("root topic", '') reg.endGroup() if "current session" in reg.childKeys() and not reg.value( "current session", '') in reg.childGroups(): reg.remove("current session") self._mqttSwitchingConnection = False self._mqttClient = client self._mqttServerHost = self._host self._mqttServerPort = self._port self._mqttRootTopic = self._rootTopic self._mqttSubTopic = '#' if self._rootTopic: self._mqttSubTopic = self._rootTopic + '/#' QApplication.desktop().screenCountChanged.connect(self.restoreWindow) QApplication.desktop().resized.connect(self.restoreWindow) self.setWindowTitle(self._session) self.setWindowIcon(QIcon(':/view-eye.svg')) self._tabWidget = QTabWidget() self._cloudLabel = QLabel() self._connectionStateLabel = QLabel() self.builUi() reg.beginGroup(self._session) inbox = reg.value("param inbox", 'inbox') outbox = reg.value("param outbox", 'outbox') regexInbox = reg.value("regex inbox", r'^%ROOT%/(?P<correspondent>.+)/%INBOX%$') regexOutbox = reg.value("regex outbox", r'^%ROOT%/(?P<correspondent>.+)/%OUTBOX%$') regexDefault = reg.value("regex default", r'.*/(?P<correspondent>[^/]+)/[^/]+$') reg.endGroup() regexInbox = regexInbox.replace("%ROOT%", self._rootTopic).replace( "%INBOX%", inbox) regexOutbox = regexOutbox.replace("%ROOT%", self._rootTopic).replace( "%OUTBOX%", outbox) self._topicRegexInbox = None try: self._topicRegexInbox = re.compile(regexInbox) except Exception as e: self._logger.error(self.tr("Failed to compile inbox regex")) self._logger.debug(e) self._topicRegexOutbox = None try: self._topicRegexOutbox = re.compile(regexOutbox) except Exception as e: self._logger.error(self.tr("Failed to compile outbox regex")) self._logger.debug(e) self._topicRegexDefault = None try: self._topicRegexDefault = re.compile(regexDefault) except Exception as e: self._logger.error( self.tr("Failed to compile topic default regex")) self._logger.debug(e) self.addDisplay(self.tr("All messsages")) reg.beginGroup(self._session) for i in reg.childGroups(): self.addDisplay(i) reg.endGroup() self.changeConnectionState(ConnectionState.DISCONNECTED) self._logger.info("{0} : {1}".format(self.tr("MQTT server host"), self._mqttServerHost)) self._logger.info("{0} : {1}".format(self.tr("MQTT server port"), self._mqttServerPort)) self._logger.info("{0} : {1}".format( self.tr("MQTT clientid"), self._mqttClient._client_id.decode("latin1"))) Timer(0, self.layoutLoadSettings).start() Timer(0, self.start).start()
class QtDownload(QtWidgets.QWidget, Ui_download): def __init__(self, owner): super(self.__class__, self).__init__(owner) Ui_download.__init__(self) self.setupUi(self) self.owner = weakref.ref(owner) self.downloadingList = [] # 正在下载列表 self.downloadList = [] # 下载队列 self.downloadDict = {} # bookId :downloadInfo self.tableWidget.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.tableWidget.setColumnCount(7) self.tableWidget.setHorizontalHeaderLabels( ["id", "标题", "下载状态", "下载页数", "下载章节", "下载速度", "累计下载"]) self.timer = QTimer(self.tableWidget) self.timer.setInterval(1000) self.timer.timeout.connect(self.UpdateTable) self.timer.start() self.settings = QSettings('download.ini', QSettings.IniFormat) self.InitSetting() self.tableWidget.customContextMenuRequested.connect(self.SelectMenu) self.downloadMenu = QMenu(self.tableWidget) action = self.downloadMenu.addAction("打开目录") action.triggered.connect(self.ClickOpenFilePath) action = self.downloadMenu.addAction("暂停") action.triggered.connect(self.ClickPause) action = self.downloadMenu.addAction("刪除记录") action.triggered.connect(self.DelRecording) action = self.downloadMenu.addAction("刪除记录和文件") action.triggered.connect(self.DelRecordingAndFile) self.startMenu = QMenu(self.tableWidget) action = self.startMenu.addAction("打开目录") action.triggered.connect(self.ClickOpenFilePath) action = self.startMenu.addAction("开始") action.triggered.connect(self.ClickStart) action = self.startMenu.addAction("刪除记录") action.triggered.connect(self.DelRecording) action = self.startMenu.addAction("刪除记录和文件") action.triggered.connect(self.DelRecordingAndFile) self.checkMenu = QMenu(self.tableWidget) action = self.checkMenu.addAction("暂停") action.triggered.connect(self.ClickPause) action = self.checkMenu.addAction("开始") action.triggered.connect(self.ClickStart) action = self.checkMenu.addAction("刪除记录") action.triggered.connect(self.DelRecording) action = self.checkMenu.addAction("刪除记录和文件") action.triggered.connect(self.DelRecordingAndFile) self.otherMenu = QMenu(self.tableWidget) action = self.otherMenu.addAction("打开目录") action.triggered.connect(self.ClickOpenFilePath) action = self.otherMenu.addAction("刪除记录") action.triggered.connect(self.DelRecording) action = self.otherMenu.addAction("刪除记录和文件") action.triggered.connect(self.DelRecordingAndFile) self.tableWidget.doubleClicked.connect(self.OpenBookInfo) def InitSetting(self): for bookId in self.settings.childKeys(): data = self.settings.value(bookId) task = DownloadInfo() task.bookId = bookId task.status = DownloadStatus.Pause ToolUtil.ParseFromData(task, data) self.downloadDict[task.bookId] = task self.downloadList.append(task) rowCont = self.tableWidget.rowCount() task.tableRow = rowCont self.tableWidget.insertRow(rowCont) self.UpdateTableItem(task) pass def SaveSetting(self, bookId, task=None): if not task: self.settings.remove(bookId) else: isinstance(task, DownloadInfo) self.settings.setValue( bookId, { "allDownloadCount": task.allDownloadCount, "downloadCount": task.downloadCount, "downloadEps": task.downloadEps, "downSize": task.downSize, "downloadLen": task.downloadLen, "epsCount": task.epsCount, "pagesCount": task.pagesCount, "title": task.title, "savePath": task.savePath }) return def SwitchCurrent(self): pass def UpdateTable(self): for task in self.downloadingList: assert isinstance(task, DownloadInfo) downloadLen = task.speedDownloadLen size = ToolUtil.GetDownloadSize(downloadLen) allSize = ToolUtil.GetDownloadSize(task.downloadLen) task.speed = size + "/s" task.downSize = allSize self.UpdateTableItem(task) task.speedDownloadLen = 0 def AddDownload(self, bookId): if bookId in self.downloadDict: return False task = DownloadInfo() task.bookId = bookId task.status = DownloadStatus.Reading self.downloadDict[task.bookId] = task self.downloadList.append(task) rowCont = self.tableWidget.rowCount() task.tableRow = rowCont self.tableWidget.insertRow(rowCont) self.StartLoadBookInfo(bookId=bookId, isBack=False) self.UpdateTableItem(task) self.SaveSetting(bookId, task) return True def UpdateTableItem(self, info): self.tableWidget.setItem(info.tableRow, 0, QTableWidgetItem(info.bookId)) self.tableWidget.setItem(info.tableRow, 1, QTableWidgetItem(info.title)) self.tableWidget.setItem(info.tableRow, 2, QTableWidgetItem(info.status)) self.tableWidget.setItem( info.tableRow, 3, QTableWidgetItem("{}/{}".format(str(info.allDownloadCount), str(info.pagesCount)))) self.tableWidget.setItem( info.tableRow, 4, QTableWidgetItem("{}/{}".format(str(info.downloadEps - 1), str(info.epsCount)))) self.tableWidget.setItem(info.tableRow, 5, QTableWidgetItem(info.speed)) self.tableWidget.setItem(info.tableRow, 6, QTableWidgetItem(info.downSize)) self.tableWidget.update() return # 加载书籍信息 def StartLoadBookInfo(self, msg="", bookId="", isBack=True): info = self.downloadDict.get(bookId) if not info: return assert isinstance(info, DownloadInfo) if not isBack: self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookById(bookId, x), self.StartLoadBookInfo, bookId) elif msg != Status.Ok: info.resetCnt += 1 if info.resetCnt >= config.ResetCnt: info.status = DownloadStatus.Error else: self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookById(bookId, x), self.StartLoadBookInfo, bookId) else: info.resetCnt = 0 book = BookMgr().books.get(bookId) info.status = DownloadStatus.ReadingEps info.title = book.title info.savePath = os.path.join( os.path.join(config.SavePath, config.SavePathDir), info.title.replace("/", "").replace("|", "").replace( "*", "").replace("\\", "").replace("?", "").replace( ":", "").replace("*", "").replace("<", "").replace( ">", "").replace("\"", "")) info.pagesCount = book.pagesCount info.epsCount = book.epsCount self.StartLoadEpsInfo(bookId=bookId, isBack=False) self.UpdateTableItem(info) # 加载章节信息 def StartLoadEpsInfo(self, msg="", bookId="", isBack=True): info = self.downloadDict.get(bookId) if not info: return assert isinstance(info, DownloadInfo) if not isBack: self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookEpsInfo(bookId, x), self.StartLoadEpsInfo, bookId) elif msg != Status.Ok: info.resetCnt += 1 if info.resetCnt >= config.ResetCnt: info.status = DownloadStatus.Error else: self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookEpsInfo(bookId, x), self.StartLoadEpsInfo, bookId) else: info.resetCnt = 0 book = BookMgr().books.get(bookId) info.title = book.title info.pagesCount = book.pagesCount info.epsCount = book.epsCount info.status = DownloadStatus.ReadingPicture # 先准备好第一章 self.StartLoadPicUrl(bookId=bookId, isBack=False) self.HandlerDownloadList() self.UpdateTableItem(info) def StartLoadPicUrl(self, msg="", bookId="", isBack=True): info = self.downloadDict.get(bookId) if not info: return assert isinstance(info, DownloadInfo) if not isBack: # 如果已经加载好了 if info.preDownloadEps > info.epsCount: if info.status not in [ DownloadStatus.Success, DownloadStatus.Downloading ]: info.status = DownloadStatus.Waiting return self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookEpsPicInfo( bookId, info.preDownloadEps, x), self.StartLoadPicUrl, bookId) elif msg != Status.Ok: info.resetCnt += 1 if info.resetCnt >= config.ResetCnt: info.status = DownloadStatus.Error else: self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookEpsPicInfo( bookId, info.preDownloadEps, x), self.StartLoadPicUrl, bookId) else: info.resetCnt = 0 info.preDownloadEps += 1 if info.status == DownloadStatus.Downloading: # 加载完成了 if info.preDownloadEps > info.epsCount: return # 加载下一章 self.owner().qtTask.AddHttpTask( lambda x: BookMgr().AddBookEpsPicInfo( bookId, info.preDownloadEps, x), self.StartLoadPicUrl, bookId) elif info.status == DownloadStatus.ReadingPicture: info.status = DownloadStatus.Waiting self.HandlerDownloadList() self.UpdateTableItem(info) def HandlerDownloadList(self): downloadNum = config.DownloadThreadNum addNum = downloadNum - len(self.downloadingList) if addNum <= 0: return for _ in range(addNum): if len(self.downloadList) <= 0: return for task in self.downloadList: assert isinstance(task, DownloadInfo) if task.status != DownloadStatus.Waiting: continue self.downloadList.remove(task) self.downloadingList.append(task) task.status = DownloadStatus.Downloading self.StartLoadPicUrl(bookId=task.bookId, isBack=False) self.StartDownload(task) self.UpdateTableItem(task) break pass # 开始下载 def StartDownload(self, task): assert isinstance(task, DownloadInfo) bookInfo = BookMgr().books.get(task.bookId) picInfo = None st = None while task.downloadEps <= task.epsCount: epsInfo = bookInfo.eps[task.downloadEps - 1] # 图片路径还没有加载好 if not epsInfo.pics: st = Status.WaitLoad break if task.downloadCount > len(epsInfo.pics): task.downloadCount = 1 task.downloadEps += 1 else: picInfo = epsInfo.pics[task.downloadCount - 1] st = Status.Ok break if st == Status.Ok: task.saveName = picInfo.originalName self.owner().qtTask.AddDownloadTask( picInfo.fileServer, picInfo.path, downloadCallBack=self.DownloadCallBack, completeCallBack=self.CompleteDownloadPic, backParam=task.bookId, isSaveCache=False) task.allDownloadCount += 1 else: if st == Status.WaitLoad: task.status = DownloadStatus.ReadingPicture self.StartLoadPicUrl(bookId=task.bookId, isBack=False) self.downloadList.append(task) self.HandlerDownloadList() else: task.status = DownloadStatus.Success self.UpdateTable() self.downloadingList.remove(task) self.SaveSetting(task.bookId, task) self.UpdateTableItem(task) def DownloadCallBack(self, data, laveFileSize, bookId): task = self.downloadDict.get(bookId) if not task: return assert isinstance(task, DownloadInfo) task.downloadLen += len(data) task.speedDownloadLen += len(data) def CompleteDownloadPic(self, data, msg, bookId): task = self.downloadDict.get(bookId) if not task: return assert isinstance(task, DownloadInfo) # 丢弃 if task.status != DownloadStatus.Downloading: return if msg != Status.Ok: task.resetCnt += 1 if task.resetCnt >= config.ResetCnt: task.status = DownloadStatus.Error else: if task.status == DownloadStatus.Downloading: self.StartDownload(task) else: try: savePath = os.path.join(task.savePath, str(task.downloadEps)) if not os.path.isdir(savePath): os.makedirs(savePath) f = open(os.path.join(savePath, task.saveName), "wb+") f.write(data) f.close() task.downloadCount += 1 if task.status == DownloadStatus.Downloading: self.StartDownload(task) except Exception as es: import sys cur_tb = sys.exc_info()[ 2] # return (exc_type, exc_value, traceback) e = sys.exc_info()[1] Log.Error(cur_tb, e) task.status = DownloadStatus.Error self.downloadingList.remove(task) self.UpdateTableItem(task) def RemoveRecord(self, bookId): task = self.downloadDict.get(bookId) if not task: return assert isinstance(task, DownloadInfo) if task in self.downloadingList: self.downloadingList.remove(task) if task in self.downloadList: self.downloadList.remove(task) self.downloadDict.pop(bookId) self.tableWidget.removeRow(task.tableRow) self.SaveSetting(bookId) def UpdateTableRow(self): count = self.tableWidget.rowCount() for i in range(count): bookId = self.tableWidget.item(i, 0).text() info = self.downloadDict.get(bookId) if info: info.tableRow = i # 右键菜单 def SelectMenu(self, pos): index = self.tableWidget.indexAt(pos) if index.isValid(): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if not selectRows: return if len(selectRows) == 1: # 单选 row = selectRows.pop() col = 0 bookId = self.tableWidget.item(row, col).text() task = self.downloadDict.get(bookId) if not task: return assert isinstance(task, DownloadInfo) if task.status in [DownloadStatus.Pause, DownloadStatus.Error]: self.startMenu.exec_(QCursor.pos()) elif task.status in [ DownloadStatus.Downloading, DownloadStatus.Waiting, DownloadStatus.Reading, DownloadStatus.ReadingPicture, DownloadStatus.ReadingEps ]: self.downloadMenu.exec_(QCursor.pos()) else: self.otherMenu.exec(QCursor.pos()) else: # 多选 self.checkMenu.exec_(QCursor.pos()) pass def ClickOpenFilePath(self): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if not selectRows: return # 只去第一个 row = selectRows.pop() col = 0 bookId = self.tableWidget.item(row, col).text() task = self.downloadDict.get(bookId) # TODO 未实现 return def ClickPause(self): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if not selectRows: return for row in selectRows: col = 0 bookId = self.tableWidget.item(row, col).text() task = self.downloadDict.get(bookId) if not task: continue if task in self.downloadingList: self.downloadingList.remove(task) task.status = DownloadStatus.Pause self.UpdateTableItem(task) return def ClickStart(self): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if not selectRows: return for row in selectRows: col = 0 bookId = self.tableWidget.item(row, col).text() task = self.downloadDict.get(bookId) if not task: continue if task.status not in [DownloadStatus.Pause, DownloadStatus.Error]: continue task.status = DownloadStatus.Reading if task not in self.downloadList: self.downloadList.append(task) self.StartLoadBookInfo(bookId=bookId, isBack=False) self.UpdateTableItem(task) def DelRecording(self): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if not selectRows: return for row in selectRows: col = 0 bookId = self.tableWidget.item(row, col).text() self.RemoveRecord(bookId) self.UpdateTableRow() def DelRecordingAndFile(self): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if not selectRows: return try: selectRows = list(selectRows) selectRows.sort(reverse=True) for row in selectRows: col = 0 bookId = self.tableWidget.item(row, col).text() bookInfo = self.downloadDict.get(bookId) if not bookInfo: continue self.RemoveRecord(bookId) if os.path.isdir(bookInfo.savePath): shutil.rmtree(bookInfo.savePath) except Exception as es: import sys cur_tb = sys.exc_info()[ 2] # return (exc_type, exc_value, traceback) e = sys.exc_info()[1] Log.Error(cur_tb, e) self.UpdateTableRow() def OpenBookInfo(self): selected = self.tableWidget.selectedIndexes() selectRows = set() for index in selected: selectRows.add(index.row()) if len(selectRows) > 1: return if len(selectRows) <= 0: return row = list(selectRows)[0] col = 0 bookId = self.tableWidget.item(row, col).text() if not bookId: return self.owner().bookInfoForm.OpenBook(bookId)
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self._version = __version__ self.setWindowIcon(QIcon(":/logo.png")) self.setWindowTitle("Tasmota Device Manager {}".format(self._version)) self.unknown = [] self.env = TasmotaEnvironment() self.device = None self.topics = [] self.mqtt_queue = [] self.fulltopic_queue = [] # ensure TDM directory exists in the user directory if not os.path.isdir("{}/TDM".format(QDir.homePath())): os.mkdir("{}/TDM".format(QDir.homePath())) self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()), QSettings.IniFormat) self.setMinimumSize(QSize(1000, 600)) # configure logging logging.basicConfig(filename="{}/TDM/tdm.log".format(QDir.homePath()), level=self.settings.value("loglevel", "INFO"), datefmt="%Y-%m-%d %H:%M:%S", format='%(asctime)s [%(levelname)s] %(message)s') logging.info("### TDM START ###") # load devices from the devices file, create TasmotaDevices and add the to the envvironment for mac in self.devices.childGroups(): self.devices.beginGroup(mac) device = TasmotaDevice(self.devices.value("topic"), self.devices.value("full_topic"), self.devices.value("friendly_name")) device.debug = self.devices.value("debug", False, bool) device.p['Mac'] = mac.replace("-", ":") device.env = self.env self.env.devices.append(device) # load device command history self.devices.beginGroup("history") for k in self.devices.childKeys(): device.history.append(self.devices.value(k)) self.devices.endGroup() self.devices.endGroup() self.device_model = TasmotaDevicesModel(self.env) self.setup_mqtt() self.setup_main_layout() self.add_devices_tab() self.build_mainmenu() # self.build_toolbars() self.setStatusBar(QStatusBar()) pbSubs = QPushButton("Show subscriptions") pbSubs.setFlat(True) pbSubs.clicked.connect(self.showSubs) self.statusBar().addPermanentWidget(pbSubs) self.queue_timer = QTimer() self.queue_timer.timeout.connect(self.mqtt_publish_queue) self.queue_timer.start(250) self.auto_timer = QTimer() self.auto_timer.timeout.connect(self.auto_telemetry) self.load_window_state() if self.settings.value("connect_on_startup", False, bool): self.actToggleConnect.trigger() self.tele_docks = {} self.consoles = [] def setup_main_layout(self): self.mdi = QMdiArea() self.mdi.setActivationOrder(QMdiArea.ActivationHistoryOrder) self.mdi.setTabsClosable(True) self.setCentralWidget(self.mdi) def setup_mqtt(self): self.mqtt = MqttClient() self.mqtt.connecting.connect(self.mqtt_connecting) self.mqtt.connected.connect(self.mqtt_connected) self.mqtt.disconnected.connect(self.mqtt_disconnected) self.mqtt.connectError.connect(self.mqtt_connectError) self.mqtt.messageSignal.connect(self.mqtt_message) def add_devices_tab(self): self.devices_list = ListWidget(self) sub = self.mdi.addSubWindow(self.devices_list) sub.setWindowState(Qt.WindowMaximized) self.devices_list.deviceSelected.connect(self.selectDevice) self.devices_list.openConsole.connect(self.openConsole) self.devices_list.openRulesEditor.connect(self.openRulesEditor) self.devices_list.openTelemetry.connect(self.openTelemetry) self.devices_list.openWebUI.connect(self.openWebUI) def load_window_state(self): wndGeometry = self.settings.value('window_geometry') if wndGeometry: self.restoreGeometry(wndGeometry) def build_mainmenu(self): mMQTT = self.menuBar().addMenu("MQTT") self.actToggleConnect = QAction(QIcon(":/disconnect.png"), "Connect") self.actToggleConnect.setCheckable(True) self.actToggleConnect.toggled.connect(self.toggle_connect) mMQTT.addAction(self.actToggleConnect) mMQTT.addAction(QIcon(), "Broker", self.setup_broker) mMQTT.addAction(QIcon(), "Autodiscovery patterns", self.patterns) mMQTT.addSeparator() mMQTT.addAction(QIcon(), "Clear obsolete retained LWTs", self.clear_LWT) mMQTT.addSeparator() mMQTT.addAction(QIcon(), "Auto telemetry period", self.auto_telemetry_period) self.actToggleAutoUpdate = QAction(QIcon(":/auto_telemetry.png"), "Auto telemetry") self.actToggleAutoUpdate.setCheckable(True) self.actToggleAutoUpdate.toggled.connect(self.toggle_autoupdate) mMQTT.addAction(self.actToggleAutoUpdate) mSettings = self.menuBar().addMenu("Settings") mSettings.addAction(QIcon(), "BSSId aliases", self.bssid) mSettings.addSeparator() mSettings.addAction(QIcon(), "Preferences", self.prefs) # mExport = self.menuBar().addMenu("Export") # mExport.addAction(QIcon(), "OpenHAB", self.openhab) def build_toolbars(self): main_toolbar = Toolbar(orientation=Qt.Horizontal, iconsize=24, label_position=Qt.ToolButtonTextBesideIcon) main_toolbar.setObjectName("main_toolbar") def initial_query(self, device, queued=False): for c in initial_commands(): cmd, payload = c cmd = device.cmnd_topic(cmd) if queued: self.mqtt_queue.append([cmd, payload]) else: self.mqtt.publish(cmd, payload, 1) def setup_broker(self): brokers_dlg = BrokerDialog() if brokers_dlg.exec_( ) == QDialog.Accepted and self.mqtt.state == self.mqtt.Connected: self.mqtt.disconnect() def toggle_autoupdate(self, state): if state == True: if self.mqtt.state == self.mqtt.Connected: for d in self.env.devices: self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8) self.auto_timer.setInterval( self.settings.value("autotelemetry", 5000, int)) self.auto_timer.start() else: self.auto_timer.stop() def toggle_connect(self, state): if state and self.mqtt.state == self.mqtt.Disconnected: self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) self.mqtt.connectToHost() elif not state and self.mqtt.state == self.mqtt.Connected: self.mqtt_disconnect() def auto_telemetry(self): if self.mqtt.state == self.mqtt.Connected: for d in self.env.devices: self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8) def mqtt_connect(self): self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) if self.mqtt.state == self.mqtt.Disconnected: self.mqtt.connectToHost() def mqtt_disconnect(self): self.mqtt.disconnectFromHost() def mqtt_connecting(self): self.statusBar().showMessage("Connecting to broker") def mqtt_connected(self): self.actToggleConnect.setIcon(QIcon(":/connect.png")) self.actToggleConnect.setText("Disconnect") self.statusBar().showMessage("Connected to {}:{} as {}".format( self.broker_hostname, self.broker_port, self.broker_username if self.broker_username else '[anonymous]')) self.mqtt_subscribe() def mqtt_subscribe(self): # clear old topics self.topics.clear() custom_patterns.clear() # load custom autodiscovery patterns self.settings.beginGroup("Patterns") for k in self.settings.childKeys(): custom_patterns.append(self.settings.value(k)) self.settings.endGroup() # expand fulltopic patterns to subscribable topics for pat in default_patterns: # tasmota default and SO19 self.topics += expand_fulltopic(pat) # check if custom patterns can be matched by default patterns for pat in custom_patterns: if pat.startswith("%prefix%") or pat.split('/')[1] == "%prefix%": continue # do nothing, default subcriptions will match this topic else: self.topics += expand_fulltopic(pat) for d in self.env.devices: # if device has a non-standard pattern, check if the pattern is found in the custom patterns if not d.is_default() and d.p['FullTopic'] not in custom_patterns: # if pattern is not found then add the device topics to subscription list. # if the pattern is found, it will be matched without implicit subscription self.topics += expand_fulltopic(d.p['FullTopic']) # passing a list of tuples as recommended by paho self.mqtt.subscribe([(topic, 0) for topic in self.topics]) @pyqtSlot(str, str) def mqtt_publish(self, t, p): self.mqtt.publish(t, p) def mqtt_publish_queue(self): for q in self.mqtt_queue: t, p = q self.mqtt.publish(t, p) self.mqtt_queue.pop(self.mqtt_queue.index(q)) def mqtt_disconnected(self): self.actToggleConnect.setIcon(QIcon(":/disconnect.png")) self.actToggleConnect.setText("Connect") self.statusBar().showMessage("Disconnected") def mqtt_connectError(self, rc): reason = { 1: "Incorrect protocol version", 2: "Invalid client identifier", 3: "Server unavailable", 4: "Bad username or password", 5: "Not authorized", } self.statusBar().showMessage("Connection error: {}".format(reason[rc])) self.actToggleConnect.setChecked(False) def mqtt_message(self, topic, msg): # try to find a device by matching known FullTopics against the MQTT topic of the message device = self.env.find_device(topic) if device: if topic.endswith("LWT"): if not msg: msg = "Offline" device.update_property("LWT", msg) if msg == 'Online': # known device came online, query initial state self.initial_query(device, True) else: # forward the message for processing device.parse_message(topic, msg) if device.debug: logging.debug("MQTT: %s %s", topic, msg) else: # unknown device, start autodiscovery process if topic.endswith("LWT"): self.env.lwts.append(topic) logging.info("DISCOVERY: LWT from an unknown device %s", topic) # STAGE 1 # load default and user-provided FullTopic patterns and for all the patterns, # try matching the LWT topic (it follows the device's FullTopic syntax for p in default_patterns + custom_patterns: match = re.fullmatch( p.replace("%topic%", "(?P<topic>.*?)").replace( "%prefix%", "(?P<prefix>.*?)") + ".*$", topic) if match: # assume that the matched topic is the one configured in device settings possible_topic = match.groupdict().get('topic') if possible_topic not in ('tele', 'stat'): # if the assumed topic is different from tele or stat, there is a chance that it's a valid topic # query the assumed device for its FullTopic. False positives won't reply. possible_topic_cmnd = p.replace( "%prefix%", "cmnd").replace( "%topic%", possible_topic) + "FullTopic" logging.debug( "DISCOVERY: Asking an unknown device for FullTopic at %s", possible_topic_cmnd) self.mqtt_queue.append([possible_topic_cmnd, ""]) elif topic.endswith("RESULT") or topic.endswith( "FULLTOPIC"): # reply from an unknown device # STAGE 2 full_topic = loads(msg).get('FullTopic') if full_topic: # the device replies with its FullTopic # here the Topic is extracted using the returned FullTopic, identifying the device parsed = parse_topic(full_topic, topic) if parsed: # got a match, we query the device's MAC address in case it's a known device that had its topic changed logging.debug( "DISCOVERY: topic %s is matched by fulltopic %s", topic, full_topic) d = self.env.find_device(topic=parsed['topic']) if d: d.update_property("FullTopic", full_topic) else: logging.info( "DISCOVERY: Discovered topic=%s with fulltopic=%s", parsed['topic'], full_topic) d = TasmotaDevice(parsed['topic'], full_topic) self.env.devices.append(d) self.device_model.addDevice(d) logging.debug( "DISCOVERY: Sending initial query to topic %s", parsed['topic']) self.initial_query(d, True) self.env.lwts.remove(d.tele_topic("LWT")) d.update_property("LWT", "Online") def export(self): fname, _ = QFileDialog.getSaveFileName(self, "Export device list as...", directory=QDir.homePath(), filter="CSV files (*.csv)") if fname: if not fname.endswith(".csv"): fname += ".csv" with open(fname, "w", encoding='utf8') as f: column_titles = [ 'mac', 'topic', 'friendly_name', 'full_topic', 'cmnd_topic', 'stat_topic', 'tele_topic', 'module', 'module_id', 'firmware', 'core' ] c = csv.writer(f) c.writerow(column_titles) for r in range(self.device_model.rowCount()): d = self.device_model.index(r, 0) c.writerow([ self.device_model.mac(d), self.device_model.topic(d), self.device_model.friendly_name(d), self.device_model.fullTopic(d), self.device_model.commandTopic(d), self.device_model.statTopic(d), self.device_model.teleTopic(d), # modules.get(self.device_model.module(d)), self.device_model.module(d), self.device_model.firmware(d), self.device_model.core(d) ]) def bssid(self): BSSIdDialog().exec_() def patterns(self): PatternsDialog().exec_() # def openhab(self): # OpenHABDialog(self.env).exec_() def showSubs(self): QMessageBox.information(self, "Subscriptions", "\n".join(sorted(self.topics))) def clear_LWT(self): dlg = ClearLWTDialog(self.env) if dlg.exec_() == ClearLWTDialog.Accepted: for row in range(dlg.lw.count()): itm = dlg.lw.item(row) if itm.checkState() == Qt.Checked: topic = itm.text() self.mqtt.publish(topic, retain=True) self.env.lwts.remove(topic) logging.info("MQTT: Cleared %s", topic) def prefs(self): dlg = PrefsDialog() if dlg.exec_() == QDialog.Accepted: update_devices = False devices_short_version = self.settings.value( "devices_short_version", True, bool) if devices_short_version != dlg.cbDevShortVersion.isChecked(): update_devices = True self.settings.setValue("devices_short_version", dlg.cbDevShortVersion.isChecked()) update_consoles = False console_font_size = self.settings.value("console_font_size", 9) if console_font_size != dlg.sbConsFontSize.value(): update_consoles = True self.settings.setValue("console_font_size", dlg.sbConsFontSize.value()) console_word_wrap = self.settings.value("console_word_wrap", True, bool) if console_word_wrap != dlg.cbConsWW.isChecked(): update_consoles = True self.settings.setValue("console_word_wrap", dlg.cbConsWW.isChecked()) if update_consoles: for c in self.consoles: c.console.setWordWrapMode(dlg.cbConsWW.isChecked()) new_font = QFont(c.console.font()) new_font.setPointSize(dlg.sbConsFontSize.value()) c.console.setFont(new_font) self.settings.sync() def auto_telemetry_period(self): curr_val = self.settings.value("autotelemetry", 5000, int) period, ok = QInputDialog.getInt( self, "Set AutoTelemetry period", "Values under 5000ms may cause increased ESP LoadAvg", curr_val, 1000) if ok: self.settings.setValue("autotelemetry", period) self.settings.sync() @pyqtSlot(TasmotaDevice) def selectDevice(self, d): self.device = d @pyqtSlot() def openTelemetry(self): if self.device: tele_widget = TelemetryWidget(self.device) self.addDockWidget(Qt.RightDockWidgetArea, tele_widget) self.mqtt_publish(self.device.cmnd_topic('STATUS'), "8") @pyqtSlot() def openConsole(self): if self.device: console_widget = ConsoleWidget(self.device) self.mqtt.messageSignal.connect(console_widget.consoleAppend) console_widget.sendCommand.connect(self.mqtt.publish) self.addDockWidget(Qt.BottomDockWidgetArea, console_widget) console_widget.command.setFocus() self.consoles.append(console_widget) @pyqtSlot() def openRulesEditor(self): if self.device: rules = RulesWidget(self.device) self.mqtt.messageSignal.connect(rules.parseMessage) rules.sendCommand.connect(self.mqtt_publish) self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.addSubWindow(rules) rules.setWindowState(Qt.WindowMaximized) rules.destroyed.connect(self.updateMDI) self.mqtt_queue.append((self.device.cmnd_topic("ruletimer"), "")) self.mqtt_queue.append((self.device.cmnd_topic("rule1"), "")) self.mqtt_queue.append((self.device.cmnd_topic("Var"), "")) self.mqtt_queue.append((self.device.cmnd_topic("Mem"), "")) @pyqtSlot() def openWebUI(self): if self.device and self.device.p.get('IPAddress'): url = QUrl("http://{}".format(self.device.p['IPAddress'])) try: webui = QWebEngineView() webui.load(url) frm_webui = QFrame() frm_webui.setWindowTitle("WebUI [{}]".format( self.device.p['FriendlyName1'])) frm_webui.setFrameShape(QFrame.StyledPanel) frm_webui.setLayout(VLayout(0)) frm_webui.layout().addWidget(webui) frm_webui.destroyed.connect(self.updateMDI) self.mdi.addSubWindow(frm_webui) self.mdi.setViewMode(QMdiArea.TabbedView) frm_webui.setWindowState(Qt.WindowMaximized) except NameError: QDesktopServices.openUrl( QUrl("http://{}".format(self.device.p['IPAddress']))) def updateMDI(self): if len(self.mdi.subWindowList()) == 1: self.mdi.setViewMode(QMdiArea.SubWindowView) self.devices_list.setWindowState(Qt.WindowMaximized) def closeEvent(self, e): self.settings.setValue("version", self._version) self.settings.setValue("window_geometry", self.saveGeometry()) self.settings.setValue("views_order", ";".join(self.devices_list.views.keys())) self.settings.beginGroup("Views") for view, items in self.devices_list.views.items(): self.settings.setValue(view, ";".join(items[1:])) self.settings.endGroup() self.settings.sync() for d in self.env.devices: mac = d.p.get('Mac') topic = d.p['Topic'] full_topic = d.p['FullTopic'] friendly_name = d.p['FriendlyName1'] if mac: self.devices.beginGroup(mac.replace(":", "-")) self.devices.setValue("topic", topic) self.devices.setValue("full_topic", full_topic) self.devices.setValue("friendly_name", friendly_name) for i, h in enumerate(d.history): self.devices.setValue("history/{}".format(i), h) self.devices.endGroup() self.devices.sync() e.accept()
def _buildUi(self): self._clues = {} self._selectionComboBox = None self.loadLanguage() self.loadClueAlbum('clue-album.ini') if self._clues: self._selectionComboBox = QComboBox() self._selectionComboBox.addItem(self.tr("Load clue..."), None) for k in self._clues: clue = self._clues[k] self._selectionComboBox.addItem(clue.title, k) self._options = {} if os.path.isfile('definitions.ini'): definitions = QSettings('definitions.ini', QSettings.IniFormat) for group in definitions.childGroups(): definitions.beginGroup(group) if group == "options": for key in definitions.childKeys(): self._options[key] = definitions.value(key) definitions.endGroup() main_layout = QVBoxLayout() main_layout.setSpacing(12) self._led = LedWidget(self.tr("Raspberry Teletext"), QSize(40, 20)) self._led.setRedAsBold(True) self._led.setRedAsRed(True) self._led.switchOn('gray') settings_button = QPushButton() settings_button.setIcon(QIcon("./settings.svg")) settings_button.setFlat(True) settings_button.setToolTip(self.tr("Effects and language")) settings_button.setIconSize(QSize(16, 16)) settings_button.setFixedSize(QSize(24, 24)) header_layout = QHBoxLayout() header_layout.addWidget(self._led) header_layout.addWidget(settings_button, Qt.AlignRight) main_layout.addLayout(header_layout) stop_button = QPushButton() stop_button.setIcon(QIcon("./cancel.svg")) stop_button.setFlat(True) stop_button.setToolTip(self.tr("Clear TV screen")) stop_button.setIconSize(QSize(16, 16)) stop_button.setFixedSize(QSize(24, 24)) if 'tv-screen-width' in self._options and 'tv-screen-height' in self._options: hint = QSize(int(self._options['tv-screen-width']), int(self._options['tv-screen-height'])) # self._tvScreen = TvScreenLabel(self.tr("<div align=center>Display on TV</div>"), hint) self._tvScreen = TvScreenLabel('', hint) policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: # self._tvScreen = TvScreenLabel(self.tr("<div align=center>Display on TV</div>")) self._tvScreen = TvScreenLabel('') policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) policy.setHeightForWidth(True) self._tvScreen.setSizePolicy(policy) display_layout = QHBoxLayout() display_layout.addWidget(self._tvScreen) display_layout.addStretch() display_layout.addWidget(stop_button, Qt.AlignRight) main_layout.addLayout(display_layout) if self._selectionComboBox: main_layout.addWidget(self._selectionComboBox) self._editor = QPlainTextEdit() self._editor.setFrameShape(QFrame.NoFrame) self._editor.setCursorWidth(8) main_layout.addWidget(self._editor) clear_button = QPushButton(self.tr("Erase draft")) clear_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) send_button = QPushButton(self.tr("Display on TV")) send_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) button_layout = QHBoxLayout() button_layout.addWidget(send_button) button_layout.addWidget(clear_button) main_layout.addLayout(button_layout) self.setLayout(main_layout) settings_button.pressed.connect(self.settings) stop_button.pressed.connect(self.stop) clear_button.pressed.connect(self.erase) send_button.pressed.connect(self.send) self.switchLed.connect(self._led.switchOn) if self._selectionComboBox: self._selectionComboBox.activated.connect(self.selectClue) self._editor.setFocusPolicy(Qt.StrongFocus) self._editor.setFocus(Qt.OtherFocusReason)
class BSSIdDialog(QDialog): def __init__(self, *args, **kwargs): super(BSSIdDialog, self).__init__(*args, **kwargs) self.setMinimumHeight(400) self.setMinimumWidth(400) self.setWindowTitle("BSSId aliases") self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.settings.beginGroup("BSSId") vl = VLayout() cols = ["BSSId", "Alias"] self.tw = QTableWidget(0, 2) self.tw.setHorizontalHeaderLabels(cols) self.tw.verticalHeader().hide() for c in range(2): self.tw.horizontalHeader().setSectionResizeMode( c, QHeaderView.Stretch) for k in self.settings.childKeys(): row = self.tw.rowCount() self.tw.insertRow(row) self.tw.setItem(row, 0, QTableWidgetItem(k)) self.tw.setItem(row, 1, QTableWidgetItem(self.settings.value(k))) vl.addWidget(self.tw) hl_btns = HLayout([0, 3, 0, 3]) btnAdd = QPushButton("Add") btnDel = QPushButton("Delete") btnCancel = QPushButton("Cancel") btnSave = QPushButton("Save") hl_btns.addWidgets([btnAdd, btnDel, btnSave, btnCancel]) hl_btns.insertStretch(2) vl.addLayout(hl_btns) self.setLayout(vl) self.idx = None self.tw.clicked.connect(self.select) btnAdd.clicked.connect(self.add) btnDel.clicked.connect(self.delete) btnSave.clicked.connect(self.accept) btnCancel.clicked.connect(self.reject) def select(self, idx): self.idx = idx def add(self): self.tw.insertRow(self.tw.rowCount()) def delete(self): if self.idx: key = self.tw.item(self.idx.row(), 0).text() self.settings.remove(key) self.tw.removeRow(self.idx.row()) def accept(self): for r in range(self.tw.rowCount()): key = self.tw.item(r, 0).text() val = self.tw.item(r, 1).text() self.settings.setValue(key, val) self.settings.endGroup() self.done(QDialog.Accepted)
def __init__(self, title, logger, session, server, port, topic, remove=True, filter=True): super(ObserverDisplayWidget, self).__init__() self._observation = title self._displayFormat = int(TopicDisplay.EMPHASIZEDCORRESPONDENT) self._filter1 = None self._filter2 = None self._filter3 = None self._filter4 = None self._hiddenCorrespondents = [] self._hiddenTopics = [] self._logger = logger self._session = session self._mqttServerHost = server self._mqttServerPort = port self._mqttTopicRoot = topic self._bufferSize = 50000 self._correspondents = [] self._topics = [] self._mainDisplay = QTextEdit() main_layout = QVBoxLayout() main_layout.setSpacing(12) self._mainDisplay.setFrameShape(QFrame.NoFrame) self._mainDisplay.setLineWrapMode(QTextEdit.NoWrap) self._mainDisplay.setReadOnly(True) main_layout.addWidget(self._mainDisplay) button_layout = QHBoxLayout() clear_button = QPushButton(self.tr("Erase")) button_layout.addWidget(clear_button) if filter: filter_button = QPushButton(self.tr("Observation...")) button_layout.addWidget(filter_button) filter_button.pressed.connect(self.filterSettings) else: clear_wills_button = QPushButton(self.tr("Wills...")) button_layout.addWidget(clear_wills_button) clear_wills_button.pressed.connect(self.clearWillselection) if remove: del_tab_button = QPushButton() del_tab_button.setIcon(QIcon(":/trash.svg")) del_tab_button.setFlat(True) del_tab_button.setToolTip(self.tr("Delete this observation")) del_tab_button.setIconSize(QSize(16, 16)) del_tab_button.setFixedSize(QSize(24, 24)) button_layout.addWidget(del_tab_button) del_tab_button.pressed.connect(self.delObservation) add_tab_button = QPushButton() add_tab_button.setIcon(QIcon(":/add.svg")) add_tab_button.setFlat(True) add_tab_button.setToolTip(self.tr("Add new observation")) add_tab_button.setIconSize(QSize(16, 16)) add_tab_button.setFixedSize(QSize(24, 24)) button_layout.addStretch(1) button_layout.addWidget(add_tab_button, Qt.AlignRight) main_layout.addLayout(button_layout) self.setLayout(main_layout) clear_button.pressed.connect(self.erase) add_tab_button.pressed.connect(self.addObservation) self._mainDisplay.setStyleSheet( "font-family:monospace,courier new,courier; white-space:pre; color:black;" ) reg = QSettings() reg.beginGroup(self._session) reg.beginGroup(self._observation) self._displayFormat = reg.value( "display", int(TopicDisplay.EMPHASIZEDCORRESPONDENT), type=int) self._filter1 = reg.value("filter 1", '') self._filter2 = reg.value("filter 2", '') self._filter3 = reg.value("filter 1", '') self._filter4 = reg.value("filter 4", '') self._bufferSize = reg.value("buffer size", 50000, type=int) if "Hidden correspondents" in reg.childGroups(): reg.beginGroup("Hidden correspondents") for p in reg.childKeys(): self._hiddenCorrespondents.append(p.replace('\\', '/')) reg.endGroup() if "Hidden topics" in reg.childGroups(): reg.beginGroup("Hidden topics") for t in reg.childKeys(): self._hiddenTopics.append(t.replace('\\', '/')) reg.endGroup() reg.endGroup() reg.endGroup()
class ini_setting: """ class for managing .ini file and project defaults """ release_mode = False def __init__(self, file_name, model): """ constructor: read setting values into a QSetting object Args: file_name: ini file name """ self.file_name = file_name self.model = model self.error_msg = "" self.groups_with_values = {} # all values read self.groups = {} # group names and key names self.config = None try: self.config = QSettings(QSettings.IniFormat, QSettings.UserScope, "EPA", self.model, None) if (not os.path.isfile(self.file_name) and file_name): from shutil import copyfile copyfile(self.config.fileName(), file_name) # self.create_ini_file(file_name) else: self.file_name = self.config.fileName() self.build_setting_map() print("Read project settings from " + self.file_name) except Exception as exINI: self.config = None self.error_msg = "Reading Error" + ":\n" + str(exINI) def create_ini_file(self, file_name): """ Specify .ini file path after instantiation if setting is not yet instantiated: a new setting is instantiated if the new setting has no key: a new ini file is created Args: file_name: the full path to an ini file Returns: True if successful; False if failed """ if os.path.isfile(self.file_name): return False if self.config is None: try: self.config = QSettings(file_name, QSettings.IniFormat) if len(self.config.allKeys()) == 0: # this is a brand new ini file self.config.setValue("Model/Name", self.model) self.config.sync() print("created ini file: " + file_name) else: print("Read settings from ini file: " + file_name) self.file_name = file_name return True except Exception as exINI: self.config = None self.error_msg = "Reading Error" + ":\n" + str(exINI) return False def get_setting_value(self, group, key): """ Get the value of a ini setting, assuming all settings are grouped Args: group: the string name of a group or section key: the string name of a key Returns: a list of two elements, first being the value, second being the value type """ rval = [None, None] if len(self.groups) == 0: return rval if not group in self.groups: return rval self.config.beginGroup(group) qvar = self.config.value(key) if qvar is None: self.config.endGroup() return rval str_val = str(qvar) if len(str_val) > 0: tval, tval_is_good = ParseData.intTryParse(str_val) if tval_is_good: rval[0] = tval rval[1] = "integer" self.config.endGroup() return rval tval, tval_is_good = ParseData.floatTryParse(str_val) if tval_is_good: rval[0] = tval rval[1] = "float" self.config.endGroup() return rval rval[0] = str_val rval[1] = "string" self.config.endGroup() return rval elif str_val == "": rval[0] = "" rval[1] = "string" else: str_list = qvar.toStringList().join(",") if len(str_list) > 0: rval[0] = str_list.strip(",").split(",") rval[1] = "stringlist" self.config.endGroup() return rval self.config.endGroup() return rval def build_setting_map(self): """ Build a setting group-key-value mapping dictionary as below: self.groups_with_values [group_name] -> [key_name] -> [value, value_type] Also create group dictionary keyed on group name, pointing to its list of keys so we can get setting values on a as needed basis self.groups [group_name] -> [key names] """ self.groups.clear() self.groups_with_values.clear() for group in self.config.childGroups(): self.config.beginGroup(group) self.groups_with_values[group] = {} self.groups[group] = [] for key in self.config.childKeys(): self.groups[group].append(key) val, vtype = self.get_setting_value(group, key) self.groups_with_values[group][key] = val # print group + "::" + key + "::" + self.config.value(key).toString() self.config.endGroup() def read_ini_file_cp(self): self.config.read(self.file_name) for section in self.config.sections(): dict1 = self.config_section_map(section) pass pass def config_section_map(self, section): dict1 = {} for option in self.config.options(section): try: dict1[option] = self.config.get(section, option) if dict1[option] == -1: print ("skip: %s" % option) pass except: print ("exception on %s!" % option) dict1[option] = None return dict1
def _buildUi(self): self._options = {} if os.path.isfile('definitions.ini'): definitions = QSettings('definitions.ini', QSettings.IniFormat) for group in definitions.childGroups(): definitions.beginGroup(group) if group == "options": for key in definitions.childKeys(): self._options[key] = definitions.value(key) definitions.endGroup() main_layout = QVBoxLayout() main_layout.setSpacing(12) self._led = LedWidget(self.tr("Arduino Echo"), QSize(40, 20)) self._led.setRedAsBold(True) self._led.setRedAsRed(True) self._led.switchOn('gray') settings_button = QPushButton() settings_button.setIcon(QIcon("./images/settings.svg")) settings_button.setFlat(True) settings_button.setToolTip(self.tr("Configuration")) settings_button.setIconSize(QSize(16, 16)) settings_button.setFixedSize(QSize(24, 24)) header_layout = QHBoxLayout() header_layout.addWidget(self._led) header_layout.addWidget(settings_button, Qt.AlignRight) main_layout.addLayout(header_layout) self._selectionComboBox = QComboBox() self.loadMessages() main_layout.addWidget(self._selectionComboBox) self._props_messages_input = QPlainTextEdit() self._props_messages_input.setFrameShape(QFrame.NoFrame) self._props_messages_input.setCursorWidth(8) main_layout.addWidget(self._props_messages_input) buttons_layout = QHBoxLayout() main_layout.addLayout(buttons_layout) send_button = QPushButton(self.tr("Send")) buttons_layout.addWidget(send_button) clear_button = QPushButton(self.tr("Clear")) buttons_layout.addWidget(clear_button) self._props_messages_display = QTextEdit() self._props_messages_display.setReadOnly(True) main_layout.addWidget(self._props_messages_display) main_layout.addStretch(0) self.setLayout(main_layout) self._selectionComboBox.activated.connect(self.onSelectionBox) clear_button.pressed.connect(self.onClearButton) send_button.pressed.connect(self.onSendButton) settings_button.pressed.connect(self.settings) self.switchLed.connect(self._led.switchOn)
class PatternsDialog(QDialog): def __init__(self, *args, **kwargs): super(PatternsDialog, self).__init__(*args, **kwargs) self.setMinimumHeight(400) self.setMinimumWidth(400) self.setWindowTitle("Autodiscovery patterns") self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.settings.beginGroup("Patterns") vl = VLayout() cols = ["Pattern"] self.tw = QTableWidget(0, 1) self.tw.setHorizontalHeaderLabels(cols) self.tw.verticalHeader().hide() self.tw.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) for k in self.settings.childKeys(): row = self.tw.rowCount() self.tw.insertRow(row) self.tw.setItem(row, 0, QTableWidgetItem(self.settings.value(k))) vl.addWidgets([ QLabel( "Add your modified FullTopic patterns to enable auto-discovery of such devices\n" "Patterns MUST include %prefix%, %topic% and trailing /\n" "Default Tasmota FullTopics are built-in\n\n" "You have to reconnect to your Broker after topic changes."), self.tw ]) hl_btns = HLayout([0, 3, 0, 3]) btnAdd = QPushButton("Add") btnDel = QPushButton("Delete") btnCancel = QPushButton("Cancel") btnSave = QPushButton("Save") hl_btns.addWidgets([btnAdd, btnDel, btnSave, btnCancel]) hl_btns.insertStretch(2) vl.addLayout(hl_btns) self.setLayout(vl) self.idx = None self.tw.clicked.connect(self.select) btnAdd.clicked.connect(self.add) btnDel.clicked.connect(self.delete) btnSave.clicked.connect(self.accept) btnCancel.clicked.connect(self.reject) def select(self, idx): self.idx = idx def add(self): row = self.tw.rowCount() self.tw.insertRow(row) self.tw.setItem(row, 0, QTableWidgetItem("%prefix%/%topic%/")) def delete(self): if self.idx: self.tw.removeRow(self.idx.row()) def accept(self): for k in self.settings.childKeys(): self.settings.remove(k) for r in range(self.tw.rowCount()): val = self.tw.item(r, 0).text() # check for trailing / if (not val.endswith('/')): val += '/' self.settings.setValue(str(r), val) self.settings.endGroup() self.done(QDialog.Accepted)
class NodeeraMain(QMainWindow, Ui_NodeeraMain): displayWelcomeMsg = pyqtSignal(str) closeWelcomeMsg = pyqtSignal() """ Create Ui_NodeeraMain and display it. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(NodeeraMain, self).__init__(parent) self.setupUi(self) self.parent = parent # object data self.pageDict = {} self.curPage = None self.helper = Helper() # get startup settings self.initSettings() # launch the welcome wagon self.welcomeDlg = HelloUserDlg(self) self.welcomeDlg.show() QApplication.processEvents() self.displayWelcomeMsg.emit("Starting NodeEra...") # setup stuff not covered by generated code icon = QIcon() icon.addPixmap(QPixmap("icons/RUN and DATA TAB/Disconnected.png"), QIcon.Normal, QIcon.Off) self.actionOpen_Connection.setIcon(icon) # main window self.setTitle("No Connection") self.resize(self.winSize) self.move(self.position) # display welcome message self.displayWelcomeMsg.emit("Welcome To NodeEra") # display announcement message from website try: url = 'https://{}'.format(appPage) response = requests.get(url) if response.status_code == 200: start = response.text.find('class="display"') + 16 end = response.text.find('</p>', start) self.displayWelcomeMsg.emit(response.text[start:end]) self.displayWelcomeMsg.emit( "For more information see www.singerlinks.com/nodeera") else: self.logMsg( "Unable to access announcement page:{} http response:{}". format(appPage, response.status_code)) self.displayWelcomeMsg.emit( "Unable to access announcement page:{} http response:{}". format(appPage, response.status_code)) except Exception as e: self.logMsg("Error accessing announcement page - {}".format( repr(e))) self.displayWelcomeMsg.emit( "Error accessing announcement page - {}".format(repr(e))) # auto open last used connection try: lastNeoConName = self.settings.value("NeoCon/LastUsed") if not lastNeoConName is None: neoDict = self.settings.value( "NeoCon/connection/{}".format(lastNeoConName)) self.openConnection(neoConName=lastNeoConName, neoConDict=neoDict) self.displayWelcomeMsg.emit(" ") self.displayWelcomeMsg.emit( "Opened most recent connection: {}".format(lastNeoConName)) self.setTitle(lastNeoConName) except: self.logMsg( "Unable to open last connection: {}".format(lastNeoConName)) self.displayWelcomeMsg.emit( "Unable to open last connection: {}".format(lastNeoConName)) # auto close the welcome dialog box # time.sleep(5) # self.closeWelcomeMsg.emit() def logMsg(self, msg): if logging: logging.info(msg) def initSettings(self, ): ''' get the system settings needed to start NodeEra. If a system setting doesn't exist then create it with default value - this happens on initial startup ''' self.settings = QSettings() try: self.expirationDate = self.helper.getText( self.settings.value("License/expirationDate")) if self.expirationDate is None: self.expirationDate = 'No expiration date set' self.settings.setValue( "License/expirationDate", self.helper.putText(self.expirationDate)) except: self.expirationDate = 'No expiration date set' self.settings.setValue("License/expirationDate", self.helper.putText(self.expirationDate)) try: self.currentVersion = self.settings.value("License/currentVersion") if self.currentVersion is None: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) elif self.currentVersion != currentVersion: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) except: self.currentVersion = currentVersion self.settings.setValue("License/currentVersion", self.currentVersion) try: self.winSize = self.settings.value("MainWindow/Size") if self.winSize is None: self.winSize = QSize(800, 500) except: self.winSize = QSize(800, 500) try: self.position = self.settings.value("MainWindow/Position") if self.position is None: self.position = QPoint(0, 0) except: self.position = QPoint(0, 0) try: self.recentList = self.settings.value("Default/RecentList") if self.recentList is None: self.recentList = [] self.settings.setValue("Default/RecentList", self.recentList) except: self.recentList = [] self.settings.setValue("Default/RecentList", self.recentList) try: self.defaultLoggingPath = self.settings.value( "Default/LoggingPath") if self.defaultLoggingPath is None: self.logDir = os.getcwd() self.logDir = os.path.realpath(os.path.abspath(self.logDir)) self.settings.setValue("Default/LoggingPath", self.logDir) except: self.logDir = os.getcwd() self.logDir = os.path.realpath(os.path.abspath(self.logDir)) self.settings.setValue("Default/LoggingPath", self.logDir) try: self.defaultProjPath = self.settings.value("Default/ProjPath") if self.defaultProjPath is None: self.defaultProjPath = os.getcwd() self.defaultProjPath = os.path.realpath( os.path.abspath(self.defaultProjPath)) self.settings.setValue("Default/ProjectPath", self.defaultProjPath) except: self.defaultProjPath = os.getcwd() self.defaultProjPath = os.path.realpath( os.path.abspath(self.defaultProjPath)) self.settings.setValue("Default/ProjectPath", self.defaultProjPath) # default custom formats for diagram objects # Display Format - Instance Node try: test = self.settings.value("Default/Format/InstanceNode") if test is None: self.settings.setValue("Default/Format/InstanceNode", INodeFormat().formatDict) except: self.settings.setValue("Default/Format/InstanceNode", INodeFormat().formatDict) # Display Format - Instance Relationship try: test = self.settings.value("Default/Format/InstanceRelation") if test is None: self.settings.setValue("Default/Format/InstanceRelation", IRelFormat().formatDict) except: self.settings.setValue("Default/Format/InstanceRelation", IRelFormat().formatDict) # Display Format - Template Node try: test = self.settings.value("Default/Format/TemplateNode") if test is None: self.settings.setValue("Default/Format/TemplateNode", TNodeFormat().formatDict) except: self.settings.setValue("Default/Format/TemplateNode", TNodeFormat().formatDict) # Display Format - Template Relationship try: test = self.settings.value("Default/Format/TemplateRelation") if test is None: self.settings.setValue("Default/Format/TemplateRelation", TRelFormat().formatDict) except: self.settings.setValue("Default/Format/TemplateRelation", TRelFormat().formatDict) # page setup try: test = self.settings.value("Default/PageSetup") if test is None: self.settings.setValue("Default/PageSetup", PageSetup().objectDict) except: self.settings.setValue("Default/PageSetup", PageSetup().objectDict) # default project neocon try: defaultNeoConName = self.settings.value("NeoCon/Default") if defaultNeoConName is None: self.settings.setValue("NeoCon/Default", "LOCAL") except: self.settings.setValue("NeoCon/Default", "LOCAL") # LOCAL neocon definition try: self.localNeoCon = self.settings.value("NeoCon/connection/LOCAL") if self.localNeoCon is None: self.settings.setValue("NeoCon/connection/LOCAL", NeoDriver().localNeoConDict()) except: self.settings.setValue("NeoCon/connection/LOCAL", NeoDriver().localNeoConDict()) # default lexer font size try: defaultLexerFontSize = self.settings.value("Lexer/FontSize") if defaultLexerFontSize is None: self.settings.setValue("Lexer/FontSize", "10") except: self.settings.setValue("Lexer/FontSize", "10") # validate all neocons have the prompt dictionary key which was added in 1.04 self.settings.beginGroup("NeoCon/connection") neoKeys = self.settings.childKeys() for key in neoKeys: neoDict = self.settings.value(key) promptVal = neoDict.get("prompt", None) if promptVal is None: neoDict["prompt"] = "False" self.settings.setValue(key, neoDict) self.settings.endGroup() def setTitle(self, filename): self.setWindowTitle("{} - {}".format(productName, filename)) def setMenuAccess(self, ): ''' determine what connection tab is currently selected and enable/disable menu items as needed ''' # turn on all Settings menu items for action in self.menuSettings.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(True) try: # get tab widget (schema or project) that is currently active - if there isn't one this will cause an exception currentTab = self.stackedPageItems.currentWidget( ).tabPage.currentWidget() # enable all the schema actions for action in self.menuNeo.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(True) for action in self.menuProject.actions(): # enable project actions if type(action) == QAction and not action.isSeparator(): if currentTab.pageType == "PROJECT": action.setEnabled(True) else: if action.text() in [ "New", "Open...", "Recent Projects" ]: action.setEnabled(True) else: action.setEnabled(False) except: # no connection tabs are open # disable all project menu actions for action in self.menuProject.actions(): if type(action) == QAction and not action.isSeparator(): action.setEnabled(False) for action in self.menuNeo.actions(): if type(action) == QAction and not action.isSeparator(): if action.text() in [ "Close Connection", "Generate Schema..." ]: action.setEnabled(False) else: action.setEnabled(True) ######################################################################## # NEO CONNECTION Dropdown Menu Actions ######################################################################## @pyqtSlot() def on_actionOpen_Connection_triggered(self): """ This slot provides functionality for the open connection button """ d = dlgNeoCons(parent=self) if d.exec_(): if d.selectedNeoConName: # make sure it isn't already opened if d.selectedNeoConName not in self.pageDict: self.openConnection(neoConName=d.selectedNeoConName, neoConDict=d.selectedNeoConDict) else: self.helper.displayErrMsg( "NodeEra - Open Connection", "Connection {} is already open.".format( d.selectedNeoConName)) # switch to the page they tried to open self.pageDict[d.selectedNeoConName].actionButton.trigger() def openConnection(self, neoConName=None, neoConDict=None): ''' User selects a connection to open so create the schema page and display it ''' # set the last used neocon in system settings self.settings.setValue("NeoCon/LastUsed", neoConName) # create a new toolbar button and add it to the toolbar newConAction = QAction(self) newConAction.setObjectName("newConnection") newConAction.setText("Neo4j - {}".format(neoConName)) newConAction.setData(neoConName) newConAction.setToolTip(neoConDict["URL"]) newConAction.setCheckable(True) newConAction.setChecked(True) newConAction.triggered.connect(self.connectionClicked) self.tbConnection.addAction(newConAction) # add a tabPage widget to the stacked widget newPageWidget = PageWidget(parent=self) newPageWidget.pageType = "Schema" widgetIndex = self.stackedPageItems.addWidget(newPageWidget) # save new pageItem newPageItem = PageItem(neoConName=neoConName, actionButton=newConAction, pageWidget=newPageWidget, pageWidgetIndex=widgetIndex) self.pageDict[neoConName] = newPageItem # add the schema tab cypherTab = CypherPageWidget(parent=self, pageItem=newPageItem) newPageWidget.tabPage.addTab(cypherTab, "Schema - {}".format(neoConName)) # click the new action to force selection logic newConAction.trigger() self.logMsg("Open Connection: {}".format(neoConName)) @pyqtSlot() def connectionClicked(self): ''' User clicks on a connection in the menu bar so switch to that stacked widget ''' self.logMsg("connection clicked {}".format(self.sender().text())) # uncheck all the page action buttons for pageName in self.pageDict: self.pageDict[pageName].actionButton.setChecked(False) # check the one just clicked self.sender().setChecked(True) # save the current page name self.curPage = self.sender().data() # switch the stacked page widget to the one just clicked self.stackedPageItems.setCurrentIndex( self.pageDict[self.curPage].pageWidgetIndex) # update the main window title self.setTitle(self.curPage) # adjust the menu items self.setMenuAccess() @pyqtSlot() def on_actionClose_Connection_triggered(self): """ Close the active connection and remove the page from the UI """ if self.curPage: if self.curPage in self.pageDict: # must find the schema tab and tell it to close, it will tell all other tabs to close. schema tab is always the first one (index=0) self.pageDict[self.curPage].pageWidget.closeSchemaTab() self.removeConnection() def removeConnection(self, ): '''if the tab page widget is responding to the close request it only needs this logic to remove the connection ''' curPage = self.pageDict.get(self.curPage, None) if not curPage is None: # remove the pageWidget from the stacked widget self.stackedPageItems.removeWidget( self.pageDict[self.curPage].pageWidget) del self.pageDict[self.curPage].pageWidget # remove the action from menu's and toolbars self.tbConnection.removeAction( self.pageDict[self.curPage].actionButton) # take the page out of the dictionary del self.pageDict[self.curPage] # if any pages left select the first one if len(self.pageDict) > 0: for pageName in self.pageDict: self.pageDict[pageName].actionButton.trigger() break else: # there are no open connections and the home page is closed self.setTitle("No Connection") @pyqtSlot() def on_actionNeo4j_Connection_Manager_triggered(self): """ Display the Connection Manager """ d = dlgNeoCons(parent=self) if d.exec_(): pass @pyqtSlot() def on_actionExit_triggered(self): """ User selected the File / Exit menu item. Tell all the open connections to close """ self.closeOpenStuff() # close the app self.close() def closeEvent(self, event): # close open connections self.closeOpenStuff() #save the window state self.settings.setValue("MainWindow/Size", self.size()) self.settings.setValue("MainWindow/Position", self.pos()) event.accept() def closeOpenStuff(self): # get a list of all the keys in the pageDict dictionary keys = list(self.pageDict.keys()) # iterate thru the list of dictionary keys. this is required as the dictionary will be changing size, i.e. you can't simply iterate thru the dictionary for key in keys: pageItem = self.pageDict[key] actionButton = pageItem.actionButton actionButton.trigger() # must find the schema tab and tell it to close, it will tell all other tabs to close. schema tab is always the first one (index=0) pageItem.pageWidget.closeSchemaTab() self.removeConnection() ##################################################################### # SETTINGS DROPDOWNS ##################################################################### @pyqtSlot() def on_actionSystem_Preferences_triggered(self): """ User selects the System Preferences menu item. Display the system preferences dialog box. """ self.editSystemPreferences() def editSystemPreferences(self, ): """ User selects the System Preferences menu item. Display the system preferences dialog box. """ if not (self.settings is None): d = SystemPreferenceBox(self, settings=self.settings) if d.exec_(): self.settings.sync() ##################################################################### # PROJECT METHODS ##################################################################### @pyqtSlot() def on_actionNewProject_triggered(self): """ Open new project """ self.loadProject(fileName=None) @pyqtSlot() def on_actionOpenProject_triggered(self): """ Open an existing project file """ dlg = QFileDialog() dlg.setFileMode(QFileDialog.ExistingFile) dlg.setAcceptMode(QFileDialog.AcceptOpen) dlg.setNameFilters(["NodeEra Project (*.mdl)", "all files (*.*)"]) dlg.setDirectory(self.settings.value("Default/ProjPath")) if dlg.exec_(): fileNames = dlg.selectedFiles() if fileNames: fileName = fileNames[0] self.loadProject(fileName=fileName) @pyqtSlot() def on_actionSaveProject_triggered(self): """ Save the open project """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": newName = curPageWidget.tabPage.currentWidget().saveProject() # if this was an unsaved project then it was really a save as so the tab name has to be updated if newName is not None: curPageWidget.tabPage.setTabText( curPageWidget.tabPage.currentIndex(), "Project: {} - {}".format( newName, self.pageDict[self.curPage].neoConName)) @pyqtSlot() def on_actionSaveProjectAs_triggered(self): """ Save Project As """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": newName = curPageWidget.tabPage.currentWidget().saveProjectAs() if newName is not None: curPageWidget.tabPage.setTabText( curPageWidget.tabPage.currentIndex(), "Project: {} - {}".format( newName, self.pageDict[self.curPage].neoConName)) @pyqtSlot() def on_actionReverse_Engineer_triggered(self): """ User selects the Reverse Engineer menu item. Display the Reverse Engineer dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().reverseEngineerGraph() else: self.helper.displayErrMsg( "Reverse Engineer", "{} not a project".format( curPageWidget.tabPage.currentWidget().pageType)) @pyqtSlot() def on_actionProjectProperties_triggered(self): """ User selects the project properties menu item. Display the project properties dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().editProjectProperties() else: self.helper.displayErrMsg( "Project Properties", "{} not a project".format( curPageWidget.tabPage.currentWidget().pageType)) def getSchemaObject(self, ): curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "CYPHER": return tab.schemaModel return None def getSchemaTab(self, ): curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "CYPHER": return tab return None def loadProject(self, fileName=None): ''' Load the project file with name fileName. If fileName is None, create a new empty project. ''' # create a temporary file name if its a new project if fileName is None: # update unnamed file counter global unNamedFileCounter unNamedFileCounter = unNamedFileCounter + 1 shortName = "{}".format( "New Project-0{}".format(unNamedFileCounter)) else: head, shortName = ntpath.split(QFileInfo(fileName).fileName()) # make sure the project isn't already loaded curPageWidget = self.stackedPageItems.currentWidget() for ctr in range(0, curPageWidget.tabPage.count()): tab = curPageWidget.tabPage.widget(ctr) if tab.pageType == "PROJECT": if (not fileName is None) and (tab.fileName == fileName): self.helper.displayErrMsg( "Open Project", "The project file: {} is already open. It can only be open once." .format(fileName)) return # create the project widget projectTab = ProjectPageWidget(parent=self, settings=self.settings, pageItem=self.pageDict[self.curPage], fileName=fileName) curPageWidget = self.stackedPageItems.currentWidget() # add the project widget as a tab on the current page widget x = curPageWidget.tabPage.addTab( projectTab, "Project: {} - {}".format(shortName, self.pageDict[self.curPage].neoConName)) curPageWidget.tabPage.setCurrentIndex(x) @pyqtSlot() def on_actionOnline_Help_triggered(self): """ User selects the Online Help menu item. Dislay Online Help menu. """ d = OnlineHelpDLG(self) if d.exec_(): pass @pyqtSlot() def on_actionAbout_triggered(self): """ User selects Help / About menu item. Display the about dialog box """ d = HelpAboutDLG(self) if d.exec_(): pass @pyqtSlot() def on_actionGenerate_Schema_triggered(self): """ User requests the generate schema dialog box from main menu """ d = GenerateSchemaDlg(self) if d.exec_(): pass @pyqtSlot() def on_actionForward_Engineer_triggered(self): """ User requests to perform forward engineering from the open project from main menu """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().forwardEngineerGraph() else: self.logMsg( "User requested to forward engineer but current tab is not a Project" ) @pyqtSlot() def on_actionGenerate_Reports_triggered(self): """ User selects the Generate Project Reports menu item. Display the Generate Reports dialog box. """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "PROJECT": curPageWidget.tabPage.currentWidget().generateProjectReports() @pyqtSlot() def on_actionReset_User_Password_triggered(self): """ User requests the change user password from main menu """ curPageWidget = self.stackedPageItems.currentWidget() if not curPageWidget is None: if curPageWidget.tabPage.currentWidget().pageType == "CYPHER": curPageWidget.tabPage.currentWidget().resetPassword() @pyqtSlot(QAction) def on_menuRecent_Projects_triggered(self, action): """ User clicked on a recent file menu item @param action DESCRIPTION @type QAction """ self.loadProject(fileName=action.data()) @pyqtSlot() def on_menuRecent_Projects_aboutToShow(self): """ user hovering on recent projects menu item """ recentList = self.settings.value("Default/RecentList") if len(recentList) == 0: return else: # remove any existing actions self.menuRecent_Projects.clear() for projectFile in recentList: # create actions for the recent files aSubAction = self.menuRecent_Projects.addAction(projectFile) aSubAction.setData(projectFile)