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()
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
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 get_record_list(friend_id): record = [] if not os.path.exists(GlobalVariable.RecordDataDirectory + friend_id + ".ini"): return None mem_file = QSettings( GlobalVariable.RecordDataDirectory + friend_id + ".ini", QSettings.IniFormat) child = mem_file.childGroups() for msg_id in child: mem_file.beginGroup(msg_id) temp = Record.get_record_object("text") temp.Time = mem_file.value("Time") temp.MsgId = msg_id if mem_file.value("ISend") == "1": temp.Sender = get_local_id() else: temp.Sender = mem_file.value("Sender") temp.Content = mem_file.value("Content") record.append(temp) mem_file.endGroup() del mem_file cmpfun = operator.attrgetter('Time') record.sort(key=cmpfun) return record
def getServers(self): """ Gets all servers from QSettings """ settings = QSettings() settings.beginGroup('PostgreSQL/connections/') currentConnections = settings.childGroups() settings.endGroup() return currentConnections
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 addDisplay(self, title=None): if not title: if self._countUntitledDisplay: title = "{0} ({1})".format(self.tr("New observation"), self._countUntitledDisplay) else: title = self.tr("New observation") self._countUntitledDisplay = self._countUntitledDisplay + 1 reg = QSettings() reg.beginGroup(self._session) if title not in reg.childGroups(): reg.beginGroup(title) reg.setValue("display", int(TopicDisplay.EMPHASIZEDCORRESPONDENT)) reg.endGroup() reg.endGroup() reg.sync() root_observation = not len(self._displays) new_display = ObserverDisplayWidget(title, self._logger, self._session, self._mqttServerHost, self._mqttServerPort, self._mqttRootTopic, remove=not root_observation, filter=not root_observation) new_display.addObservation.connect(self.addDisplay) new_display.delObservation.connect(self.removeCurrentDisplay) self.newFilterSettingsApplied.connect(new_display.applyFilterSettings) self._displays.append(new_display) self._tabWidget.addTab(new_display, title) self.resetDisplays.connect(new_display.reset) self.messageReceived.connect(new_display.processMessage) new_display.newFilterSettings.connect(self.applyFilterSettings) new_display.resetWills.connect(self.applyResetWills) self._tabWidget.setCurrentWidget(new_display) self._logger.info("{0} : {1}".format(self.tr("Added observation"), title)) dspmsg = '' for d in self._displays: if dspmsg: dspmsg += ' | ' else: dspmsg = 'Displays : ' dspmsg += d.title() self._logger.debug(dspmsg)
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 load_from_qsetting(self, setting_class: str) -> Dict[str, PrintSetting]: settings = QSettings() settings.beginGroup(setting_class) ls_setting_dict = OrderedDict() for setting_name in settings.childGroups(): settings.beginGroup(setting_name) q_path = '%s/%s' % (setting_class, setting_name) process_type = settings.value('process', type=str) setting_dict = {} for param in settings.childGroups(): settings.beginGroup(param) param_type = settings.value('type', type=str) entry_obj = MAP_TYPE[param_type] entry_inst = entry_obj(param, parent_key=setting_name) if setting_class == SettingTypeKey.printer.value: entry_inst.update_val.connect(self.printer_update_cb) elif setting_class == SettingTypeKey.printhead.value: entry_inst.update_val.connect(self.printhead_update_cb) elif setting_class == SettingTypeKey.print_param.value: entry_inst.update_val.connect(self.print_param_update_cb) else: raise ValueError('unknown setting class {%s}' % (setting_class)) entry_inst.load_entry(q_path) setting_dict[param] = entry_inst settings.endGroup() settings.endGroup() ls_setting_dict[setting_name] = SETTING_TYPE_MAP[setting_class][ process_type](**setting_dict) self.add_setting_signal.emit(setting_class, setting_name) settings.endGroup() return ls_setting_dict
def findkey_from_interface(interface_name): """ 通过网络接口名字查找注册表位置 :param interface_name:string 网络接口 :return:QSettings 注册表对象 """ logging.debug("查找接口名为{0}的注册表对象".format(interface_name)) #key_str="HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}" key_str = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}" key = QSettings(key_str, QSettings.NativeFormat) dev_reg = None for x in key.childGroups(): key_str2 = key_str + "\\" + x key2 = QSettings(key_str2, QSettings.NativeFormat) if key2.value("DriverDesc", "-1") == interface_name: dev_reg = key2 logging.debug("成功查找到{0}的注册表对象\n地址为:{1}".format( interface_name, dev_reg.fileName())) return dev_reg
def onBrowseConnection(self): s = QSettings() base = "/PostgreSQL/connections" s.beginGroup("/PostgreSQL/connections") children = s.childGroups() connections = {} map = { "dbname": "database", "host": "host", "port": "port", "service": "service", "password": "******", "user": "******", "sslmode": "sslmode" } for g in children: s.beginGroup(g) cstring = "" for k, v in map.items(): # Strings attributes. if s.value(v) and k != "sslmode": cstring += k + "=" + s.value(v) + " " # Enum attributes (Ssl mode). elif s.value(v) and k == "sslmode": mode = self.sslModeToString(s.value(v)) if mode != "": cstring += k + "=" + mode + " " connections[g] = cstring s.endGroup() menu = QMenu(self) for k in sorted(connections.keys()): menu.addAction(k) def onMenu(action): self.dbConnectionText.setText(connections[action.text()]) self.reloadBtn.click() menu.triggered.connect(onMenu) menu.exec_(self.dbConnectionBtn.mapToGlobal(QPoint(0, 0)))
def initDatabaseConenctionsList(self): QgsMessageLog.logMessage('Init Connections List') self.dockwidget.connectionsComboBox.clear() s = QSettings() s.beginGroup("PostgreSQL/connections") keys = s.childGroups() if (len(keys) == 0): QgsMessageLog.logMessage("No database connections found", self.pluginName) return () for key in keys: self.dockwidget.connectionsComboBox.addItem(str(key)) QgsMessageLog.logMessage(key, self.pluginName) s.endGroup() # Use the first connection to connect to the database self.onConnectDatabase(keys[0])
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)
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()
class Config(QObject): "Configuration provider for the whole program, wapper for QSettings" row_height_changed = pyqtSignal(int) def __init__(self, log=None): super().__init__() if log: self.log = log.getChild('Conf') self.log.setLevel(30) else: self.log = logging.getLogger() self.log.setLevel(99) self.log.debug('Initializing') self.qsettings = QSettings() self.qsettings.setIniCodec('UTF-8') self.options = None self.option_spec = self.load_option_spec() self.options = self.load_options() self.full_name = "{} {}".format(QCoreApplication.applicationName(), QCoreApplication.applicationVersion()) # options that need fast access are also definded as attributes, which # are updated by calling update_attributes() # (on paper it's 4 times faster, but i don't think it matters in my case) self.logger_table_font = None self.logger_table_font_size = None self.loop_event_delay = None self.benchmark_interval = None self.update_attributes() def __getitem__(self, name): # self.log.debug('Getting "{}"'.format(name)) value = self.options.get(name, None) if value is None: raise Exception('No option with name "{}"'.format(name)) # self.log.debug('Returning "{}"'.format(value)) return value def __setitem__(self, name, value): # self.log.debug('Setting "{}"'.format(name)) if name not in self.options: raise Exception('No option with name "{}"'.format(name)) self.options[name] = value def set_option(self, name, value): self[name] = value @staticmethod def get_resource_path(name, directory='ui'): data_dir = resource_filename('cutelog', directory) path = os.path.join(data_dir, name) if not os.path.exists(path): raise FileNotFoundError( 'Resource file not found in this path: "{}"'.format(path)) return path def get_ui_qfile(self, name): file = QFile(':/ui/{}'.format(name)) if not file.exists(): raise FileNotFoundError( 'ui file not found: ":/ui/{}"'.format(name)) file.open(QFile.ReadOnly) return file @property def listen_address(self): host = self.options.get('listen_host', None) port = self.options.get('listen_port', None) if host is None or port is None: raise Exception( 'Listen host or port not in options: "{}:{}"'.format( host, port)) return (host, port) def load_option_spec(self): option_spec = [] for spec in OPTION_SPEC: option = Option(*spec) option_spec.append(option) return option_spec def load_options(self): self.log.debug('Loading options') options = {} self.qsettings.beginGroup('Configuration') for option in self.option_spec: value = self.qsettings.value(option.name, option.default) if option.type == bool: value = str(value).lower( ) # needed because QSettings stores bools as strings value = True if value == "true" or value is True else False else: value = option.type(value) options[option.name] = value self.qsettings.endGroup() return options def update_options(self, new_options, save=True): self.options.update(new_options) if save: self.save_options() self.update_attributes(new_options) def update_attributes(self, options=None): "Updates fast attributes and everything else outside of self.options" if options: # here will be things that only need to be updated when they actually changed new_row_height = options.get('logger_row_height', self.options['logger_row_height']) if new_row_height != self.options['logger_row_height']: self.row_height_changed.emit(new_row_height) else: options = self.options self.loop_event_delay = options.get('loop_event_delay', self.loop_event_delay) self.benchmark_interval = options.get('benchmark_interval', self.benchmark_interval) self.logger_table_font = options.get('logger_table_font', self.logger_table_font) self.logger_table_font_size = options.get('logger_table_font_size', self.logger_table_font_size) self.set_logging_level( options.get('console_logging_level', ROOT_LOG.level)) def save_options(self): self.log.debug('Saving options') self.qsettings.beginGroup('Configuration') for option in self.option_spec: self.qsettings.setValue(option.name, self.options[option.name]) self.qsettings.endGroup() self.sync() def sync(self): self.log.debug('Syncing QSettings') self.qsettings.sync() def set_settings_value(self, name, value): self.qsettings.beginGroup('Configuration') self.qsettings.setValue(name, value) self.qsettings.endGroup() def set_logging_level(self, level): global ROOT_LOG ROOT_LOG.setLevel(level) self.log.setLevel(level) # def save_levels_preset(self, levels, preset_name): # pass def get_header_presets(self): self.qsettings.beginGroup('Header_Presets') result = self.qsettings.childGroups() self.qsettings.endGroup() return result def save_header_preset(self, name, columns): self.log.debug('Saving header preset "{}"'.format(name)) s = self.qsettings s.beginGroup('Header_Presets') s.beginWriteArray(name, len(columns)) for i, col in enumerate(columns): s.setArrayIndex(i) s.setValue('column', col.dump_to_string()) s.endArray() s.endGroup() def load_header_preset(self, name): from .logger_table_header import Column self.log.debug('Loading header preset "{}"'.format(name)) s = self.qsettings result = [] if name not in self.get_header_presets(): return None s.beginGroup('Header_Presets') size = s.beginReadArray(name) for i in range(size): s.setArrayIndex(i) new_column = Column(load=s.value('column')) result.append(new_column) s.endArray() s.endGroup() return result def save_geometry(self, geometry): s = self.qsettings s.beginGroup('Geometry') s.setValue('Main_Window_Geometry', geometry) s.endGroup() self.sync() def load_geometry(self): s = self.qsettings s.beginGroup('Geometry') geometry = s.value('Main_Window_Geometry') s.endGroup() return geometry
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 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
class rheasDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" super(rheasDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.schema.currentIndexChanged.connect(self.refreshTables) self.database.currentIndexChanged.connect(self.refreshSchemas) self.render.clicked.connect(self.loadRaster) self.addDatabases() self.addSchemas() self.addTables() def addDatabases(self): """Add available database connections.""" self.settings = QSettings() self.settings.beginGroup('/PostgreSQL/connections/') connections = self.settings.childGroups() self.settings.endGroup() for conn in connections: self.database.addItem(conn) def getConnectionParameters(self): """Get database connection information.""" conn = self.database.itemText(self.database.currentIndex()) self.settings.beginGroup('PostgreSQL/connections') dbname = self.settings.value("{0}/database".format(conn)) host = self.settings.value("{0}/host".format(conn)) port = self.settings.value("{0}/port".format(conn)) username = self.settings.value("{0}/username".format(conn)) password = self.settings.value("{0}/password".format(conn)) self.settings.endGroup() return dbname, host, port, username, password def addSchemas(self): """Add available schemas from database.""" dbname, host, port, username, password = self.getConnectionParameters() db = pg.connect(dbname=dbname, host=host, port=port, user=username, password=password) cur = db.cursor() sql = "select schema_name from information_schema.schemata" cur.execute(sql) results = cur.fetchall() for r in results: name = r[0] if not name.startswith("pg_"): self.schema.addItem(name) cur.close() db.close() @pyqtSlot() def refreshSchemas(self): """Refresh list of schemas when database changes.""" self.schema.clear() self.addSchemas() @pyqtSlot() def refreshTables(self): """Refresh list of tables when selected schema changes.""" self.table.clear() self.addTables() @pyqtSlot() def loadRaster(self): """Load rasters when button is clicked.""" dbname, host, port, username, password = self.getConnectionParameters() schema = self.schema.itemText(self.schema.currentIndex()) table = self.table.itemText(self.table.currentIndex()) startdate = self.startdate.date() enddate = self.enddate.date() dates = [startdate] while dates[-1] < enddate: dates.append(dates[-1].addDays(1)) for dt in dates: connString = "PG: dbname={3} host={4} user={5} password={6} port={7} mode=2 schema={0} column=rast table={1} where='fdate=date\\'{2}\\''".format(schema, table, dt.toString("yyyy-M-d"), dbname, host, username, password, port) layer = QgsRasterLayer(connString, "{0}".format(dt.toString("yyyy-M-d"))) if layer.isValid(): layer.setContrastEnhancement(QgsContrastEnhancement.StretchToMinimumMaximum) root = QgsProject.instance().layerTreeRoot() group = root.findGroup(schema) if group is None: group = root.addGroup(schema) QgsProject.instance().addMapLayer(layer, False) group.insertChildNode(0, QgsLayerTreeLayer(layer)) def addTables(self): """Add available tables contained in selected schema.""" dbname, host, port, username, password = self.getConnectionParameters() db = pg.connect(dbname=dbname, host=host, port=port, user=username, password=password) cur = db.cursor() idx = self.schema.currentIndex() schema = self.schema.itemText(idx) sql = "select table_name from information_schema.tables where table_schema='{0}'".format(schema) cur.execute(sql) results = cur.fetchall() for r in results: name = r[0] self.table.addItem(name) cur.close() db.close()
class TasmotaDevicesModel(QAbstractTableModel): def __init__(self, *args, **kwargs): super(TasmotaDevicesModel, self).__init__(*args, **kwargs) self.settings = QSettings() self.settings.beginGroup("Devices") self._devices = [] for d in self.settings.childGroups(): self.loadDevice(d, self.settings.value("{}/full_topic".format(d)), self.settings.value("{}/friendly_name".format(d))) self.settings.endGroup() def addDevice(self, topic, full_topic, lwt="undefined"): rc = self.rowCount() self.beginInsertRows(QModelIndex(), rc, rc) self._devices.append([lwt, topic, full_topic, topic] + ([''] * (len(columns) - 4))) self.settings.beginGroup("Devices") self.settings.setValue("{}/full_topic".format(topic), full_topic) self.settings.setValue("{}/friendly_name".format(topic), full_topic) self.settings.endGroup() self.endInsertRows() return self.index(rc, 0) def loadDevice(self, topic, full_topic, friendly_name="", lwt="undefined"): rc = self.rowCount() self.beginInsertRows(QModelIndex(), rc, rc) self._devices.append([ lwt, topic, full_topic, friendly_name if friendly_name else topic ] + ([''] * (len(columns) - 4))) self.endInsertRows() return True def findDevice(self, topic): split_topic = topic.split('/') possible_topic = split_topic[1] if possible_topic in ('tele', 'stat'): possible_topic = split_topic[0] for i, d in enumerate(self._devices): full_topic = d[DevMdl.FULL_TOPIC] + "(?P<reply>.*)" full_topic = full_topic.replace("%topic%", "(?P<topic>.*?)") full_topic = full_topic.replace("%prefix%", "(?P<prefix>.*?)") match = re.fullmatch(full_topic, topic) if match: found = match.groupdict() if found['topic'] == d[DevMdl.TOPIC]: found.update({'index': self.index(i, DevMdl.LWT)}) return found_obj(found) return found_obj({ 'index': QModelIndex(), 'topic': possible_topic, 'reply': split_topic[-1] }) def columnCount(self, parent=None): return len(columns) def rowCount(self, parent=None): return len(self._devices) def insertRows(self, pos, rows, parent=QModelIndex()): self.beginInsertRows(parent, pos, pos + rows - 1) for i in range(rows): self._devices.append(['undefined'] + ([''] * (len(columns) - 1))) self.endInsertRows() return True def removeRows(self, pos, rows, parent=QModelIndex()): if pos + rows <= self.rowCount(): self.beginRemoveRows(parent, pos, pos + rows - 1) for r in range(rows): d = self._devices[pos][DevMdl.TOPIC] self.settings.beginGroup("Devices") if d in self.settings.childGroups(): self.settings.remove(d) self.settings.endGroup() self._devices.pop(pos + r) self.endRemoveRows() return True return False def headerData(self, col, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: if col <= len(columns): return columns[col][0] else: return '' def data(self, idx, role=Qt.DisplayRole): if idx.isValid(): row = idx.row() col = idx.column() if role in (Qt.DisplayRole, Qt.EditRole): val = self._devices[row][col] if val and col == DevMdl.UPTIME: if val.startswith("0T"): val = val.replace('0T', '') return val.replace('T', 'd ') elif val and col == DevMdl.MODULE: return modules.get(val, 'Unknown') elif val and col == DevMdl.FIRMWARE: return val.replace('(', ' (') elif col == DevMdl.LOADAVG: if val: return val return "n/a" if self._devices[row][ DevMdl.LWT] == 'online' else '' elif col == DevMdl.BSSID: alias = self.settings.value("BSSID/{}".format(val)) if alias: return alias return self._devices[row][col] elif role == Qt.TextAlignmentRole: if col in (DevMdl.RSSI, DevMdl.MAC, DevMdl.IP, DevMdl.SSID, DevMdl.BSSID, DevMdl.CHANNEL, DevMdl.POWER, DevMdl.LOADAVG, DevMdl.CORE, DevMdl.TELEPERIOD): return Qt.AlignCenter elif col == DevMdl.UPTIME: return Qt.AlignRight | Qt.AlignVCenter elif col == DevMdl.RESTART_REASON: return Qt.AlignLeft | Qt.AlignVCenter | Qt.TextWordWrap elif role == Qt.BackgroundColorRole and col == DevMdl.RSSI: rssi = self._devices[row][DevMdl.RSSI] if rssi: rssi = int(rssi) if rssi < 50: return QColor("#ef4522") elif rssi > 75: return QColor("#7eca27") else: return QColor("#fcdd0f") elif role == Qt.ToolTipRole: if col == DevMdl.FIRMWARE: return self._devices[row][DevMdl.FIRMWARE] elif col == DevMdl.FRIENDLY_NAME: return "Topic: {}\nFull topic: {}".format( self._devices[row][DevMdl.TOPIC], self._devices[row][DevMdl.FULL_TOPIC]) def setData(self, idx, val, role=Qt.EditRole): row = idx.row() col = idx.column() if role == Qt.EditRole: dev = self._devices[row][DevMdl.TOPIC] old_val = self._devices[row][col] if val != old_val: self.settings.beginGroup("Devices") if col == DevMdl.FRIENDLY_NAME: self.settings.setValue("{}/friendly_name".format(dev), val) elif col == DevMdl.FULL_TOPIC: self.settings.setValue("{}/full_topic".format(dev), val) self.settings.endGroup() self._devices[row][col] = val self.dataChanged.emit(idx, idx) self.settings.sync() return True return False def flags(self, idx): return Qt.ItemIsSelectable | Qt.ItemIsEnabled def updateValue(self, idx, column, val): if idx.isValid(): row = idx.row() idx = self.index(row, column) self.setData(idx, val) def topic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.TOPIC] return None def friendly_name(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FRIENDLY_NAME] return None def commandTopic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FULL_TOPIC].replace( "%prefix%", "cmnd").replace("%topic%", self._devices[row][DevMdl.TOPIC]) return None def statTopic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FULL_TOPIC].replace( "%prefix%", "stat").replace("%topic%", self._devices[row][DevMdl.TOPIC]) return None def teleTopic(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.FULL_TOPIC].replace( "%prefix%", "tele").replace("%topic%", self._devices[row][DevMdl.TOPIC]) return None def isDefaultTemplate(self, idx): if idx.isValid(): return self._devices[idx.row()][DevMdl.FULL_TOPIC] in [ "%prefix%/%topic%/", "%topic%/%prefix%/" ] def bssid(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.BSSID] return None def power(self, idx): if idx.isValid(): row = idx.row() return self._devices[row][DevMdl.POWER] return None def refreshBSSID(self): first = self.index(0, DevMdl.BSSID) last = self.index(self.rowCount(), DevMdl.BSSID) self.dataChanged.emit(first, last)
def __init__(self, logger, session): super(ObserverSettingsDialog, self).__init__() self._logger = logger self._session = session self._count = QLabel() self._countLeftEstimated = QLabel() self._metersLeftEstimated = QLabel() self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setWindowTitle(self.tr("Observer settings")) self.setWindowIcon(QIcon(':/settings.svg')) self._mqttServerInput = QLineEdit() self._mqttPortInput = QLineEdit() self._mqttTopicInput = QLineEdit() self._sessionNameInput = QLineEdit() self._retrieveComboBox = QComboBox() self._deleteComboBox = QComboBox() self.buildUi() reg = QSettings() self._sessionNameInput.setText(self._session) try: port = reg.value("port", 1883, type=int) except: port = 1883 reg.beginGroup(self._session) self._mqttServerInput.setText(reg.value("host", 'localhost')) self._mqttPortInput.setText(str(port)) self._mqttTopicInput.setText(reg.value("root topic", '')) reg.endGroup() retrieveable = [] for session in reg.childGroups(): if session != self._session: retrieveable.append(session) if retrieveable: self._retrieveComboBox.addItem( self.tr("Select session to retrieve..."), '') for i in retrieveable: self._retrieveComboBox.addItem(i, i) else: self._retrieveComboBox.addItem(self.tr("No session to retrieve"), '') self._retrieveComboBox.setDisabled(True) deleteable = [] for session in reg.childGroups(): if session != 'Default session' and session != self._session: deleteable.append(session) if deleteable: self._deleteComboBox.addItem( self.tr("Select session to delete..."), '') for i in deleteable: self._deleteComboBox.addItem(i, i) else: self._deleteComboBox.addItem(self.tr("No session to delete"), '') self._deleteComboBox.setDisabled(True) self._retrieveComboBox.currentIndexChanged.connect( self.retrieveSession) self._deleteComboBox.currentIndexChanged.connect(self.deleteSession)
def get_mssql_connections(): """ Read PostgreSQL connection names from QSettings stored by QGIS """ settings = QSettings() settings.beginGroup(u"/MSSQL/connections/") return settings.childGroups()
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()
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 TasmotaDevicesModel(QAbstractTableModel): def __init__(self, tasmota_env): super().__init__() self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()), QSettings.IniFormat) self.tasmota_env = tasmota_env self.columns = [] self.devices_short_version = self.settings.value( "devices_short_version", True, bool) for d in self.tasmota_env.devices: d.property_changed = self.notify_change d.module_changed = self.module_change def setupColumns(self, columns): self.beginResetModel() self.columns = columns self.endResetModel() def deviceAtRow(self, row): if len(self.tasmota_env.devices) > 0: return self.tasmota_env.devices[row] return None def notify_change(self, d, key): row = self.tasmota_env.devices.index(d) if key.startswith("POWER") and "Power" in self.columns: power_idx = self.columns.index("Power") idx = self.index(row, power_idx) self.dataChanged.emit(idx, idx) elif key in ("RSSI", "LWT"): fname_idx = self.columns.index("FriendlyName") idx = self.index(row, fname_idx) self.dataChanged.emit(idx, idx) elif key in self.columns: col = self.columns.index(key) idx = self.index(row, col) self.dataChanged.emit(idx, idx) def module_change(self, d): self.notify_change(d, "Module") def columnCount(self, parent=None): return len(self.columns) def rowCount(self, parent=None): return len(self.tasmota_env.devices) def flags(self, idx): return Qt.ItemIsSelectable | Qt.ItemIsEnabled def headerData(self, col, orientation, role=Qt.DisplayRole): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.columns[col] def data(self, idx, role=Qt.DisplayRole): if idx.isValid(): row = idx.row() col = idx.column() col_name = self.columns[col] d = self.tasmota_env.devices[row] if role in [Qt.DisplayRole, Qt.EditRole]: val = d.p.get(col_name, "") if col_name == "FriendlyName": val = d.p.get("FriendlyName1", d.p['Topic']) elif col_name == "Module": if val == 0: return d.p['Template'].get( 'NAME', "Fetching template name...") else: return d.module() elif col_name == "Version" and val: if self.devices_short_version and "(" in val: return val[0:val.index("(")] return val.replace("(", " (") elif col_name in ("Uptime", "Downtime") and val: val = str(val) if val.startswith("0T"): val = val.replace('0T', '') val = val.replace('T', 'd ') elif col_name == "Core" and val: return val.replace('_', '.') elif col_name == "Time" and val: return val.replace('T', ' ') elif col_name == "Power": return d.power() elif col_name == "Color": return d.color() elif col_name == "CommandTopic": return d.cmnd_topic() elif col_name == "StatTopic": return d.stat_topic() elif col_name == "TeleTopic": return d.tele_topic() elif col_name == "FallbackTopic": return "cmnd/{}_fb/".format(d.p.get('MqttClient')) elif col_name == "BSSId": alias = self.settings.value("BSSId/{}".format(val)) if alias: return alias elif col_name == "RSSI": val = int(d.p.get("RSSI", 0)) return val return val elif role == LWTRole: val = d.p.get('LWT', 'Offline') return val elif role == RestartReasonRole: val = d.p.get('RestartReason') return val elif role == RSSIRole: val = int(d.p.get('RSSI', 0)) return val elif role == FirmwareRole: val = d.p.get('Version', "") return val elif role == Qt.TextAlignmentRole: # Left-aligned columns if col_name in ("FriendlyName", "Module", "RestartReason", "OtaUrl", "Hostname") or col_name.endswith("Topic"): return Qt.AlignLeft | Qt.AlignVCenter | Qt.TextWordWrap # Right-aligned columns elif col_name in ("Uptime"): return Qt.AlignRight | Qt.AlignVCenter else: return Qt.AlignCenter elif role == Qt.DecorationRole and col_name == "FriendlyName": if d.p['LWT'] == "Online": rssi = int(d.p.get("RSSI", 0)) if rssi > 0 and rssi < 50: return QIcon(":/status_low.png") elif rssi < 75: return QIcon(":/status_medium.png") elif rssi >= 75: return QIcon(":/status_high.png") return QIcon(":/status_offline.png") elif role == Qt.InitialSortOrderRole: if col_name in ("Uptime", "Downtime"): val = d.p.get(col_name, "") if val: d, hms = val.split("T") h, m, s = hms.split(":") return int( s) + int(m) * 60 + int(h) * 3600 + int(d) * 86400 else: return idx.data() elif role == Qt.ToolTipRole: if col_name == "Version": val = d.p.get('Version') if val: return val[val.index("(") + 1:val.index(")")] return "" elif col_name == "BSSId": return d.p.get('BSSId') elif col_name == "FriendlyName": fns = [d.p['FriendlyName1']] for i in range(2, 5): fn = d.p.get("FriendlyName{}".format(i)) if fn: fns.append(fn) return "\n".join(fns) def addDevice(self, device): self.beginInsertRows(QModelIndex(), 0, 0) device.property_changed = self.notify_change device.module_changed = self.module_change self.endInsertRows() def removeRows(self, pos, rows, parent=QModelIndex()): if pos + rows <= self.rowCount(): self.beginRemoveRows(parent, pos, pos + rows - 1) device = self.deviceAtRow(pos) self.tasmota_env.devices.pop( self.tasmota_env.devices.index(device)) topic = device.p['Topic'] self.settings.beginGroup("Devices") if topic in self.settings.childGroups(): self.settings.remove(topic) self.settings.endGroup() self.endRemoveRows() return True return False def deleteDevice(self, idx): row = idx.row() mac = self.deviceAtRow(row).p['Mac'].replace(":", "-") self.devices.remove(mac) self.devices.sync() self.removeRows(row, 1) def columnIndex(self, column): return self.columns.index(column)
def set_database_connection(self, connection=None, crs=None): """ Create a database connection """ # fetch settings settings_plugin = QSettings() settings_postgis = QSettings() settings_plugin.beginGroup('CoGo Plugin') settings_postgis.beginGroup('PostgreSQL/connections') self.connection = connection if not bool(self.connection): # fetch pre-chosen database connection self.connection = settings_plugin.value("DatabaseConnection", None) # check if still exists if bool(self.connection): if self.connection not in settings_postgis.childGroups(): settings_plugin.setValue("DatabaseConnection", "") self.connection = None # fetch from user if necessary if not bool(self.connection): dialog = DatabaseConnectionDialog() dialog.show() if bool(dialog.exec_()): self.connection = dialog.get_database_connection() if dialog.get_crs(): self.crs = QgsCoordinateReferenceSystem( dialog.get_crs().get('auth_id')) settings_plugin.setValue("DatabaseConnection", self.connection) # validate database connection if bool(self.connection): db_service = settings_postgis.value(self.connection + '/service') db_host = settings_postgis.value(self.connection + '/host') db_port = settings_postgis.value(self.connection + '/port') db_name = settings_postgis.value(self.connection + '/database') db_username = settings_postgis.value(self.connection + '/username') db_password = settings_postgis.value(self.connection + '/password') max_attempts = 3 self.uri = QgsDataSourceUri() self.uri.setConnection(db_host, db_port, db_name, db_username, db_password) if db_username and db_password: for i in range(max_attempts): error_message = self.connect_to_db(db_service, db_host, db_port, db_name, db_username, db_password) if error_message: ok, db_username, db_password = ( QgsCredentials.instance().get( self.uri.connectionInfo(), db_username, db_password, error_message)) if not ok: break else: break else: msg = "Please enter the username and password." for i in range(max_attempts): ok, db_username, db_password = ( QgsCredentials.instance().get( self.uri.connectionInfo(), db_username, db_password, msg)) if not ok: break error_message = self.connect_to_db(db_service, db_host, db_port, db_name, db_username, db_password) if not error_message: break settings_plugin.endGroup() settings_postgis.endGroup()
class TasmotaDevicesTree(QAbstractItemModel): def __init__(self, root=Node(""), parent=None): super(TasmotaDevicesTree, self).__init__(parent) self._rootNode = root self.devices = {} self.settings = QSettings() self.settings.beginGroup("Devices") for d in self.settings.childGroups(): self.devices[d] = self.addDevice( TasmotaDevice, self.settings.value("{}/friendly_name".format(d), d)) def rowCount(self, parent=QModelIndex()): if not parent.isValid(): parentNode = self._rootNode else: parentNode = parent.internalPointer() return parentNode.childCount() def columnCount(self, parent): return 2 def data(self, index, role): if not index.isValid(): return None node = index.internalPointer() if role == Qt.DisplayRole: if index.column() == 0: return node.name() elif index.column() == 1: return node.value() elif role == Qt.DecorationRole: if index.column() == 0: typeInfo = node.typeInfo() if typeInfo: return QIcon("GUI/icons/{}.png".format(typeInfo)) elif role == Qt.TextAlignmentRole: if index.column() == 1: return Qt.AlignVCenter | Qt.AlignRight def get_device_by_topic(self, topic): for i in range(self._rootNode.childCount()): d = self._rootNode.child(i) if d.name() == topic: return self.index(d.row(), 0, QModelIndex()) return None def setData(self, index, value, role=Qt.EditRole): if index.isValid(): if role == Qt.EditRole: node = index.internalPointer() node.setValue(value) self.dataChanged.emit(index, index, [Qt.DisplayRole]) return True return False def setDeviceFriendlyName(self, index, value, role=Qt.EditRole): if index.isValid(): if role == Qt.EditRole: node = index.internalPointer() if value != node.friendlyName(): node.setFriendlyName(value) self.dataChanged.emit(index, index, [Qt.DisplayRole]) return True return False def setDeviceName(self, index, value, role=Qt.EditRole): if index.isValid(): if role == Qt.EditRole: node = index.internalPointer() node.setName(value) self.dataChanged.emit(index, index, [Qt.DisplayRole]) return True return False def headerData(self, section, orientation, role): if role == Qt.DisplayRole: if section == 0: return "Device" else: return "Value" def flags(self, index): return Qt.ItemIsEnabled | Qt.ItemIsSelectable def parent(self, index): node = self.getNode(index) parentNode = node.parent() if parentNode == self._rootNode: return QModelIndex() return self.createIndex(parentNode.row(), 0, parentNode) def index(self, row, column, parent): parentNode = self.getNode(parent) childItem = parentNode.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QModelIndex() def getNode(self, index): if index.isValid(): node = index.internalPointer() if node: return node return self._rootNode def insertRows(self, position, rows, parent=QModelIndex()): parentNode = self.getNode(parent) self.beginInsertRows(parent, position, position + rows - 1) for row in range(rows): childCount = parentNode.childCount() childNode = Node("untitled" + str(childCount)) success = parentNode.insertChild(childCount, childNode) self.endInsertRows() return success def addDevice(self, device_type, name, parent=QModelIndex()): rc = self.rowCount(parent) parentNode = self.getNode(parent) device = device_type(name) self.beginInsertRows(parent, rc, rc + 1) parentNode.insertChild(rc, device) dev_idx = self.index(rc, 0, parent) self.endInsertRows() parentNode.devices()[name] = dev_idx self.beginInsertRows(dev_idx, 0, len(device.provides())) for p in device.provides().keys(): cc = device.childCount() device.insertChild(cc, node_map[p](name=p)) device.provides()[p] = self.index(cc, 1, dev_idx) self.endInsertRows() return dev_idx def removeRows(self, position, rows, parent=QModelIndex()): parentNode = self.getNode(parent) self.beginRemoveRows(parent, position, position + rows - 1) for row in range(rows): success = parentNode.removeChild(position) self.endRemoveRows() return success
class frmAccess(QDialog, Ui_frmAccess): databaseCreated = pyqtSignal(ConnectionQt) def __init__(self, module, settingsSection, settings=None, parent=None): QDialog.__init__(self, parent) if settings == None: self.settings = QSettings() else: self.settings = settings self.settingsSection = settingsSection self.module = module self.setModal(True) self.setupUi(self) self.setResources() self.resize( self.settings.value(self.settingsSection + "/qdialog_size", QSize(200, 60))) self.parent = parent self.languages = TranslationLanguageManager() self.languages.load_all() self.languages.selected = self.languages.find_by_id( self.settings.value(self.settingsSection + "/language", "en")) self.languages.qcombobox(self.cmbLanguages, self.languages.selected) self.con = ConnectionQt() #Pointer to connection self.setTitle(self.tr("Log in PostreSQL database")) self.cmbProfiles_update() current_profile = self.settings.value( self.settingsSection + "/current_profile", "") if current_profile == "": self.txtDB.setText( self.settings.value(self.settingsSection + "/db", "")) self.txtPort.setText( self.settings.value(self.settingsSection + "/port", "5432")) self.txtUser.setText( self.settings.value(self.settingsSection + "/user", "postgres")) self.txtServer.setText( self.settings.value(self.settingsSection + "/server", "127.0.0.1")) else: self.cmbProfiles.setCurrentText(current_profile) ## Reimplements QDialog.exec_ method to make an autologin if PGPASSWORD environment variable is detected. def exec_(self): try: self.password = environ['PGPASSWORD'] debug("Password automatically set from environment variable") self.txtPass.setText(self.password) self.cmdYN.accepted.emit() except: self.txtPass.setFocus() QDialog.exec_(self) self.settings.setValue(self.settingsSection + "/qdialog_size", self.size()) self.settings.sync() def setResources(self, pixmap=":/reusingcode/frmaccess_pixmap.png", icon=":/reusingcode/frmaccess_icon.png", database_new=":/reusingcode/database_new.png", profile_new=":/reusingcode/profile_new.png", profile_update=":/reusingcode/profile_update.png", profile_delete=":/reusingcode/button_cancel.png"): self.lblPixmap.setPixmap(QPixmap(pixmap)) self.setWindowIcon(QIcon(icon)) self.cmdDatabaseNew.setIcon(QIcon(database_new)) self.cmdProfileNew.setIcon(QIcon(profile_new)) self.cmdProfileUpdate.setIcon(QIcon(profile_update)) self.cmdProfileDelete.setIcon(QIcon(profile_delete)) def setTitle(self, text): self.setWindowTitle(text) def setLabel(self, text): self.lbl.setText(text) def setLanguagesVisible(self, boolean): if boolean == False: self.lblLanguage.hide() self.cmbLanguages.hide() def setProfilesVisible(self, boolean): if boolean == False: self.lineProfile.hide() self.lblProfile.hide() self.cmbProfiles.hide() self.cmdProfileNew.hide() self.cmdProfileUpdate.hide() self.cmdProfileDelete.hide() @pyqtSlot(int) def on_cmbLanguages_currentIndexChanged(self, stri): self.languages.selected = self.languages.find_by_id( self.cmbLanguages.itemData(self.cmbLanguages.currentIndex())) self.settings.setValue(self.settingsSection + "/language", self.languages.selected.id) self.languages.cambiar(self.languages.selected.id, self.module) self.retranslateUi(self) @pyqtSlot(str) def on_cmbProfiles_currentTextChanged(self, stri): self.txtDB.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/db", "xulpymoney")) self.txtPort.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/port", "5432")) self.txtUser.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/user", "postgres")) self.txtServer.setText( self.settings.value( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/server", "127.0.0.1")) def __save_current_profile(self): if self.cmbProfiles.currentText() == "": self.settings.setValue(self.settingsSection + "/db", self.txtDB.text()) self.settings.setValue(self.settingsSection + "/port", self.txtPort.text()) self.settings.setValue(self.settingsSection + "/user", self.txtUser.text()) self.settings.setValue(self.settingsSection + "/server", self.txtServer.text()) self.settings.setValue( self.settingsSection + "/language", self.cmbLanguages.itemData(self.cmbLanguages.currentIndex())) self.settings.setValue(self.settingsSection + "/current_profile", "") else: self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/db", self.txtDB.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/port", self.txtPort.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/user", self.txtUser.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/server", self.txtServer.text()) self.settings.setValue( self.settingsSection + "_profile_" + self.cmbProfiles.currentText() + "/language", self.cmbLanguages.itemData(self.cmbLanguages.currentIndex())) self.settings.setValue(self.settingsSection + "/current_profile", self.cmbProfiles.currentText()) self.settings.sync() @pyqtSlot() def on_cmdYN_accepted(self): self.__save_current_profile() self.con.init__create(self.txtUser.text(), self.txtPass.text(), self.txtServer.text(), self.txtPort.text(), self.txtDB.text()) self.con.connect() if self.con.is_active(): self.accept() else: qmessagebox( self.tr("Error conecting to {} database in {} server").format( self.con.db, self.con.server)) @pyqtSlot() def on_cmdYN_rejected(self): self.reject() def on_cmdProfileNew_released(self): name = qinputbox_string(self.tr("Profile name")) self.cmbProfiles.addItem(name) self.settings.setValue(self.settingsSection + "/db", self.txtDB.text()) def on_cmdProfileUpdate_released(self): before = self.cmbProfiles.currentText() after = qinputbox_string(self.tr("Profile name")) self.cmbProfiles.setItemText(self.cmbProfiles.currentIndex(), after) self.settings.remove(self.settingsSection + "_profile_" + before) self.__save_current_profile() def on_cmdProfileDelete_released(self): self.settings.remove(self.settingsSection + "_profile_" + self.cmbProfiles.currentText()) self.settings.setValue(self.settingsSection + "/current_profile", "") self.cmbProfiles_update() ## @return List of string with profile names def __list_of_profiles(self): r = [] for group in self.settings.childGroups(): if group.startswith(self.settingsSection + "_profile_"): r.append(group.replace(self.settingsSection + "_profile_", "")) print(r) return r def cmbProfiles_update(self, selected=None): profiles = self.__list_of_profiles() self.cmbProfiles.blockSignals(True) self.cmbProfiles.clear() for profile in profiles: self.cmbProfiles.addItem(profile) #Force without signals to be in -1. There were problems when 0 is selected, becouse it didn't emit anything self.cmbProfiles.setCurrentIndex(-1) if selected is None: self.cmbProfiles.blockSignals(False) else: self.cmbProfiles.blockSignals(False) self.cmbProfiles.setCurrentIndex( self.cmbProfiles.findData(selected.id)) def on_cmdDatabaseNew_released(self): respuesta = QMessageBox.warning( self, self.windowTitle(), self.tr("Do you want to create {} database in {}?".format( self.txtDB.text(), self.cmbLanguages.currentText())), QMessageBox.Ok | QMessageBox.Cancel) if respuesta == QMessageBox.Ok: admin_pg = AdminPG(self.txtUser.text(), self.txtPass.text(), self.txtServer.text(), self.txtPort.text()) if admin_pg.db_exists(self.txtDB.text()) == True: qmessagebox(self.tr("Xulpymoney database already exists")) return if admin_pg.create_db(self.txtDB.text()) == False: qmessagebox(self.newdb.error) else: self.__save_current_profile() self.con = admin_pg.connect_to_database(self.txtDB.text(), connectionqt=True) if self.con.is_active(): self.databaseCreated.emit(self.con) self.accept() else: qmessagebox( self. tr("Error conecting to {} database in {} server, after creating database" ).format(self.con.db, self.con.server))
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()
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)