Exemple #1
0
 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()
Exemple #2
0
    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()
Exemple #4
0
    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
Exemple #5
0
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
Exemple #6
0
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)
Exemple #7
0
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
Exemple #8
0
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
Exemple #9
0
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()
Exemple #10
0
    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)
Exemple #11
0
    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)
Exemple #12
0
 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)))
Exemple #13
0
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
Exemple #14
0
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
Exemple #15
0
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()
Exemple #17
0
    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()
Exemple #18
0
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()
Exemple #19
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)
Exemple #20
0
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
Exemple #21
0
    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()]}"
        )
Exemple #23
0
    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)
Exemple #25
0
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()
Exemple #26
0
    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)
Exemple #27
0
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)
Exemple #28
0
    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
Exemple #30
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("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)
Exemple #31
0
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)
Exemple #32
0
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)