Ejemplo n.º 1
0
 def loadSettings(self):
     settings = QSettings(QSettings.UserScope, 'Georgia Tech', 'RamanGui',
         self)
     if settings.contains('alpha'):
         self.alphaBox.setText(settings.value('alpha'))
     if settings.contains('l1Ratio'):
         self.l1RatioBox.setText(settings.value('l1Ratio'))
Ejemplo n.º 2
0
    def loadSettings(self, firstTime):
        qsettings   = QSettings()
        websettings = self.ui.webview.settings()

        self.fSavedSettings = {
            # WebView
            MOD_KEY_WEBVIEW_INSPECTOR:      qsettings.value(MOD_KEY_WEBVIEW_INSPECTOR,      MOD_DEFAULT_WEBVIEW_INSPECTOR,      type=bool),
            MOD_KEY_WEBVIEW_SHOW_INSPECTOR: qsettings.value(MOD_KEY_WEBVIEW_SHOW_INSPECTOR, MOD_DEFAULT_WEBVIEW_SHOW_INSPECTOR, type=bool)
        }

        inspectorEnabled = self.fSavedSettings[MOD_KEY_WEBVIEW_INSPECTOR] and not USING_LIVE_ISO

        websettings.setAttribute(QWebSettings.DeveloperExtrasEnabled, inspectorEnabled)

        if firstTime:
            self.restoreGeometry(qsettings.value("Geometry", ""))

            if inspectorEnabled and self.fSavedSettings[MOD_KEY_WEBVIEW_SHOW_INSPECTOR]:
                QTimer.singleShot(1000, self.ui.webinspector.show)

        self.ui.act_file_inspect.setVisible(inspectorEnabled)

        if self.fIdleTimerId != 0:
            self.killTimer(self.fIdleTimerId)

        self.fIdleTimerId = self.startTimer(MOD_DEFAULT_MAIN_REFRESH_INTERVAL)
Ejemplo n.º 3
0
    def _load_settings(self):
        # try:
        #     with open(self.settings_path, 'r') as f:
        #         settings = json.load(f)
        settings = QSettings("Maccesch", "SongScreen")

        if settings.contains('lyrics_screen'):
            self.display_lyrics_on_screen(settings.value('lyrics_screen'))

        if settings.contains('control_window_position'):
            self.move(settings.value('control_window_position'))

        for key in settings.allKeys():
            self.settings[key] = settings.value(key)

        # self.settings.update(settings)

        self.songtext_widget.set_font_size(self.settings['font_size'])
        self.songtext_widget.set_line_increment(self.settings['line_increment'])

        # except (FileNotFoundError, ValueError):
        #     pass

        if not os.path.exists(self.lyrics_language_path) or not self.settings['lyrics_language']:
            languages = list(
                filter(lambda p: os.path.isdir(os.path.join(self.lyrics_path, p)) and p != "timings",
                       os.listdir(self.lyrics_path))
            )
            self.settings['lyrics_language'] = languages[0] if languages else ""
Ejemplo n.º 4
0
def langs():
    """Returns a list of language codes wished for documentation.
    
    If the list is empty, english (untranslated) is assumed.
    If a language code also has a country suffix, a hyphen will be used
    as separator (as required per RFC2616, Accept-Language header).
    
    """
    s = QSettings()
    langs = []
    lang = s.value("documentation/language", "default", str)

    if lang == "default":
        lang = s.value("language", "", str)
    if lang and lang != "C":
        langs.append(lang)
    langs.extend(po.setup.preferred())
    
    # now fixup the list, remove dups and
    # language/country codes in Accept-Language headers must have '-' and not '_'
    result = []
    def add(item):
        if item not in result:
            result.append(item)
    for l in langs:
        # if there is a language/country code, insert also the generic language code
        if '_' in l:
            add(l.replace('_', '-'))
            add(l.split('_')[0])
        else:
            add(l)
    return result
Ejemplo n.º 5
0
 def showEvent(self, event):
     #Show Event
     QWidget.showEvent(self, event)
     #Avoid recalculate the panel sizes if they are already loaded
     if self._splitterArea.count() == 2:
         return
     #Rearrange widgets on Window
     self._splitterArea.insertWidget(0, self._splitterMain)
     if not event.spontaneous():
         self.change_misc_visibility()
     if bin(settings.UI_LAYOUT)[-1] == '1':
         self.splitter_central_rotate()
     if bin(settings.UI_LAYOUT >> 1)[-1] == '1':
         self.splitter_misc_rotate()
     if bin(settings.UI_LAYOUT >> 2)[-1] == '1':
         self.splitter_central_orientation()
     qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
     #Lists of sizes as list of QVariant- heightList = [QVariant, QVariant]
     heightList = list(qsettings.value("window/central/mainSize",
         [(self.height() / 3) * 2, self.height() / 3]))
     widthList = list(qsettings.value("window/central/areaSize",
         [(self.width() / 6) * 5, self.width() / 6]))
     self._splitterMainSizes = [int(heightList[0]), int(heightList[1])]
     self._splitterAreaSizes = [int(widthList[0]), int(widthList[1])]
     #Set the sizes to splitters
     #self._splitterMain.setSizes(self._splitterMainSizes)
     self._splitterMain.setSizes(self._splitterMainSizes)
     self._splitterArea.setSizes(self._splitterAreaSizes)
     self.misc.setVisible(
         qsettings.value("window/show_misc", False, type=bool))
Ejemplo n.º 6
0
 def readSettings(self):
     """read the settings"""
     settings = QSettings('Pixel Stereo', 'lekture')
     pos = settings.value('pos', QPoint(200, 200))
     size = settings.value('size', QSize(1000, 650))
     self.move(pos)
     self.resize(size)
Ejemplo n.º 7
0
    def loadSettings(self):
        settings = QSettings("falkTX", "Carla2")

        audioDevice     = settings.value("%s%s/Device"     % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), "",                              type=str)
        audioNumPeriods = settings.value("%s%s/NumPeriods" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), CARLA_DEFAULT_AUDIO_NUM_PERIODS, type=int)
        audioBufferSize = settings.value("%s%s/BufferSize" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), CARLA_DEFAULT_AUDIO_BUFFER_SIZE, type=int)
        audioSampleRate = settings.value("%s%s/SampleRate" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), CARLA_DEFAULT_AUDIO_SAMPLE_RATE, type=int)

        if audioDevice and audioDevice in self.fDeviceNames:
            self.ui.cb_device.setCurrentIndex(self.fDeviceNames.index(audioDevice))
        else:
            self.ui.cb_device.setCurrentIndex(-1)

        # fill combo-boxes first
        self.slot_updateDeviceInfo()

        if audioNumPeriods in (2, 3):
            self.ui.sb_numperiods.setValue(audioNumPeriods)
        else:
            self.ui.sb_numperiods.setValue(CARLA_DEFAULT_AUDIO_NUM_PERIODS)

        if audioBufferSize and audioBufferSize in self.fBufferSizes:
            self.ui.cb_buffersize.setCurrentIndex(self.fBufferSizes.index(audioBufferSize))
        elif self.fBufferSizes == BUFFER_SIZE_LIST:
            self.ui.cb_buffersize.setCurrentIndex(BUFFER_SIZE_LIST.index(CARLA_DEFAULT_AUDIO_BUFFER_SIZE))
        else:
            self.ui.cb_buffersize.setCurrentIndex(len(self.fBufferSizes)/2)

        if audioSampleRate and audioSampleRate in self.fSampleRates:
            self.ui.cb_samplerate.setCurrentIndex(self.fSampleRates.index(audioSampleRate))
        elif self.fSampleRates == SAMPLE_RATE_LIST:
            self.ui.cb_samplerate.setCurrentIndex(SAMPLE_RATE_LIST.index(CARLA_DEFAULT_AUDIO_SAMPLE_RATE))
        else:
            self.ui.cb_samplerate.setCurrentIndex(len(self.fSampleRates)/2)
Ejemplo n.º 8
0
 def loadSettings(self):
     output_port = midihub.default_output()
     input_port = midihub.default_input()
     s = QSettings()
     s.beginGroup("midi")
     self._playerPort.setEditText(s.value("player/output_port", output_port, str))
     self._inputPort.setEditText(s.value("midi/input_port", input_port, str))
Ejemplo n.º 9
0
 def load_settings(self):
     """Load application-wide settings relating to extensions."""
     s = QSettings()
     s.beginGroup('extension-settings')
     self._active = s.value('active', True, bool)
     self._inactive_extensions = s.value('installed/inactive', [], str)
     self._root_directory = s.value('root-directory', '', str)
Ejemplo n.º 10
0
    def readThemeIndex(self, themeName):

        dirList = []
        parents = []
        themeIndex = QFile()

        # Read theme index files
        for i in range(len(self.iconDirs)):
            themeIndex.setFileName(path.join(unicode(self.iconDirs[i]), 
                unicode(themeName), "index.theme"))
            if themeIndex.exists():
                indexReader = QSettings(themeIndex.fileName(), 
                        QSettings.IniFormat)
                for key in indexReader.allKeys():
                    if str(key).endswith("/Size"):
                        size = str(indexReader.value(key))
                        
                        dirList.append((size, 
                            unicode(key[:-5])))
                
                parents = indexReader.value('Icon Theme/Inherits')
                dump=parents
                parents = list()
                parents.append(dump)
                break
            
        return QIconTheme(dirList, parents)
Ejemplo n.º 11
0
def preferred():
    """Return the quotes desired by the Frescobaldi user.
    
    Always returns a quote set.
    Only this function depends on Qt and Frescobaldi.
    
    """
    
    from PyQt5.QtCore import QSettings
    import po.setup

    s = QSettings()
    s.beginGroup("typographical_quotes")
    language = s.value("language", "current", str)
    
    default = _quotes["C"]
    if language == "current":
        language = po.setup.current()
    elif language == "custom":
        return QuoteSet(
            primary = Quotes(
                left = s.value("primary_left", default.primary.left, str),
                right = s.value("primary_right", default.primary.right, str),
            ),
            secondary = Quotes(
                left = s.value("secondary_left", default.secondary.left, str),
                right = s.value("secondary_right", default.secondary.right, str),
            )
        )
    return quotes(language) or default
Ejemplo n.º 12
0
    def _load_settings(self):
        """ Load the user specific settings. """

        settings = QSettings()

        self.resize(settings.value('size', QSize(860, 400)))
        self.move(settings.value('pos', QPoint(200, 200)))
Ejemplo n.º 13
0
def batch_convert_markdown2lyx(openfile):

    settings = QSettings('Pandoc', 'PanConvert')
    path_multimarkdown = settings.value('path_multimarkdown','')
    batch_settings = QSettings('Pandoc', 'PanConvert')
    batch_open_path_output = batch_settings.value('batch_open_path_output')

    if os.path.isfile(path_multimarkdown):

        filename, file_extension = os.path.splitext(openfile)

        if batch_open_path_output == '':
            args = [path_multimarkdown, openfile, '--to=' + 'lyx',  '--output=' + filename + '.' + 'lyx']

        else:
            outputfile = os.path.basename(filename)
            args = [path_multimarkdown, openfile, '--to=' + 'lyx',  '--output=' + batch_open_path_output + '/' + outputfile + '.' + 'lyx']

        p = subprocess.Popen(
                args,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)
#TODO# WRITE ERROR RETURN CODES
        return p.communicate(openfile.encode('utf-8'))[0].decode('utf-8')
Ejemplo n.º 14
0
    def exportColoredHtml(self):
        doc = self.currentDocument()
        name, ext = os.path.splitext(os.path.basename(doc.url().path()))
        if name:
            if ext.lower() == ".html":
                name += "_html"
            name += ".html"
        dir = os.path.dirname(doc.url().toLocalFile())
        if dir:
            name = os.path.join(dir, name)
        filename = QFileDialog.getSaveFileName(self, app.caption(_("Export as HTML")),
            name, "{0} (*.html)".format("HTML Files"))[0]
        if not filename:
            return #cancelled

        s = QSettings()
        s.beginGroup("source_export")
        number_lines = s.value("number_lines", False, bool)
        inline_style = s.value("inline_export", False, bool)
        wrap_tag = s.value("wrap_tag", "pre", str)
        wrap_attrib = s.value("wrap_attrib", "id", str)
        wrap_attrib_name = s.value("wrap_attrib_name", "document", str)
        import highlight2html
        html = highlight2html.html_document(doc, inline=inline_style, number_lines=number_lines,
            wrap_tag=wrap_tag, wrap_attrib=wrap_attrib, wrap_attrib_name=wrap_attrib_name)
        try:
            with open(filename, "wb") as f:
                f.write(html.encode('utf-8'))
        except IOError as e:
            msg = _("{message}\n\n{strerror} ({errno})").format(
                message = _("Could not write to: {url}").format(url=filename),
                strerror = e.strerror,
                errno = e.errno)
            QMessageBox.critical(self, app.caption(_("Error")), msg)
Ejemplo n.º 15
0
 def loadSettings(self):
     s = QSettings()
     s.beginGroup("helper_applications")
     self.printCommand.setPath(s.value("printcommand", "", str))
     self.printDialogCheck.setChecked(s.value("printcommand/dialog", False, bool))
     with qutil.signalsBlocked(self.resolution):
         self.resolution.setEditText(format(s.value("printcommand/dpi", 300, int)))
Ejemplo n.º 16
0
    def loadSettings(self):
        settings = QSettings("falkTX", "Carla2")

        audioDevice       = settings.value("%s%s/Device"       % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), "",                                type=str)
        audioBufferSize   = settings.value("%s%s/BufferSize"   % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), CARLA_DEFAULT_AUDIO_BUFFER_SIZE,   type=int)
        audioSampleRate   = settings.value("%s%s/SampleRate"   % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), CARLA_DEFAULT_AUDIO_SAMPLE_RATE,   type=int)
        audioTripleBuffer = settings.value("%s%s/TripleBuffer" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, self.fDriverName), CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER, type=bool)

        if audioDevice and audioDevice in self.fDeviceNames:
            self.ui.cb_device.setCurrentIndex(self.fDeviceNames.index(audioDevice))
        else:
            self.ui.cb_device.setCurrentIndex(-1)

        # fill combo-boxes first
        self.slot_updateDeviceInfo()

        if audioBufferSize and audioBufferSize in self.fBufferSizes:
            self.ui.cb_buffersize.setCurrentIndex(self.fBufferSizes.index(audioBufferSize))
        elif self.fBufferSizes == BUFFER_SIZE_LIST:
            self.ui.cb_buffersize.setCurrentIndex(BUFFER_SIZE_LIST.index(CARLA_DEFAULT_AUDIO_BUFFER_SIZE))
        else:
            self.ui.cb_buffersize.setCurrentIndex(len(self.fBufferSizes)/2)

        if audioSampleRate and audioSampleRate in self.fSampleRates:
            self.ui.cb_samplerate.setCurrentIndex(self.fSampleRates.index(audioSampleRate))
        elif self.fSampleRates == SAMPLE_RATE_LIST:
            self.ui.cb_samplerate.setCurrentIndex(SAMPLE_RATE_LIST.index(CARLA_DEFAULT_AUDIO_SAMPLE_RATE))
        else:
            self.ui.cb_samplerate.setCurrentIndex(len(self.fSampleRates)/2)

        self.ui.cb_triple_buffer.setChecked(audioTripleBuffer and self.ui.cb_triple_buffer.isEnabled())
Ejemplo n.º 17
0
 def readSettings(self):
     s = QSettings()
     s.beginGroup("pitch-menu")
     self.actionCollection.pitch_relative_assume_first_pitch_absolute.setChecked(
         s.value("relative-first-pitch-absolute", False, bool))
     self.actionCollection.pitch_relative_write_startpitch.setChecked(
         s.value("relative-write-startpitch", True, bool))
Ejemplo n.º 18
0
Archivo: main.py Proyecto: hugsy/cemu
    def updateRecentFileActions(self, insert_file=None):
        settings = QSettings('Cemu', 'Recent Files')
        files = settings.value('recentFileList')
        if files is None:
            # if setting doesn't exist, create it
            settings.setValue('recentFileList', [])
            files = settings.value('recentFileList')

        maxRecentFiles = CEmuWindow.MaxRecentFiles

        if insert_file:
            # insert new file to list
            if insert_file not in files:
                files.insert(0, insert_file)
            # ensure list size
            if len(files) > maxRecentFiles:
                files = files[0:maxRecentFiles]
            # save the setting
            settings.setValue('recentFileList', files)

        numRecentFiles = min(len(files), maxRecentFiles)

        for i in range(numRecentFiles):
            text = "&%d %s" % (i + 1, self.strippedName(files[i]))
            self.recentFileActions[i].setText(text)
            self.recentFileActions[i].setData(files[i])
            self.recentFileActions[i].setVisible(True)

        for j in range(numRecentFiles, maxRecentFiles):
            self.recentFileActions[j].setVisible(False)
        return
Ejemplo n.º 19
0
Archivo: helper.py Proyecto: MazeFX/pat
class DbFileHandler(object):

    def __init__(self, *args):
        Lumberjack.info('spawning a << DbFileHandler >>')
        self.settings = QSettings()

    def store_file(self, mapper_file_name, file_projection):
        Lumberjack.info('< DbFileHandler > - -> (store_file)')
        print(Back.GREEN + 'Storing File: ', mapper_file_name, ', for: ', file_projection)
        print(Back.GREEN + 'ROOT_DIR = ', ROOT_DIR)
        if not mapper_file_name:
            return None
        db_path_type = self.settings.value('db_type')
        db_path = self.settings.value('db_base_path')
        if db_path_type == 0:
            db_dir_path = os.path.join(ROOT_DIR, db_path, file_projection[0])
        print(Back.GREEN + 'DB_dir_path = ', db_dir_path)
        if not os.path.isdir(db_dir_path):
            os.makedirs(db_dir_path)

        full_file_name = os.path.join(db_dir_path, file_projection[1])
        print(Back.GREEN + 'full file name = ', full_file_name)
        print(Back.GREEN + 'with type = ', type(full_file_name))
        print(Back.GREEN + 'mapper_file_name = ', mapper_file_name)
        print(Back.GREEN + 'with type = ', type(mapper_file_name))
        if not os.path.exists(full_file_name) or mapper_file_name[0] != full_file_name:
            copyfile(mapper_file_name[0], full_file_name)

        return full_file_name
Ejemplo n.º 20
0
def playNotification(file):
    # getting user setting from persepolis_setting
    persepolis_setting = QSettings('persepolis_download_manager', 'persepolis')

    # enbling or disabling notification sound in persepolis_setting
    enable_notification = str(persepolis_setting.value('settings/sound'))

    # volume of notification in persepolis_setting(an integer between 0 to 100)
    volume_percent = int(persepolis_setting.value('settings/sound-volume'))

# Paplay volume value must be between 0 (silent) and 65536 (100% volume)
    volume = int((65536 * volume_percent)/100)

    if enable_notification == 'yes':
        if os_type == 'Linux' or os_type == 'FreeBSD' or os_type == 'OpenBSD':
            answer = os.system("paplay --volume='" + str(volume) + "' '" + file + "' &")
            if answer != 0:
                print("paplay not installed!Install it for playing sound notification")
                logger.sendToLog(
                    "paplay not installed!Install it for playing sound notification", "WARNING")


        elif os_type == 'Darwin':
            os.system("osascript -e 'set volume alert volume " +
                      str(volume) + "'")
            os.system("osascript -e 'beep 3' &")

        elif os_type == 'Windows':
            CREATE_NO_WINDOW = 0x08000000
            subprocess.Popen(['rundll32', 'user32.dll,MessageBeep'],
                             shell=False, creationflags=CREATE_NO_WINDOW)
Ejemplo n.º 21
0
def main():
    global app
    # register representation factories
    baseRepresentationFactories.registerAllFactories()
    representationFactories.registerAllFactories()
    # initialize the app
    app = Application(sys.argv)
    app.setOrganizationName("TruFont")
    app.setOrganizationDomain("trufont.github.io")
    app.setApplicationName("TruFont")
    app.setApplicationVersion(__version__)
    app.setWindowIcon(QIcon(":app.png"))
    app.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

    # Install stream redirection
    app.outputWindow = OutputWindow()
    # Exception handling
    sys.excepthook = exceptionCallback

    # Qt's translation for itself. May not be installed.
    qtTranslator = QTranslator()
    qtTranslator.load("qt_" + QLocale.system().name(),
                      QLibraryInfo.location(QLibraryInfo.TranslationsPath))
    app.installTranslator(qtTranslator)

    appTranslator = QTranslator()
    appTranslator.load("trufont_" + QLocale.system().name(),
                       os.path.dirname(os.path.realpath(__file__)) +
                       "/resources")
    app.installTranslator(appTranslator)

    # parse options and open fonts
    parser = QCommandLineParser()
    parser.setApplicationDescription(QApplication.translate(
        "Command-line parser", "The TruFont font editor."))
    parser.addHelpOption()
    parser.addVersionOption()
    parser.addPositionalArgument(QApplication.translate(
        "Command-line parser", "files"), QApplication.translate(
        "Command-line parser", "The UFO files to open."))
    parser.process(app)
    args = parser.positionalArguments()
    if not args:
        fontPath = None
        # maybe load recent file
        settings = QSettings()
        loadRecentFile = settings.value("misc/loadRecentFile", False, bool)
        if loadRecentFile:
            recentFiles = settings.value("core/recentFiles", [], type=str)
            if len(recentFiles) and os.path.exists(recentFiles[0]):
                fontPath = recentFiles[0]
                app.openFile(fontPath)
        # otherwise, create a new file
        if fontPath is None:
            app.newFile()
    else:
        for fontPath in args:
            app.openFile(fontPath)
    sys.exit(app.exec_())
Ejemplo n.º 22
0
 def open(self):
     s = QSettings()
     self._portname = s.value("midi/input_port", midihub.default_input(), str)
     self._pollingtime = s.value("midi/polling_time", 10, int)
     self._portmidiinput = midihub.input_by_name(self._portname)
     
     self._listener = Listener(self._portmidiinput, self._pollingtime)
     self._listener.NoteEventSignal.connect(self.analyzeevent)
Ejemplo n.º 23
0
 def load_window_geometry(self):
     """Load from QSettings the window size of Ninja IDE"""
     qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
     if qsettings.value("window/maximized", True, type=bool):
         self.setWindowState(Qt.WindowMaximized)
     else:
         self.resize(qsettings.value("window/size", QSizeF(800, 600)))
         self.move(qsettings.value("window/pos", QPointF(100, 100)))
Ejemplo n.º 24
0
 def readSettings(self):
     s = QSettings()
     s.beginGroup("copy_image")
     self.dpiCombo.setEditText(s.value("dpi", "100", str))
     self.colorButton.setColor(s.value("papercolor", QColor(Qt.white), QColor))
     self.crop.setChecked(s.value("autocrop", False, bool))
     self.antialias.setChecked(s.value("antialias", True, bool))
     self.scaleup.setChecked(s.value("scaleup", False, bool))
Ejemplo n.º 25
0
 def open(self):
     s = QSettings()
     self._portname = s.value("midi/input_port", midihub.default_input(), str)
     self._pollingtime = s.value("midi/polling_time", 10, int)
     self._portmidiinput = midihub.input_by_name(self._portname)
     
     self._listener = Listener(self._portmidiinput, self._pollingtime)
     QObject.connect(self._listener, SIGNAL("caughtevent"), self.analyzeevent)
Ejemplo n.º 26
0
def GetQSVGSignaturPfad():
    # SVG's im Shape-Pfad oder im temporären Projektpfad speichern
    # Überarbeitung 15.02.18
    s = QSettings( "EZUSoft", fncProgKennung() )
    if s.value( "bSHPexp") == "Ja": # and s.value("txtSHPDir","nicht festgelegt") != "nicht festgelegt":
        return s.value("txtSHPDir") + '/'
    else:      
        return tempfile.gettempdir() + "/{D5E6A1F8-392F-4241-A0BD-5CED09CFABC7}/" + 'projekt_svg' + '/' + GetCGProjektName() + '/'
Ejemplo n.º 27
0
 def loadSettings(self):
     """Loads the settings from config."""
     s = QSettings()
     s.beginGroup("sidebar")
     line_numbers = s.value("line_numbers", self._line_numbers, bool)
     self.setLineNumbersVisible(line_numbers)
     folding = s.value("folding", self._folding, bool)
     self.setFoldingVisible(folding)
Ejemplo n.º 28
0
 def load(self, scheme):
     """Load the settings for the scheme. Called on init."""
     s = QSettings()
     s.beginGroup("fontscolors/" + scheme)
     
     # load font
     defaultfont = "Lucida Console" if os.name == "nt" else "monospace"
     self.font = QFont(s.value("fontfamily", defaultfont, str))
     self.font.setPointSizeF(s.value("fontsize", 10.0, float))
     
     # load base colors
     s.beginGroup("basecolors")
     for name in baseColors:
         if s.contains(name):
             self.baseColors[name] = QColor(s.value(name, "", str))
         else:
             self.baseColors[name] = baseColorDefaults[name]()
     s.endGroup()
     
     # get the list of supported styles from ly.colorize
     all_styles = ly.colorize.default_mapping()
     default_styles = set()
     for group, styles in all_styles:
         d = self._inherits[group] = {}
         for style in styles:
             if style.base:
                 default_styles.add(style.base)
                 d[style.name] = style.base
     
     default_scheme = ly.colorize.default_scheme
     
     # load default styles
     s.beginGroup("defaultstyles")
     for name in default_styles:
         self.defaultStyles[name] = f = QTextCharFormat()
         css = default_scheme[None].get(name)
         if css:
             css2fmt(css, f)
         s.beginGroup(name)
         self.loadTextFormat(f, s)
         s.endGroup()
     s.endGroup()
     
     # load specific styles
     s.beginGroup("allstyles")
     for group, styles in all_styles:
         self.allStyles[group]= {}
         s.beginGroup(group)
         for style in styles:
             self.allStyles[group][style.name] = f = QTextCharFormat()
             css = default_scheme[group].get(style.name)
             if css:
                 css2fmt(css, f)
             s.beginGroup(style.name)
             self.loadTextFormat(f, s)
             s.endGroup()
         s.endGroup()
     s.endGroup()
Ejemplo n.º 29
0
 def loadSettings(self):
     """ get users previous settings """
     s = QSettings()
     s.beginGroup('mode_shift')
     key = s.value('key', "", str)
     self.keyInput.setText(key)
     index = s.value('mode', 0, int)
     self.modeCombo.setCurrentIndex(index)
     self.readKeyInput()
Ejemplo n.º 30
0
    def loadSettings(self):
        settings = QSettings()
        settings.beginGroup("ConnectDialog")

        self.ui.comboBox.setCurrentIndex(settings.value("index", 0, type=int))
        self.ui.sb_devnumber_bt.setValue(settings.value("bt-number", 1, type=int))
        self.ui.le_ip_lan.setText(settings.value("lan-ip", "127.0.0.1", type=str))
        self.ui.sb_port_lan.setValue(settings.value("lan-port", 8888, type=int))
        self.ui.le_ip_usb.setText(settings.value("usb-ip", "192.168.51.1", type=str))
Ejemplo n.º 31
0
class MainWindow(QMainWindow):
    """The main window, containing the search bar and results table."""
    def __init__(self, db, keybinds):
        super(MainWindow, self).__init__()
        self.settings = QSettings("xapers-qt", "xapers-qt")
        self.shortcuts = []
        self.db = db
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.results = self.ui.resultsWidget
        self.searchBar = self.ui.searchBar
        self.results.setDb(self.db)
        self.results.setSettings(self.settings)
        self.setupKeybinds(keybinds)
        self.restore_size()
        self.show()

    def restore_size(self):
        """Resize to size stored in settings.
        """
        try:
            width = int(self.settings.value("main/width", 800))
        except ValueError:
            width = 800
        try:
            height = int(self.settings.value("main/height", 300))
        except ValueError:
            height = 300
        self.resize(width, height)

    def startSearch(self):
        """Launches a search."""
        self.results.doSearch(self.searchBar.text())

    def setupKeybinds(self, binds):
        """Setup the keyboard shortcuts.
        """
        for shortcut in self.shortcuts:
            pass  # TODO Delete shortcut?
        for keys, action in binds:
            if action == "Search":
                func = self.focusSearchBar
            elif action == "Next":
                func = self.results.selectNext
            elif action == "Prev":
                func = self.results.selectPrev
            elif action == "OpenPDF":
                func = self.results.openPDF
            elif action == "OpenDoc":
                func = self.results.openDoc
            elif action == "Exit":
                func = self.close
            else:
                action = None
            if action:
                shortcut = QShortcut(QKeySequence(keys), self)
                shortcut.activated.connect(func)
                self.shortcuts.append(shortcut)

    def focusSearchBar(self):
        """Set focus to the search bar."""
        self.searchBar.searchLine.setFocus(Qt.ShortcutFocusReason)

    def saveSettings(self):
        """Save any settings we may have changed."""
        self.settings.setValue("main/width", self.width())
        self.settings.setValue("main/height", self.height())
        self.results.saveSettings()

    def closeEvent(self, event):
        """Things to do when closing the window.
        """
        self.saveSettings()

    def resizeEvent(self, resizeEvent):
        """Things to do when resizing the window.
        """
        self.results.refresh()
Ejemplo n.º 32
0
class PugdebugSettings():

    defaults = {
        'debugger/host': '127.0.0.1',
        'debugger/port_number': 9000,
        'debugger/idekey': 'pugdebug',
        'debugger/break_at_first_line': Qt.Checked,
        'debugger/max_depth': '3',
        'debugger/max_children': '128',
        'debugger/max_data': '512',
        'path/project_root': os.path.expanduser('~'),
        'path/path_mapping': ''
    }

    def __init__(self):
        """Model object to handle application settings

        Sets up initial application settings.

        QSettings promises to work cross-platform.
        """
        QCoreApplication.setOrganizationName("pugdebug")
        QCoreApplication.setOrganizationDomain(
            "http://github.com/robertbasic/pugdebug")
        QCoreApplication.setApplicationName("pugdebug")
        self.application_settings = QSettings()

        self.setup_default_settings()

    def setup_default_settings(self):
        """Set the default values for settings which don't have a value."""
        for key, value in self.defaults.items():
            if not self.has(key):
                self.set(key, value)

    def get(self, key):
        return self.application_settings.value(key)

    def get_default(self, key):
        return self.defaults[key] if key in self.defaults else None

    def has(self, key):
        return self.application_settings.contains(key)

    def set(self, key, value):
        return self.application_settings.setValue(key, value)

    def remove(self, key):
        return self.application_settings.remove(key)

    def add_project(self, project):
        index = self.__get_next_index(project)

        if index is not False:
            self.application_settings.beginWriteArray('projects')
            self.application_settings.setArrayIndex(index)
            self.application_settings.setValue('projects', project)
            self.application_settings.endArray()

    def delete_project(self, project):
        size = self.application_settings.beginReadArray('projects')

        for i in range(0, size):
            self.application_settings.setArrayIndex(i)
            existing_project = self.application_settings.value('projects')

            if existing_project == project:
                self.application_settings.remove('projects')
                break

        self.application_settings.endArray()

        self.__reindex_projects_array()

    def get_projects(self):
        size = self.application_settings.beginReadArray('projects')

        projects = []
        for i in range(0, size):
            self.application_settings.setArrayIndex(i)
            projects.append(self.application_settings.value('projects'))

        self.application_settings.endArray()

        return projects

    def __get_next_index(self, project):
        size = self.application_settings.beginReadArray('projects')

        index = None

        for i in range(0, size):
            self.application_settings.setArrayIndex(i)
            existing_project = self.application_settings.value('projects')

            if existing_project == project:
                index = i
                break

        self.application_settings.endArray()

        return False if index is not None else size

    def __reindex_projects_array(self):
        size = self.application_settings.beginReadArray('projects')

        projects = set()
        for i in range(0, size):
            self.application_settings.setArrayIndex(i)
            project = self.application_settings.value('projects')

            if project is not None:
                projects.add(project)

        self.application_settings.endArray()

        self.application_settings.remove('projects')

        self.application_settings.beginWriteArray('projects')

        i = 0
        for project in projects:
            self.application_settings.setArrayIndex(i)
            self.application_settings.setValue('projects', project)
            i += 1

        self.application_settings.endArray()
Ejemplo n.º 33
0
class qwProgressBox(QtWidgets.QWidget):
    ''' Widget personalise construisant la boite de progression lors du streaming d'un fichier GCode
  '''
    def __init__(self, parent=None):
        super().__init__()

        self.__settings = QSettings(QSettings.NativeFormat,
                                    QSettings.UserScope, ORG_NAME, APP_NAME)

        self.pBox = QtWidgets.QFrame(parent)
        self.pBox.setStyleSheet(
            ".QFrame{background-color: rgba(192, 192, 192, 192); border: 2px solid #000060;}"
        )

        self.pBoxBtnHeader = btnHeader(self.pBox)
        self.pBoxBtnHeader.setStyleSheet(
            ".btnHeader{background-color: rgba(48, 48, 80, 192); border-radius: 0px}"
        )

        self.pBoxLblStart = QtWidgets.QLabel(self.pBoxBtnHeader)
        debut = datetime.now()
        self.pBoxLblStart.setText(
            self.tr("GCode started at: {}").format(
                debut.strftime("%A %x %H:%M:%S")))
        self.pBoxLblStart.setStyleSheet("color: white;")
        self.pBoxLblStart.adjustSize()
        self.pBoxBtnHeader.setGeometry(2, 2,
                                       self.pBoxLblStart.width() + 36,
                                       self.pBoxBtnHeader.height())
        self.pBoxLblStart.setGeometry(
            20, (self.pBoxBtnHeader.height() - self.pBoxLblStart.height()) / 2,
            self.pBoxLblStart.width(), self.pBoxLblStart.height())

        self.pBoxProgress = QtWidgets.QProgressBar(self.pBox)
        self.pBoxProgress.setAlignment(Qt.AlignHCenter)
        self.pBoxProgress.setGeometry(
            20,
            self.pBoxBtnHeader.geometry().y() + self.pBoxBtnHeader.height() +
            9, self.pBoxLblStart.width(), 20)
        self.pBoxProgress.setRange(0, 100)
        self.pBoxProgress.setValue(37)

        self.pBoxLblComment = QtWidgets.QLabel(self.pBox)
        self.pBoxLblComment.setText("()")
        self.pBoxLblComment.setGeometry(
            20,
            self.pBoxProgress.geometry().y() + self.pBoxProgress.height() + 3,
            self.pBoxLblStart.width(), 20)

        self.pBoxLblElapse = QtWidgets.QLabel(self.pBox)
        total_seconds = int((datetime.now() - debut).total_seconds())
        hours, remainder = divmod(total_seconds, 60 * 60)
        minutes, seconds = divmod(remainder, 60)
        self.pBoxLblElapse.setText(self.tr("Elapsed time:"))
        self.pBoxLblElapse.setGeometry(
            20,
            self.pBoxLblComment.geometry().y() + self.pBoxLblComment.height() +
            3, self.pBoxLblStart.width(), 20)

        pBoxLargeur = self.pBoxLblStart.width() + 40
        pBoxHauteur = self.pBoxLblElapse.geometry().y(
        ) + self.pBoxLblElapse.height() + 6
        defaultX = int((parent.width() - pBoxLargeur) / 2)
        defaultY = int((parent.height() - pBoxHauteur) / 5 * 3)
        pBoxX = self.__settings.value("ProgressBox/posX", defaultX, type=int)
        pBoxY = self.__settings.value("ProgressBox/posY", defaultY, type=int)

        self.pBox.setGeometry(pBoxX, pBoxY, pBoxLargeur, pBoxHauteur)
        self.pBox.setVisible(False)

    def start(self):
        debut = datetime.now()
        self.pBoxLblStart.setText(
            self.tr("GCode started at: {}").format(
                debut.strftime("%A %x %H:%M:%S")))
        self.pBox.setVisible(True)
        # Démarre la mise à jour régulière du temps elapsed
        self.__elapseThread = elapseThread(self.pBoxLblElapse)
        self.__elapseThread.start()

    def stop(self):
        # Masque la boite
        self.pBox.setVisible(False)
        # Arrete la mise à jour régulière du temps elapsed
        self.__elapseThread.stop()

    def setRange(self, mini: int, maxi: int):
        self.pBoxProgress.setRange(mini, maxi)

    def setValue(self, val: int):
        self.pBoxProgress.setValue(val)
        self.pBoxProgress.setToolTip(
            self.tr("Line {} of {}").format(val, self.pBoxProgress.maximum()))

    def setComment(self, comment: str):
        self.pBoxLblComment.setText(comment)

    def isVisible(self):
        return self.pBox.isVisible()
Ejemplo n.º 34
0
class MainWindow(QMainWindow):

    APP_NAME = 'Tagit'
    APP_VERSION = '0.5'
    KEY_GROUP = 'groups'
    KEY_TAG = 'tags'
    KEY_ITEM = 'items'
    KEY_SETTING = 'settings'

    def __init__(self):
        super(MainWindow, self).__init__()

        # whole views
        self.setupViews()

        # menu and toolbox
        self.createMainMenu()

        # init data: last saved database
        self.setting = QSettings('dothinking', 'tagit')
        filename = self.setting.value('database')
        self.initData(filename)

        # status bar
        self.createStatusBar()

        # window
        self.setTitle()
        self.resize(1000, 800)
        # self.showMaximized()

    # ----------------------------------------------
    # --------------- properties ---------------
    # ----------------------------------------------
    def groupsView(self):
        return self.groupsTreeView

    def tagsView(self):
        return self.tagsTableView

    def itemsView(self):
        return self.itemsTableView

    def propertyView(self):
        return self.dockProperty

    def tabViews(self):
        return self.tabWidget

    def database(self):
        return self._database

    # ----------------------------------------------
    # --------------- data operation ---------------
    # ----------------------------------------------
    def initData(self, database=None):
        '''load data from specified database, init default if failed.
           two failing situations:
           - database is not specified -> init by default and return true
           - specified but is invalid  -> init by default but return false
        '''
        ok = True
        if database and os.path.exists(database):
            # load database
            try:
                with open(database, 'rb') as f:
                    data = pickle.load(f)
            except Exception as e:
                ok = False
            else:
                if self.APP_NAME in data:  # valid database
                    self._database = database
                    self._initData(data)
                    return ok
                else:
                    ok = False

        # init by default if load data from database failed
        self._database = None
        self._initData()

        return ok

    def _initData(self, data={}):
        '''load data from database'''
        # set window title
        self.setTitle()
        self.groupsTreeView.setFocus()

        # get data
        if self.KEY_GROUP in data:
            groups = data.get(self.KEY_GROUP)[GroupModel.CHILDREN]
        else:
            groups = []
        tags = data.get(self.KEY_TAG, [])
        items = data.get(self.KEY_ITEM, [])

        # settings
        settings = data.get(self.KEY_SETTING, {})
        selected_group = settings.get('selected_group', GroupModel.ALLGROUPS)
        selected_tag = settings.get('selected_tag', TagModel.NOTAG)
        selected_style = settings.get('selected_style', None)
        dock_area = settings.get('dock_area', Qt.BottomDockWidgetArea)

        # init groups tree view
        self.groupsTreeView.setup(groups, selected_group)
        self.groupsTreeView.model().updateItems(items)
        self.groupsTreeView.setColumnHidden(GroupModel.KEY, True)

        # init tags table view
        self.tagsTableView.setup(tags, selected_tag)
        self.tagsTableView.model().updateItems(items)
        self.tagsTableView.setColumnHidden(TagModel.KEY,
                                           True)  # hide first column -> key

        # init items table view
        self.itemsTableView.setup(items)
        self.itemsTableView.setColumnHidden(ItemModel.GROUP, True)
        self.itemsTableView.setColumnHidden(ItemModel.TAGS, True)
        self.itemsTableView.setColumnHidden(ItemModel.PATH, True)
        self.itemsTableView.setColumnHidden(ItemModel.NOTES, True)

        # add dock widget
        self.addDockWidget(dock_area, self.dockProperty)

        # set style sheet
        self.main_menu.setStyleSheet(selected_style)

    def closeEvent(self, event):
        '''default method called when trying to close the app'''
        if self.main_menu.maybeSave():
            event.accept()
        else:
            event.ignore()

    def saveRequired(self):
        '''saving is required if anything is changed'''
        return (self.groupsTreeView.model().saveRequired()
                or self.tagsTableView.model().saveRequired()
                or self.itemsTableView.model().sourceModel().saveRequired())

    def serialize(self, filename):
        '''save project data to database'''
        # current group
        for index in self.groupsTreeView.selectedIndexes():
            selected_group = index.siblingAtColumn(GroupModel.KEY).data()
            break
        else:
            selected_group = self.groupsTreeView.model().ALLGROUPS

        # current tag
        for index in self.tagsTableView.selectedIndexes():
            selected_tag = index.siblingAtColumn(TagModel.KEY).data()
            break
        else:
            selected_tag = TagModel.NOTAG

        # collect all data
        data = {
            self.APP_NAME: self.APP_VERSION,
            self.KEY_GROUP: self.groupsTreeView.model().serialize(),
            self.KEY_TAG: self.tagsTableView.model().serialize(),
            self.KEY_ITEM:
            self.itemsTableView.model().sourceModel().serialize(),
            self.KEY_SETTING: {
                'selected_group': selected_group,
                'selected_tag': selected_tag,
                'selected_style': self.main_menu.getCurrentSheetStyle(),
                'dock_area': self.dockWidgetArea(self.dockProperty)
            },
        }

        # dump pickle file
        try:
            with open(filename, 'wb') as f:
                pickle.dump(data, f)
        except Exception as e:
            QMessageBox.critical(
                None, "Error",
                "Could not save current project to\n {0}.".format(filename))
        else:
            self._database = filename
            self.setting.setValue('database', filename)
            self.setTitle()
            self.statusBar().showMessage('File saved.')
            return True

        return False

    # ----------------------------------------------
    # --------------- user interface ---------------
    # ----------------------------------------------
    def setupViews(self):
        '''create main views'''

        # left widgets
        self.tabWidget = QTabWidget()
        self.groupsTreeView = GroupTreeView(['Group',
                                             'Key'])  # groups tree view
        self.tagsTableView = TagTableView(['KEY', 'TAG',
                                           'COLOR'])  # tags table view
        self.tabWidget.addTab(self.groupsTreeView, "Groups")
        self.tabWidget.addTab(self.tagsTableView, "Tags")

        # central widgets: reference item table widget
        headers = [
            'Item Title', 'Group', 'Tags', 'Path', 'Create Date', 'Notes'
        ]
        self.itemsTableView = ItemTableView(headers, self.tabWidget)

        # arranged views
        splitter = QSplitter()
        splitter.addWidget(self.tabWidget)
        splitter.addWidget(self.itemsTableView)
        splitter.setStretchFactor(0, 0)
        splitter.setStretchFactor(1, 1)
        self.setCentralWidget(splitter)

        # dock widgets
        # it has not been added to main window util called by addDockWidget() explicitly
        propWidget = PropertyWidget(self.itemsTableView)
        self.dockProperty = QDockWidget(self.tr("Properties"), self)
        self.dockProperty.setWidget(propWidget)

    def createMainMenu(self):
        '''main menu'''
        self.main_menu = MainMenu(self)
        # create menu items
        self.main_menu.createMenus()
        # set menu enable status
        self.main_menu.refreshMenus()
        # toolbar
        self.main_menu.createToolBars()

    def setTitle(self):
        '''set window title'''
        title = self._database if self._database else 'untitled.dat'
        self.setWindowTitle("Tagit - {0}".format(title))

    def createStatusBar(self):
        if self._database:
            msg = 'loading database successfully - {0}'.format(self._database)
        else:
            msg = 'New database'
        self.statusBar().showMessage(msg)
class UpdateRegistryWorker(QObject):
    '''Background worker for updating Image Registry'''

    finished = pyqtSignal(object)
    error = pyqtSignal(Exception, str)

    def __init__(self):
        QObject.__init__(self)
        self.killed = False
        self.settings = QSettings(QSettings().value("APIS/config_ini"), QSettings.IniFormat)

        # self.registryFile = pluginDir + "\\" + "apis_image_registry.json" #self.settings.value("APIS/image_registry_file", None)

        self.imageDirName = self.settings.value("APIS/image_dir")
        self.orthoDirName = self.settings.value("APIS/ortho_image_dir")
        self.imageDir = QDir(self.imageDirName)
        self.orthoDir = QDir(self.orthoDirName)

        self.imageFormats = self.settings.value("APIS/image_formats", ['jpg'])
        self.hiResFormats = self.settings.value("APIS/hires_formats", ['jpg', 'tif', 'sid', 'nef', 'raf', 'cr2', 'dng'])
        self.orthoFormats = self.settings.value("APIS/ortho_formats", ['jpg', 'tif', 'sid'])

        self.imageFormatsStr = "|".join(self.imageFormats)
        self.hiResFormatsStr = "|".join(self.hiResFormats)
        self.orthoFormatsStr = "|".join(self.orthoFormats)

    def run(self):
        try:
            self.updateImageRegistries()
            self.updateOrthoRegistries()

            if self.killed is False:
                ret = True
                ret = {
                    "imageRegistryNE": self.imageRegistryNE,
                    "hiResRegistryNE": self.hiResRegistryNE,
                    "i2cRegistryNE": self.i2cRegistryNE,
                    "orthoRegistryNE": self.orthoRegistryNE,
                    "mosaicRegistryNE": self.mosaicRegistryNE,
                    "imageRegistry": self.imageRegistry,
                    "hiResRegistry": self.hiResRegistry,
                    "i2cRegistry": self.i2cRegistry,
                    "orthoRegistry": self.orthoRegistry,
                    "mosaicRegistry": self.mosaicRegistry
                }
            else:
                ret = False
        except Exception as e:
            # forward the exception upstream
            self.error.emit(e, traceback.format_exc())
        self.finished.emit(ret)

    def kill(self):
        self.killed = True

    def updateImageRegistries(self):
        self.imageRegistry = []
        self.hiResRegistry = []
        self.i2cRegistry = []

        imageEntryList = self.imageDir.entryList(['??????????'], QDir.Dirs)
        for i in imageEntryList:
            if self.killed is True:
                # kill request received, exit loop early
                break
            iDir = QDir(self.imageDir.path() + '\\' + i)
            # FIXME implement solution for not just jpg but values from ini
            iEntryList = iDir.entryList([i + '_???.jpg'], QDir.Files)
            self.imageRegistry = self.imageRegistry + iEntryList

            hiResDirsEntryList = iDir.entryList(["highres*", "mrsid", "raw"], QDir.Dirs)
            hiResFilters = [i + '_???.' + ext for ext in self.hiResFormats]
            for hr in hiResDirsEntryList:
                if self.killed is True:
                    # kill request received, exit loop early
                    break
                hrDir = QDir(iDir.path() + '\\' + hr)
                hrEntryList = hrDir.entryList(hiResFilters, QDir.Files)
                self.hiResRegistry = self.hiResRegistry + hrEntryList

            i2cDirEntryList = iDir.entryList(["ins2cam"], QDir.Dirs)
            i2cFilters = [i + '_???.' + ext for ext in ['jpg', 'tif', 'tiff']]
            for i2c in i2cDirEntryList:
                if self.killed is True:
                    # kill request received, exit loop early
                    break
                i2cDir = QDir(iDir.path() + '\\' + i2c)
                i2cEntryList = i2cDir.entryList(i2cFilters, QDir.Files)
                self.i2cRegistry = self.i2cRegistry + i2cEntryList

        if self.killed is False:
            self.imageRegistryNE = [img[:14].replace('_', '.') for img in self.imageRegistry]
            self.hiResRegistryNE = [img[:14].replace('_', '.') for img in self.hiResRegistry]
            self.i2cRegistryNE = [img[:14].replace('_', '.') for img in self.i2cRegistry]

    def updateOrthoRegistries(self):
        self.orthoRegistryNE = []
        self.orthoRegistry = []
        self.mosaicRegistryNE = []
        self.mosaicRegistry = []
        orthoEntryList = self.orthoDir.entryList(['??????????'], QDir.Dirs)
        for o in orthoEntryList:
            if self.killed is True:
                # kill request received, exit loop early
                break
            orthoFilters = [o + '_???_op*.' + ext for ext in self.orthoFormats]
            mosaicFilters = [o + '_???_???_op*.' + ext for ext in self.orthoFormats]
            oDir = QDir(self.orthoDir.path() + '\\' + o)
            oEntryList = oDir.entryList(orthoFilters, QDir.Files)
            mEntryList = oDir.entryList(mosaicFilters, QDir.Files)
            #chekc oEntryList if _INT_op
            oEntryList = [img for img in oEntryList if img[11:14].isdigit()]
            #check mEntryList if _INT_INT_op
            mEntryList = [img for img in mEntryList if img[11:14].isdigit() and img[15:18].isdigit()]
            self.orthoRegistry = self.orthoRegistry + oEntryList
            self.mosaicRegistry = self.mosaicRegistry + mEntryList
        if self.killed is False:
            self.orthoRegistryNE = [img[:14].replace('_', '.') for img in self.orthoRegistry]
            self.mosaicRegistryNE = [f"{img[:10]}.{img[11:14]}-{img[15:18]}" for img in self.mosaicRegistry]
Ejemplo n.º 36
0
class TriblerWindow(QMainWindow):
    resize_event = pyqtSignal()
    escape_pressed = pyqtSignal()
    received_search_completions = pyqtSignal(object)

    def on_exception(self, *exc_info):
        if self.exception_handler_called:
            # We only show one feedback dialog, even when there are two consecutive exceptions.
            return

        self.exception_handler_called = True

        self.delete_tray_icon()

        # Stop the download loop
        self.downloads_page.stop_loading_downloads()

        # Add info about whether we are stopping Tribler or not
        os.environ['TRIBLER_SHUTTING_DOWN'] = str(
            self.core_manager.shutting_down)

        if not self.core_manager.shutting_down:
            self.core_manager.stop(stop_app_on_shutdown=False)

        self.setHidden(True)

        if self.debug_window:
            self.debug_window.setHidden(True)

        exception_text = "".join(traceback.format_exception(*exc_info))
        logging.error(exception_text)

        dialog = FeedbackDialog(
            self, exception_text,
            self.core_manager.events_manager.tribler_version, self.start_time)
        dialog.show()

    def __init__(self, core_args=None, core_env=None, api_port=None):
        QMainWindow.__init__(self)

        QCoreApplication.setOrganizationDomain("nl")
        QCoreApplication.setOrganizationName("TUDelft")
        QCoreApplication.setApplicationName("Tribler")
        QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

        self.gui_settings = QSettings()
        api_port = api_port or int(
            get_gui_setting(self.gui_settings, "api_port", DEFAULT_API_PORT))
        dispatcher.update_worker_settings(port=api_port)

        self.navigation_stack = []
        self.tribler_started = False
        self.tribler_settings = None
        self.debug_window = None
        self.core_manager = CoreManager(api_port)
        self.pending_requests = {}
        self.pending_uri_requests = []
        self.download_uri = None
        self.dialog = None
        self.new_version_dialog = None
        self.start_download_dialog_active = False
        self.request_mgr = None
        self.search_request_mgr = None
        self.search_suggestion_mgr = None
        self.selected_torrent_files = []
        self.vlc_available = True
        self.has_search_results = False
        self.last_search_query = None
        self.last_search_time = None
        self.start_time = time.time()
        self.exception_handler_called = False
        self.token_refresh_timer = None

        sys.excepthook = self.on_exception

        uic.loadUi(get_ui_file_path('mainwindow.ui'), self)
        TriblerRequestManager.window = self
        self.tribler_status_bar.hide()

        # Load dynamic widgets
        uic.loadUi(get_ui_file_path('torrent_channel_list_container.ui'),
                   self.channel_page_container)
        self.channel_torrents_list = self.channel_page_container.items_list
        self.channel_torrents_detail_widget = self.channel_page_container.details_tab_widget
        self.channel_torrents_detail_widget.initialize_details_widget()
        self.channel_torrents_list.itemSelectionChanged.connect(
            self.channel_page.clicked_item)

        uic.loadUi(get_ui_file_path('torrent_channel_list_container.ui'),
                   self.search_page_container)
        self.search_results_list = self.search_page_container.items_list
        self.search_torrents_detail_widget = self.search_page_container.details_tab_widget
        self.search_torrents_detail_widget.initialize_details_widget()
        self.search_results_list.itemClicked.connect(
            self.on_channel_item_click)
        self.search_results_list.itemSelectionChanged.connect(
            self.search_results_page.clicked_item)
        self.token_balance_widget.mouseReleaseEvent = self.on_token_balance_click

        def on_state_update(new_state):
            self.loading_text_label.setText(new_state)

        self.core_manager.core_state_update.connect(on_state_update)

        self.magnet_handler = MagnetHandler(self.window)
        QDesktopServices.setUrlHandler("magnet", self.magnet_handler,
                                       "on_open_magnet_link")

        self.debug_pane_shortcut = QShortcut(QKeySequence("Ctrl+d"), self)
        self.debug_pane_shortcut.activated.connect(
            self.clicked_menu_button_debug)

        # Remove the focus rect on OS X
        for widget in self.findChildren(QLineEdit) + self.findChildren(
                QListWidget) + self.findChildren(QTreeWidget):
            widget.setAttribute(Qt.WA_MacShowFocusRect, 0)

        self.menu_buttons = [
            self.left_menu_button_home, self.left_menu_button_search,
            self.left_menu_button_my_channel,
            self.left_menu_button_subscriptions,
            self.left_menu_button_video_player,
            self.left_menu_button_downloads, self.left_menu_button_discovered
        ]

        self.video_player_page.initialize_player()
        self.search_results_page.initialize_search_results_page()
        self.settings_page.initialize_settings_page()
        self.subscribed_channels_page.initialize()
        self.edit_channel_page.initialize_edit_channel_page()
        self.downloads_page.initialize_downloads_page()
        self.home_page.initialize_home_page()
        self.loading_page.initialize_loading_page()
        self.discovering_page.initialize_discovering_page()
        self.discovered_page.initialize_discovered_page()
        self.trust_page.initialize_trust_page()

        self.stackedWidget.setCurrentIndex(PAGE_LOADING)

        # Create the system tray icon
        if QSystemTrayIcon.isSystemTrayAvailable():
            self.tray_icon = QSystemTrayIcon()
            use_monochrome_icon = get_gui_setting(self.gui_settings,
                                                  "use_monochrome_icon",
                                                  False,
                                                  is_bool=True)
            self.update_tray_icon(use_monochrome_icon)

            # Create the tray icon menu
            menu = self.create_add_torrent_menu()
            show_downloads_action = QAction('Show downloads', self)
            show_downloads_action.triggered.connect(
                self.clicked_menu_button_downloads)
            token_balance_action = QAction('Show token balance', self)
            token_balance_action.triggered.connect(
                lambda: self.on_token_balance_click(None))
            quit_action = QAction('Quit Tribler', self)
            quit_action.triggered.connect(self.close_tribler)
            menu.addSeparator()
            menu.addAction(show_downloads_action)
            menu.addAction(token_balance_action)
            menu.addSeparator()
            menu.addAction(quit_action)
            self.tray_icon.setContextMenu(menu)
        else:
            self.tray_icon = None

        self.hide_left_menu_playlist()
        self.left_menu_button_debug.setHidden(True)
        self.top_menu_button.setHidden(True)
        self.left_menu.setHidden(True)
        self.token_balance_widget.setHidden(True)
        self.settings_button.setHidden(True)
        self.add_torrent_button.setHidden(True)
        self.top_search_bar.setHidden(True)

        # Set various icons
        self.top_menu_button.setIcon(QIcon(get_image_path('menu.png')))

        self.search_completion_model = QStringListModel()
        completer = QCompleter()
        completer.setModel(self.search_completion_model)
        completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.item_delegate = QStyledItemDelegate()
        completer.popup().setItemDelegate(self.item_delegate)
        completer.popup().setStyleSheet("""
        QListView {
            background-color: #404040;
        }

        QListView::item {
            color: #D0D0D0;
            padding-top: 5px;
            padding-bottom: 5px;
        }

        QListView::item:hover {
            background-color: #707070;
        }
        """)
        self.top_search_bar.setCompleter(completer)

        # Toggle debug if developer mode is enabled
        self.window().left_menu_button_debug.setHidden(not get_gui_setting(
            self.gui_settings, "debug", False, is_bool=True))

        # Start Tribler
        self.core_manager.start(core_args=core_args, core_env=core_env)

        self.core_manager.events_manager.received_search_result_channel.connect(
            self.search_results_page.received_search_result_channel)
        self.core_manager.events_manager.received_search_result_torrent.connect(
            self.search_results_page.received_search_result_torrent)
        self.core_manager.events_manager.torrent_finished.connect(
            self.on_torrent_finished)
        self.core_manager.events_manager.new_version_available.connect(
            self.on_new_version_available)
        self.core_manager.events_manager.tribler_started.connect(
            self.on_tribler_started)
        self.core_manager.events_manager.events_started.connect(
            self.on_events_started)
        self.core_manager.events_manager.low_storage_signal.connect(
            self.on_low_storage)
        self.core_manager.events_manager.credit_mining_signal.connect(
            self.on_credit_mining_error)

        # Install signal handler for ctrl+c events
        def sigint_handler(*_):
            self.close_tribler()

        signal.signal(signal.SIGINT, sigint_handler)

        self.installEventFilter(self.video_player_page)

        # Resize the window according to the settings
        center = QApplication.desktop().availableGeometry(self).center()
        pos = self.gui_settings.value(
            "pos",
            QPoint(center.x() - self.width() * 0.5,
                   center.y() - self.height() * 0.5))
        size = self.gui_settings.value("size", self.size())

        self.move(pos)
        self.resize(size)

        self.show()

    def update_tray_icon(self, use_monochrome_icon):
        if not QSystemTrayIcon.isSystemTrayAvailable():
            return

        if use_monochrome_icon:
            self.tray_icon.setIcon(
                QIcon(QPixmap(get_image_path('monochrome_tribler.png'))))
        else:
            self.tray_icon.setIcon(
                QIcon(QPixmap(get_image_path('tribler.png'))))
        self.tray_icon.show()

    def delete_tray_icon(self):
        if self.tray_icon:
            try:
                self.tray_icon.deleteLater()
            except RuntimeError:
                # The tray icon might have already been removed when unloading Qt.
                # This is due to the C code actually being asynchronous.
                logging.debug(
                    "Tray icon already removed, no further deletion necessary."
                )
            self.tray_icon = None

    def on_low_storage(self):
        """
        Dealing with low storage space available. First stop the downloads and the core manager and ask user to user to
        make free space.
        :return:
        """
        self.downloads_page.stop_loading_downloads()
        self.core_manager.stop(False)
        close_dialog = ConfirmationDialog(
            self.window(), "<b>CRITICAL ERROR</b>",
            "You are running low on disk space (<100MB). Please make sure to have "
            "sufficient free space available and restart Tribler again.",
            [("Close Tribler", BUTTON_TYPE_NORMAL)])
        close_dialog.button_clicked.connect(lambda _: self.close_tribler())
        close_dialog.show()

    def on_torrent_finished(self, torrent_info):
        self.tray_show_message(
            "Download finished",
            "Download of %s has finished." % torrent_info["name"])

    def show_loading_screen(self):
        self.top_menu_button.setHidden(True)
        self.left_menu.setHidden(True)
        self.token_balance_widget.setHidden(True)
        self.settings_button.setHidden(True)
        self.add_torrent_button.setHidden(True)
        self.top_search_bar.setHidden(True)
        self.stackedWidget.setCurrentIndex(PAGE_LOADING)

    def tray_set_tooltip(self, message):
        """
        Set a tooltip message for the tray icon, if possible.

        :param message: the message to display on hover
        """
        if self.tray_icon:
            try:
                self.tray_icon.setToolTip(message)
            except RuntimeError as e:
                logging.error("Failed to set tray tooltip: %s", str(e))

    def tray_show_message(self, title, message):
        """
        Show a message at the tray icon, if possible.

        :param title: the title of the message
        :param message: the message to display
        """
        if self.tray_icon:
            try:
                self.tray_icon.showMessage(title, message)
            except RuntimeError as e:
                logging.error("Failed to set tray message: %s", str(e))

    def on_tribler_started(self):
        self.tribler_started = True

        self.top_menu_button.setHidden(False)
        self.left_menu.setHidden(False)
        self.token_balance_widget.setHidden(False)
        self.settings_button.setHidden(False)
        self.add_torrent_button.setHidden(False)
        self.top_search_bar.setHidden(False)

        # fetch the settings, needed for the video player port
        self.request_mgr = TriblerRequestManager()
        self.fetch_settings()

        self.downloads_page.start_loading_downloads()
        self.home_page.load_popular_torrents()
        if not self.gui_settings.value(
                "first_discover",
                False) and not self.core_manager.use_existing_core:
            self.window().gui_settings.setValue("first_discover", True)
            self.discovering_page.is_discovering = True
            self.stackedWidget.setCurrentIndex(PAGE_DISCOVERING)
        else:
            self.clicked_menu_button_home()

        self.setAcceptDrops(True)

    def on_events_started(self, json_dict):
        self.setWindowTitle("Tribler %s" % json_dict["version"])

    def show_status_bar(self, message):
        self.tribler_status_bar_label.setText(message)
        self.tribler_status_bar.show()

    def hide_status_bar(self):
        self.tribler_status_bar.hide()

    def process_uri_request(self):
        """
        Process a URI request if we have one in the queue.
        """
        if len(self.pending_uri_requests) == 0:
            return

        uri = self.pending_uri_requests.pop()
        if uri.startswith('file') or uri.startswith('magnet'):
            self.start_download_from_uri(uri)

    def perform_start_download_request(self,
                                       uri,
                                       anon_download,
                                       safe_seeding,
                                       destination,
                                       selected_files,
                                       total_files=0,
                                       callback=None):
        # Check if destination directory is writable
        is_writable, error = is_dir_writable(destination)
        if not is_writable:
            gui_error_message = "Insufficient write permissions to <i>%s</i> directory. Please add proper " \
                                "write permissions on the directory and add the torrent again. %s" \
                                % (destination, error)
            ConfirmationDialog.show_message(self.window(),
                                            "Download error <i>%s</i>" % uri,
                                            gui_error_message, "OK")
            return

        selected_files_uri = ""
        if len(selected_files) != total_files:  # Not all files included
            selected_files_uri = u'&' + u''.join(
                u"selected_files[]=%s&" % quote_plus_unicode(filename)
                for filename in selected_files)[:-1]

        anon_hops = int(self.tribler_settings['download_defaults']
                        ['number_hops']) if anon_download else 0
        safe_seeding = 1 if safe_seeding else 0
        post_data = "uri=%s&anon_hops=%d&safe_seeding=%d&destination=%s%s" % (
            quote_plus_unicode(uri), anon_hops, safe_seeding, destination,
            selected_files_uri)
        post_data = post_data.encode(
            'utf-8')  # We need to send bytes in the request, not unicode

        request_mgr = TriblerRequestManager()
        request_mgr.perform_request(
            "downloads",
            callback if callback else self.on_download_added,
            method='PUT',
            data=post_data)

        # Save the download location to the GUI settings
        current_settings = get_gui_setting(self.gui_settings,
                                           "recent_download_locations", "")
        recent_locations = current_settings.split(
            ",") if len(current_settings) > 0 else []
        if isinstance(destination, unicode):
            destination = destination.encode('utf-8')
        encoded_destination = destination.encode('hex')
        if encoded_destination in recent_locations:
            recent_locations.remove(encoded_destination)
        recent_locations.insert(0, encoded_destination)

        if len(recent_locations) > 5:
            recent_locations = recent_locations[:5]

        self.gui_settings.setValue("recent_download_locations",
                                   ','.join(recent_locations))

    def on_new_version_available(self, version):
        if version == str(self.gui_settings.value('last_reported_version')):
            return

        self.new_version_dialog = ConfirmationDialog(
            self, "New version available",
            "Version %s of Tribler is available.Do you want to visit the "
            "website to download the newest version?" % version,
            [('IGNORE', BUTTON_TYPE_NORMAL), ('LATER', BUTTON_TYPE_NORMAL),
             ('OK', BUTTON_TYPE_NORMAL)])
        self.new_version_dialog.button_clicked.connect(
            lambda action: self.on_new_version_dialog_done(version, action))
        self.new_version_dialog.show()

    def on_new_version_dialog_done(self, version, action):
        if action == 0:  # ignore
            self.gui_settings.setValue("last_reported_version", version)
        elif action == 2:  # ok
            import webbrowser
            webbrowser.open("https://tribler.org")
        if self.new_version_dialog:
            self.new_version_dialog.close_dialog()
            self.new_version_dialog = None

    def on_search_text_change(self, text):
        self.search_suggestion_mgr = TriblerRequestManager()
        self.search_suggestion_mgr.perform_request(
            "search/completions?q=%s" % text,
            self.on_received_search_completions)

    def on_received_search_completions(self, completions):
        if completions is None:
            return
        self.received_search_completions.emit(completions)
        self.search_completion_model.setStringList(completions["completions"])

    def fetch_settings(self):
        self.request_mgr = TriblerRequestManager()
        self.request_mgr.perform_request("settings",
                                         self.received_settings,
                                         capture_errors=False)

    def received_settings(self, settings):
        if not settings:
            return
        # If we cannot receive the settings, stop Tribler with an option to send the crash report.
        if 'error' in settings:
            raise RuntimeError(
                TriblerRequestManager.get_message_from_error(settings))

        self.tribler_settings = settings['settings']

        # Set the video server port
        self.video_player_page.video_player_port = settings["ports"][
            "video_server~port"]

        # Disable various components based on the settings
        if not self.tribler_settings['search_community']['enabled']:
            self.window().top_search_bar.setHidden(True)
        if not self.tribler_settings['video_server']['enabled']:
            self.left_menu_button_video_player.setHidden(True)
        self.downloads_creditmining_button.setHidden(
            not self.tribler_settings["credit_mining"]["enabled"])
        self.downloads_all_button.click()

        # process pending file requests (i.e. someone clicked a torrent file when Tribler was closed)
        # We do this after receiving the settings so we have the default download location.
        self.process_uri_request()

        # Set token balance refresh timer and load the token balance
        self.token_refresh_timer = QTimer()
        self.token_refresh_timer.timeout.connect(self.load_token_balance)
        self.token_refresh_timer.start(60000)

        self.load_token_balance()

    def on_top_search_button_click(self):
        current_ts = time.time()
        current_search_query = self.top_search_bar.text()

        if self.last_search_query and self.last_search_time \
                and self.last_search_query == self.top_search_bar.text() \
                and current_ts - self.last_search_time < 1:
            logging.info(
                "Same search query already sent within 500ms so dropping this one"
            )
            return

        self.left_menu_button_search.setChecked(True)
        self.has_search_results = True
        self.clicked_menu_button_search()
        self.search_results_page.perform_search(current_search_query)
        self.search_request_mgr = TriblerRequestManager()
        self.search_request_mgr.perform_request(
            "search?q=%s" % current_search_query, None)
        self.last_search_query = current_search_query
        self.last_search_time = current_ts

    def on_settings_button_click(self):
        self.deselect_all_menu_buttons()
        self.stackedWidget.setCurrentIndex(PAGE_SETTINGS)
        self.settings_page.load_settings()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def on_token_balance_click(self, _):
        self.raise_window()
        self.deselect_all_menu_buttons()
        self.stackedWidget.setCurrentIndex(PAGE_TRUST)
        self.load_token_balance()
        self.trust_page.load_blocks()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def load_token_balance(self):
        self.request_mgr = TriblerRequestManager()
        self.request_mgr.perform_request("trustchain/statistics",
                                         self.received_trustchain_statistics,
                                         capture_errors=False)

    def received_trustchain_statistics(self, statistics):
        if not statistics or "statistics" not in statistics:
            return

        self.trust_page.received_trustchain_statistics(statistics)

        statistics = statistics["statistics"]
        if 'latest_block' in statistics:
            balance = (statistics["latest_block"]["transaction"]["total_up"] -
                       statistics["latest_block"]["transaction"]["total_down"])
            self.set_token_balance(balance)
        else:
            self.token_balance_label.setText("0 MB")

        # If trust page is currently visible, then load the graph as well
        if self.stackedWidget.currentIndex() == PAGE_TRUST:
            self.trust_page.load_blocks()

    def set_token_balance(self, balance):
        if abs(balance) > 1024**4:  # Balance is over a TB
            balance /= 1024.0**4
            self.token_balance_label.setText("%.1f TB" % balance)
        elif abs(balance) > 1024**3:  # Balance is over a GB
            balance /= 1024.0**3
            self.token_balance_label.setText("%.1f GB" % balance)
        else:
            balance /= 1024.0**2
            self.token_balance_label.setText("%d MB" % balance)

    def raise_window(self):
        self.setWindowState(self.windowState() & ~Qt.WindowMinimized
                            | Qt.WindowActive)
        self.raise_()
        self.activateWindow()

    def create_add_torrent_menu(self):
        """
        Create a menu to add new torrents. Shows when users click on the tray icon or the big plus button.
        """
        menu = TriblerActionMenu(self)

        browse_files_action = QAction('Import torrent from file', self)
        browse_directory_action = QAction('Import torrent(s) from directory',
                                          self)
        add_url_action = QAction('Import torrent from magnet/URL', self)

        browse_files_action.triggered.connect(self.on_add_torrent_browse_file)
        browse_directory_action.triggered.connect(
            self.on_add_torrent_browse_dir)
        add_url_action.triggered.connect(self.on_add_torrent_from_url)

        menu.addAction(browse_files_action)
        menu.addAction(browse_directory_action)
        menu.addAction(add_url_action)

        return menu

    def on_add_torrent_button_click(self, pos):
        self.create_add_torrent_menu().exec_(
            self.mapToGlobal(self.add_torrent_button.pos()))

    def on_add_torrent_browse_file(self):
        filenames = QFileDialog.getOpenFileNames(
            self, "Please select the .torrent file", QDir.homePath(),
            "Torrent files (*.torrent)")
        if len(filenames[0]) > 0:
            [
                self.pending_uri_requests.append(u"file:%s" % filename)
                for filename in filenames[0]
            ]
            self.process_uri_request()

    def start_download_from_uri(self, uri):
        self.download_uri = uri

        if get_gui_setting(self.gui_settings,
                           "ask_download_settings",
                           True,
                           is_bool=True):
            # If tribler settings is not available, fetch the settings and inform the user to try again.
            if not self.tribler_settings:
                self.fetch_settings()
                ConfirmationDialog.show_error(
                    self, "Download Error",
                    "Tribler settings is not available yet. "
                    "Fetching it now. Please try again later.")
                return
            # Clear any previous dialog if exists
            if self.dialog:
                self.dialog.close_dialog()
                self.dialog = None

            self.dialog = StartDownloadDialog(self, self.download_uri)
            self.dialog.button_clicked.connect(self.on_start_download_action)
            self.dialog.show()
            self.start_download_dialog_active = True
        else:
            # In the unlikely scenario that tribler settings are not available yet, try to fetch settings again and
            # add the download uri back to self.pending_uri_requests to process again.
            if not self.tribler_settings:
                self.fetch_settings()
                if self.download_uri not in self.pending_uri_requests:
                    self.pending_uri_requests.append(self.download_uri)
                return

            self.window().perform_start_download_request(
                self.download_uri,
                self.window().tribler_settings['download_defaults']
                ['anonymity_enabled'],
                self.window().tribler_settings['download_defaults']
                ['safeseeding_enabled'],
                self.tribler_settings['download_defaults']['saveas'], [], 0)
            self.process_uri_request()

    def on_start_download_action(self, action):
        if action == 1:
            if self.dialog and self.dialog.dialog_widget:
                self.window().perform_start_download_request(
                    self.download_uri,
                    self.dialog.dialog_widget.anon_download_checkbox.isChecked(
                    ),
                    self.dialog.dialog_widget.safe_seed_checkbox.isChecked(),
                    self.dialog.dialog_widget.destination_input.currentText(),
                    self.dialog.get_selected_files(),
                    self.dialog.dialog_widget.files_list_view.
                    topLevelItemCount())
            else:
                ConfirmationDialog.show_error(
                    self, "Tribler UI Error",
                    "Something went wrong. Please try again.")
                logging.exception(
                    "Error while trying to download. Either dialog or dialog.dialog_widget is None"
                )

        self.dialog.request_mgr.cancel_request(
        )  # To abort the torrent info request
        self.dialog.close_dialog()
        self.dialog = None
        self.start_download_dialog_active = False

        if action == 0:  # We do this after removing the dialog since process_uri_request is blocking
            self.process_uri_request()

    def on_add_torrent_browse_dir(self):
        chosen_dir = QFileDialog.getExistingDirectory(
            self, "Please select the directory containing the .torrent files",
            QDir.homePath(), QFileDialog.ShowDirsOnly)

        if len(chosen_dir) != 0:
            self.selected_torrent_files = [
                torrent_file
                for torrent_file in glob.glob(chosen_dir + "/*.torrent")
            ]
            self.dialog = ConfirmationDialog(
                self, "Add torrents from directory",
                "Are you sure you want to add %d torrents to Tribler?" %
                len(self.selected_torrent_files),
                [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)])
            self.dialog.button_clicked.connect(
                self.on_confirm_add_directory_dialog)
            self.dialog.show()

    def on_confirm_add_directory_dialog(self, action):
        if action == 0:
            for torrent_file in self.selected_torrent_files:
                escaped_uri = u"file:%s" % pathname2url(torrent_file)
                self.perform_start_download_request(
                    escaped_uri,
                    self.window().tribler_settings['download_defaults']
                    ['anonymity_enabled'],
                    self.window().tribler_settings['download_defaults']
                    ['safeseeding_enabled'],
                    self.tribler_settings['download_defaults']['saveas'], [],
                    0)

        if self.dialog:
            self.dialog.close_dialog()
            self.dialog = None

    def on_add_torrent_from_url(self):
        # Make sure that the window is visible (this action might be triggered from the tray icon)
        self.raise_window()

        if self.video_player_page.isVisible():
            # If we're adding a torrent from the video player page, go to the home page.
            # This is necessary since VLC takes the screen and the popup becomes invisible.
            self.clicked_menu_button_home()

        self.dialog = ConfirmationDialog(
            self,
            "Add torrent from URL/magnet link",
            "Please enter the URL/magnet link in the field below:",
            [('ADD', BUTTON_TYPE_NORMAL), ('CANCEL', BUTTON_TYPE_CONFIRM)],
            show_input=True)
        self.dialog.dialog_widget.dialog_input.setPlaceholderText(
            'URL/magnet link')
        self.dialog.dialog_widget.dialog_input.setFocus()
        self.dialog.button_clicked.connect(
            self.on_torrent_from_url_dialog_done)
        self.dialog.show()

    def on_torrent_from_url_dialog_done(self, action):
        if self.dialog and self.dialog.dialog_widget:
            uri = self.dialog.dialog_widget.dialog_input.text()

            # Remove first dialog
            self.dialog.close_dialog()
            self.dialog = None

            if action == 0:
                self.start_download_from_uri(uri)

    def on_download_added(self, result):
        if not result:
            return
        if len(self.pending_uri_requests
               ) == 0:  # Otherwise, we first process the remaining requests.
            self.window().left_menu_button_downloads.click()
        else:
            self.process_uri_request()

    def on_top_menu_button_click(self):
        if self.left_menu.isHidden():
            self.left_menu.show()
        else:
            self.left_menu.hide()

    def deselect_all_menu_buttons(self, except_select=None):
        for button in self.menu_buttons:
            if button == except_select:
                button.setEnabled(False)
                continue
            button.setEnabled(True)

            if button == self.left_menu_button_search and not self.has_search_results:
                button.setEnabled(False)

            button.setChecked(False)

    def clicked_menu_button_home(self):
        self.deselect_all_menu_buttons(self.left_menu_button_home)
        self.stackedWidget.setCurrentIndex(PAGE_HOME)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_search(self):
        self.deselect_all_menu_buttons(self.left_menu_button_search)
        self.stackedWidget.setCurrentIndex(PAGE_SEARCH_RESULTS)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_discovered(self):
        self.deselect_all_menu_buttons(self.left_menu_button_discovered)
        self.stackedWidget.setCurrentIndex(PAGE_DISCOVERED)
        self.discovered_page.load_discovered_channels()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_my_channel(self):
        self.deselect_all_menu_buttons(self.left_menu_button_my_channel)
        self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL)
        self.edit_channel_page.load_my_channel_overview()
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_video_player(self):
        self.deselect_all_menu_buttons(self.left_menu_button_video_player)
        self.stackedWidget.setCurrentIndex(PAGE_VIDEO_PLAYER)
        self.navigation_stack = []
        self.show_left_menu_playlist()

    def clicked_menu_button_downloads(self):
        self.raise_window()
        self.left_menu_button_downloads.setChecked(True)
        self.deselect_all_menu_buttons(self.left_menu_button_downloads)
        self.stackedWidget.setCurrentIndex(PAGE_DOWNLOADS)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def clicked_menu_button_debug(self):
        if not self.debug_window:
            self.debug_window = DebugWindow(
                self.tribler_settings,
                self.core_manager.events_manager.tribler_version)
        self.debug_window.show()

    def clicked_menu_button_subscriptions(self):
        self.deselect_all_menu_buttons(self.left_menu_button_subscriptions)
        self.subscribed_channels_page.load_subscribed_channels()
        self.stackedWidget.setCurrentIndex(PAGE_SUBSCRIBED_CHANNELS)
        self.navigation_stack = []
        self.hide_left_menu_playlist()

    def hide_left_menu_playlist(self):
        self.left_menu_seperator.setHidden(True)
        self.left_menu_playlist_label.setHidden(True)
        self.left_menu_playlist.setHidden(True)

    def show_left_menu_playlist(self):
        self.left_menu_seperator.setHidden(False)
        self.left_menu_playlist_label.setHidden(False)
        self.left_menu_playlist.setHidden(False)

    def on_channel_item_click(self, channel_list_item):
        list_widget = channel_list_item.listWidget()
        from TriblerGUI.widgets.channel_list_item import ChannelListItem
        if isinstance(list_widget.itemWidget(channel_list_item),
                      ChannelListItem):
            channel_info = channel_list_item.data(Qt.UserRole)
            self.channel_page.initialize_with_channel(channel_info)
            self.navigation_stack.append(self.stackedWidget.currentIndex())
            self.stackedWidget.setCurrentIndex(PAGE_CHANNEL_DETAILS)

    def on_playlist_item_click(self, playlist_list_item):
        list_widget = playlist_list_item.listWidget()
        from TriblerGUI.widgets.playlist_list_item import PlaylistListItem
        if isinstance(list_widget.itemWidget(playlist_list_item),
                      PlaylistListItem):
            playlist_info = playlist_list_item.data(Qt.UserRole)
            self.playlist_page.initialize_with_playlist(playlist_info)
            self.navigation_stack.append(self.stackedWidget.currentIndex())
            self.stackedWidget.setCurrentIndex(PAGE_PLAYLIST_DETAILS)

    def on_page_back_clicked(self):
        try:
            prev_page = self.navigation_stack.pop()
            self.stackedWidget.setCurrentIndex(prev_page)
            if prev_page == PAGE_SEARCH_RESULTS:
                self.stackedWidget.widget(
                    prev_page).load_search_results_in_list()
            if prev_page == PAGE_SUBSCRIBED_CHANNELS:
                self.stackedWidget.widget(prev_page).load_subscribed_channels()
            if prev_page == PAGE_DISCOVERED:
                self.stackedWidget.widget(prev_page).load_discovered_channels()
        except IndexError:
            logging.exception("Unknown page found in stack")

    def on_credit_mining_error(self, error):
        ConfirmationDialog.show_error(self, "Credit Mining Error",
                                      error[u'message'])

    def on_edit_channel_clicked(self):
        self.stackedWidget.setCurrentIndex(PAGE_EDIT_CHANNEL)
        self.navigation_stack = []
        self.channel_page.on_edit_channel_clicked()

    def resizeEvent(self, _):
        # Resize home page cells
        cell_width = self.home_page_table_view.width(
        ) / 3 - 3  # We have some padding to the right
        cell_height = cell_width / 2 + 60

        for i in range(0, 3):
            self.home_page_table_view.setColumnWidth(i, cell_width)
            self.home_page_table_view.setRowHeight(i, cell_height)
        self.resize_event.emit()

    def exit_full_screen(self):
        self.top_bar.show()
        self.left_menu.show()
        self.video_player_page.is_full_screen = False
        self.showNormal()

    def close_tribler(self):
        if not self.core_manager.shutting_down:

            def show_force_shutdown():
                self.loading_text_label.setText(
                    "Tribler is taking longer than expected to shut down. You can force "
                    "Tribler to shutdown by pressing the button below. This might lead "
                    "to data loss.")
                self.window().force_shutdown_btn.show()

            self.delete_tray_icon()
            self.show_loading_screen()
            self.hide_status_bar()
            self.loading_text_label.setText("Shutting down...")

            self.shutdown_timer = QTimer()
            self.shutdown_timer.timeout.connect(show_force_shutdown)
            self.shutdown_timer.start(SHUTDOWN_WAITING_PERIOD)

            self.gui_settings.setValue("pos", self.pos())
            self.gui_settings.setValue("size", self.size())

            if self.core_manager.use_existing_core:
                # Don't close the core that we are using
                QApplication.quit()

            self.core_manager.stop()
            self.core_manager.shutting_down = True
            self.downloads_page.stop_loading_downloads()
            request_queue.clear()

            # Stop the token balance timer
            if self.token_refresh_timer:
                self.token_refresh_timer.stop()

    def closeEvent(self, close_event):
        self.close_tribler()
        close_event.ignore()

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.escape_pressed.emit()
            if self.isFullScreen():
                self.exit_full_screen()

    def dragEnterEvent(self, e):
        file_urls = [_qurl_to_path(url) for url in e.mimeData().urls()
                     ] if e.mimeData().hasUrls() else []

        if any(os.path.isfile(filename) for filename in file_urls):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):
        file_urls = ([(_qurl_to_path(url), url.toString())
                      for url in e.mimeData().urls()]
                     if e.mimeData().hasUrls() else [])

        for filename, fileurl in file_urls:
            if os.path.isfile(filename):
                self.start_download_from_uri(fileurl)

        e.accept()

    def clicked_force_shutdown(self):
        process_checker = ProcessChecker()
        if process_checker.already_running:
            core_pid = process_checker.get_pid_from_lock_file()
            os.kill(int(core_pid), 9)
        # Stop the Qt application
        QApplication.quit()
Ejemplo n.º 37
0
def init():

    global COMPANY
    COMPANY = "MeltinPop"

    # SECTIONS -------------------------------

    global APPLICATION
    APPLICATION = "SpinTool"

    global DEVICES
    DEVICES = "Devices"

    global PORTS
    PORTS = "Ports"

    # Enums ----------------------------------

    global COLOR_RED
    COLOR_RED = "RED"

    global COLOR_AMBER
    COLOR_AMBER = "AMBER"

    # APPLICATION ------------------------------------------------------------------------------------------

    appSettings = QSettings(COMPANY, APPLICATION)

    # Load preferences

    global use_big_fonts_playlist
    use_big_fonts_playlist = appSettings.value('use_big_fonts_playlist',
                                               'false') == 'true'

    global use_big_fonts_scenes
    use_big_fonts_scenes = appSettings.value('use_big_fonts_scenes',
                                             'false') == 'true'

    global auto_connect_output
    auto_connect_output = appSettings.value('auto_connect_output',
                                            'true') == 'true'

    global auto_connect_input
    auto_connect_input = appSettings.value('auto_connect_input',
                                           'true') == 'true'

    # save mixer settings
    global save_mixerstrip_gain
    save_mixerstrip_gain = appSettings.value('save_mixerstrip_gain',
                                             'false') == 'true'

    global save_mixerstrip_send1
    save_mixerstrip_send1 = appSettings.value('save_mixerstrip_send1',
                                              'false') == 'true'

    global save_mixerstrip_send2
    save_mixerstrip_send2 = appSettings.value('save_mixerstrip_send2',
                                              'false') == 'true'

    global save_mixerstrip_volume
    save_mixerstrip_volume = appSettings.value('save_mixerstrip_volume',
                                               'false') == 'true'

    global save_mixerstrip_mute
    save_mixerstrip_mute = appSettings.value('save_mixerstrip_mute',
                                             'false') == 'true'

    global save_mixerstrip_to_master
    save_mixerstrip_to_master = appSettings.value('save_mixerstrip_to_master',
                                                  'false') == 'true'

    # reset all
    global customreset_mixerstrip_gain
    customreset_mixerstrip_gain = appSettings.value(
        'customreset_mixerstrip_gain', 'false') == 'true'

    global customreset_mixerstrip_send1
    customreset_mixerstrip_send1 = appSettings.value(
        'customreset_mixerstrip_send1', 'false') == 'true'

    global customreset_mixerstrip_send2
    customreset_mixerstrip_send2 = appSettings.value(
        'customreset_mixerstrip_send2', 'false') == 'true'

    global customreset_mixerstrip_volume
    customreset_mixerstrip_volume = appSettings.value(
        'customreset_mixerstrip_volume', 'false') == 'true'

    global customreset_mixerstrip_mute
    customreset_mixerstrip_mute = appSettings.value(
        'customreset_mixerstrip_mute', 'false') == 'true'

    # Performance
    global disable_shift_after_processing
    disable_shift_after_processing = appSettings.value(
        'disable_shift_after_processing', 'true') == 'true'

    global show_clip_details_on_trigger
    show_clip_details_on_trigger = appSettings.value(
        'show_clip_details_on_trigger', 'false') == 'true'

    global play_clip_after_record
    play_clip_after_record = appSettings.value('play_clip_after_record',
                                               'false') == 'true'

    global show_scenes_on_start
    show_scenes_on_start = appSettings.value('show_scenes_on_start',
                                             'false') == 'true'

    global allow_record_empty_clip
    allow_record_empty_clip = appSettings.value('allow_record_empty_clip',
                                                'false') == 'true'

    global auto_assign_new_clip_column
    auto_assign_new_clip_column = appSettings.value(
        'auto_assign_new_clip_column', 'false') == 'true'

    global show_playlist_on_start
    show_playlist_on_start = appSettings.value('show_playlist_on_start',
                                               'false') == 'true'

    global show_song_annotation_on_load
    show_song_annotation_on_load = appSettings.value(
        'show_song_annotation_on_load', 'false') == 'true'

    global slower_processing
    slower_processing = appSettings.value('slower_processing',
                                          'false') == 'true'

    global system_monitoring
    system_monitoring = appSettings.value('system_monitoring',
                                          'false') == 'true'

    global rec_color
    rec_color = appSettings.value('rec_color', COLOR_AMBER)

    global grid_rows
    grid_rows = int(appSettings.value('grid_rows', 8))

    global grid_columns
    grid_columns = int(appSettings.value('grid_columns', 8))

    global new_song_master_volume
    new_song_master_volume = int(
        appSettings.value('new_song_master_volume', 50))

    global new_song_bpm
    new_song_bpm = int(appSettings.value('new_song_bpm', 120))

    global new_song_beats
    new_song_beats = int(appSettings.value('new_song_beats', 4))

    global bigFontSize
    bigFontSize = int(appSettings.value('bigFontSize', 14))

    global prevent_song_save
    prevent_song_save = appSettings.value('prevent_song_save',
                                          'false') == 'true'

    # and windows positions

    global playlist_geometry
    playlist_geometry = appSettings.value('playlist_geometry', None)

    global scenes_geometry
    scenes_geometry = appSettings.value('scenes_geometry', None)

    global gui_geometry
    gui_geometry = appSettings.value('gui_geometry', None)

    global song_annotation_geometry
    song_annotation_geometry = appSettings.value('song_annotation_geometry',
                                                 None)

    global mixer_geometry
    mixer_geometry = appSettings.value('mixer_geometry', None)

    # session

    global paths_used
    paths_used = appSettings.value('paths_used', {})

    global playlist
    playlist = appSettings.value('playlist', []) or []

    # DEVICE ---------------------------------------------------------------------------------------------

    devSettings = QSettings(COMPANY, DEVICES)

    global devices
    devices = devSettings.value('devices', None)

    global hasDevices
    hasDevices = devSettings.contains('devices')

    # PORTS AND MIXER VALUES ------------------------------------------------------------------------------

    portSettings = QSettings(COMPANY, PORTS)

    # A dict maintaining the output ports. Every item of the list is a dict
    # which defines elements like: name:{"vol", "mute", "gain"}
    # e.g. output_ports = {"vol":1, "mute":False, "gain":0.5, "send1":0, "send2":0}
    # the default dict can be found in common.py
    global output_ports
    output_ports = portSettings.value('output_ports', None)

    # master port final volume
    global master_port_final_volume
    master_port_final_volume = float(
        portSettings.value('master_port_final_volume', 1))

    # master port mute
    global master_port_mute
    master_port_mute = portSettings.value('master_port_mute',
                                          'false') == 'true'
Ejemplo n.º 38
0
class Checkers(QMainWindow):
    def __init__(self, share_dir):
        QMainWindow.__init__(self)
        self.share_dir = share_dir
        self.setWindowTitle(_("HCheckers client"))
        self.setWindowIcon(self._icon("hcheckers.svg"))
        self.settings = QSettings("hcheckers", "hcheckers")
        self._board_setup_mode = False
        self._game_active = False
        self._connection_failed = False
        self._poll_try_number = 0
        self._ai_session = None
        self._waiting_ai_hint = False
        self.splashscreen = None
        self._show_splashcreen(_("Starting HCheckers..."))
        self.server_url = self.settings.value("server_url", DEFAULT_SERVER_URL)
        self._start_server()
        self._prepare()
        self._gui_setup()
        self._setup_actions()
        self._default_new_game()

    def get_board_setup_mode(self):
        return self._board_setup_mode

    def _enable_action(self, action, enable):
        action.setEnabled(enable)
        #w = self.toolbar.widgetForAction(action)
        #if w:
        #    print("W", w, enable)
        #    w.setVisible(enable)

    def set_board_setup_mode(self, mode):
        self._board_setup_mode = mode
        self._enable_action(self.run_action, mode)
        self._enable_action(self.put_first_action, mode)
        self._enable_action(self.put_second_action, mode)
        self._enable_action(self.erase_action, mode)

    board_setup_mode = property(get_board_setup_mode, set_board_setup_mode)

    def get_my_turn(self):
        return self.board.my_turn

    def set_my_turn(self, value):
        self.board.my_turn = value
        if value:
            self.statusBar().showMessage(_("Your turn."))
            self.board.hide_text_message()
            #self.board.repaint()
        else:
            self.statusBar().showMessage(
                _("Awaiting a turn from another side."))
        self._ai_session_dependencies()

    my_turn = property(get_my_turn, set_my_turn)

    def get_game_active(self):
        return self._game_active

    def set_game_active(self, value):
        self._game_active = value
        self.board.locked = not value

    game_active = property(get_game_active, set_game_active)

    def get_ai_session(self):
        return self._ai_session

    def set_ai_session(self, value):
        self._ai_session = value
        self._ai_session_dependencies()

    ai_session = property(get_ai_session, set_ai_session)

    def _waiting_draw_response(self):
        return self.game.draw_state == WE_REQUESTED_DRAW

    def _ai_session_dependencies(self):
        is_waiting = self._waiting_ai_hint or self._waiting_draw_response()
        stop_ai_enabled = (is_waiting or
                           not self.my_turn) and self._ai_session is not None
        self.stop_ai_action.setEnabled(self.game_active and stop_ai_enabled)
        self.ai_hint_action.setEnabled(self.game_active
                                       and not stop_ai_enabled)
        self._enable_game_control_actions(self.game_active and self.my_turn
                                          and not is_waiting)
        self._enable_file_actions((self.my_turn or not self.game_active)
                                  and not is_waiting)

    def _start_server(self):
        self.proxy_usage = self.settings.value("proxy_usage", type=int)
        self.proxy_address = self.settings.value("proxy_address",
                                                 EXAMPLE_PROXY)

        server_running = Game.check_server(
            self.server_url,
            Game.get_proxies_static(self.proxy_usage, self.proxy_address))
        use_local_server = self.settings.value("use_local_server", type=bool)
        self.local_server_used = False
        if server_running:
            if use_local_server:
                logging.info(
                    _("Server appears to be already running, do not start a local server"
                      ))
        else:
            if use_local_server:
                self.splashscreen.showMessage(_("Starting local server..."))
                QApplication.processEvents()
                server_path = self.settings.value("local_server_path")
                message = _("Running local server: {}").format(server_path)
                print(message)
                logging.info(message)
                server = subprocess.Popen(server_path,
                                          shell=True,
                                          stdout=subprocess.PIPE,
                                          stderr=subprocess.STDOUT)
                time.sleep(1)
                server.poll()
                if server.returncode is not None and server.returncode != 0:
                    output = server.stdout.read()
                    message = _(
                        "Could not start local server; exit code: {}; message:\n{}"
                    ).format(server.returncode, output.decode('utf-8'))
                    logging.error(message)
                    QMessageBox.critical(self, _("Exception"), message)
                else:
                    self.local_server_used = True

    def _prepare(self):
        if self.share_dir is None:
            raise Exception("Cant locate share directory")
        theme_name = self.settings.value("theme", "default")
        self.theme = Theme(join(self.share_dir, "themes", theme_name), None)
        self.theme.enable_sound = self.settings.value("enable_sound",
                                                      type=bool)
        self.game = Game(url=self.server_url,
                         proxy_usage=self.proxy_usage,
                         proxy_address=self.proxy_address)
        self.poll_timer = self.startTimer(500)
        self.setup_fields_on_poll = False

    def _icon(self, name):
        return QIcon(join(self.share_dir, "icons", name))

    @handling_error
    def _gui_setup(self):
        widget = QWidget(self)
        layout = QVBoxLayout()
        self.board = Board(self.theme, self.settings, self.game, self)
        self.board.message.connect(self._on_board_message)
        self.board.on_fields_setup.connect(self._on_board_update)
        self.board.field_clicked.connect(self._on_field_clicked)
        #self.board.show()
        self.toolbar = QToolBar(self)

        topbox = QHBoxLayout()
        self.message = QLabel(self)
        topbox.addWidget(self.message)
        self.count_status = CountsWidget(self)
        self.board.on_theme_changed.connect(self.count_status.update_icons)
        topbox.addStretch()
        topbox.addWidget(self.count_status)

        layout.addWidget(self.toolbar)
        layout.addLayout(topbox)
        layout.addWidget(self.board, stretch=1)
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self.status_info = QLabel(self)
        self.statusBar().addPermanentWidget(self.status_info)
        self.rules_info = QLabel(self)
        self.rules_info.setFrameStyle(QFrame.Sunken | QFrame.Panel)
        #self.rules_info.setLineWidth(3)
        self.statusBar().addPermanentWidget(self.rules_info)
        self.opponent_info = QLabel(self)
        self.opponent_info.setFrameStyle(QFrame.Sunken | QFrame.Panel)
        #self.opponent_info.setLineWidth(3)
        self.statusBar().addPermanentWidget(self.opponent_info)

        self.history = HistoryWidget(self.game, self.board, self)
        self.history_dock = QDockWidget(_("History"), self)
        self.history_dock.setAllowedAreas(Qt.AllDockWidgetAreas)
        self.history_dock.setWidget(self.history)
        self.history_dock.setObjectName("history")
        self.addDockWidget(Qt.RightDockWidgetArea, self.history_dock)

        self.log = QListWidget(self)
        self.log.setContextMenuPolicy(Qt.CustomContextMenu)
        self.log.customContextMenuRequested.connect(self._on_log_context_menu)
        self.log_dock = QDockWidget(_("Log"), self)
        self.log_dock.setAllowedAreas(Qt.AllDockWidgetAreas)
        self.log_dock.setWidget(self.log)
        self.log_dock.setObjectName("log")
        self.addDockWidget(Qt.BottomDockWidgetArea, self.log_dock)

        console_handler = logging.getLogger().handlers[0]
        logging.getLogger().removeHandler(console_handler)
        log_handler = UiLogHandler(self.log)
        level = self.settings.value("log_level", logging.INFO, type=int)
        logging.getLogger().setLevel(level)
        logging.getLogger().addHandler(log_handler)
        for logger in lowered_loggers:
            logging.getLogger(logger).addFilter(
                LogFilter(lowered_regexps=lowered_regexps))

        self.board.server_log.connect(self._on_server_log)

        geometry = self.settings.value("UI/geometry")
        if geometry is not None:
            self.restoreGeometry(geometry)
        state = self.settings.value("UI/windowState")
        if state is not None:
            self.restoreState(state)

    def _create_action(self,
                       icon,
                       title,
                       menu,
                       handler=None,
                       group=None,
                       toggle=False,
                       toolbar=True,
                       key=None):
        if group is None:
            parent = self
        else:
            parent = group
        action = QAction(title, parent)
        if icon is not None:
            action.setIcon(icon)
        if key is not None:
            action.setShortcut(key)
        if toggle:
            action.setCheckable(True)
        if toolbar:
            self.toolbar.addAction(action)
        menu.addAction(action)
        if handler:
            action.triggered.connect(handler)
        return action

    def _setup_actions(self):
        menu = self.menuBar().addMenu(_("&Game"))
        self.new_game_action = self._create_action(
            QIcon.fromTheme("document-new"),
            _("&New Game"),
            menu,
            self._on_new_game,
            key="Ctrl+N")
        self.open_game_action = self._create_action(
            QIcon.fromTheme("document-open"),
            _("&Open Game..."),
            menu,
            self._on_open_game,
            key="Ctrl+O")
        self.save_game_action = self._create_action(
            QIcon.fromTheme("document-save"),
            _("&Save Position"),
            menu,
            self._on_save_game,
            key="Ctrl+S")
        self.undo_action = self._create_action(QIcon.fromTheme("edit-undo"),
                                               _("&Undo"),
                                               menu,
                                               self._on_undo,
                                               key="Ctrl+Z")

        menu.addSeparator()
        self.toolbar.addSeparator()

        self.stop_ai_action = self._create_action(
            QIcon.fromTheme("process-stop"), _("Ask AI to stop thinking"),
            menu, self.stop_ai)
        self.stop_ai_action.setEnabled(False)
        self.ai_hint_action = self._create_action(
            QIcon.fromTheme("dialog-information"), _("Ask for AI advice"),
            menu, self._on_ai_hint)

        menu.addSeparator()
        self.toolbar.addSeparator()

        self.request_draw_action = self._create_action(
            self._icon("draw_offer.svg"), _("Offer a &draw"), menu,
            self._on_draw_rq)
        self.capitulate_action = self._create_action(self._icon("handsup.svg"),
                                                     _("Capitulate"), menu,
                                                     self._on_capitulate)

        menu.addSeparator()

        self.clear_log_action = self._create_action(
            QIcon.fromTheme("edit-clear"),
            _("&Clear log"),
            menu,
            self._on_clear_log,
            toolbar=False)
        self.copy_log_action = self._create_action(
            QIcon.fromTheme("edit-copy"),
            _("Copy selected log record"),
            menu,
            self._on_copy_log,
            toolbar=False)
        self.save_log_action = self._create_action(
            QIcon.fromTheme("document-save"),
            _("Save &log..."),
            menu,
            self._on_save_log,
            toolbar=False)

        menu.addSeparator()
        self.toolbar.addSeparator()

        self.run_action = self._create_action(
            QIcon.fromTheme("media-playback-start"),
            _("Start &Game"),
            menu,
            self._on_run_game,
            key="Ctrl+R")
        menu.addSeparator()
        self._create_action(QIcon.fromTheme("preferences-system"),
                            _("Se&ttings"),
                            menu,
                            self._on_settings,
                            toolbar=False)
        menu.addSeparator()
        self._create_action(QIcon.fromTheme("application-exit"),
                            _("E&xit"),
                            menu,
                            self._on_exit,
                            toolbar=False,
                            key="Ctrl+Q")

        menu = self.menuBar().addMenu(_("&Position"))
        self.setup_actions = setup = QActionGroup(self)
        setup.setExclusive(True)
        self.put_first_action = self._create_action(self._icon("manwhite.svg"),
                                                    _("Put &white piece"),
                                                    menu,
                                                    group=setup,
                                                    toggle=True)
        self.put_second_action = self._create_action(
            self._icon("manblack.svg"),
            _("Put &black piece"),
            menu,
            group=setup,
            toggle=True)
        self.erase_action = self._create_action(QIcon.fromTheme("list-remove"),
                                                _("&Remove piece"),
                                                menu,
                                                group=setup,
                                                toggle=True)
        self.board_setup_mode = False
        menu.addSeparator()
        self.toolbar.addSeparator()

        menu = self.menuBar().addMenu(_("&View"))
        self.flip_action = self._create_action(
            QIcon.fromTheme("object-flip-vertical"),
            _("&Flip board"),
            menu,
            self._on_flip_board,
            toggle=True,
            key="Ctrl+T")
        flip = self.settings.value("flip_board", False, type=bool)
        self.flip_action.setChecked(flip)
        self._set_flip_board(flip)
        menu.addAction(self.history_dock.toggleViewAction())
        menu.addAction(self.log_dock.toggleViewAction())

    def _game_control_actions(self):
        return [
            self.undo_action, self.request_draw_action, self.capitulate_action
        ]

    def _file_actions(self):
        return [
            self.new_game_action, self.open_game_action, self.save_game_action
        ]

    def _enable_game_control_actions(self, enable):
        for action in self._game_control_actions():
            action.setEnabled(enable)

    def _enable_file_actions(self, enable):
        for action in self._file_actions():
            action.setEnabled(enable)

    @handling_error
    def _on_run_game(self, checked=None):
        self.board_setup_mode = False
        for action in self.setup_actions.actions():
            action.setChecked(False)
        board = self.board.json()
        self.game.start_new_game(
            self.game_settings.user_name,
            rules=self.game_settings.rules,
            user_turn_first=self.game_settings.user_turn_first,
            ai=self.game_settings.ai,
            board=board)
        self.board.hide_text_message()
        self.board.fields_setup()

    @handling_error
    def _on_field_clicked(self, row, col):
        if not self.board_setup_mode:
            return

        field = self.board.fields[(row, col)]
        if not field.usable:
            logging.debug("You cant put piece at this field")
            return

        first = self.put_first_action.isChecked()
        second = self.put_second_action.isChecked()
        erase = self.erase_action.isChecked()

        if not first and not second and not erase:
            return
        if first:
            side = FIRST
        elif second:
            side = SECOND

        piece = field.piece
        if not erase:
            if piece and piece.side == side:
                if piece.kind == MAN:
                    piece.kind = KING
                else:
                    piece.kind = MAN
            else:
                piece = Piece(MAN, side)
        else:
            piece = None
        self.board.fields[(row, col)].piece = piece

    @handling_error
    def _default_new_game(self):
        self.splashscreen.finish(self)
        if len(sys.argv) == 2:
            path = sys.argv[1]
            if path.endswith(".pdn"):
                mask = PDN_MASK
            elif path.endswith(".fen"):
                mask = FEN_MASK
            else:
                mask = None
        else:
            path, mask = None, None
        self._on_new_game(show_exit=True, open_file=(path, mask))

    @handling_error
    def _on_exit(self, *args):
        self.close()

    def _screen_size(self):
        rect = QApplication.desktop().availableGeometry(self)
        return min(rect.width(), rect.height())

    def _splashscreen_size(self):
        screen_size = self._screen_size()
        return screen_size / 2

    def _show_splashcreen(self, message=None):
        splash_size = self._splashscreen_size()
        splash_pix = self._icon("splashscreen.svg").pixmap(
            QSize(splash_size, splash_size))
        self.splashscreen = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint)
        self.splashscreen.show()
        QApplication.processEvents()
        if message is not None:
            self.splashscreen.showMessage(message)
            QApplication.processEvents()

    def _new_game(self, dialog):
        # Show splashcreen after user pressed Ok in the "new game" dialog
        self._show_splashcreen(_("Starting new game..."))

        if self.game.is_active():
            self.game.capitulate()
        self.message.setText("")
        self.board.hide_text_message()
        self.game_active = True
        self.game.game_id = None

        self.request_draw_action.setEnabled(True)
        self.capitulate_action.setEnabled(True)

        self.game_settings = game = dialog.get_settings()
        if game.action == START_AI_GAME:
            if game.board_setup:
                self.board.empty()
                self.board_setup_mode = True
                self.game.rules = game.rules
            else:
                self.game.start_new_game(
                    game.user_name,
                    rules=game.rules,
                    user_turn_first=game.user_turn_first,
                    ai=game.ai,
                    fen_path=game.fen_path,
                    pdn_path=game.pdn_path,
                    previous_board_game=game.previous_board_game)
                state = self.game.get_state()
                my_side = 'First' if self.game.user_side == FIRST else 'Second'
                self.my_turn = state["side"] == my_side
                self.rules_info.setText(
                    _("Rules: {}").format(rules_dict[game.rules]))
                self.opponent_info.setText(_("AI: {}").format(game.ai.title))
                self.status_info.setText("")
        elif game.action == START_HUMAN_GAME:
            game_id = self.game.new_game(game.rules)
            logging.info(_("New game ID: {}").format(game_id))
            if game.user_turn_first:
                self.game.register_user(game.user_name, FIRST)
            else:
                self.game.register_user(game.user_name, SECOND)
            self.setup_fields_on_poll = True
            self.rules_info.setText(_("Rules: {}").format(game.rules))
            self.opponent_info.setText("")
            self.status_info.setText("")
            self.game_active = False
            message = _("Waiting for another side to join the game.")
            self.statusBar().showMessage(message)
            self.board.show_text_message(message)
        elif game.action == JOIN_HUMAN_GAME:
            self.game.game_id = dialog.lobby.get_game_id()
            self.game.user_side = side = dialog.lobby.get_free_side()
            self.game.rules = dialog.lobby.get_rules()
            #used_name = dialog.lobby.get_used_name()
            self.game.register_user(game.user_name, side)
            self.game.run_game()
            self.setup_fields_on_poll = True
            self.my_turn = side == FIRST
            self.rules_info.setText(_("Rules: {}").format(game.rules))
            self.opponent_info.setText("")
            self.status_info.setText("")

        self._enable_game_control_actions(True)

        size, invert, notation, border_notation = self.game.get_notation(
            game.rules)
        self.board.invert_colors = invert
        self.board.topology = self.game.get_topology(game.rules)
        self.board.set_notation(size, notation, border_notation)

        self.board.theme = self.board.theme
        self.board.hint_moves = None
        self.board.invalidate()
        self.board.repaint()
        self.history.fill()
        self.count_status.update_icons()

    @handling_error
    def _on_new_game(self,
                     checked=None,
                     show_exit=False,
                     open_file=(None, None)):
        dialog = NewGameDialog(self.settings,
                               self.game,
                               self.share_dir,
                               show_exit,
                               open_file=open_file,
                               parent=self)
        result = dialog.exec_()

        if result == QDialog.Accepted:
            self._new_game(dialog)

        if self.splashscreen:
            self.splashscreen.finish(self)

        if result == EXIT:
            print("Exit!")
            #QApplication.quit()
            QTimer.singleShot(0, lambda: self.close())

    @handling_error
    def _on_open_game(self, checked=None):
        path, mask = select_game_file(self)
        if path:
            dialog = NewGameDialog(self.settings,
                                   self.game,
                                   self.share_dir,
                                   show_exit=False,
                                   open_file=(path, mask),
                                   parent=self)
            result = dialog.exec_()

            if result == QDialog.Accepted:
                self._new_game(dialog)

            if self.splashscreen:
                self.splashscreen.finish(self)

    @handling_error
    def _on_save_game(self, checked=None):
        (path, mask) = QFileDialog.getSaveFileName(self.board, _("Save file"),
                                                   ".",
                                                   FEN_MASK + ";;" + PDN_MASK)
        if path:
            if mask == FEN_MASK:
                fen = self.game.get_fen()
                with open(path, 'w') as f:
                    f.write(fen)
            elif mask == PDN_MASK:
                pdn = self.game.get_pdn()
                with open(path, 'w') as f:
                    f.write(pdn)

    @handling_error
    def _on_undo(self, checked=None):
        try:
            prev_board = self.game.undo()
            self.board.fields_setup(prev_board)
            self.board.hint_moves = None
            self.board.repaint()
            self.history.fill()
        except RequestError as e:
            error = e.rs.json().get("error", None)
            if error == "NothingToUndo":
                logging.warning(_("Nothing to undo."))
            else:
                raise e

    @handling_error
    def stop_ai(self, checked=None):
        self.game.stop_ai(self.ai_session)
        self.ai_session = None
        self._waiting_ai_hint = False

    @handling_error
    def _on_ai_hint(self, checked=None):
        self._waiting_ai_hint = True
        self.ai_session = self.game.ai_hint()
        self.board.setCursor(Qt.WaitCursor)
        self.board.locked = True
        self.ai_hint_action.setEnabled(False)
        text = _("Waiting for an advice from AI")
        self.statusBar().showMessage(text)
        self.board.show_text_message(text, delay=WAITING_MOVE_MESSAGE_DELAY)

    @handling_error
    def _on_draw_rq(self, checked=None):
        ok = QMessageBox.question(
            self, _("Offer a draw?"),
            _("Are you sure you want to offer a draw to the other side? This action can not be undone."
              ))
        if ok == QMessageBox.Yes:
            self.ai_session, messages = self.game.request_draw()
            for message in messages:
                self.board.process_message(message)
            self.request_draw_action.setEnabled(False)
            self.capitulate_action.setEnabled(False)
            self.ai_hint_action.setEnabled(False)
            self.board.locked = True
            self.board.setCursor(Qt.WaitCursor)
            text = _("Waiting for a response on draw offer from another side")
            self.statusBar().showMessage(text)
            self.board.show_text_message(text,
                                         delay=WAITING_MOVE_MESSAGE_DELAY)

    @handling_error
    def _on_accept_draw(self, checked=None):
        messages = self.game.accept_draw(True)
        for message in messages:
            self.board.process_message(message)

    @handling_error
    def _on_decline_draw(self, checked=None):
        messages = self.game.accept_draw(False)
        for message in messages:
            self.board.process_message(message)

    @handling_error
    def _on_capitulate(self, checked=None):
        ok = QMessageBox.question(
            self, _("Capitulate?"),
            _("Are you sure you want to capitulate? This action can not be undone."
              ))
        if ok == QMessageBox.Yes:
            messages = self.game.capitulate()
            for message in messages:
                self.board.process_message(message)
            self.board.invalidate()
            self.board.repaint()
            #self.my_turn = False

    @handling_error
    def get_result_str(self, result):
        first, second = self.game.get_colors(self.game.rules)
        if result == 'FirstWin':
            return _("{} win").format(first)
        elif result == 'SecondWin':
            return _("{} win").format(second)
        else:
            return _("Draw")

    def _on_board_update(self):
        counts = self.board.piece_counts()
        self.count_status.set(*counts)

    def _on_board_message(self, message):
        if isinstance(message, GameResultMessage):
            self.game_active = False
            result = self.get_result_str(message.result)
            result_text = _("Game result: {}").format(result)
            self.status_info.setText(result_text)
            self.board.setCursor(Qt.ArrowCursor)
            game_over = _("Game over.")
            self.statusBar().showMessage(game_over)
            self.board.show_text_message(game_over + " " + result_text)
            self.history.fill()
            #self.request_draw_action.setEnabled(False)
            #self.capitulate_action.setEnabled(False)
            self.stop_ai_action.setEnabled(False)
            self.ai_hint_action.setEnabled(False)
            self._enable_game_control_actions(False)
            self.board.hint_moves = None
            self.board.invalidate()
            self.board.repaint()
        elif isinstance(message, OtherSideMove):
            self.message.setText(str(message))
            self.history.fill()
            self.my_turn = True
            self.board.hide_text_message()
            self.board.repaint()
        elif isinstance(message, WaitingMove):
            text = str(message)
            self.statusBar().showMessage(text)
            self.board.show_text_message(text,
                                         delay=WAITING_MOVE_MESSAGE_DELAY)
        elif isinstance(message, DrawRequestedMessage):
            ok = QMessageBox.question(
                self, _("Accept the draw?"),
                _("Another side have offered you a draw. Do you wish to accept it?"
                  ))
            if ok == QMessageBox.Yes:
                self._on_accept_draw()
            else:
                self._on_decline_draw()
        elif isinstance(message, DrawResponseMessage):
            self.board.hide_text_message()
            text = str(message)
            self.message.setText(text)
            QMessageBox.information(self, _("Response on a draw offer"), text)
            #self.board.show_text_message(text)
            #self.board.repaint()
            self.stop_ai_action.setEnabled(False)
            self.request_draw_action.setEnabled(True)
            self.capitulate_action.setEnabled(True)
            self.ai_hint_action.setEnabled(True)
            self.game.draw_state = None
            self.board.locked = False
            self.board.setCursor(Qt.ArrowCursor)
            self.statusBar().showMessage(_("Your turn."))
            self.ai_session = None
            self.board.invalidate()
            self.board.repaint()
        elif isinstance(message, AiHintMessage):
            logging.info(
                _("AI suggested the following move(s): {}").format("; ".join(
                    [self.board.show_move(m) for m in message.moves])))
            self._waiting_ai_hint = False
            self.board.hint_moves = message.moves
            self.board.setCursor(Qt.ArrowCursor)
            self.board.locked = False
            self.board.hide_text_message()
            self.statusBar().showMessage(str(message))
            self.ai_hint_action.setEnabled(True)
            self.stop_ai_action.setEnabled(False)
            self.ai_session = None
            self.board.repaint()

    def _on_server_log(self, level, message):
        if level == "DEBUG":
            logging.debug(message)
        elif level == "INFO":
            logging.info(message)
        elif level == "WARNING":
            logging.warning(message)
        elif level == "ERROR":
            logging.error(message)

    def _log_context_menu(self):
        menu = QMenu(self)
        menu.addAction(self.clear_log_action)
        menu.addAction(self.copy_log_action)
        menu.addAction(self.save_log_action)
        return menu

    def _on_log_context_menu(self, pos):
        menu = self._log_context_menu()
        menu.exec_(self.log.mapToGlobal(pos))

    def _on_clear_log(self, checked):
        self.log.clear()
        logging.info(_("Log has been cleared."))

    def _on_save_log(self, checked):
        text = ""
        for row in range(self.log.count()):
            text = text + self.log.item(row).text() + "\n"
        (path, mask) = QFileDialog.getSaveFileName(self, _("Save file"), ".",
                                                   LOG_MASK)
        if path:
            with open(path, 'w') as f:
                f.write(text)  #.encode("utf-8"))

    def _on_copy_log(self, checked):
        items = self.log.selectedItems()
        if not items:
            return
        text = items[0].text()
        QApplication.clipboard().setText(text)

    def _set_flip_board(self, value):
        self.board.flip = value
        self.settings.setValue("flip_board", self.board.flip)

    def _on_flip_board(self):
        self._set_flip_board(self.flip_action.isChecked())

    def _on_settings(self):
        dialog = SettingsDialog(self.settings, self.share_dir, self)
        result = dialog.exec_()
        if result == QDialog.Accepted:
            self.board.show_possible_moves = dialog.get_show_possible_moves()
            self.board.show_notation = dialog.get_show_notation()
            self.board.show_border = dialog.get_show_border()
            self.board.theme = dialog.get_theme()
            self.board.theme.enable_sound = dialog.get_enable_sound()
            level = self.settings.value("log_level", logging.INFO, type=int)
            logging.getLogger().setLevel(level)
            self.settings.sync()
            logging.info(_("Settings have been updated."))

    def _handle_game_error(self, rs):
        try:
            json = rs.json()
        except:
            json = None

        message_format = _(
            "Unexpected response received from the server.\nRequest URL: {}\nResponse code: {}\nResponse message: {}"
        )
        if json is None:
            message = message_format.format(rs.url, rs.status_code, rs.text)
        else:
            err_msg = json.get("error", "Unspecified")
            if err_msg == "no such move":
                move = json.get("move", "?")
                #board = Game.parse_board(json["board"])
                #board = self.board
                #possible = json.get("possible", [])
                #possible = [board.show_move(Move.fromJson(m)) for m in possible]
                message = _("No such move: {}").format(move)
            elif err_msg == "invalid game status":
                expected = json.get("expected", "?")
                actual = json.get("actual", "?")
                message = _(
                    "Status of current game is unsuitable for this operation. Game status is {}; required status is {}"
                ).format(actual, expected)
            else:
                message = message_format.format(rs.url, rs.status_code,
                                                err_msg)

        QMessageBox.critical(self, _("Exception"), message)
        logging.exception(message)

    def _handle_connection_error(self, url, e):
        message = _(
            "An exception occured while connecting to the server.\nRequest URL: {}\nException text: {}"
        ).format(url, e)
        QMessageBox.critical(self, _("Exception"), message)
        logging.exception(message)
        self._connection_failed = True

    def closeEvent(self, ev):

        if self.game.is_active():
            if self.ai_session is not None:
                try:
                    self.stop_ai()
                except Exception as e:
                    logging.exception(e)
                    print(e)

            try:
                self.game.capitulate()
            except Exception as e:
                logging.exception(e)
                print(e)

        use_local_server = self.settings.value("use_local_server", type=bool)
        if use_local_server and self.local_server_used:
            try:
                self.game.shutdown()
            except RequestError as e:
                self._handle_game_error(e.rs)
            except Exception as e:
                logging.exception(e)
                print(e)

        self.settings.setValue("UI/geometry", self.saveGeometry())
        self.settings.setValue("UI/windowState", self.saveState())
        QMainWindow.closeEvent(self, ev)

    @handling_error
    def timerEvent(self, e):
        if e.timerId() != self.poll_timer:
            return

        if self._connection_failed:
            self._poll_try_number = self._poll_try_number + 1
            if self._poll_try_number < 10:
                return

        if self.setup_fields_on_poll and not self.game_active:
            state = self.game.get_state()
            if state["status"] == 'Running':
                self.game_active = True
                my_side = 'First' if self.game.user_side == FIRST else 'Second'
                self.my_turn = state['side'] == my_side
                #self.statusBar().clearMessage()

        if not self.game.is_active():
            return

        self._poll_try_number = 0

        board, messages = self.game.poll()
        for message in messages:
            self.board.process_message(message)
            if "move" in message:
                self.my_turn = True
        if self.setup_fields_on_poll:
            self.board.fields_setup(board)
Ejemplo n.º 39
0
class Window(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setWindowIcon(QIcon(":/network.svg"))

        # fix stuff imposible to do in qtdesigner
        # remove dock titlebar for addressbar
        w = QWidget()
        self.ui.addrDockWidget.setTitleBarWidget(w)
        # tabify some docks
        self.tabifyDockWidget(self.ui.evDockWidget, self.ui.subDockWidget)
        self.tabifyDockWidget(self.ui.subDockWidget, self.ui.refDockWidget)
        self.tabifyDockWidget(self.ui.refDockWidget, self.ui.graphDockWidget)

        # we only show statusbar in case of errors
        self.ui.statusBar.hide()

        # setup QSettings for application and get a settings object
        QCoreApplication.setOrganizationName("FreeOpcUa")
        QCoreApplication.setApplicationName("OpcUaClient")
        self.settings = QSettings()

        self._address_list = self.settings.value("address_list", ["opc.tcp://localhost:4840", "opc.tcp://localhost:53530/OPCUA/SimulationServer/"])
        print("ADR", self._address_list)
        self._address_list_max_count = int(self.settings.value("address_list_max_count", 10))

        # init widgets
        for addr in self._address_list:
            self.ui.addrComboBox.insertItem(100, addr)

        self.uaclient = UaClient()

        self.tree_ui = TreeWidget(self.ui.treeView)
        self.tree_ui.error.connect(self.show_error)
        self.setup_context_menu_tree()
        self.ui.treeView.selectionModel().currentChanged.connect(self._update_actions_state)

        self.refs_ui = RefsWidget(self.ui.refView)
        self.refs_ui.error.connect(self.show_error)
        self.attrs_ui = AttrsWidget(self.ui.attrView)
        self.attrs_ui.error.connect(self.show_error)
        self.datachange_ui = DataChangeUI(self, self.uaclient)
        self.event_ui = EventUI(self, self.uaclient)
        self.graph_ui = GraphUI(self, self.uaclient)

        self.ui.addrComboBox.currentTextChanged.connect(self._uri_changed)
        self._uri_changed(self.ui.addrComboBox.currentText())  # force update for current value at startup

        self.ui.treeView.selectionModel().selectionChanged.connect(self.show_refs)
        self.ui.actionCopyPath.triggered.connect(self.tree_ui.copy_path)
        self.ui.actionCopyNodeId.triggered.connect(self.tree_ui.copy_nodeid)
        self.ui.actionCall.triggered.connect(self.call_method)

        self.ui.treeView.selectionModel().selectionChanged.connect(self.show_attrs)
        self.ui.attrRefreshButton.clicked.connect(self.show_attrs)

        self.resize(int(self.settings.value("main_window_width", 800)), int(self.settings.value("main_window_height", 600)))
        data = self.settings.value("main_window_state", None)
        if data:
            self.restoreState(data)

        self.ui.connectButton.clicked.connect(self.connect)
        self.ui.disconnectButton.clicked.connect(self.disconnect)
        # self.ui.treeView.expanded.connect(self._fit)

        self.ui.actionConnect.triggered.connect(self.connect)
        self.ui.actionDisconnect.triggered.connect(self.disconnect)

        self.ui.connectOptionButton.clicked.connect(self.show_connection_dialog)

    def _uri_changed(self, uri):
        self.uaclient.load_security_settings(uri)

    def show_connection_dialog(self):
        dia = ConnectionDialog(self, self.ui.addrComboBox.currentText())
        dia.security_mode = self.uaclient.security_mode
        dia.security_policy = self.uaclient.security_policy
        dia.certificate_path = self.uaclient.certificate_path
        dia.private_key_path = self.uaclient.private_key_path
        ret = dia.exec_()
        if ret:
            self.uaclient.security_mode = dia.security_mode
            self.uaclient.security_policy = dia.security_policy
            self.uaclient.certificate_path = dia.certificate_path
            self.uaclient.private_key_path = dia.private_key_path

    @trycatchslot
    def show_refs(self, selection):
        if isinstance(selection, QItemSelection):
            if not selection.indexes(): # no selection
                return

        node = self.get_current_node()
        if node:
            self.refs_ui.show_refs(node)
    
    @trycatchslot
    def show_attrs(self, selection):
        if isinstance(selection, QItemSelection):
            if not selection.indexes(): # no selection
                return

        node = self.get_current_node()
        if node:
            self.attrs_ui.show_attrs(node)

    def show_error(self, msg):
        logger.warning("showing error: %s")
        self.ui.statusBar.show()
        self.ui.statusBar.setStyleSheet("QStatusBar { background-color : red; color : black; }")
        self.ui.statusBar.showMessage(str(msg))
        QTimer.singleShot(1500, self.ui.statusBar.hide)

    def get_current_node(self, idx=None):
        return self.tree_ui.get_current_node(idx)

    def get_uaclient(self):
        return self.uaclient

    @trycatchslot
    def connect(self):
        uri = self.ui.addrComboBox.currentText()
        try:
            self.uaclient.connect(uri)
        except Exception as ex:
            self.show_error(ex)
            raise

        self._update_address_list(uri)
        self.tree_ui.set_root_node(self.uaclient.client.get_root_node())
        self.ui.treeView.setFocus()
        self.load_current_node()

    def _update_address_list(self, uri):
        if uri == self._address_list[0]:
            return
        if uri in self._address_list:
            self._address_list.remove(uri)
        self._address_list.insert(0, uri)
        if len(self._address_list) > self._address_list_max_count:
            self._address_list.pop(-1)

    def disconnect(self):
        try:
            self.uaclient.disconnect()
        except Exception as ex:
            self.show_error(ex)
            raise
        finally:
            self.save_current_node()
            self.tree_ui.clear()
            self.refs_ui.clear()
            self.attrs_ui.clear()
            self.datachange_ui.clear()
            self.event_ui.clear()


    def closeEvent(self, event):
        self.tree_ui.save_state()
        self.attrs_ui.save_state()
        self.refs_ui.save_state()
        self.settings.setValue("main_window_width", self.size().width())
        self.settings.setValue("main_window_height", self.size().height())
        self.settings.setValue("main_window_state", self.saveState())
        self.settings.setValue("address_list", self._address_list)
        self.disconnect()
        event.accept()

    def save_current_node(self):
        current_node = self.tree_ui.get_current_node()
        if current_node:
            mysettings = self.settings.value("current_node", None)
            if mysettings is None:
                mysettings = {}
            uri = self.ui.addrComboBox.currentText()
            mysettings[uri] = current_node.nodeid.to_string()
            self.settings.setValue("current_node", mysettings)

    def load_current_node(self):
        mysettings = self.settings.value("current_node", None)
        if mysettings is None:
            return
        uri = self.ui.addrComboBox.currentText()
        if uri in mysettings:
            nodeid = ua.NodeId.from_string(mysettings[uri])
            node = self.uaclient.client.get_node(nodeid)
            self.tree_ui.expand_to_node(node)

    def setup_context_menu_tree(self):
        self.ui.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.treeView.customContextMenuRequested.connect(self._show_context_menu_tree)
        self._contextMenu = QMenu()
        self.addAction(self.ui.actionCopyPath)
        self.addAction(self.ui.actionCopyNodeId)
        self._contextMenu.addSeparator()
        self._contextMenu.addAction(self.ui.actionCall)
        self._contextMenu.addSeparator()

    def addAction(self, action):
        self._contextMenu.addAction(action)

    @trycatchslot
    def _update_actions_state(self, current, previous):
        node = self.get_current_node(current)
        self.ui.actionCall.setEnabled(False)
        if node:
            if node.get_node_class() == ua.NodeClass.Method:
                self.ui.actionCall.setEnabled(True)

    def _show_context_menu_tree(self, position):
        node = self.tree_ui.get_current_node()
        if node:
            self._contextMenu.exec_(self.ui.treeView.viewport().mapToGlobal(position))

    def call_method(self):
        node = self.get_current_node()
        dia = CallMethodDialog(self, self.uaclient.client, node)
        dia.show()
Ejemplo n.º 40
0
class WindowSR(QMainWindow):
    """Main window of SoundRain"""

    bad_id = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.initWin()
        self.init_client_id()

    def initWin(self):
        """Create main parts of the window"""

        # Main Window
        self.setFixedSize(900, 500)
        self.center()
        self.setWindowTitle("SoundRain")
        self.setWindowIcon(QIcon('soundrainlogo.jpg'))

        # Central Widget
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        # QVBox stocking every row
        self.vertical_grid = QVBoxLayout()

        # First row: URL request
        row_url   = QHBoxLayout()
        label_url = QLabel("URL:", self)
        self.text_url  = QLineEdit(self)
        self.text_url.textChanged.connect(self.block_dl)
        self.but_url   = QPushButton("Search", self)
        self.but_url.clicked.connect(self.check_url)
        row_url.addWidget(label_url)
        row_url.addWidget(self.text_url)
        row_url.addWidget(self.but_url)

        # Row of separation 1
        row_sep1 = QHBoxLayout()
        line_sep = QFrame()
        line_sep.setFrameShape(QFrame.HLine)
        row_sep1.addWidget(line_sep)

        # Second row (splitted screen between cover image and music info): 
        row_split  = QHBoxLayout()
        # Cover image
        column_image = QVBoxLayout()
        self.label_image  = QLabel(self)
        self.label_image.setMaximumHeight(280)
        self.label_image.setMinimumHeight(280)
        self.label_image.setMaximumWidth(280)
        self.label_image.setMinimumHeight(280)
        self.cover = QPixmap(280, 280)
        self.cover.load("unknownperson.jpg")
        self.label_image.setPixmap(self.cover)
        column_image.addWidget(self.label_image)
        # music info
        column_info  = QVBoxLayout()
        label_name   = QLabel("Name", self)
        self.name    = QLineEdit(self)
        label_artist = QLabel("Artist", self)
        self.artist  = QLineEdit(self)
        label_album  = QLabel("Album", self)
        self.album   = QLineEdit(self)
        label_genre  = QLabel("Genre", self)
        self.genre   = QLineEdit(self)
        # --
        column_info.addWidget(label_name)
        column_info.addWidget(self.name)
        column_info.addWidget(label_artist)
        column_info.addWidget(self.artist)
        column_info.addWidget(label_album)
        column_info.addWidget(self.album)
        column_info.addWidget(label_genre)
        column_info.addWidget(self.genre)
        # --
        row_split.addLayout(column_image)
        row_split.addLayout(column_info)

        # Row of separation 2
        row_sep2  = QHBoxLayout()
        line_sep2 = QFrame()
        line_sep2.setFrameShape(QFrame.HLine)
        row_sep2.addWidget(line_sep2)

        # Add the file location selection row
        row_file       = QHBoxLayout()
        self.but_file  = QPushButton("Save location", self)
        self.but_file.clicked.connect(self.open_f)
        self.text_file = QLineEdit(self.default_path(), self)
        row_file.addWidget(self.but_file)
        row_file.addWidget(self.text_file)

        # Row of separation 3
        row_sep3  = QHBoxLayout()
        line_sep3 = QFrame()
        line_sep3.setFrameShape(QFrame.HLine)
        row_sep3.addWidget(line_sep3)

        # Download button row
        row_dl      = QHBoxLayout()
        self.bar_dl = QProgressBar(self)
        self.bar_dl.setFixedSize(600, 30)
        self.bar_dl.setMaximum(100)
        self.bar_dl.setMinimum(0)
        self.bar_dl.hide()
        self.label_dl = QLabel(self)
        self.label_dl.hide()
        self.but_dl = QPushButton("Download", self)
        self.but_dl.clicked.connect(self.manage_download)
        self.but_dl.setDisabled(True)
        row_dl.addWidget(self.bar_dl)
        row_dl.addWidget(self.label_dl)
        row_dl.addStretch(1)
        row_dl.addWidget(self.but_dl)

        # Add every row to the vertical grid
        self.vertical_grid.addLayout(row_url)
        self.vertical_grid.addLayout(row_sep1)
        self.vertical_grid.addLayout(row_split)
        self.vertical_grid.addLayout(row_sep2)
        self.vertical_grid.addLayout(row_file)
        self.vertical_grid.addLayout(row_sep3)
        self.vertical_grid.addLayout(row_dl)

        # Set layout of the vertical grid to the central widget
        self.central_widget.setLayout(self.vertical_grid)

        self.show()

    def init_client_id(self):
        """Ask for client id if it as never been entered, else load it from
        register with QSettings"""

        self.client_id = None
        self.setting   = QSettings(QSettings.UserScope, "BoBibelo",
                                   "SoundRain", self)
        if not self.setting.value("SR_bool"): # Setting never set
            self.client_id_box()
            self.setting.setValue("SR_bool", True)
            self.setting.setValue("SR_id", self.client_id)
        else:
            self.client_id = self.setting.value("SR_id")
        self.client = soundcloud.Client(client_id=self.client_id)

    def client_id_box(self):
        """Generate the client id box"""

        self.client_id_bo = QDialog(self)
        self.client_id_bo.setFixedSize(400, 200)
        self.client_id_bo.setModal(True)

        client_id_grid = QVBoxLayout()
        label_request  = QLabel("Enter your Soundcloud Client ID:", self.client_id_bo)
        self.input_id  = QLineEdit(self.client_id_bo)
        label_help     = QLabel("<a href=\"http://bobibelo.github.io/soundrain/help.html\">Need help ?</a>",
                                self.client_id_bo)
        label_help.setTextFormat(Qt.RichText);
        label_help.setTextInteractionFlags(Qt.TextBrowserInteraction);
        label_help.setOpenExternalLinks(True);

        self.got_id = False
        button_cancel = QPushButton("Cancel", self.client_id_bo)
        button_cancel.clicked.connect(self.reject_id)
        button_accept = QPushButton("Ok", self.client_id_bo)
        button_accept.clicked.connect(self.get_id)
        button_grid   = QHBoxLayout()
        button_grid.addStretch(1)
        button_grid.addWidget(button_cancel)
        button_grid.addWidget(button_accept)

        client_id_grid.addWidget(label_request)
        client_id_grid.addWidget(self.input_id)
        client_id_grid.addWidget(label_help)
        client_id_grid.addLayout(button_grid)
        self.client_id_bo.setLayout(client_id_grid)

        self.client_id_bo.rejected.connect(self.reject_id)
        self.client_id_bo.exec_()

    def get_id(self):
        """Get client id from the qdialog"""

        self.client_id = self.input_id.text().strip()
        if len(self.client_id) != 0:
            self.got_id = True
            self.client_id_bo.close()

    def reject_id(self):
        """Quit app after user not giving client id"""

        if not self.got_id:
            self.close()
            sys.exit(1)

    def open_f(self):
        """Choose the directory where to save music"""

        self.dirname = QFileDialog.getExistingDirectory()
        if self.dirname and len(self.dirname) > 0:
            self.text_file.setText(self.dirname)

    def center(self):
        """Places window in the screen's center"""

        center_geo = self.frameGeometry()
        center_pos = QDesktopWidget().availableGeometry().center()
        center_geo.moveCenter(center_pos)
        self.move(center_geo.topLeft())

    def check_url(self):
        """Test if the music url is correct and if so fill info"""

        url_pattern = "^https?://(www\.)?soundcloud\.com"
        if not re.match(url_pattern, self.text_url.text()):
            QMessageBox.about(self, "Invalid URL",
                              "The requested URL is invalid: %s" % self.text_url.text())
        else:
            self.but_dl.setDisabled(False)
            if "/sets/" in self.text_url.text(): # Is playlist
                self.artist.setText("Not available for playlist.")
                self.name.setText("Not available for playlist.")
                self.disable_input(True)
            elif len(self.text_url.text().split('/')) == 4: # Likes
                self.artist.setText("Not available for likes.")
                self.name.setText("Not available for likes.")
                self.disable_input(False)
            else:
                self.enable_input()
            self.get_music_info()

    def disable_input(self, bo):
        """Disable artist, and name in case of playlist"""

        if bo:
          self.is_playlist = True
          self.is_likes = False
        else:
          self.is_playlist = False
          self.is_likes = True
        self.artist.setDisabled(True)
        self.name.setDisabled(True)

    def enable_input(self):
        """Enable artist, and name after a playlist"""

        self.is_likes = False
        self.is_playlist = False
        self.artist.setDisabled(False)
        self.name.setDisabled(False)

    def get_track(self):
        """Returns track"""

        http_page = httplib2.Http()
        resp = http_page.request(self.url_str, "HEAD")
        if int(resp[0]["status"]) >= 400:
            QMessageBox.about(self,
                              "Error URL",
                              "URL doesn't exist.")
            return False

        try:
            self.track = self.client.get("/resolve", url=self.url_str)
        except:
            self.setting.setValue("SR_bool", False)
            self.init_client_id()
            self.get_track()

        return True

    def get_music_info(self):
        """Get music info, which will be stocked in self.track, and fill info"""

        self.url_str = self.text_url.text()
        if not self.get_track():
            return

        if not self.is_playlist and not self.is_likes:
            self.artist.setText(self.track.user['username'])
            self.name.setText(self.track.title)
            url = self.modifiy_image_size()
            if url:
                self.image = requests.get(url).content
                self.cover.loadFromData(self.image)
            self.cover = self.cover.scaledToWidth(280)
            self.label_image.setPixmap(self.cover)
        else:
            # Get the last part of URL ( == to playlist name)
            self.album.setText(self.text_url.text().rsplit('/', 1)[-1])
            if self.album.text() != "":
                self.text_file.setText("%s/%s" % (self.text_file.text(), self.album.text()))
        if not self.is_likes:
          self.genre.setText(self.track.genre)
        else:
          self.album.setText(self.track.username + "'s favorites")

    def modifiy_image_size(self):
        """Change artwork_url so the image can (potentially) look better"""

        artwork_url = self.track.artwork_url
        if not artwork_url:
            return None
        if "large" in artwork_url:
            return artwork_url.replace("large", "t500x500")
        else:
            return artwork_url

    def manage_download(self):
        """Manage download in case of playlist"""

        if self.is_playlist:
            playlist = self.client.get('/playlists/%s' % (self.track.id))
            count = 1
            self.label_dl.show()
            for song_url in playlist.tracks:
                self.label_dl.setText("%d / %d" % (count, len(playlist.tracks)))
                count += 1
                self.url_str = song_url["permalink_url"]
                self.get_track()
                self.image = requests.get(self.modifiy_image_size()).content
                self.download()
            if len(playlist.tracks) == 0:
                self.fail_box()
            else:
                self.success_box() # Success box for playlist
            self.label_dl.hide()
            self.enable_input()
        elif self.is_likes:
            likes = self.client.get('/users/%s/favorites/' % (self.track.id),
                                    linked_partitioning=1, limit=200)
            set_likes = set()
            while True:
              try:
                link = likes.next_href
              except:
                break
              for like in likes.collection:
                set_likes.add(like)
              likes = self.client.get(link, linked_partitioning=1,
                                      limit=200)
            for like in likes.collection:
              set_likes.add(like)
            count = 1
            self.label_dl.show()
            for like in set_likes:
              self.url_str = like.user['permalink_url']
              self.track = like
              self.label_dl.setText("%d / %d" % (count, len(set_likes)))
              count += 1
              self.image = requests.get(self.modifiy_image_size()).content
              self.download()
              sys.exit(0)
            else:
                self.success_box() # Success box for playlist
            self.label_dl.hide()
            self.enable_input()
        else:
            if self.download():
                self.success_box() # Succes box for single song

        self.reset()

    def download(self):
        """Try to download a single song"""


        self.setDisabled(True)
        self.bar_dl.setDisabled(False)
        self.bar_dl.show()
        try:
            self.fi_mp3, headers = urllib.request.urlretrieve(self.create_url(),
                                                              self.create_filename(),
                                                              reporthook=self.reporthook)
        except:
            self.fail_box()
            self.setDisabled(False)
            self.bar_dl.hide()
            return False

        self.add_tags()
        self.setDisabled(False)
        self.bar_dl.hide()
        return True

    def fail_box(self):
        """Fail box for playlist and single song"""

        if self.is_playlist:
            QMessageBox.about(self, "Error Download",
                              "Playlist '%s' failed to download." % self.text_url.text().rsplit('/', 1)[-1])
        else:
            QMessageBox.about(self, "Error Download",
                              "Download failed for song: %s" % self.track.title)


    def reset(self):
        """Reset all input & image after end of download"""

        self.text_url.setText("")
        self.artist.setText("")
        self.name.setText("")
        self.album.setText("")
        self.genre.setText("")
        self.image = None
        self.cover.load("unknownperson.jpg")
        self.label_image.setPixmap(self.cover)
        self.but_dl.setDisabled(True)
        self.text_file.setText(self.default_path())

    def block_dl(self):
        """Disable download button if user change URL (forces him to re-'search')"""

        self.but_dl.setDisabled(True)

    def add_tags(self):
        """Add artists name, music name, album, genre, and cover"""

        # Set artist name, music name, album, and genre
        audio_file = EasyMP3(self.fi_mp3)
        audio_file.tags = None
        if self.is_playlist:
            audio_file["artist"] = self.track.user["username"]
            audio_file["title"]  = self.track.title
        if self.is_likes:
            audio_file["artist"] = self.track.user["username"]
            audio_file["title"] = self.track.title
        else:
            audio_file["artist"] = self.artist.text()
            audio_file["title"]  = self.name.text()
        audio_file["genre"]  = self.genre.text()
        audio_file["album"]  = self.album.text()
        audio_file.save()

        # Determine the mime
        artwork_url = self.modifiy_image_size()
        if not artwork_url:
            return
        mime = "image/jpeg"
        if ".png" in artwork_url:
            mime = "image/png"

        # Set cover
        audio_file = MP3(self.fi_mp3, ID3=ID3)
        audio_file.tags.add(
                APIC(
                    encoding=3,
                    mime=mime,
                    type=3,
                    desc="Cover",
                    data=self.image
                )
        )
        audio_file.save()

    def success_box(self):
        """Display a sucess box"""

        if self.is_playlist:
            QMessageBox.about(self, "Success",
                              "%s playlist has just been downloaded right into %s"
                              % (self.text_url.text().rsplit('/', 1)[-1], self.text_file.text()))
        else:
            QMessageBox.about(self, "Success",
                              "%s has just been downloaded right into %s"
                              % (self.name.text(), self.text_file.text()))

    def create_url(self):
        url_str = "http://api.soundcloud.com/tracks/%s/stream?client_id=%s" % (self.track.id, self.client_id)
        return url_str

    def create_filename(self):
        path = self.text_file.text()
        if self.is_playlist or self.is_likes:
            name = self.track.title + ".mp3"
        else:
            name = self.name.text() + ".mp3"

        if not os.path.exists(path):
            os.makedirs(path)

        fi_str  = os.path.join(path, name)
        return fi_str

    def default_path(self):
        """Set the default path"""

        list_user = os.listdir("/Users")
        for user in list_user:
            if user != "Shared" and user != ".localized":
                break
        else:
            user = "******"

        path = "/Users/" + user + "/Music"

        return path

    def reporthook(self, blocks_read, block_size, total_size):
        """Tracks the progress of music download"""

        if blocks_read == 0:
            self.bar_dl.setValue(0)
        else:
            amount_read = blocks_read * block_size
            percent     = int((amount_read / total_size) * 100) # Percent of achieved download
            self.bar_dl.setValue(percent)
            QApplication.processEvents()
        return
Ejemplo n.º 41
0
class ScudCloud(QtWidgets.QMainWindow):

    forceClose = False
    messages = 0
    speller = Speller()
    title = 'ScudCloud'

    def __init__(self, debug = False, minimized = None, urgent_hint = None, settings_path = '', cache_path = ''):
        super(ScudCloud, self).__init__(None)
        self.debug = debug
        self.minimized = minimized
        self.urgent_hint = urgent_hint
        self.setWindowTitle(self.title)
        self.settings_path = settings_path
        self.cache_path = cache_path
        self.notifier = Notifier(Resources.APP_NAME, Resources.get_path('scudcloud.png'))
        self.settings = QSettings(self.settings_path + '/scudcloud_qt5.cfg', QSettings.IniFormat)
        self.notifier.enabled = self.settings.value('Notifications', defaultValue=True, type=bool)
        self.identifier = self.settings.value("Domain")
        if Unity is not None:
            self.launcher = Unity.LauncherEntry.get_for_desktop_id("scudcloud.desktop")
        else:
            self.launcher = DummyLauncher(self)
        self.webSettings()
        self.snippetsSettings()
        self.leftPane = LeftPane(self)
        self.stackedWidget = QtWidgets.QStackedWidget()
        centralWidget = QtWidgets.QWidget(self)
        layout = QtWidgets.QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.leftPane)
        layout.addWidget(self.stackedWidget)
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)
        self.startURL = Resources.SIGNIN_URL
        if self.identifier is not None:
            if isinstance(self.identifier, str):
                self.domains = self.identifier.split(",")
            else:
                self.domains = self.identifier
            self.startURL = self.normalize(self.domains[0])
        else:
            self.domains = []
        self.addWrapper(self.startURL)
        self.addMenu()
        self.tray = Systray(self)
        self.systray(self.minimized)
        self.installEventFilter(self)
        self.statusBar().showMessage('Loading Slack...')
        self.tickler = QTimer(self)
        self.tickler.setInterval(1800000)
        # Watch for ScreenLock events
        if DBusQtMainLoop is not None:
            DBusQtMainLoop(set_as_default=True)
            sessionBus = dbus.SessionBus()
            # Ubuntu 12.04 and other distros
            sessionBus.add_match_string("type='signal',interface='org.gnome.ScreenSaver'")
            # Ubuntu 14.04
            sessionBus.add_match_string("type='signal',interface='com.ubuntu.Upstart0_6'")
            # Ubuntu 16.04 and KDE
            sessionBus.add_match_string("type='signal',interface='org.freedesktop.ScreenSaver'")
            # Cinnamon
            sessionBus.add_match_string("type='signal',interface='org.cinnamon.ScreenSaver'")
            sessionBus.add_message_filter(self.screenListener)
            self.tickler.timeout.connect(self.sendTickle)
        # If dbus is not present, tickler timer will act like a blocker to not send tickle too often
        else:
            self.tickler.setSingleShot(True)
        self.tickler.start()

    def screenListener(self, bus, message):
        event = message.get_member()
        # "ActiveChanged" for Ubuntu 12.04 and other distros. "EventEmitted" for Ubuntu 14.04 and above
        if event == "ActiveChanged" or event == "EventEmitted":
            arg = message.get_args_list()[0]
            # True for Ubuntu 12.04 and other distros. "desktop-lock" for Ubuntu 14.04 and above
            if (arg == True or arg == "desktop-lock") and self.tickler.isActive():
                self.tickler.stop()
            elif (arg == False or arg == "desktop-unlock") and not self.tickler.isActive():
                self.sendTickle()
                self.tickler.start()

    def sendTickle(self):
        for i in range(0, self.stackedWidget.count()):
            self.stackedWidget.widget(i).sendTickle()

    def addWrapper(self, url):
        webView = Wrapper(self)
        webView.load(QtCore.QUrl(url))
        webView.show()
        webView.setZoomFactor(self.zoom)
        self.stackedWidget.addWidget(webView)
        self.stackedWidget.setCurrentWidget(webView)
        self.clearMemory()

    def webSettings(self):
        self.cookiesjar = PersistentCookieJar(self)
        self.zoom = self.readZoom()
        # We don't want Flash (it causes a lot of trouble in some distros)
        QWebSettings.globalSettings().setAttribute(QWebSettings.PluginsEnabled, False)
        # We don't need Java
        QWebSettings.globalSettings().setAttribute(QWebSettings.JavaEnabled, False)
        # Enabling Local Storage (now required by Slack)
        QWebSettings.globalSettings().setAttribute(QWebSettings.LocalStorageEnabled, True)
        # We need browsing history (required to not limit LocalStorage)
        QWebSettings.globalSettings().setAttribute(QWebSettings.PrivateBrowsingEnabled, False)
        # Enabling Cache
        self.diskCache = QNetworkDiskCache(self)
        self.diskCache.setCacheDirectory(self.cache_path)
        # Required for copy and paste clipboard integration
        QWebSettings.globalSettings().setAttribute(QWebSettings.JavascriptCanAccessClipboard, True)
        # Enabling Inspeclet only when --debug=True (requires more CPU usage)
        QWebSettings.globalSettings().setAttribute(QWebSettings.DeveloperExtrasEnabled, self.debug)
        # Sharing the same networkAccessManager
        self.networkAccessManager = QNetworkAccessManager(self)
        self.networkAccessManager.setCookieJar(self.cookiesjar)
        self.networkAccessManager.setCache(self.diskCache)

    def snippetsSettings(self):
        self.disable_snippets = self.settings.value("Snippets")
        if self.disable_snippets is not None:
            self.disable_snippets = self.disable_snippets == "False"
        else:
            self.disable_snippets = False
        if self.disable_snippets:
            disable_snippets_css = ''
            with open(Resources.get_path('disable_snippets.css'), 'r') as f:
                disable_snippets_css = f.read()
            with open(os.path.join(self.cache_path, 'resources.css'), 'a') as f:
                f.write(disable_snippets_css)

    def toggleFullScreen(self):
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def toggleMenuBar(self):
        menu = self.menuBar()
        state = menu.isHidden()
        menu.setVisible(state)
        if state:
            self.settings.setValue("Menu", "True")
        else:
            self.settings.setValue("Menu", "False")

    def restore(self):
        geometry = self.settings.value("geometry")
        if geometry is not None:
            self.restoreGeometry(geometry)
        windowState = self.settings.value("windowState")
        if windowState is not None:
            self.restoreState(windowState)
        else:
            self.setWindowState(QtCore.Qt.WindowMaximized)

    def systray(self, show=None):
        if show is None:
            show = self.settings.value("Systray") == "True"
        if show:
            self.tray.show()
            self.menus["file"]["close"].setEnabled(True)
            self.settings.setValue("Systray", "True")
        else:
            self.tray.setVisible(False)
            self.menus["file"]["close"].setEnabled(False)
            self.settings.setValue("Systray", "False")

    def readZoom(self):
        default = 1
        if self.settings.value("Zoom") is not None:
            default = float(self.settings.value("Zoom"))
        return default

    def setZoom(self, factor=1):
        if factor > 0:
            for i in range(0, self.stackedWidget.count()):
                widget = self.stackedWidget.widget(i)
                widget.setZoomFactor(factor)
            self.settings.setValue("Zoom", factor)

    def zoomIn(self):
        self.setZoom(self.current().zoomFactor() + 0.1)

    def zoomOut(self):
        self.setZoom(self.current().zoomFactor() - 0.1)

    def zoomReset(self):
        self.setZoom()

    def addTeam(self):
        self.switchTo(Resources.SIGNIN_URL)

    def addMenu(self):
        # We'll register the webpage shorcuts with the window too (Fixes #338)
        undo = self.current().pageAction(QWebPage.Undo)
        redo = self.current().pageAction(QWebPage.Redo)
        cut = self.current().pageAction(QWebPage.Cut)
        copy = self.current().pageAction(QWebPage.Copy)
        paste = self.current().pageAction(QWebPage.Paste)
        back = self.current().pageAction(QWebPage.Back)
        forward = self.current().pageAction(QWebPage.Forward)
        reload = self.current().pageAction(QWebPage.Reload)
        self.menus = {
            "file": {
                "preferences": self.createAction("Preferences", lambda : self.current().preferences()),
                "systray":     self.createAction("Close to Tray", self.systray, None, True),
                "addTeam":     self.createAction("Sign in to Another Team", lambda : self.addTeam()),
                "signout":     self.createAction("Signout", lambda : self.current().logout()),
                "close":       self.createAction("Close", self.close, QKeySequence.Close),
                "exit":        self.createAction("Quit", self.exit, QKeySequence.Quit)
            },
            "edit": {
                "undo":        self.createAction(undo.text(), lambda : self.current().page().triggerAction(QWebPage.Undo), undo.shortcut()),
                "redo":        self.createAction(redo.text(), lambda : self.current().page().triggerAction(QWebPage.Redo), redo.shortcut()),
                "cut":         self.createAction(cut.text(), lambda : self.current().page().triggerAction(QWebPage.Cut), cut.shortcut()),
                "copy":        self.createAction(copy.text(), lambda : self.current().page().triggerAction(QWebPage.Copy), copy.shortcut()),
                "paste":       self.createAction(paste.text(), lambda : self.current().page().triggerAction(QWebPage.Paste), paste.shortcut()),
                "back":        self.createAction(back.text(), lambda : self.current().page().triggerAction(QWebPage.Back), back.shortcut()),
                "forward":     self.createAction(forward.text(), lambda : self.current().page().triggerAction(QWebPage.Forward), forward.shortcut()),
                "reload":      self.createAction(reload.text(), lambda : self.current().page().triggerAction(QWebPage.Reload), reload.shortcut()),
            },
            "view": {
                "zoomin":      self.createAction("Zoom In", self.zoomIn, QKeySequence.ZoomIn),
                "zoomout":     self.createAction("Zoom Out", self.zoomOut, QKeySequence.ZoomOut),
                "reset":       self.createAction("Reset", self.zoomReset, QtCore.Qt.CTRL + QtCore.Qt.Key_0),
                "fullscreen":  self.createAction("Toggle Full Screen", self.toggleFullScreen, QtCore.Qt.Key_F11),
                "hidemenu":    self.createAction("Toggle Menubar", self.toggleMenuBar, QtCore.Qt.Key_F12)
            },
            "help": {
                "help":       self.createAction("Help and Feedback", lambda : self.current().help(), QKeySequence.HelpContents),
                "center":     self.createAction("Slack Help Center", lambda : self.current().helpCenter()),
                "about":      self.createAction("About", lambda : self.current().about())
             }
        }
        menu = self.menuBar()
        fileMenu = menu.addMenu("&File")
        fileMenu.addAction(self.menus["file"]["preferences"])
        fileMenu.addAction(self.menus["file"]["systray"])
        fileMenu.addSeparator()
        fileMenu.addAction(self.menus["file"]["addTeam"])
        fileMenu.addAction(self.menus["file"]["signout"])
        fileMenu.addSeparator()
        fileMenu.addAction(self.menus["file"]["close"])
        fileMenu.addAction(self.menus["file"]["exit"])
        editMenu = menu.addMenu("&Edit")
        editMenu.addAction(self.menus["edit"]["undo"])
        editMenu.addAction(self.menus["edit"]["redo"])
        editMenu.addSeparator()
        editMenu.addAction(self.menus["edit"]["cut"])
        editMenu.addAction(self.menus["edit"]["copy"])
        editMenu.addAction(self.menus["edit"]["paste"])
        editMenu.addSeparator()
        editMenu.addAction(self.menus["edit"]["back"])
        editMenu.addAction(self.menus["edit"]["forward"])
        editMenu.addAction(self.menus["edit"]["reload"])
        viewMenu = menu.addMenu("&View")
        viewMenu.addAction(self.menus["view"]["zoomin"])
        viewMenu.addAction(self.menus["view"]["zoomout"])
        viewMenu.addAction(self.menus["view"]["reset"])
        viewMenu.addSeparator()
        viewMenu.addAction(self.menus["view"]["fullscreen"])
        viewMenu.addAction(self.menus["view"]["hidemenu"])
        helpMenu = menu.addMenu("&Help")
        helpMenu.addAction(self.menus["help"]["help"])
        helpMenu.addAction(self.menus["help"]["center"])
        helpMenu.addSeparator()
        helpMenu.addAction(self.menus["help"]["about"])
        self.enableMenus(False)
        showSystray = self.settings.value("Systray") == "True"
        self.menus["file"]["systray"].setChecked(showSystray)
        self.menus["file"]["close"].setEnabled(showSystray)
        # Restore menu visibility
        visible = self.settings.value("Menu")
        if visible is not None and visible == "False":
            menu.setVisible(False)

    def enableMenus(self, enabled):
        self.menus["file"]["preferences"].setEnabled(enabled == True)
        self.menus["file"]["addTeam"].setEnabled(enabled == True)
        self.menus["file"]["signout"].setEnabled(enabled == True)
        self.menus["help"]["help"].setEnabled(enabled == True)

    def createAction(self, text, slot, shortcut=None, checkable=False):
        action = QtWidgets.QAction(text, self)
        action.triggered.connect(slot)
        if shortcut is not None:
            action.setShortcut(shortcut)
            self.addAction(action)
        if checkable:
            action.setCheckable(True)
        return action

    def normalize(self, url):
        if url.endswith(".slack.com"):
            url+= "/"
        elif not url.endswith(".slack.com/"):
            url = "https://"+url+".slack.com/"
        return url

    def current(self):
        return self.stackedWidget.currentWidget()

    def teams(self, teams):
        if len(self.domains) == 0:
            self.domains.append(teams[0]['team_url'])
        team_list = [t['team_url'] for t in teams]
        for t in teams:
            for i in range(0, len(self.domains)):
                self.domains[i] = self.normalize(self.domains[i])
                # When team_icon is missing, the team already exists (Fixes #381, #391)
                if 'team_icon' in t:
                    if self.domains[i] in team_list:
                        add = next(item for item in teams if item['team_url'] == self.domains[i])
                        if 'team_icon' in add:
                            self.leftPane.addTeam(add['id'], add['team_name'], add['team_url'], add['team_icon']['image_44'], add == teams[0])
                            # Adding new teams and saving loading positions
                            if t['team_url'] not in self.domains:
                                self.leftPane.addTeam(t['id'], t['team_name'], t['team_url'], t['team_icon']['image_44'], t == teams[0])
                                self.domains.append(t['team_url'])
                                self.settings.setValue("Domain", self.domains)
        if len(teams) > 1:
            self.leftPane.show()

    def switchTo(self, url):
        exists = False
        for i in range(0, self.stackedWidget.count()):
            if self.stackedWidget.widget(i).url().toString().startswith(url):
                self.stackedWidget.setCurrentIndex(i)
                self.quicklist(self.current().listChannels())
                self.current().setFocus()
                self.leftPane.click(i)
                self.clearMemory()
                exists = True
                break
        if not exists:
            self.addWrapper(url)

    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.ActivationChange and self.isActiveWindow():
            self.focusInEvent(event)
        if event.type() == QtCore.QEvent.KeyPress:
            # Ctrl + <n>
            modifiers = QtWidgets.QApplication.keyboardModifiers()
            if modifiers == QtCore.Qt.ControlModifier:
                if event.key() == QtCore.Qt.Key_1:   self.leftPane.click(0)
                elif event.key() == QtCore.Qt.Key_2: self.leftPane.click(1)
                elif event.key() == QtCore.Qt.Key_3: self.leftPane.click(2)
                elif event.key() == QtCore.Qt.Key_4: self.leftPane.click(3)
                elif event.key() == QtCore.Qt.Key_5: self.leftPane.click(4)
                elif event.key() == QtCore.Qt.Key_6: self.leftPane.click(5)
                elif event.key() == QtCore.Qt.Key_7: self.leftPane.click(6)
                elif event.key() == QtCore.Qt.Key_8: self.leftPane.click(7)
                elif event.key() == QtCore.Qt.Key_9: self.leftPane.click(8)
                # Ctrl + Tab
                elif event.key() == QtCore.Qt.Key_Tab: self.leftPane.clickNext(1)
            # Ctrl + BackTab
            if (modifiers & QtCore.Qt.ControlModifier) and (modifiers & QtCore.Qt.ShiftModifier):
                if event.key() == QtCore.Qt.Key_Backtab: self.leftPane.clickNext(-1)
            # Ctrl + Shift + <key>
            if (modifiers & QtCore.Qt.ShiftModifier) and (modifiers & QtCore.Qt.ShiftModifier):
                if event.key() == QtCore.Qt.Key_V: self.current().createSnippet()
        return QtWidgets.QMainWindow.eventFilter(self, obj, event);

    def focusInEvent(self, event):
        self.launcher.set_property("urgent", False)
        self.tray.stopAlert()
        # Let's tickle all teams on window focus, but only if tickle was not fired in last 30 minutes
        if DBusQtMainLoop is None and not self.tickler.isActive():
            self.sendTickle()
            self.tickler.start()

    def titleChanged(self):
        self.setWindowTitle(self.current().title())

    def setForceClose(self):
        self.forceClose = True

    def closeEvent(self, event):
        if not self.forceClose and self.settings.value("Systray") == "True":
            self.hide()
            event.ignore()
        elif self.forceClose:
            self.cookiesjar.save()
            self.settings.setValue("Domain", self.domains)
            self.settings.setValue("geometry", self.saveGeometry())
            self.settings.setValue("windowState", self.saveState())
            self.settings.setValue("Domain", self.domains)

    def show(self):
        self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
        self.activateWindow()
        self.setVisible(True)

    def exit(self):
        # Make sure tray is not visible (Fixes #513)
        self.tray.setVisible(False)
        self.setForceClose()
        self.close()

    def quicklist(self, channels):
        if Dbusmenu is not None:
            if channels is not None:
                ql = Dbusmenu.Menuitem.new()
                self.launcher.set_property("quicklist", ql)
                for c in channels:
                    if type(c) is dict and hasattr(c, '__getitem__') and c['is_member']:
                        item = Dbusmenu.Menuitem.new ()
                        item.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "#"+c['name'])
                        item.property_set ("id", c['name'])
                        item.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, True)
                        item.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, self.current().openChannel)
                        ql.child_append(item)
                self.launcher.set_property("quicklist", ql)

    def notify(self, title, message, icon):
        if self.debug: print("Notification: title [{}] message [{}] icon [{}]".format(title, message, icon))
        self.notifier.notify(title, message, icon)
        self.alert()

    def alert(self):
        if not self.isActiveWindow():
            self.launcher.set_property("urgent", True)
            self.tray.alert()
        if self.urgent_hint is True:
            QApplication.alert(self)

    def count(self):
        total = 0
        unreads = 0
        for i in range(0, self.stackedWidget.count()):
            widget = self.stackedWidget.widget(i)
            highlights = widget.highlights
            unreads+= widget.unreads
            total+=highlights
        if total > self.messages:
            self.alert()
        if 0 == total:
            self.launcher.set_property("count_visible", False)
            self.tray.setCounter(0)
            if unreads > 0:
                self.setWindowTitle("*{}".format(self.title))
            else:
                self.setWindowTitle(self.title)
        else:
            self.tray.setCounter(total)
            self.launcher.set_property("count", total)
            self.launcher.set_property("count_visible", True)
            self.setWindowTitle("[{}]{}".format(str(total), self.title))
        self.messages = total

    def clearMemory(self):
        QWebSettings.globalSettings().clearMemoryCaches()
        QWebSettings.globalSettings().clearIconDatabase()
Ejemplo n.º 42
0
    def restoreSettingsFromConfig(self):
        if self.oacmode == True:
            settings = self.settings
        else:
            settings = QSettings(QSettings.UserScope, "astrastudio", "OnAirScreen")

        # polulate text clock languages
        self.textClockLanguage.clear()
        self.textClockLanguage.addItems(self.textClockLanguages)

        settings.beginGroup("General")
        self.StationName.setText(settings.value('stationname', 'Radio Eriwan'))
        self.Slogan.setText(settings.value('slogan', 'Your question is our motivation'))
        self.setStationNameColor(self.getColorFromName(settings.value('stationcolor', '#FFAA00')))
        self.setSloganColor(self.getColorFromName(settings.value('slogancolor', '#FFAA00')))
        settings.endGroup()

        settings.beginGroup("NTP")
        self.checkBox_NTPCheck.setChecked(settings.value('ntpcheck', True, type=bool))
        self.NTPCheckServer.setText(settings.value('ntpcheckserver', 'pool.ntp.org'))
        settings.endGroup()

        settings.beginGroup("LEDS")
        self.setLEDInactiveBGColor(self.getColorFromName(settings.value('inactivebgcolor', '#222222')))
        self.setLEDInactiveFGColor(self.getColorFromName(settings.value('inactivetextcolor', '#555555')))
        settings.endGroup()

        settings.beginGroup("LED1")
        self.LED1.setChecked(settings.value('used', True, type=bool))
        self.LED1Text.setText(settings.value('text', 'ON AIR'))
        self.LED1Demo.setText(settings.value('text', 'ON AIR'))
        self.setLED1BGColor(self.getColorFromName(settings.value('activebgcolor', '#FF0000')))
        self.setLED1FGColor(self.getColorFromName(settings.value('activetextcolor', '#FFFFFF')))
        self.LED1Autoflash.setChecked(settings.value('autoflash', False, type=bool))
        self.LED1Timedflash.setChecked(settings.value('timedflash', False, type=bool))
        settings.endGroup()

        settings.beginGroup("LED2")
        self.LED2.setChecked(settings.value('used', True, type=bool))
        self.LED2Text.setText(settings.value('text', 'PHONE'))
        self.LED2Demo.setText(settings.value('text', 'PHONE'))
        self.setLED2BGColor(self.getColorFromName(settings.value('activebgcolor', '#DCDC00')))
        self.setLED2FGColor(self.getColorFromName(settings.value('activetextcolor', '#FFFFFF')))
        self.LED2Autoflash.setChecked(settings.value('autoflash', False, type=bool))
        self.LED2Timedflash.setChecked(settings.value('timedflash', False, type=bool))
        settings.endGroup()

        settings.beginGroup("LED3")
        self.LED3.setChecked(settings.value('used', True, type=bool))
        self.LED3Text.setText(settings.value('text', 'DOORBELL'))
        self.LED3Demo.setText(settings.value('text', 'DOORBELL'))
        self.setLED3BGColor(self.getColorFromName(settings.value('activebgcolor', '#00C8C8')))
        self.setLED3FGColor(self.getColorFromName(settings.value('activetextcolor', '#FFFFFF')))
        self.LED3Autoflash.setChecked(settings.value('autoflash', False, type=bool))
        self.LED3Timedflash.setChecked(settings.value('timedflash', False, type=bool))
        settings.endGroup()

        settings.beginGroup("LED4")
        self.LED4.setChecked(settings.value('used', True, type=bool))
        self.LED4Text.setText(settings.value('text', 'ARI'))
        self.LED4Demo.setText(settings.value('text', 'ARI'))
        self.setLED4BGColor(self.getColorFromName(settings.value('activebgcolor', '#FF00FF')))
        self.setLED4FGColor(self.getColorFromName(settings.value('activetextcolor', '#FFFFFF')))
        self.LED4Autoflash.setChecked(settings.value('autoflash', False, type=bool))
        self.LED4Timedflash.setChecked(settings.value('timedflash', False, type=bool))
        settings.endGroup()

        settings.beginGroup("Clock")
        self.clockDigital.setChecked(settings.value('digital', True, type=bool))
        self.clockAnalog.setChecked(not settings.value('digital', True, type=bool))
        self.showSeconds.setChecked(settings.value('showSeconds', False, type=bool))
        self.setDigitalHourColor(self.getColorFromName(settings.value('digitalhourcolor', '#3232FF')))
        self.setDigitalSecondColor(self.getColorFromName(settings.value('digitalsecondcolor', '#FF9900')))
        self.setDigitalDigitColor(self.getColorFromName(settings.value('digitaldigitcolor', '#3232FF')))
        self.logoPath.setText(
            settings.value('logopath', ':/astrastudio_logo/images/astrastudio_transparent.png'))
        settings.endGroup()

        settings.beginGroup("Network")
        self.udpport.setText(settings.value('udpport', '3310'))
        self.httpport.setText(settings.value('httpport', '8010'))
        settings.endGroup()

        settings.beginGroup("Formatting")
        self.dateFormat.setText(settings.value('dateFormat', 'dddd, dd. MMMM yyyy'))
        self.textClockLanguage.setCurrentIndex(self.textClockLanguage.findText(settings.value('textClockLanguage', 'English')))
        self.time_am_pm.setChecked(settings.value('isAmPm', False, type=bool))
        self.time_24h.setChecked(not settings.value('isAmPm', False, type=bool))
        settings.endGroup()

        settings.beginGroup("WeatherWidget")
        self.weatherWidgetEnabled.setChecked(settings.value('WeatherWidgetEnabled', False, type=bool))
        self.weatherWidgetCode.setEnabled(settings.value('WeatherWidgetEnabled', False, type=bool))
        self.weatherWidgetCode.setPlainText(settings.value('WeatherWidgetCode', weatherWidgetFallback))
        settings.endGroup()
Ejemplo n.º 43
0
    def __init__(self, mainWindow):
        QWidget.__init__(self)
        self.setupUi(self)
        self.mw = mainWindow

        # UI
        for l in [
                self.lblTitleGeneral,
                self.lblTitleGeneral_2,
                self.lblTitleViews,
                self.lblTitleLabels,
                self.lblTitleStatus,
                self.lblTitleFullscreen,
        ]:
            l.setStyleSheet(S.titleLabelSS())

        icons = [
            QIcon.fromTheme("configure"),
            QIcon.fromTheme("history-view"),
            QIcon.fromTheme("gnome-settings"),
            themeIcon("label"),
            themeIcon("status"),
            QIcon.fromTheme("preferences-desktop-theme")
        ]
        for i in range(self.lstMenu.count()):
            item = self.lstMenu.item(i)
            item.setSizeHint(QSize(item.sizeHint().width(), 42))
            item.setTextAlignment(Qt.AlignCenter)
            if icons[i]:
                item.setIcon(icons[i])
        self.lstMenu.setMaximumWidth(140)
        self.lstMenu.setMinimumWidth(140)

        # General
        self.cmbStyle.addItems(list(QStyleFactory.keys()))
        self.cmbStyle.setCurrentIndex([
            i.lower() for i in list(QStyleFactory.keys())
        ].index(qApp.style().objectName()))
        self.cmbStyle.currentIndexChanged[str].connect(self.setStyle)

        self.cmbTranslation.clear()
        tr = OrderedDict()
        tr["English"] = ""
        tr["Deutsch"] = "manuskript_de.qm"
        tr["Español"] = "manuskript_es.qm"
        tr["Français"] = "manuskript_fr.qm"
        tr["Norwegian Bokmål"] = "manuskript_nb_NO.qm"
        tr["Dutch"] = "manuskript_nl.qm"
        tr["Polish"] = "manuskript_pl.qm"
        tr["Portuguese (Brazil)"] = "manuskript_pt_BR.qm"
        tr["Portuguese (Portugal)"] = "manuskript_pt_PT.qm"
        tr["Russian"] = "manuskript_ru.qm"
        tr["Svenska"] = "manuskript_sv.qm"
        tr["Chinese"] = "manuskript_zh.qm"
        self.translations = tr

        for name in tr:
            self.cmbTranslation.addItem(name, tr[name])

        sttgs = QSettings(qApp.organizationName(), qApp.applicationName())
        if (sttgs.contains("applicationTranslation")
                and sttgs.value("applicationTranslation") in tr.values()):
            # Sets the correct translation
            self.cmbTranslation.setCurrentText([
                i for i in tr if tr[i] == sttgs.value("applicationTranslation")
            ][0])

        self.cmbTranslation.currentIndexChanged.connect(self.setTranslation)

        f = qApp.font()
        self.spnGeneralFontSize.setValue(f.pointSize())
        self.spnGeneralFontSize.valueChanged.connect(self.setAppFontSize)

        self.txtAutoSave.setValidator(QIntValidator(0, 999, self))
        self.txtAutoSaveNoChanges.setValidator(QIntValidator(0, 999, self))
        self.chkAutoSave.setChecked(settings.autoSave)
        self.chkAutoSaveNoChanges.setChecked(settings.autoSaveNoChanges)
        self.txtAutoSave.setText(str(settings.autoSaveDelay))
        self.txtAutoSaveNoChanges.setText(str(settings.autoSaveNoChangesDelay))
        self.chkSaveOnQuit.setChecked(settings.saveOnQuit)
        self.chkSaveToZip.setChecked(settings.saveToZip)
        self.chkAutoSave.stateChanged.connect(self.saveSettingsChanged)
        self.chkAutoSaveNoChanges.stateChanged.connect(
            self.saveSettingsChanged)
        self.chkSaveOnQuit.stateChanged.connect(self.saveSettingsChanged)
        self.chkSaveToZip.stateChanged.connect(self.saveSettingsChanged)
        self.txtAutoSave.textEdited.connect(self.saveSettingsChanged)
        self.txtAutoSaveNoChanges.textEdited.connect(self.saveSettingsChanged)
        autoLoad, last = self.mw.welcome.getAutoLoadValues()
        self.chkAutoLoad.setChecked(autoLoad)
        self.chkAutoLoad.stateChanged.connect(self.saveSettingsChanged)

        # Revisions
        opt = settings.revisions
        self.chkRevisionsKeep.setChecked(opt["keep"])
        self.chkRevisionsKeep.stateChanged.connect(
            self.revisionsSettingsChanged)
        self.chkRevisionRemove.setChecked(opt["smartremove"])
        self.chkRevisionRemove.toggled.connect(self.revisionsSettingsChanged)
        self.spnRevisions10Mn.setValue(60 / opt["rules"][10 * 60])
        self.spnRevisions10Mn.valueChanged.connect(
            self.revisionsSettingsChanged)
        self.spnRevisionsHour.setValue(60 * 10 / opt["rules"][60 * 60])
        self.spnRevisionsHour.valueChanged.connect(
            self.revisionsSettingsChanged)
        self.spnRevisionsDay.setValue(60 * 60 / opt["rules"][60 * 60 * 24])
        self.spnRevisionsDay.valueChanged.connect(
            self.revisionsSettingsChanged)
        self.spnRevisionsMonth.setValue(60 * 60 * 24 /
                                        opt["rules"][60 * 60 * 24 * 30])
        self.spnRevisionsMonth.valueChanged.connect(
            self.revisionsSettingsChanged)
        self.spnRevisionsEternity.setValue(60 * 60 * 24 * 7 /
                                           opt["rules"][None])
        self.spnRevisionsEternity.valueChanged.connect(
            self.revisionsSettingsChanged)

        # Views
        self.tabViews.setCurrentIndex(0)
        lst = ["Nothing", "POV", "Label", "Progress", "Compile"]
        for cmb in self.viewSettingsDatas():
            item, part = self.viewSettingsDatas()[cmb]
            cmb.setCurrentIndex(lst.index(settings.viewSettings[item][part]))
            cmb.currentIndexChanged.connect(self.viewSettingsChanged)

        for chk in self.outlineColumnsData():
            col = self.outlineColumnsData()[chk]
            chk.setChecked(col in settings.outlineViewColumns)
            chk.stateChanged.connect(self.outlineColumnsChanged)

        self.chkOutlinePOV.setVisible(
            settings.viewMode !=
            "simple")  #  Hides checkbox if non-fiction view mode

        for item, what, value in [
            (self.rdoTreeItemCount, "InfoFolder", "Count"),
            (self.rdoTreeWC, "InfoFolder", "WC"),
            (self.rdoTreeProgress, "InfoFolder", "Progress"),
            (self.rdoTreeSummary, "InfoFolder", "Summary"),
            (self.rdoTreeNothing, "InfoFolder", "Nothing"),
            (self.rdoTreeTextWC, "InfoText", "WC"),
            (self.rdoTreeTextProgress, "InfoText", "Progress"),
            (self.rdoTreeTextSummary, "InfoText", "Summary"),
            (self.rdoTreeTextNothing, "InfoText", "Nothing"),
        ]:
            item.setChecked(settings.viewSettings["Tree"][what] == value)
            item.toggled.connect(self.treeViewSettignsChanged)

        self.sldTreeIconSize.valueChanged.connect(self.treeViewSettignsChanged)
        self.sldTreeIconSize.valueChanged.connect(
            lambda v: self.lblTreeIconSize.setText("{}x{}".format(v, v)))
        self.sldTreeIconSize.setValue(
            settings.viewSettings["Tree"]["iconSize"])

        self.rdoCorkOldStyle.setChecked(settings.corkStyle == "old")
        self.rdoCorkNewStyle.setChecked(settings.corkStyle == "new")
        self.rdoCorkNewStyle.toggled.connect(self.setCorkStyle)
        self.rdoCorkOldStyle.toggled.connect(self.setCorkStyle)

        self.populatesCmbBackgrounds(self.cmbCorkImage)
        self.setCorkImageDefault()
        self.updateCorkColor()
        self.cmbCorkImage.currentIndexChanged.connect(self.setCorkBackground)
        self.btnCorkColor.clicked.connect(self.setCorkColor)

        # Text editor
        opt = settings.textEditor
        # Font
        self.setButtonColor(self.btnEditorFontColor, opt["fontColor"])
        self.btnEditorFontColor.clicked.connect(self.choseEditorFontColor)
        self.setButtonColor(self.btnEditorMisspelledColor, opt["misspelled"])
        self.btnEditorMisspelledColor.clicked.connect(
            self.choseEditorMisspelledColor)
        self.setButtonColor(self.btnEditorBackgroundColor, opt["background"])
        self.btnEditorBackgroundColor.clicked.connect(
            self.choseEditorBackgroundColor)
        self.chkEditorBackgroundTransparent.setChecked(
            opt["backgroundTransparent"])
        self.chkEditorBackgroundTransparent.stateChanged.connect(
            self.updateEditorSettings)
        self.btnEditorColorDefault.clicked.connect(self.restoreEditorColors)
        f = QFont()
        f.fromString(opt["font"])
        self.cmbEditorFontFamily.setCurrentFont(f)
        self.cmbEditorFontFamily.currentFontChanged.connect(
            self.updateEditorSettings)
        self.spnEditorFontSize.setValue(f.pointSize())
        self.spnEditorFontSize.valueChanged.connect(self.updateEditorSettings)
        # Cursor
        self.chkEditorCursorWidth.setChecked(opt["cursorWidth"] != 1)
        self.chkEditorCursorWidth.stateChanged.connect(
            self.updateEditorSettings)
        self.spnEditorCursorWidth.setValue(
            opt["cursorWidth"] if opt["cursorWidth"] != 1 else 9)
        self.spnEditorCursorWidth.valueChanged.connect(
            self.updateEditorSettings)
        self.spnEditorCursorWidth.setEnabled(opt["cursorWidth"] != 1)
        self.chkEditorNoBlinking.setChecked(opt["cursorNotBlinking"])
        self.chkEditorNoBlinking.stateChanged.connect(
            self.setApplicationCursorBlinking)
        self.chkEditorTypeWriterMode.setChecked(opt["alwaysCenter"])
        self.chkEditorTypeWriterMode.stateChanged.connect(
            self.updateEditorSettings)
        self.cmbEditorFocusMode.setCurrentIndex(
            0 if not opt["focusMode"] else 1 if opt["focusMode"] ==
            "sentence" else 2 if opt["focusMode"] == "line" else 3)
        self.cmbEditorFocusMode.currentIndexChanged.connect(
            self.updateEditorSettings)
        # Text areas
        self.chkEditorMaxWidth.setChecked(opt["maxWidth"] != 0)
        self.chkEditorMaxWidth.stateChanged.connect(self.updateEditorSettings)
        self.spnEditorMaxWidth.setEnabled(opt["maxWidth"] != 0)
        self.spnEditorMaxWidth.setValue(500 if opt["maxWidth"] ==
                                        0 else opt["maxWidth"])
        self.spnEditorMaxWidth.valueChanged.connect(self.updateEditorSettings)
        self.spnEditorMarginsLR.setValue(opt["marginsLR"])
        self.spnEditorMarginsLR.valueChanged.connect(self.updateEditorSettings)
        self.spnEditorMarginsTB.setValue(opt["marginsTB"])
        self.spnEditorMarginsTB.valueChanged.connect(self.updateEditorSettings)
        # Paragraphs
        self.cmbEditorAlignment.setCurrentIndex(opt["textAlignment"])
        self.cmbEditorAlignment.currentIndexChanged.connect(
            self.updateEditorSettings)
        self.cmbEditorLineSpacing.setCurrentIndex(
            0 if opt["lineSpacing"] == 100 else 1 if opt["lineSpacing"] ==
            150 else 2 if opt["lineSpacing"] == 200 else 3)
        self.cmbEditorLineSpacing.currentIndexChanged.connect(
            self.updateEditorSettings)
        self.spnEditorLineSpacing.setValue(opt["lineSpacing"])
        self.spnEditorLineSpacing.valueChanged.connect(
            self.updateEditorSettings)
        self.spnEditorLineSpacing.setEnabled(
            opt["lineSpacing"] not in [100, 150, 200])
        self.spnEditorLineSpacing.valueChanged.connect(
            self.updateEditorSettings)
        self.spnEditorTabWidth.setValue(opt["tabWidth"])
        self.spnEditorTabWidth.valueChanged.connect(self.updateEditorSettings)
        self.chkEditorIndent.setChecked(opt["indent"])
        self.chkEditorIndent.stateChanged.connect(self.updateEditorSettings)
        self.spnEditorParaAbove.setValue(opt["spacingAbove"])
        self.spnEditorParaAbove.valueChanged.connect(self.updateEditorSettings)
        self.spnEditorParaBelow.setValue(opt["spacingBelow"])
        self.spnEditorParaBelow.valueChanged.connect(self.updateEditorSettings)
        self.timerUpdateWidgets = QTimer()
        self.timerUpdateWidgets.setSingleShot(True)
        self.timerUpdateWidgets.setInterval(250)
        self.timerUpdateWidgets.timeout.connect(self.updateAllWidgets)

        # Labels
        self.lstLabels.setModel(self.mw.mdlLabels)
        self.lstLabels.setRowHidden(0, True)
        self.lstLabels.clicked.connect(self.updateLabelColor)
        self.btnLabelAdd.clicked.connect(self.addLabel)
        self.btnLabelRemove.clicked.connect(self.removeLabel)
        self.btnLabelColor.clicked.connect(self.setLabelColor)

        # Statuses
        self.lstStatus.setModel(self.mw.mdlStatus)
        self.lstStatus.setRowHidden(0, True)
        self.btnStatusAdd.clicked.connect(self.addStatus)
        self.btnStatusRemove.clicked.connect(self.removeStatus)

        # Fullscreen
        self._editingTheme = None
        self.btnThemeEditOK.setIcon(qApp.style().standardIcon(
            QStyle.SP_DialogApplyButton))
        self.btnThemeEditOK.clicked.connect(self.saveTheme)
        self.btnThemeEditCancel.setIcon(qApp.style().standardIcon(
            QStyle.SP_DialogCancelButton))
        self.btnThemeEditCancel.clicked.connect(self.cancelEdit)
        self.cmbThemeEdit.currentIndexChanged.connect(
            self.themeEditStack.setCurrentIndex)
        self.cmbThemeEdit.setCurrentIndex(0)
        self.cmbThemeEdit.currentIndexChanged.emit(0)
        self.themeStack.setCurrentIndex(0)
        self.lstThemes.currentItemChanged.connect(self.themeSelected)
        self.populatesThemesList()
        self.btnThemeAdd.clicked.connect(self.newTheme)
        self.btnThemeEdit.clicked.connect(self.editTheme)
        self.btnThemeRemove.clicked.connect(self.removeTheme)
        self.timerUpdateFSPreview = QTimer()
        self.timerUpdateFSPreview.setSingleShot(True)
        self.timerUpdateFSPreview.setInterval(250)
        self.timerUpdateFSPreview.timeout.connect(self.updatePreview)
Ejemplo n.º 44
0
def run_gui(api_port, api_key, root_state_dir, parsed_args):
    logger.info('Running GUI' +
                ' in gui_test_mode' if parsed_args.gui_test_mode else '')

    # Workaround for macOS Big Sur, see https://github.com/Tribler/tribler/issues/5728
    if sys.platform == "darwin":
        logger.info('Enabling a workaround for macOS Big Sur')
        os.environ["QT_MAC_WANTS_LAYER"] = "1"
    # Workaround for Ubuntu 21.04+, see https://github.com/Tribler/tribler/issues/6701
    elif sys.platform == "linux":
        logger.info(
            'Enabling a workaround for Ubuntu 21.04+ wayland environment')
        os.environ["GDK_BACKEND"] = "x11"

    # Set up logging
    load_logger_config('tribler-gui', root_state_dir)

    # Enable tracer using commandline args: --trace-debug or --trace-exceptions
    trace_logger = check_and_enable_code_tracing('gui', root_state_dir)
    try:
        enable_fault_handler(root_state_dir)
        # Exit if we cant read/write files, etc.
        check_environment()
        check_free_space()

        app_name = os.environ.get('TRIBLER_APP_NAME', 'triblerapp')
        app = TriblerApplication(app_name, sys.argv)

        # Note (@ichorid): translator MUST BE created and assigned to a separate variable
        # before calling installTranslator on app. Otherwise, it won't work for some reason
        settings = QSettings('nl.tudelft.tribler')
        translator = get_translator(settings.value('translation', None))
        app.installTranslator(translator)

        if app.is_running():
            # if an application is already running, then send the command line
            # argument to it and close the current instance
            logger.info(
                'GUI Application is already running. Passing a torrent file path to it.'
            )
            for arg in sys.argv[1:]:
                if os.path.exists(arg) and arg.endswith(".torrent"):
                    app.send_message(path_to_uri(arg))
                elif arg.startswith('magnet'):
                    app.send_message(arg)
            logger.info('Close the current application.')
            sys.exit(1)

        logger.info('Start Tribler Window')
        window = TriblerWindow(settings,
                               root_state_dir,
                               api_port=api_port,
                               api_key=api_key)
        window.setWindowTitle("Tribler")
        app.set_activation_window(window)
        app.parse_sys_args(sys.argv)
        sys.exit(app.exec_())

    except ImportError as ie:
        logger.exception(ie)
        error_and_exit("Import Error", f"Import error: {ie}")

    except TriblerException as te:
        logger.exception(te)
        error_and_exit("Tribler Exception", f"{te}")

    except SystemExit:
        logger.info("Shutting down Tribler")
        if trace_logger:
            trace_logger.close()

        # Flush all the logs to make sure it is written to file before it exits
        for handler in logging.getLogger().handlers:
            handler.flush()

        gui_sentry_reporter.global_strategy = SentryStrategy.SEND_SUPPRESSED
        raise
Ejemplo n.º 45
0
class MainWindow(QMainWindow):
    """Show the main window of SCCT."""

    EXIT_CODE_REBOOT = -123

    def __init__(self, controller, app, showChangelog):
        """Init the main window."""
        try:
            super().__init__()

            self._save = True
            self.tlock = TriggerLock()
            self.controller = controller

            self.max_no_sets = scctool.settings.max_no_sets
            self.scoreWidth = 35
            self.raceWidth = 45
            self.labelWidth = 25
            self.mimumLineEditWidth = 130

            self.createTabs()
            self.createMatchDataTabs()
            self.createHorizontalGroupBox()
            self.createLowerTabWidget()

            self.createMenuBar()

            mainLayout = QVBoxLayout()
            mainLayout.addWidget(self.tabs, 0)
            mainLayout.addWidget(self.matchDataTabWidget, 1)
            # mainLayout.addWidget(self.fromMatchDataBox, 1)
            mainLayout.addWidget(self.lowerTabWidget, 0)
            mainLayout.addWidget(self.horizontalGroupBox, 0)

            self.setWindowTitle("StarCraft Casting Tool v{}".format(
                scctool.__version__))

            self.window = QWidget()
            self.window.setLayout(mainLayout)
            self.setCentralWidget(self.window)

            # self.size
            self.statusBar()

            self.leds = dict()
            for scope in self.controller.websocketThread.get_primary_scopes():
                self.leds[scope] = LedIndicator(self)

            for key, led in self.leds.items():
                self.controller.toogleLEDs(0, key, self)
                self.statusBar().addPermanentWidget(led)

            self.app = app
            self.controller.setView(self)
            self.controller.refreshButtonStatus()

            self.processEvents()
            self.settings = QSettings(ClientConfig.APP_NAME,
                                      ClientConfig.COMPANY_NAME)
            self.restoreGeometry(
                self.settings.value("geometry", self.saveGeometry()))
            self.restoreState(
                self.settings.value("windowState", self.saveState()))

            self.mysubwindows = dict()

            self.show()
            self.raise_()

            if showChangelog:
                self.openChangelog()

        except Exception as e:
            module_logger.exception("message")

    def showAbout(self):
        """Show subwindow with about info."""
        html = markdown2.markdown_path(scctool.settings.getResFile("about.md"))

        html = html.replace("%VERSION%", scctool.__version__)
        if (not scctool.__new_version__):
            new_version = _("StarCraft Casting Tool is up to date.")
        else:
            new_version = _("The new version {} is available!").format(
                scctool.__latest_version__)
        html = html.replace('%NEW_VERSION%', new_version)

        # use self as parent here
        QMessageBox.about(self, _("StarCraft Casting Tool - About"), html)

    def closeEvent(self, event):
        """Close and clean up window."""
        try:
            try:
                for name, window in self.mysubwindows.items():
                    if (window and window.isVisible()):
                        window.close()
            finally:
                self.settings.setValue("geometry", self.saveGeometry())
                self.settings.setValue("windowState", self.saveState())
                self.controller.cleanUp(self._save)
                QMainWindow.closeEvent(self, event)
                # event.accept()
        except Exception as e:
            module_logger.exception("message")

    def createMenuBar(self):
        """Create the menu bar."""
        try:
            menubar = self.menuBar()
            settingsMenu = menubar.addMenu(_('Settings'))

            apiAct = QAction(QIcon(scctool.settings.getResFile('browser.png')),
                             _('Browser Sources'), self)
            apiAct.setToolTip(_('Edit Settings for all Browser Sources'))
            apiAct.triggered.connect(self.openBrowserSourcesDialog)
            settingsMenu.addAction(apiAct)
            apiAct = QAction(QIcon(scctool.settings.getResFile('twitch.png')),
                             _('Twitch && Nightbot'), self)
            apiAct.setToolTip(
                _('Edit Intro-Settings and API-Settings'
                  ' for Twitch and Nightbot'))
            apiAct.triggered.connect(self.openApiDialog)
            settingsMenu.addAction(apiAct)
            styleAct = QAction(
                QIcon(scctool.settings.getResFile('pantone.png')), _('Styles'),
                self)
            styleAct.setToolTip('')
            styleAct.triggered.connect(self.openStyleDialog)
            settingsMenu.addAction(styleAct)
            miscAct = QAction(
                QIcon(scctool.settings.getResFile('settings.png')), _('Misc'),
                self)
            miscAct.setToolTip('')
            miscAct.triggered.connect(self.openMiscDialog)
            settingsMenu.addAction(miscAct)

            self.createBrowserSrcMenu()

            ProfileMenu(self, self.controller)

            self.createLangMenu()

            infoMenu = menubar.addMenu(_('Info && Links'))

            myAct = QAction(QIcon(scctool.settings.getResFile('about.png')),
                            _('About'), self)
            myAct.triggered.connect(self.showAbout)
            infoMenu.addAction(myAct)

            myAct = QAction(QIcon(scctool.settings.getResFile('readme.ico')),
                            _('Readme'), self)
            myAct.triggered.connect(self.openReadme)
            infoMenu.addAction(myAct)

            websiteAct = QAction(
                QIcon(scctool.settings.getResFile('youtube.png')),
                _('Video Tutorial'), self)
            websiteAct.triggered.connect(lambda: self.controller.openURL(
                "https://youtu.be/j5iWa4JB8bM"))
            infoMenu.addAction(websiteAct)

            myAct = QAction(QIcon(scctool.settings.getResFile('update.png')),
                            _('Check for new version'), self)
            myAct.triggered.connect(lambda: self.controller.checkVersion(True))
            infoMenu.addAction(myAct)

            myAct = QAction(
                QIcon(scctool.settings.getResFile('changelog.png')),
                _('Changelog'), self)
            myAct.triggered.connect(self.openChangelog)
            infoMenu.addAction(myAct)

            myAct = QAction(QIcon(scctool.settings.getResFile('folder.png')),
                            _('Open log folder'), self)
            myAct.triggered.connect(lambda: self.controller.open_file(
                scctool.settings.getAbsPath(scctool.settings.getLogDir())))
            infoMenu.addAction(myAct)

            infoMenu.addSeparator()

            websiteAct = QAction(
                QIcon(scctool.settings.getResFile('scct.ico')),
                'StarCraft Casting Tool', self)
            websiteAct.triggered.connect(lambda: self.controller.openURL(
                "https://teampheenix.github.io/StarCraft-Casting-Tool/"))
            infoMenu.addAction(websiteAct)

            discordAct = QAction(
                QIcon(scctool.settings.getResFile('discord.png')), 'Discord',
                self)
            discordAct.triggered.connect(
                lambda: self.controller.openURL("https://discord.gg/G9hFEfh"))
            infoMenu.addAction(discordAct)

            ixAct = QAction(QIcon(scctool.settings.getResFile('icon.png')),
                            'team pheeniX', self)
            ixAct.triggered.connect(
                lambda: self.controller.openURL("http://team-pheenix.de"))
            infoMenu.addAction(ixAct)

            alphaAct = QAction(QIcon(scctool.settings.getResFile('alpha.png')),
                               'AlphaTL', self)
            alphaAct.triggered.connect(
                lambda: self.controller.openURL("https://alpha.tl"))
            infoMenu.addAction(alphaAct)

            rslAct = QAction(QIcon(scctool.settings.getResFile('rsl.png')),
                             'RSL', self)
            rslAct.triggered.connect(
                lambda: self.controller.openURL("https://rfcs.ru/"))
            infoMenu.addAction(rslAct)

            infoMenu.addSeparator()

            myAct = QAction(QIcon(scctool.settings.getResFile('patreon.png')),
                            _('Become a Patron'), self)
            myAct.triggered.connect(lambda: self.controller.openURL(
                "https://www.patreon.com/StarCraftCastingTool"))
            infoMenu.addAction(myAct)

            myAct = QAction(QIcon(scctool.settings.getResFile('donate.ico')),
                            _('Donate via PayPal'), self)
            myAct.triggered.connect(lambda: self.controller.openURL(
                "https://paypal.me/StarCraftCastingTool"))
            infoMenu.addAction(myAct)

        except Exception as e:
            module_logger.exception("message")

    def createLangMenu(self):
        menubar = self.menuBar()

        langMenu = menubar.addMenu(_('Language'))

        language = scctool.settings.config.parser.get("SCT", "language")

        languages = []
        languages.append({
            'handle': 'de_DE',
            'icon': 'de.png',
            'name': 'Deutsch',
            'active': True
        })
        languages.append({
            'handle': 'en_US',
            'icon': 'en.png',
            'name': 'English',
            'active': True
        })
        languages.append({
            'handle': 'fr_FR',
            'icon': 'fr.png',
            'name': 'Français',
            'active': True
        })
        languages.append({
            'handle': 'ru_RU',
            'icon': 'ru.png',
            'name': 'Pусский',
            'active': True
        })

        for lang in languages:
            myAct = QAction(QIcon(scctool.settings.getResFile(lang['icon'])),
                            lang['name'],
                            self,
                            checkable=True)
            myAct.setChecked(language == lang['handle'])
            myAct.setDisabled(not lang['active'])
            myAct.triggered.connect(
                lambda x, handle=lang['handle']: self.changeLanguage(handle))
            langMenu.addAction(myAct)

    def createBrowserSrcMenu(self):
        menubar = self.menuBar()
        main_menu = menubar.addMenu(_('Browser Sources'))

        srcs = []
        srcs.append({
            'name': _('Intro'),
            'file': 'intro.html',
            'settings': lambda: self.openBrowserSourcesDialog('intro')
        })
        srcs.append({
            'name':
            _('Mapstats'),
            'file':
            'mapstats.html',
            'settings':
            lambda: self.openBrowserSourcesDialog('mapstats')
        })
        srcs.append({'name': _('Score'), 'file': 'score.html'})
        srcs.append({
            'name':
            _('Map Icons Box'),
            'settings':
            lambda: self.openBrowserSourcesDialog('mapicons_box'),
            'sub': [{
                'name': _('Icon Set {}').format(1),
                'file': 'mapicons_box_1.html'
            }, {
                'name': _('Icon Set {}').format(2),
                'file': 'mapicons_box_2.html'
            }, {
                'name': _('Icon Set {}').format(3),
                'file': 'mapicons_box_3.html'
            }]
        })
        srcs.append({
            'name':
            _('Map Icons Landscape'),
            'settings':
            lambda: self.openBrowserSourcesDialog("mapicons_landscape"),
            'sub': [{
                'name': _('Icon Set {}').format(1),
                'file': 'mapicons_landscape_1.html'
            }, {
                'name': _('Icon Set {}').format(2),
                'file': 'mapicons_landscape_2.html'
            }, {
                'name': _('Icon Set {}').format(3),
                'file': 'mapicons_landscape_3.html'
            }]
        })
        srcs.append({
            'name':
            _('Misc'),
            'sub': [{
                'name': _('Logo {}').format(1),
                'file': 'logo1.html'
            }, {
                'name': _('Logo {}').format(2),
                'file': 'logo2.html'
            }, {
                'name': _('UI Logo {}').format(1),
                'file': 'ui_logo_1.html'
            }, {
                'name': _('UI Logo {}').format(2),
                'file': 'ui_logo_2.html'
            }, {
                'name': _('Aligulac (only 1vs1)'),
                'file': 'aligulac.html'
            }, {
                'name': _('Countdown'),
                'file': 'countdown.html'
            }, {
                'name': _('League (ALphaTL && RSL only)'),
                'file': 'league.html'
            }, {
                'name': _('Matchbanner (AlphaTL)'),
                'file': 'matchbanner.html',
                'settings': lambda: self.openMiscDialog('alphatl')
            }]
        })

        act = QAction(QIcon(scctool.settings.getResFile('folder.png')),
                      _('Open Folder'), self)
        act.triggered.connect(lambda: self.controller.open_file(
            scctool.settings.getAbsPath(scctool.settings.casting_html_dir)))
        main_menu.addAction(act)
        main_menu.addSeparator()

        for src in srcs:
            myMenu = QMenu(src['name'], self)
            sub = src.get('sub', False)
            if sub:
                for icon in sub:
                    mySubMenu = QMenu(icon['name'], self)
                    icon['file'] = os.path.join(
                        scctool.settings.casting_html_dir, icon['file'])
                    act = QAction(
                        QIcon(scctool.settings.getResFile('html.png')),
                        _('Open in Browser'), self)
                    act.triggered.connect(
                        lambda x, file=icon['file']: self.controller.openURL(
                            scctool.settings.getAbsPath(file)))
                    mySubMenu.addAction(act)
                    act = QAction(
                        QIcon(scctool.settings.getResFile('copy.png')),
                        _('Copy URL to Clipboard'), self)
                    act.triggered.connect(
                        lambda x, file=icon['file']: QApplication.clipboard(
                        ).setText(scctool.settings.getAbsPath(file)))
                    mySubMenu.addAction(act)
                    if icon.get('settings', None) is not None:
                        act = QAction(
                            QIcon(scctool.settings.getResFile('browser.png')),
                            _('Settings'), self)
                        act.triggered.connect(icon['settings'])
                        mySubMenu.addAction(act)
                    myMenu.addMenu(mySubMenu)
            else:
                src['file'] = os.path.join(scctool.settings.casting_html_dir,
                                           src['file'])
                act = QAction(QIcon(scctool.settings.getResFile('html.png')),
                              _('Open in Browser'), self)
                act.triggered.connect(
                    lambda x, file=src['file']: self.controller.openURL(
                        scctool.settings.getAbsPath(file)))
                myMenu.addAction(act)
                act = QAction(QIcon(scctool.settings.getResFile('copy.png')),
                              _('Copy URL to Clipboard'), self)
                act.triggered.connect(
                    lambda x, file=src['file']: QApplication.clipboard(
                    ).setText(scctool.settings.getAbsPath(file)))
                myMenu.addAction(act)

            if src.get('settings', None) is not None:
                act = QAction(
                    QIcon(scctool.settings.getResFile('browser.png')),
                    _('Settings'), self)
                act.triggered.connect(src['settings'])
                myMenu.addAction(act)
            main_menu.addMenu(myMenu)

        main_menu.addSeparator()

        apiAct = QAction(QIcon(scctool.settings.getResFile('browser.png')),
                         _('Settings'), self)
        apiAct.setToolTip(_('Edit Settings for all Browser Sources'))
        apiAct.triggered.connect(self.openBrowserSourcesDialog)
        main_menu.addAction(apiAct)

        styleAct = QAction(QIcon(scctool.settings.getResFile('pantone.png')),
                           _('Styles'), self)
        styleAct.setToolTip('')
        styleAct.triggered.connect(self.openStyleDialog)
        main_menu.addAction(styleAct)

    def openApiDialog(self):
        """Open subwindow with connection settings."""
        self.mysubwindows['connections'] = SubwindowConnections()
        self.mysubwindows['connections'].createWindow(self)
        self.mysubwindows['connections'].show()

    def openStyleDialog(self):
        """Open subwindow with style settings."""
        self.mysubwindows['styles'] = SubwindowStyles()
        self.mysubwindows['styles'].createWindow(self)
        self.mysubwindows['styles'].show()

    def openMiscDialog(self, tab=''):
        """Open subwindow with misc settings."""
        self.mysubwindows['misc'] = SubwindowMisc()
        self.mysubwindows['misc'].createWindow(self, tab)
        self.mysubwindows['misc'].show()

    def openBrowserSourcesDialog(self, tab=''):
        """Open subwindow with misc settings."""
        self.mysubwindows['browser'] = SubwindowBrowserSources()
        self.mysubwindows['browser'].createWindow(self, tab)
        self.mysubwindows['browser'].show()

    def openReadme(self):
        """Open subwindow with readme viewer."""
        self.mysubwindows['readme'] = SubwindowMarkdown()
        self.mysubwindows['readme'].createWindow(
            self, _("Readme"), scctool.settings.getResFile('readme.ico'),
            scctool.settings.getResFile("../README.md"))
        self.mysubwindows['readme'].show()

    def openChangelog(self):
        """Open subwindow with readme viewer."""
        self.mysubwindows['changelog'] = SubwindowMarkdown()
        self.mysubwindows['changelog'].createWindow(
            self, "StarCraft Casting Tool " + _("Changelog"),
            scctool.settings.getResFile("changelog.png"),
            scctool.settings.getResFile("../CHANGELOG.md"))
        self.mysubwindows['changelog'].show()

    def changeLanguage(self, language):
        """Change the language."""
        scctool.settings.config.parser.set("SCT", "language", language)
        self.restart()

    def updateAllMapCompleters(self):
        for idx in range(self.matchDataTabWidget.count()):
            self.matchDataTabWidget.widget(idx).updateMapCompleters()

    def updateAllPlayerCompleters(self):
        for idx in range(self.matchDataTabWidget.count()):
            self.matchDataTabWidget.widget(idx).updatePlayerCompleters()

    def updateAllMapButtons(self):
        for idx in range(self.matchDataTabWidget.count()):
            self.matchDataTabWidget.widget(idx).updateMapButtons()

    def createMatchDataTabs(self):
        self.matchDataTabWidget = QTabWidget()
        self.matchDataTabWidget.setMovable(True)
        closeable = self.controller.matchControl.countMatches() > 1
        self.matchDataTabWidget.setUsesScrollButtons(True)
        for match in self.controller.matchControl.getMatches():
            MatchDataWidget(self, self.matchDataTabWidget, match, closeable)

        container = QWidget()
        buttonLayout = QHBoxLayout()
        buttonLayout.setContentsMargins(2, 1, 1, 2)
        buttonLayout.setSpacing(1)
        button = QPushButton()
        pixmap = QIcon(scctool.settings.getResFile('add.png'))
        button.setIcon(pixmap)
        button.setFixedSize(28, 28)
        # button.setFlat(True)
        button.setToolTip(_('Add Match Tab'))
        button.clicked.connect(self.addMatchTab)
        buttonLayout.addWidget(button)
        button = QPushButton()
        button.setFixedSize(28, 28)
        pixmap = QIcon(scctool.settings.getResFile('copy.png'))
        button.setIcon(pixmap)
        # button.setFlat(True)
        button.setToolTip(_('Copy Match Tab'))
        button.clicked.connect(self.copyMatchTab)
        buttonLayout.addWidget(button)
        container.setLayout(buttonLayout)
        self.matchDataTabWidget.setCornerWidget(container)

        tabBar = self.matchDataTabWidget.tabBar()
        tabBar.setExpanding(True)
        self.matchDataTabWidget.currentChanged.connect(
            self.currentMatchTabChanged)
        tabBar.tabMoved.connect(self.tabMoved)

    def addMatchTab(self):
        match = self.controller.matchControl.newMatchData()
        MatchDataWidget(self, self.matchDataTabWidget, match)
        count = self.matchDataTabWidget.count()
        self.matchDataTabWidget.setCurrentIndex(count - 1)
        if count > 1:
            for idx in range(count):
                self.matchDataTabWidget.widget(idx).setClosable(True)

    def copyMatchTab(self):
        matchId = self.controller.matchControl.selectedMatchId()
        data = self.controller.matchControl.selectedMatch().getData()
        match = self.controller.matchControl.newMatchData(data)
        self.controller.logoManager.copyMatch(match.getControlID(), matchId)
        MatchDataWidget(self, self.matchDataTabWidget, match)
        count = self.matchDataTabWidget.count()
        self.matchDataTabWidget.setCurrentIndex(count - 1)
        if count > 1:
            for idx in range(count):
                self.matchDataTabWidget.widget(idx).setClosable(True)

    def currentMatchTabChanged(self, idx):
        dataWidget = self.matchDataTabWidget.widget(idx)
        ident = dataWidget.matchData.getControlID()
        self.controller.matchControl.selectMatch(ident)
        with self.tlock:
            self.controller.updateMatchFormat()

    def tabMoved(self, toIdx, fromIdx):
        self.controller.matchControl.updateOrder(toIdx, fromIdx)

    def createTabs(self):
        """Create tabs in main window."""
        try:
            # Initialize tab screen
            self.tabs = QTabWidget()
            self.tab1 = QWidget()
            self.tab2 = QWidget()
            # self.tabs.resize(300,200)

            # Add tabs
            self.tabs.addTab(self.tab1, _("Match Grabber for AlphaTL && RSL"))
            self.tabs.addTab(self.tab2, _("Custom Match"))

            # Create first tab
            self.tab1.layout = QVBoxLayout()

            self.le_url = MatchComboBox(self)
            self.le_url.returnPressed.connect(self.refresh_click)

            minWidth = self.scoreWidth + 2 * self.raceWidth + \
                2 * self.mimumLineEditWidth + 4 * 6
            self.le_url.setMinimumWidth(minWidth)

            self.pb_openBrowser = QPushButton(_("Open in Browser"))
            self.pb_openBrowser.clicked.connect(self.openBrowser_click)
            width = (self.scoreWidth + 2 * self.raceWidth +
                     2 * self.mimumLineEditWidth + 4 * 6) / 2 - 2
            self.pb_openBrowser.setMinimumWidth(width)

            container = QHBoxLayout()
            label = QLabel()
            label.setFixedWidth(self.labelWidth)
            container.addWidget(label, 0)
            label = QLabel(_("Match-URL:"))
            label.setMinimumWidth(80)
            container.addWidget(label, 0)
            container.addWidget(self.le_url, 1)
            button = QPushButton()
            pixmap = QIcon(scctool.settings.getResFile('alpha.png'))
            button.setIcon(pixmap)
            button.clicked.connect(
                lambda: self.controller.openURL("https://alpha.tl/"))
            container.addWidget(button, 0)
            button = QPushButton()
            pixmap = QIcon(scctool.settings.getResFile('rsl.png'))
            button.setIcon(pixmap)
            button.clicked.connect(
                lambda: self.controller.openURL("https://rfcs.ru/en/"))
            container.addWidget(button, 0)

            self.tab1.layout = QFormLayout()
            self.tab1.layout.addRow(container)

            container = QHBoxLayout()

            # self.pb_download = QPushButton("Download Images from URL")
            # container.addWidget(self.pb_download)
            label = QLabel()
            label.setFixedWidth(self.labelWidth)
            container.addWidget(label, 0)
            label = QLabel()
            label.setMinimumWidth(80)
            container.addWidget(label, 0)
            self.pb_refresh = QPushButton(_("Load Data from URL"))
            self.pb_refresh.clicked.connect(self.refresh_click)
            container.addWidget(self.pb_openBrowser, 3)
            container.addWidget(self.pb_refresh, 3)

            self.tab1.layout.addRow(container)
            self.tab1.setLayout(self.tab1.layout)

            # Create second tab

            self.tab2.layout = QVBoxLayout()

            container = QHBoxLayout()

            label = QLabel()
            label.setMinimumWidth(self.labelWidth)
            container.addWidget(label, 0)

            label = QLabel(_("Match Format:"))
            label.setMinimumWidth(80)
            container.addWidget(label, 0)

            container.addWidget(QLabel(_("Best of")), 0)

            self.cb_bestof = QComboBox()
            for idx in range(0, scctool.settings.max_no_sets):
                self.cb_bestof.addItem(str(idx + 1))
            self.cb_bestof.setCurrentIndex(3)
            string = _('"Best of 6/4": First, a Bo5/3 is played and the'
                       ' ace map gets extended to a Bo3 if needed;'
                       ' Best of 2: Bo3 with only two maps played.')
            self.cb_bestof.setToolTip(string)
            self.cb_bestof.setMaximumWidth(45)
            self.cb_bestof.currentIndexChanged.connect(self.changeBestOf)
            container.addWidget(self.cb_bestof, 0)

            container.addWidget(QLabel(_(" but at least")), 0)

            self.cb_minSets = QComboBox()

            self.cb_minSets.setToolTip(
                _('Minimum number of maps played (even if the match'
                  ' is decided already)'))
            self.cb_minSets.setMaximumWidth(45)
            container.addWidget(self.cb_minSets, 0)
            container.addWidget(QLabel(" " + _("maps") + "  "), 0)
            self.cb_minSets.currentIndexChanged.connect(
                lambda idx: self.highlightApplyCustom())

            self.cb_allkill = QCheckBox(_("All-Kill Format"))
            self.cb_allkill.setChecked(False)
            self.cb_allkill.setToolTip(
                _('Winner stays and is automatically'
                  ' placed into the next set'))
            self.cb_allkill.stateChanged.connect(self.allkill_change)
            container.addWidget(self.cb_allkill, 0)

            self.cb_solo = QCheckBox(_("1vs1"))
            self.cb_solo.setChecked(False)
            self.cb_solo.setToolTip(_('Select for solo (non-team matches)'))
            container.addWidget(self.cb_solo, 0)
            self.cb_solo.stateChanged.connect(
                lambda idx: self.highlightApplyCustom())

            label = QLabel("")
            container.addWidget(label, 1)

            self.applycustom_is_highlighted = False

            self.pb_applycustom = QToolButton()
            action = QAction(_("Apply Format"))
            action.triggered.connect(self.applycustom_click)
            self.pb_applycustom.setDefaultAction(action)
            self.custom_menu = QMenu(self.pb_applycustom)
            for format, icon in \
                    self.controller.matchControl.getCustomFormats():
                if icon:
                    action = self.custom_menu.addAction(
                        QIcon(scctool.settings.getResFile(icon)), format)
                else:
                    action = self.custom_menu.addAction(format)
                action.triggered.connect(
                    lambda x, format=format: self.applyCustomFormat(format))
            self.pb_applycustom.setMenu(self.custom_menu)
            self.pb_applycustom.setPopupMode(QToolButton.MenuButtonPopup)

            self.pb_applycustom.setFixedWidth(150)
            container.addWidget(self.pb_applycustom, 0)

            self.defaultButtonPalette = self.pb_applycustom.palette()

            self.tab2.layout.addLayout(container)

            container = QHBoxLayout()

            label = QLabel()
            label.setMinimumWidth(self.labelWidth)
            container.addWidget(label, 0)

            label = QLabel(_("Match-URL:"))
            label.setMinimumWidth(80)
            container.addWidget(label, 0)

            self.le_url_custom = MonitoredLineEdit()
            self.le_url_custom.setAlignment(Qt.AlignCenter)
            self.le_url_custom.setToolTip(
                _('Optionally specify the Match-URL,'
                  ' e.g., for Nightbot commands'))
            self.le_url_custom.setPlaceholderText(
                _("Specify the Match-URL of your Custom Match"))

            completer = QCompleter(["http://"], self.le_url_custom)
            completer.setCaseSensitivity(Qt.CaseInsensitive)
            completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
            completer.setWrapAround(True)
            self.le_url_custom.setCompleter(completer)
            self.le_url_custom.setMinimumWidth(360)
            self.le_url_custom.textModified.connect(self.highlightApplyCustom)

            container.addWidget(self.le_url_custom, 11)

            label = QLabel("")
            container.addWidget(label, 1)

            self.pb_resetdata = QPushButton(_("Reset Match Data"))
            self.pb_resetdata.setFixedWidth(150)
            self.pb_resetdata.clicked.connect(self.resetdata_click)
            container.addWidget(self.pb_resetdata, 0)

            self.tab2.layout.addLayout(container)

            self.tab2.setLayout(self.tab2.layout)

        except Exception as e:
            module_logger.exception("message")

    def allkill_change(self):
        try:
            self.controller.matchControl.\
                selectedMatch().setAllKill(self.cb_allkill.isChecked())
        except Exception as e:
            module_logger.exception("message")

    def changeBestOf(self, bestof):
        """Change the minimum sets combo box on change of BoX."""
        bestof = bestof + 1
        self.cb_minSets.clear()
        self.highlightApplyCustom()
        for idx in range(0, bestof):
            self.cb_minSets.addItem(str(idx + 1))
            if bestof == 2:
                self.cb_minSets.setCurrentIndex(1)
            else:
                self.cb_minSets.setCurrentIndex((bestof - 1) / 2)

    def createHorizontalGroupBox(self):
        """Create horizontal group box for tasks."""
        try:
            self.horizontalGroupBox = QGroupBox(_("Tasks"))
            layout = QHBoxLayout()

            self.pb_twitchupdate = QPushButton(_("Update Twitch Title"))
            self.pb_twitchupdate.clicked.connect(self.updatetwitch_click)

            self.pb_nightbotupdate = QPushButton(_("Update Nightbot"))
            self.pb_nightbotupdate.clicked.connect(self.updatenightbot_click)

            self.pb_resetscore = QPushButton(_("Reset Score"))
            self.pb_resetscore.clicked.connect(self.resetscore_click)

            layout.addWidget(self.pb_twitchupdate)
            layout.addWidget(self.pb_nightbotupdate)
            layout.addWidget(self.pb_resetscore)

            self.horizontalGroupBox.setLayout(layout)

        except Exception as e:
            module_logger.exception("message")

    def createLowerTabWidget(self):
        """Create the tab widget at the bottom."""
        try:
            self.lowerTabWidget = QTabWidget()
            self.createBackgroundTasksTab()
            self.countdownTab = CountdownWidget(self.controller, self)
            self.lowerTabWidget.addTab(self.backgroundTasksTab,
                                       _("Background Tasks"))
            self.lowerTabWidget.addTab(self.countdownTab, _("Countdown"))
        except Exception as e:
            module_logger.exception("message")

    def createBackgroundTasksTab(self):
        """Create group box for background tasks."""
        try:

            self.backgroundTasksTab = QWidget()

            self.cb_autoUpdate = QCheckBox(_("Auto Score Update"))
            self.cb_autoUpdate.setChecked(False)
            string = _('Automatically detects the outcome' +
                       ' of SC2 matches that are ' +
                       'played/observed in your SC2-client' +
                       ' and updates the score accordingly.')
            self.cb_autoUpdate.setToolTip(string)
            self.cb_autoUpdate.stateChanged.connect(self.autoUpdate_change)

            self.cb_autoToggleScore = QCheckBox(_("Set Ingame Score"))
            self.cb_autoToggleScore.setChecked(False)
            string = _('Automatically sets the score of your ingame' +
                       ' UI-interface at the begining of a game.')
            self.cb_autoToggleScore.setToolTip(string)
            self.cb_autoToggleScore.stateChanged.connect(
                self.autoToggleScore_change)

            self.cb_autoToggleProduction = QCheckBox(
                _("Toggle Production Tab"))
            self.cb_autoToggleProduction.setChecked(False)
            string = _('Automatically toggles the production tab of your' +
                       ' ingame UI-interface at the begining of a game.')
            self.cb_autoToggleProduction.setToolTip(string)
            self.cb_autoToggleProduction.stateChanged.connect(
                self.autoToggleProduction_change)

            self.cb_autoTwitch = QCheckBox(_("Auto Twitch Update"))
            self.cb_autoTwitch.setChecked(False)
            self.cb_autoTwitch.stateChanged.connect(self.autoTwitch_change)

            self.cb_autoNightbot = QCheckBox(_("Auto Nightbot Update"))
            self.cb_autoNightbot.setChecked(False)
            self.cb_autoNightbot.stateChanged.connect(self.autoNightbot_change)

            if (not scctool.settings.windows):
                self.cb_autoToggleScore.setEnabled(False)
                self.cb_autoToggleScore.setAttribute(Qt.WA_AlwaysShowToolTips)
                self.cb_autoToggleScore.setToolTip(_('Only Windows'))
                self.cb_autoToggleProduction.setEnabled(False)
                self.cb_autoToggleProduction.setAttribute(
                    Qt.WA_AlwaysShowToolTips)
                self.cb_autoToggleProduction.setToolTip(_('Only Windows'))

            layout = QGridLayout()

            layout.addWidget(self.cb_autoTwitch, 0, 0)
            layout.addWidget(self.cb_autoNightbot, 0, 1)

            layout.addWidget(self.cb_autoUpdate, 1, 0)
            layout.addWidget(self.cb_autoToggleScore, 1, 1)
            layout.addWidget(self.cb_autoToggleProduction, 1, 2)

            self.backgroundTasksTab.setLayout(layout)

        except Exception as e:
            module_logger.exception("message")

    def autoTwitch_change(self):
        """Handle change of auto twitch check box."""
        try:
            if (self.cb_autoTwitch.isChecked()):
                self.controller.autoRequestsThread.activateTask('twitch')
            else:
                self.controller.autoRequestsThread.deactivateTask('twitch')
        except Exception as e:
            module_logger.exception("message")

    def autoNightbot_change(self):
        """Handle change of auto twitch check box."""
        try:
            if (self.cb_autoNightbot.isChecked()):
                self.controller.autoRequestsThread.activateTask('nightbot')
            else:
                self.controller.autoRequestsThread.deactivateTask('nightbot')
        except Exception as e:
            module_logger.exception("message")

    def autoUpdate_change(self):
        """Handle change of auto score update check box."""
        try:
            if (self.cb_autoUpdate.isChecked()):
                self.controller.runSC2ApiThread("updateScore")
            else:
                self.controller.stopSC2ApiThread("updateScore")
        except Exception as e:
            module_logger.exception("message")

    def autoToggleScore_change(self):
        """Handle change of toggle score check box."""
        try:
            if (self.cb_autoToggleScore.isChecked()):
                self.controller.runSC2ApiThread("toggleScore")
            else:
                self.controller.stopSC2ApiThread("toggleScore")
        except Exception as e:
            module_logger.exception("message")

    def autoToggleProduction_change(self):
        """Handle change of toggle production tab check box."""
        try:
            if (self.cb_autoToggleProduction.isChecked()):
                self.controller.runSC2ApiThread("toggleProduction")
            else:
                self.controller.stopSC2ApiThread("toggleProduction")
        except Exception as e:
            module_logger.exception("message")

    def applyCustomFormat(self, format):
        """Handle click to apply custom format."""
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            with self.tlock:
                self.controller.matchControl.\
                    selectedMatch().applyCustomFormat(format)
                self.controller.updateMatchFormat()
                matchWidget = self.matchDataTabWidget.currentWidget()
                matchWidget.updateForms()
                self.resizeWindow()
            self.highlightApplyCustom(False)
        except Exception as e:
            module_logger.exception("message")
        finally:
            QApplication.restoreOverrideCursor()

    def applycustom_click(self):
        """Handle click to apply custom match."""
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            with self.tlock:
                self.statusBar().showMessage(_('Applying Custom Match...'))
                msg = self.controller.applyCustom(
                    int(self.cb_bestof.currentText()),
                    self.cb_allkill.isChecked(), self.cb_solo.isChecked(),
                    int(self.cb_minSets.currentText()),
                    self.le_url_custom.text().strip())
                self.statusBar().showMessage(msg)
            self.highlightApplyCustom(False)
        except Exception as e:
            module_logger.exception("message")
        finally:
            QApplication.restoreOverrideCursor()

    def resetdata_click(self):
        """Handle click to reset the data."""
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            with self.tlock:
                msg = self.controller.resetData()
                self.statusBar().showMessage(msg)
        except Exception as e:
            module_logger.exception("message")
        finally:
            QApplication.restoreOverrideCursor()

    def refresh_click(self):
        """Handle click to refresh/load data from an URL."""
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            url = self.le_url.lineEdit().text()
            with self.tlock:
                self.statusBar().showMessage(_('Reading {}...').format(url))
                msg = self.controller.refreshData(url)
                self.statusBar().showMessage(msg)
        except Exception as e:
            module_logger.exception("message")
        finally:
            QApplication.restoreOverrideCursor()

    def openBrowser_click(self):
        """Handle request to open URL in browser."""
        try:
            url = self.le_url.text()
            self.controller.openURL(url)
        except Exception as e:
            module_logger.exception("message")

    def updatenightbot_click(self):
        """Handle click to change nightbot command."""
        try:
            self.statusBar().showMessage(_('Updating Nightbot Command...'))
            msg = self.controller.updateNightbotCommand()
            self.statusBar().showMessage(msg)
        except Exception as e:
            module_logger.exception("message")

    def updatetwitch_click(self):
        """Handle click to change twitch title."""
        try:
            self.statusBar().showMessage(_('Updating Twitch Title...'))
            msg = self.controller.updateTwitchTitle()
            self.statusBar().showMessage(msg)
        except Exception as e:
            module_logger.exception("message")

    def resetscore_click(self, myteam=False):
        """Handle click to reset the score."""
        try:
            self.statusBar().showMessage(_('Resetting Score...'))
            with self.tlock:
                matchDataWidget = self.matchDataTabWidget.currentWidget()
                for set_idx in range(self.max_no_sets):
                    matchDataWidget.sl_score[set_idx].setValue(0)
                    self.controller.matchControl.selectedMatch().setMapScore(
                        set_idx, 0, overwrite=True)
                self.controller.autoSetNextMap()
                if myteam:
                    matchDataWidget.sl_team.setValue(0)
                    self.controller.matchControl.selectedMatch().setMyTeam(0)
                if not self.controller.resetWarning():
                    self.statusBar().showMessage('')

        except Exception as e:
            module_logger.exception("message")

    def highlightApplyCustom(self, highlight=True, force=False):
        if not force and not self.tlock.trigger():
            return
        try:
            if self.applycustom_is_highlighted == highlight:
                return highlight
        except AttributeError:
            return False

        if highlight:
            myPalette = self.pb_applycustom.palette()
            myPalette.setColor(QPalette.Background, Qt.darkBlue)
            myPalette.setColor(QPalette.ButtonText, Qt.darkBlue)
            self.pb_applycustom.setPalette(myPalette)
        else:
            self.pb_applycustom.setPalette(self.defaultButtonPalette)

        self.applycustom_is_highlighted = highlight
        return highlight

    def logoDialog(self, team, matchDataWidget):
        """Open dialog for team logo."""
        self.controller.logoManager.resetLogoChanged()
        self.mysubwindows['icons'] = SubwindowLogos()
        self.mysubwindows['icons'].createWindow(self, self.controller, team,
                                                matchDataWidget)
        self.mysubwindows['icons'].show()

    def resizeWindow(self):
        """Resize the window height to size hint."""
        if (not self.isMaximized()):
            self.processEvents()
            self.resize(self.width(), self.sizeHint().height())

    def processEvents(self):
        """Process ten PyQt5 events."""
        for i in range(0, 10):
            self.app.processEvents()

    def restart(self, save=True):
        """Restart the main window."""
        self._save = save
        self.close()
        self.app.exit(self.EXIT_CODE_REBOOT)
Ejemplo n.º 46
0
class DupControl(QDialog):
    '''
    Controller for the duplicatetrade.ui
    '''
    def __init__(self):
        super().__init__(parent=None)
        self.ui = DupDialog()
        self.ui.setupUi(self)
        self.setWindowTitle('Database Tool')

        self.setWindowIcon(QIcon("structjour/images/ZSLogo.png"))
        self.nextRecord = None
        self.numDups = None
        self.deletMe = None
        self.dups = None
        self.settings = QSettings('zero_substance', 'structjour')

        # Format of actionTaken:
        # [[[done, action],[done, action)]], ...]
        # Keep track of action taken on [[[dups], [deleteMe]], ...]
        self.actionTaken = list()

        self.ui.deleteTxBtn.setEnabled(False)
        self.ui.deleteTradeBtn.setEnabled(False)
        self.ui.showDupPrevBtn.setEnabled(False)

        self.ui.showDupBtn.pressed.connect(self.showNext)
        self.ui.showDupPrevBtn.pressed.connect(self.showPrev)
        self.ui.deleteTxBtn.pressed.connect(self.deleteTx)
        self.ui.deleteTradeBtn.pressed.connect(self.deleteTrade)
        self.ui.accountEdit.editingFinished.connect(self.setAccount)

        acnt = self.settings.value('account')
        self.ui.accountEdit.setText(acnt)
        self.account = acnt
        self.dbdr = None

    def deleteTrade(self):
        ts_id = self.ui.deleteTradeEdit.text()
        tsid1 = self.dups[self.nextRecord][10]
        tsid2 = self.dups[self.nextRecord][11]

        if int(ts_id) not in [tsid1, tsid2]:
            logging.info(f'''Deleting {ts_id} is not an option''')
            return
        if self.dbdr.deleteTradeSumById(int(ts_id)):
            self.actionTaken[self.nextRecord][1] = [True, int(ts_id)]
            self.showTrades()
            self.ui.deleteTradeEdit.setText('')
            self.ui.deleteTradeBtn.setEnabled(False)
            self.ui.deleteTxBtn.setEnabled(False)

    def deleteTx(self):
        t_id = self.ui.deleteTxEdit.text()
        id1 = self.dups[self.nextRecord][0]
        id2 = self.dups[self.nextRecord][1]

        if int(t_id) not in [id1, id2]:
            logging.info(f'''Deleting {t_id} is not an option''')
            return
        if self.dbdr.deleteTradeById(int(t_id)):
            self.actionTaken[self.nextRecord][0] = [True, int(t_id)]
            self.showTrades()
            sid = self.ui.deleteTradeEdit.text()
            if sid:
                self.ui.deleteTradeBtn.setEnabled(True)
                self.ui.deleteTxBtn.setEnabled(False)

    def initialize(self):
        if not self.dbdr:
            self.dbdr = DbDoctor(account=self.account)
        deleteMe, dups = self.dbdr.doDups()
        if dups:
            self.nextRecord = 0
            self.numDups = len(dups)
            self.deleteMe = deleteMe
            self.dups = dups
            self.actionTaken = [[[False, None], [False, None]]
                                for i in range(self.numDups)]

    def dictToTable(self, d):
        '''
        Create an html table that displays the dict d
        :d: A dict or a list of dict of the same type
        '''

        if not isinstance(d, list):
            d = [d]
        columns = list(d[0].keys())
        msg = '''<table style="width100%"><tr>'''
        for col in columns:
            msg += f'<th>{col}</th>'
        msg += '</tr>'

        for dic in d:
            msg += '<tr>'
            for col in columns:
                msg += f'<td>{dic[col]}</td>'
            msg += '</tr>'
        msg += '</table>'
        return msg

    def showPrev(self):
        if self.dups is None:
            self.initialize()
        if not self.dups:
            self.ui.showDuplicate.setHtml(
                '<h2>No duplicates have been found.</h2>')
            return

        if self.nextRecord > 0:
            self.nextRecord -= 1
            if self.nextRecord == 0:
                self.ui.showDupPrevBtn.setEnabled(False)
            else:
                self.ui.showDupPrevBtn.setEnabled(True)
            if self.nextRecord < self.numDups - 1:
                self.ui.showDupBtn.setEnabled(True)
        self.showTrades()

    def showNext(self):
        if self.dups is None:
            self.initialize()
        if not self.dups:
            self.ui.showDuplicate.setHtml(
                '<h2>No duplicates have been found.</h2>')
            return
        if self.nextRecord < self.numDups - 1:
            self.nextRecord += 1
            if self.nextRecord == self.numDups - 1:
                self.ui.showDupBtn.setEnabled(False)
            else:
                self.ui.showDupBtn.setEnabled(True)
            if self.nextRecord > 0:
                self.ui.showDupPrevBtn.setEnabled(True)
            else:
                self.ui.showDupPrevBtn.setEnabled(False)
        self.showTrades()

    def setAccount(self):
        acnt = self.ui.accountEdit.text()
        if not acnt:
            acnt = self.settings.value('account')
        if acnt:
            self.settings.setValue('account', acnt)
            self.account = acnt
        if self.dbdr:
            self.dbdr.account = acnt

    def showTrades(self):
        id1 = self.dups[self.nextRecord][0]
        id2 = self.dups[self.nextRecord][1]
        delMe = self.deleteMe[self.nextRecord]

        # head and stylesheet
        msg = '<!DOCTYPE html<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <style type="text/css">'
        msg += '''table, th, td { border: 1px solid black; border-collapse: collapse; }'''
        msg += '</style></head><body>'

        # Beginning of doc
        msg += f'<h2>The records {id1} and {id2} appear to be duplicates.</h2>'
        msg += f'<h3>Trade {self.nextRecord+1} of {self.numDups}.      Recommend to delete {delMe[0]}</h3>'
        t1 = self.dbdr.getTradesByID(id1)
        t2 = self.dbdr.getTradesByID(id2)

        if self.actionTaken[self.nextRecord][0][0]:
            msg += f'<h4>Action has been taken. Deleted record {self.actionTaken[self.nextRecord][0][1]}'
            self.ui.deleteTxBtn.setEnabled(False)
            self.ui.deleteTxEdit.setText('')
        else:
            self.ui.deleteTxEdit.setText(str(delMe[0]))
            self.ui.deleteTxBtn.setEnabled(True)
            # Display both duplicate records as html table
            msg += self.dictToTable([t1, t2])

        if delMe[1]:
            # Show trade_sum record or action taken
            msg += f'<h3>Recommend to delete trade_sum record {delMe[1]}</h4>'
            if self.actionTaken[self.nextRecord][1][0]:
                msg += f'<h4>Action has been taken. Deleted trade sum record {self.actionTaken[self.nextRecord][1][1]}'
            else:
                self.ui.deleteTradeEdit.setText(str(delMe[1]))

                tstab = list()

                # Get 1 or 2 records to show and and a column showing the related trade ids for each
                ts_1 = self.dbdr.getTradeSumByID(delMe[1])
                t1_ids = self.dbdr.getTradesForTSID(delMe[1])
                if t1_ids:
                    t1_ids = [x[0] for x in t1_ids]
                ts_1['RelTrades'] = t1_ids
                tstab.append(ts_1)

                ts2_id = None
                # t1 or t2 may have been deleted but we still want to show any the trade_sums records
                if t1 and t1['ts_id'] != delMe[1]:
                    ts2_id = t1['ts_id']
                elif t2 and t2['ts_id'] != delMe[1]:
                    ts2_id = t2['ts_id']
                if ts2_id:
                    ts_2 = self.dbdr.getTradeSumByID(ts2_id)
                    t2_ids = self.dbdr.getTradesForTSID(ts2_id)
                    if t2_ids:
                        t2_ids = [x[0] for x in t2_ids]
                    ts_2['RelTrades'] = t2_ids
                    tstab.append(ts_2)

                msg += self.dictToTable(tstab)

        msg += '</body></html>'

        self.ui.showDuplicate.setHtml(msg)
Ejemplo n.º 47
0
def update():

    # APPLICATION ------------------------------------------------------------------------------------------

    appSettings = QSettings(COMPANY, APPLICATION)

    # Saving preferences
    appSettings.setValue('auto_connect_output', auto_connect_output)
    appSettings.setValue('auto_connect_input', auto_connect_input)

    # save mixer settings
    appSettings.setValue('save_mixerstrip_gain', save_mixerstrip_gain)
    appSettings.setValue('save_mixerstrip_send1', save_mixerstrip_send1)
    appSettings.setValue('save_mixerstrip_send2', save_mixerstrip_send2)
    appSettings.setValue('save_mixerstrip_volume', save_mixerstrip_volume)
    appSettings.setValue('save_mixerstrip_mute', save_mixerstrip_mute)
    appSettings.setValue('save_mixerstrip_to_master',
                         save_mixerstrip_to_master)

    # customreset
    appSettings.setValue('customreset_mixerstrip_gain',
                         customreset_mixerstrip_gain)
    appSettings.setValue('customreset_mixerstrip_send1',
                         customreset_mixerstrip_send1)
    appSettings.setValue('customreset_mixerstrip_send2',
                         customreset_mixerstrip_send2)
    appSettings.setValue('customreset_mixerstrip_volume',
                         customreset_mixerstrip_volume)
    appSettings.setValue('customreset_mixerstrip_mute',
                         customreset_mixerstrip_mute)

    # Performance
    appSettings.setValue('disable_shift_after_processing',
                         disable_shift_after_processing)
    appSettings.setValue('show_clip_details_on_trigger',
                         show_clip_details_on_trigger)
    appSettings.setValue('use_big_fonts_playlist', use_big_fonts_playlist)
    appSettings.setValue('use_big_fonts_scenes', use_big_fonts_scenes)
    appSettings.setValue('allow_record_empty_clip', allow_record_empty_clip)
    appSettings.setValue('auto_assign_new_clip_column',
                         auto_assign_new_clip_column)
    appSettings.setValue('play_clip_after_record', play_clip_after_record)
    appSettings.setValue('show_scenes_on_start', show_scenes_on_start)
    appSettings.setValue('show_playlist_on_start', show_playlist_on_start)
    appSettings.setValue('show_song_annotation_on_load',
                         show_song_annotation_on_load)
    appSettings.setValue('slower_processing', slower_processing)
    appSettings.setValue('system_monitoring', system_monitoring)
    appSettings.setValue('rec_color', rec_color)
    appSettings.setValue('grid_rows', str(grid_rows))
    appSettings.setValue('grid_columns', str(grid_columns))
    appSettings.setValue('bigFontSize', str(bigFontSize))
    appSettings.setValue('new_song_master_volume', str(new_song_master_volume))
    appSettings.setValue('new_song_bpm', str(new_song_bpm))
    appSettings.setValue('new_song_beats', str(new_song_beats))
    appSettings.setValue('prevent_song_save', prevent_song_save)

    # and windows position and geometry
    appSettings.setValue("gui_geometry", gui_geometry)
    appSettings.setValue("scenes_geometry", scenes_geometry)
    appSettings.setValue("playlist_geometry", playlist_geometry)
    appSettings.setValue("song_annotation_geometry", song_annotation_geometry)
    appSettings.setValue("mixer_geometry", mixer_geometry)

    # session
    appSettings.setValue('paths_used', paths_used)
    appSettings.value('playlist', playlist)

    appSettings.sync()

    # DEVICE ---------------------------------------------------------------------------------------------

    devSettings = QSettings(COMPANY, DEVICES)

    devSettings.setValue('devices', devices)

    devSettings.sync()

    # PORTS ---------------------------------------------------------------------------------------------

    portSettings = QSettings(COMPANY, PORTS)

    portSettings.setValue('output_ports', output_ports)

    portSettings.setValue('master_port_final_volume',
                          str(master_port_final_volume))
    portSettings.setValue('master_port_mute', master_port_mute)

    portSettings.sync()
Ejemplo n.º 48
0
class ClientWindow(QMainWindow):
    """GUI主窗口类。"""

    def __init__(self):
        super().__init__()
        self.files = []  # 发送端待发送文件列表
        self.del_list = []  # 发送端待删除文件列表
        self.received_files = 0  # 接收端已接受文件数
        self.succeed_files = 0  # 接收端成功接受文件数
        self.failed_files = 0  # 接收端传输失败文件数
        self.client_que = Queue()
        self.server_que = Queue()

        self.setFont(QFont('Arial', 10))
        self.resolution = QGuiApplication.primaryScreen().availableGeometry()
        self.reso_height = self.resolution.height()
        self.reso_width = self.resolution.width()
        self.settings = QSettings(os.path.join(os.path.abspath('.'), 'settings.ini'), QSettings.IniFormat)
        self.settings.beginGroup('UISetting')
        self.setting_detail_view = int(self.settings.value('detail_view', False))
        self.settings.endGroup()
        self.settings.beginGroup('ClientSetting')
        self.setting_del_timeout = float(self.settings.value('del_timeout', 1))
        self.settings.endGroup()

        self.init_ui()

    """UI构造函数"""

    def init_ui(self):

        self.sender_frame = QFrame()
        self.chat_frame = QFrame()
        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.setHandleWidth(self.reso_width / 384)  # 拖动杆宽度
        self.splitter.splitterMoved.connect(self.resizeEvent)
        self.splitter.addWidget(self.sender_frame)
        self.splitter.addWidget(self.chat_frame)
        self.splitter.handle(1).setStyleSheet('QSplitterHandle{background-color: rgb(210,210,210)}')
        self.setCentralWidget(self.splitter)  # 将分隔控件作为窗口主控件

        # 菜单栏定义
        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu('文件(&F)')
        self.act_choose = QAction('选择文件(&C)', self)
        self.act_send = QAction('发送(&S)', self)
        self.act_stop_send = QAction('中止传输(&E)', self)
        self.act_exit = QAction('退出(&Q)', self)
        file_menu.addAction(self.act_choose)
        file_menu.addAction(self.act_send)
        file_menu.addAction(self.act_stop_send)
        file_menu.addSeparator()
        file_menu.addAction(self.act_exit)
        self.act_choose.triggered.connect(self.file_dialog)
        self.act_send.setDisabled(True)
        self.act_send.triggered.connect(self.file_checker)
        self.act_stop_send.setDisabled(True)
        self.act_stop_send.triggered.connect(self.abort_trans)
        self.act_exit.triggered.connect(self.close)

        setting_menu = menu_bar.addMenu('设置(&S)')
        act_client = QAction('发送端设置(&C)', self)
        act_server = QAction('接收端设置(&S)', self)
        act_ui = QAction('界面设置(&U)', self)
        setting_menu.addAction(act_client)
        setting_menu.addAction(act_server)
        setting_menu.addAction(act_ui)
        act_client.triggered.connect(self.client_setting_dialog)
        act_server.triggered.connect(self.server_setting_dialog)
        act_ui.triggered.connect(self.ui_setting_dialog)

        # 状态栏定义
        self.status_bar = self.statusBar()
        self.status_bar.setSizeGripEnabled(False)
        self.Lclient_status = QLabel('发送端已就绪')
        self.Lserver_status = QLabel('接收端未启动')
        self.status_bar.addWidget(self.Lclient_status, 1)
        self.status_bar.addWidget(self.Lserver_status, 1)

        # 传输区域控件定义
        self.Bselector = QPushButton('选择文件', self)
        self.Bselector.setDefault(True)
        self.Bselector.clicked.connect(self.file_dialog)
        self.Lfile_empty = QLabel('未选中文件')
        self.Lfile_empty.setAlignment(Qt.AlignTop)
        self.file_table = QTableWidget()  # 文件列表采用表格视图
        self.file_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.file_table.setSelectionMode(QAbstractItemView.NoSelection)
        self.file_table.verticalScrollBar().setStyleSheet('QScrollBar{{width:{}px;}}'.format(self.reso_width / 192))
        self.file_table.verticalScrollBar().setContextMenuPolicy(Qt.NoContextMenu)
        self.file_table.verticalHeader().setSectionsClickable(False)
        self.file_table.horizontalHeader().setSectionsClickable(False)
        self.file_table.hide()
        self.Bfile_sender = QPushButton('发送', self)
        self.Bfile_sender.setDisabled(True)
        self.Bfile_sender.clicked.connect(self.file_checker)

        self.vbox = QVBoxLayout()
        self.vbox.setSpacing(self.reso_height / 70)
        self.vbox.addWidget(self.Bselector)
        self.vbox.addWidget(self.file_table)
        self.vbox.addWidget(self.Lfile_empty)
        self.vbox.addWidget(self.Bfile_sender)
        self.sender_frame.setLayout(self.vbox)

        # 消息区域控件定义
        self.Emessage_area = def_widget.MessageDisplayEdit(self)
        self.Emessage_area.setContextMenuPolicy(Qt.DefaultContextMenu)
        self.Emessage_area.setReadOnly(True)
        self.Emessage_writer = def_widget.MessageWriter(self)
        self.Emessage_writer.setPlaceholderText('输入消息')
        self.Emessage_writer.returnPressed.connect(self.chat_checker)
        self.Bfolder = QPushButton('<< 收起', self)
        self.Bfolder.clicked.connect(functools.partial(self.splitter.setSizes, [self.geometry().width(), 0]))
        self.Bmessage_sender = QPushButton('发送', self)
        self.Bmessage_sender.clicked.connect(self.chat_checker)

        self.chat_vbox = QVBoxLayout()
        self.chat_vbox.setSpacing(self.reso_height / 70)
        self.chat_vbox.addWidget(self.Emessage_area)
        self.chat_vbox.addWidget(self.Emessage_writer)
        self.chat_hbox = QHBoxLayout()
        self.chat_hbox.addWidget(self.Bfolder)
        self.chat_hbox.addStretch(1)
        self.chat_hbox.addWidget(self.Bmessage_sender)
        self.chat_vbox.addLayout(self.chat_hbox)
        self.chat_frame.setLayout(self.chat_vbox)

        if not self.setting_detail_view:  # 显示模式不同,最小宽度不同
            self.sender_frame.setMinimumWidth(self.reso_width / 7.68)
        else:
            self.sender_frame.setMinimumWidth(self.reso_width / 6)
        self.ui_setting_checker()
        self.settings.beginGroup('Misc')  # 恢复上次关闭时的窗口尺寸
        setting_window_size = self.settings.value('window_size', (self.reso_height / 6.4, self.reso_width / 2.7))
        setting_frame_width = self.settings.value('frame_width', (self.geometry().width(), 0))
        self.settings.endGroup()
        self.resize(setting_window_size[0], setting_window_size[1])
        self.splitter.setSizes([setting_frame_width[0], setting_frame_width[1]])
        self.frameGeometry().moveCenter(self.resolution.center())  # 在屏幕中央打开窗口
        self.setWindowTitle('FileTransfer')
        self.show()

        self.settings.beginGroup('ServerSetting')
        setting_open_server = int(self.settings.value('open_server', True))
        self.settings.endGroup()
        if setting_open_server:  # 启动服务端
            self.settings.beginGroup('ServerSetting')
            setting_incoming_ip = self.settings.value('incoming_ip', '0.0.0.0')
            setting_bind_port = int(self.settings.value('bind_port', 54321))
            setting_receive_dir = self.settings.value('receive_dir', os.path.abspath('.'))
            self.settings.endGroup()
            self.server_starter = Process(target=server.starter, name='ServerStarter', args=(
                setting_incoming_ip, setting_bind_port, setting_receive_dir, self.server_que))
            self.server_starter.start()
            self.server_timer = QTimer()  # 服务端消息读取循环
            self.server_timer.timeout.connect(self.server_status)
            self.server_timer.start(200)

    def simple_viewer(self, add_files):
        """简明视图:仅包含按钮及进度条。"""
        # 简明视图样式:无框线,水平抬头不可见,按钮定宽,文件名列宽自适应留白
        self.file_table.setColumnCount(2)
        row = len(self.files) + len(add_files)
        self.file_table.setRowCount(row)
        self.file_table.setShowGrid(False)
        self.file_table.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.file_table.horizontalHeader().setVisible(False)
        self.file_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.file_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)

        num = 0
        for inst in add_files:
            prog_widget = QWidget()
            prog_stack = QStackedLayout()
            prog_stack.addWidget(inst.prog)
            prog_stack.addWidget(inst.label)
            prog_stack.setStackingMode(QStackedLayout.StackAll)
            prog_widget.setLayout(prog_stack)

            inst.button.clicked.connect(functools.partial(self.del_file, inst))
            inst.button.pressed.connect(self.button_pressed)
            inst.button.released.connect(self.button_released)

            index = num + len(self.files)
            num += 1
            self.file_table.setCellWidget(index, 0, inst.button)
            self.file_table.setCellWidget(index, 1, prog_widget)
        self.files += add_files
        self.file_table.show()
        for inst in self.files:  # 列表显示后再截断文件名
            inst.label.setText(self.shorten_filename(inst.name, self.file_table.columnWidth(1)))

    def detail_viewer(self, add_files):
        """详细视图:包括按钮、进度条、详细进度、文件大小、状态"""
        self.file_table.setColumnCount(5)
        row = len(self.files) + len(add_files)
        self.file_table.setRowCount(row)
        self.file_table.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.file_table.setHorizontalHeaderLabels(['', '文件名', '传输进度', '文件大小', '状态'])
        # 要用表头的ResizeMode函数而不能用列的ResizeMode函数,否则表头仍可拖动
        self.file_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.file_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)

        num = 0
        for inst in add_files:
            prog_stack = QStackedLayout()
            prog_stack.addWidget(inst.prog)
            prog_stack.addWidget(inst.label)
            prog_stack.setStackingMode(QStackedLayout.StackAll)
            prog_widget = QWidget()
            prog_widget.setLayout(prog_stack)
            # 定义按钮点击删除,长按清空的行为
            inst.button.clicked.connect(functools.partial(self.del_file, inst))
            inst.button.pressed.connect(self.button_pressed)
            inst.button.released.connect(self.button_released)

            file_prog = QTableWidgetItem('0.00 %')
            file_prog.setTextAlignment(Qt.AlignCenter)
            file_size = QTableWidgetItem(inst.size)
            file_size.setTextAlignment(Qt.AlignCenter)
            file_status = QTableWidgetItem(inst.status[1])
            file_status.setTextAlignment(Qt.AlignCenter)

            index = num + len(self.files)
            num += 1
            self.file_table.setCellWidget(index, 0, inst.button)
            self.file_table.setCellWidget(index, 1, prog_widget)
            self.file_table.setItem(index, 2, file_prog)
            self.file_table.setItem(index, 3, file_size)
            self.file_table.setItem(index, 4, file_status)
        self.files += add_files
        self.file_table.show()

        row_height = self.file_table.rowHeight(0) * self.file_table.rowCount()
        header_height = self.file_table.horizontalHeader().height()
        if row_height + header_height >= self.file_table.height():  # 计算是否出现滚动条
            for inst in self.files:
                changed_text = self.shorten_filename(inst.name, self.file_table.columnWidth(1) - self.reso_width / 192)
                inst.label.setText(changed_text)
        else:
            for inst in self.files:
                changed_text = self.shorten_filename(inst.name, self.file_table.columnWidth(1))
                inst.label.setText(changed_text)

    def ui_sending(self):
        """将UI转变为传输中状态。"""
        self.Bfile_sender.setText('中止传输')
        self.Bfile_sender.clicked.disconnect(self.file_checker)
        self.Bfile_sender.clicked.connect(self.abort_trans)
        self.act_choose.setDisabled(True)
        self.act_send.setDisabled(True)
        self.act_stop_send.setDisabled(False)
        self.Bselector.setDisabled(True)
        for inst in self.files:  # 禁用删除按钮
            inst.button.setDisabled(True)

    def ui_pending(self):
        """将UI转变为等待中状态。"""
        self.Bfile_sender.setText('发送')
        self.Bfile_sender.clicked.disconnect(self.abort_trans)
        self.Bfile_sender.clicked.connect(self.file_checker)
        self.act_choose.setDisabled(False)
        self.act_stop_send.setDisabled(True)
        self.Bselector.setDisabled(False)
        for inst in self.files:  # 启用删除按钮
            inst.button.setDisabled(False)

    def ui_setting_checker(self):
        """UI设置窗口关闭后转变UI"""
        self.settings.beginGroup('UISetting')
        setting_status_bar = int(self.settings.value('status_bar', True))
        setting_chat_frame = int(self.settings.value('chat_frame', True))
        self.settings.endGroup()
        if setting_status_bar:
            self.status_bar.show()
        else:
            self.status_bar.hide()
        if setting_chat_frame:
            self.chat_frame.show()
        else:
            self.chat_frame.hide()

    def client_setting_checker(self):
        """客户端设置窗口关闭后转变删除延时"""
        self.settings.beginGroup('ClientSetting')
        self.setting_del_timeout = float(self.settings.value('del_timeout', 1))
        self.settings.endGroup()

    """进程启动函数"""

    def chat_checker(self):
        """聊天进程启动函数。"""
        text = self.Emessage_writer.text()
        if text:
            self.settings.beginGroup('ClientSetting')
            setting_host = self.settings.value('host', '127.0.0.1')
            setting_port = int(self.settings.value('server_port', 12345))
            self.settings.endGroup()
            try:
                if self.chat_sender.is_alive():  # 若上个消息没发完就发新消息则结束掉上个子进程
                    self.chat_sender.terminate()
                    self.chat_sender.join()
                    self.Emessage_area.moveCursor(QTextCursor.End)
                    self.Emessage_area.insertHtml('<font color=red>✘<font color=black> ')
            except AttributeError:
                pass
            self.Emessage_area.append('本机(localhost):\n    {0} '.format(text))
            self.chat_sender = Process(target=chat_client.chat_starter, name='ChatSender',
                                       args=(setting_host, setting_port, text, self.server_que))
            self.chat_sender.start()
            self.Emessage_writer.clear()

    def file_checker(self):
        """文件列表构建及客户端传输进程开启。"""
        self.ui_sending()
        self.settings.beginGroup('ClientSetting')
        setting_file_at_same_time = int(self.settings.value('file_at_same_time', 2))
        setting_host = self.settings.value('host', '127.0.0.1')
        setting_port = int(self.settings.value('server_port', 12345))
        self.settings.endGroup()
        for inst in self.files:
            inst.status = 'uploading'
            inst.prog.setValue(0)
            index = self.find_index_by_name(inst.name)
            self.file_table.item(index, 4).setText('传输中')
        path_list = [inst.path for inst in self.files]
        try:
            self.file_sender = Process(target=trans_client.starter, name='FileSender', args=(
                setting_host, setting_port, path_list, setting_file_at_same_time, self.client_que))
            self.file_sender.start()
            self.Lclient_status.setText(
                '''传输中:<font color=green>0<font color=black>/<font color=red>0<font color=black>/{0} 
                (<font color=green>Comp<font color=black>/<font color=red>Err<font color=black>/Up)'''.format(
                    len(self.find_instance_by_status('uploading'))))
        except Exception as e:
            self.Lclient_status.setText(repr(e))
        self.prog_timer = QTimer()  # 启动进度条更新监控
        self.prog_timer.timeout.connect(self.update_prog)
        self.prog_timer.start(5)

    """后台监控函数"""

    def server_status(self):
        """启动时循环读取服务端状态及更新聊天信息。"""
        try:
            message = self.server_que.get(block=False)
            if message['type'] == 'server_info':
                if message['message'] == 'error':
                    self.Lserver_status.setText(message['detail'])
                elif message['message'] == 'ready':
                    self.Lserver_status.setText('接收端已就绪')
                elif message['message'] == 'MD5_passed':
                    self.received_files += 1
                    self.succeed_files += 1
                    self.Lserver_status.setText(
                        '''{0}=<font color=green>{1}<font color=black>+<font color=red>{2}<font color=black> 
                        (Tol=<font color=green>Comp<font color=black>+<font color=red>Err<font color=black>)'''.format(
                            self.received_files, self.succeed_files, self.failed_files))
                elif message['message'] == 'MD5_failed':  # 失败数统计
                    self.received_files += 1
                    self.failed_files += 1
                    self.Lserver_status.setText(
                        '''{0}=<font color=green>{1}<font color=black>+<font color=red>{2}<font color=black> 
                        (Tol=<font color=green>Comp<font color=black>+<font color=red>Err<font color=black>)'''.format(
                            self.received_files, self.succeed_files, self.failed_files))
                elif message['message'] == 'started':  # 正在传输数统计
                    self.Lserver_status.setText(
                        '''新文件传入 {0}=<font color=green>{1}<font color=black>+<font color=red>{2}<font color=black> 
                        (Tol=<font color=green>Comp<font color=black>+<font color=red>Err<font color=black>)'''.format(
                            self.received_files, self.succeed_files, self.failed_files))
                elif message['message'] == 'aborted':  # 中断数统计
                    self.Lserver_status.setText(
                        '''传输中断 {0}=<font color=green>{1}<font color=black>+<font color=red>{2}<font color=black> 
                        (Tol=<font color=green>Comp<font color=black>+<font color=red>Err<font color=black>)'''.format(
                            self.received_files, self.succeed_files, self.failed_files))
            elif message['type'] == 'chat':
                if message['status'] == 'success':
                    self.chat_sender.terminate()
                    self.chat_sender.join()
                    self.Emessage_area.moveCursor(QTextCursor.End)
                    self.Emessage_area.insertHtml('<font color=green>✓<font color=black> ')
                elif message['status'] == 'failed':
                    self.chat_sender.terminate()
                    self.chat_sender.join()
                    self.Emessage_area.moveCursor(QTextCursor.End)
                    self.Emessage_area.insertHtml('<font color=red>✘<font color=black> ')
                elif message['status'] == 'received':
                    self.Emessage_area.append(
                        '{0}:{1}:\n    {2}'.format(message['from'][0], message['from'][1], message['message']))
                    self.Lserver_status.setText('收到来自{0}:{1}的聊天消息'.format(message['from'][0], message['from'][1]))
        except queue.Empty:
            pass

    def update_prog(self):
        """传输过程管理及进度条更新函数。"""
        try:
            message = self.client_que.get(block=False)
            inst = self.find_instance_by_name(message['name'])  # 考虑到简明视图的性能,不全局计算index
            if message['type'] == 'info':
                if message['message'] == 'MD5_passed':
                    inst.status = 'complete'
                    index = self.find_index_by_name(message['name'])
                    self.file_table.item(index, 4).setText('传输完成')
                    self.file_table.item(index, 2).setText('100 %')
                    self.del_list.append(inst.path)
                elif message['message'] == 'MD5_failed':
                    inst.status = 'error'
                    index = self.find_index_by_name(message['name'])
                    self.file_table.item(index, 4).setText('传输错误')
                    self.file_table.item(index, 2).setText('100 %')
                elif message['message'] == 'aborted':
                    for inst in self.find_instance_by_status('uploading'):
                        inst.status = 'error'
                        i = self.find_index_by_name(inst.name)
                        self.file_table.item(i, 4).setText('传输中断')
                    self.ui_pending()
                    self.prog_timer.stop()
                # 若无上传中的文件且未被中断,关闭传输进程,清空上传列表且提示完成结果
                if not self.find_instance_by_status('uploading') and self.prog_timer.isActive():
                    self.Lclient_status.setText(
                        '''传输完成:<font color=green>{0}<font color=black>/<font color=red>{1}<font color=black>/0 
                        (<font color=green>Comp<font color=black>/<font color=red>Err<font color=black>/Up)'''.format(
                            len(self.find_instance_by_status('complete')), len(self.find_instance_by_status('error'))))
                    self.file_sender.terminate()
                    self.file_sender.join()
                    self.file_sender.close()
                    self.prog_timer.stop()
                    if self.del_list and not self.find_instance_by_status('error'):
                        # 待删除列表不为空且没有传输错误则考虑是否删除源文件
                        self.settings.beginGroup('ClientSetting')
                        setting_del_source = int(self.settings.value('del_source', False))
                        self.settings.endGroup()
                        if setting_del_source:
                            msg_box = QMessageBox(self)
                            msg_box.setWindowTitle('成功')
                            msg_box.setIcon(QMessageBox.Information)
                            msg_box.setText('传输成功完成!\n点击确定删除源文件')
                            msg_box.addButton('确定', QMessageBox.AcceptRole)
                            msg_box.addButton('取消', QMessageBox.DestructiveRole)
                            reply = msg_box.exec()
                            if reply == QMessageBox.AcceptRole:
                                for path in self.del_list:
                                    try:
                                        os.remove(path)
                                    except Exception as e:
                                        msg_box = QMessageBox(self)
                                        msg_box.setWindowTitle('错误')
                                        msg_box.setIcon(QMessageBox.Critical)
                                        msg_box.setInformativeText('无法删除\n{0}'.format(path))
                                        msg_box.setText(repr(e))
                                        msg_box.addButton('确定', QMessageBox.AcceptRole)
                                        msg_box.exec()
                                self.remove_all()
                        else:
                            msg_box = QMessageBox(self)
                            msg_box.setWindowTitle('成功')
                            msg_box.setIcon(QMessageBox.Information)
                            msg_box.setText('传输成功完成!')
                            msg_box.addButton('确定', QMessageBox.AcceptRole)
                            msg_box.exec()
                    else:
                        msg_box = QMessageBox(self)
                        msg_box.setWindowTitle('警告')
                        msg_box.setIcon(QMessageBox.Warning)
                        msg_box.setText('传输完成,但有文件传输出错!')
                        msg_box.addButton('确定', QMessageBox.AcceptRole)
                        msg_box.exec()
                    self.ui_pending()
                else:
                    self.Lclient_status.setText(
                        '''传输中:<font color=green>{0}<font color=black>/<font color=red>{1}<font color=black>/{2} 
                        (<font color=green>Comp<font color=black>/<font color=red>Err<font color=black>/Up)'''.format(
                            len(self.find_instance_by_status('complete')), len(self.find_instance_by_status('error')),
                            len(self.find_instance_by_status('uploading'))))
            elif message['type'] == 'prog':
                inst.prog.setValue(message['part'] + 1)
                if self.setting_detail_view:  # 详细视图:更新进度百分比
                    index = self.find_index_by_name(message['name'])
                    file_prog = message['part'] / inst.prog.maximum() * 100
                    self.file_table.item(index, 2).setText('{0:.2f} %'.format(file_prog))
        except queue.Empty:
            pass

    """绑定事件函数"""

    def del_file(self, inst):
        """按钮绑定删除事件:从当前表格中找到行索引并删除以及删除文件列表中的实例。"""
        index = self.find_index_by_name(inst.name)
        self.file_table.removeRow(index)
        self.files.remove(inst)
        if not self.files:
            self.file_table.hide()
            self.Lfile_empty.show()
            self.act_send.setDisabled(True)
            self.Bfile_sender.setDisabled(True)

    def remove_all(self):
        for i in range(len(self.files), 0, -1):  # 批量删除时需要从末尾开始逐个删除
            self.del_file(self.files[i - 1])

    def button_pressed(self):
        self.button_timer = QTimer()
        self.button_timer.timeout.connect(self.remove_all)
        self.button_timer.setSingleShot(True)
        self.button_timer.start(self.setting_del_timeout * 1000)

    def button_released(self):
        self.button_timer.stop()

    """通用功能性函数"""

    def abort_trans(self):
        """中断传输函数:发送取消消息"""
        self.settings.beginGroup('ClientSetting')
        setting_host = self.settings.value('host', '127.0.0.1')
        setting_port = int(self.settings.value('server_port', 12345))
        self.settings.endGroup()
        try:
            self.file_sender.terminate()  # 关闭当前的发送进程
            self.file_sender.join()
            self.file_sender.close()
            del self.file_sender
        except ValueError:
            pass
        except AttributeError:
            pass

        self.abort_sender = Process(target=trans_client.starter, name='AbortSender',
                                    args=(setting_host, setting_port, '', None, self.client_que))
        self.abort_sender.start()
        self.abort_sender.join(5)
        if self.abort_sender.exitcode is None:  # join超时,未收到中断回包时
            msg_box = QMessageBox(self)
            msg_box.setWindowTitle('警告')
            msg_box.setIcon(QMessageBox.Warning)
            msg_box.setText('接收端无响应!')
            msg_box.addButton('确定', QMessageBox.AcceptRole)
            msg_box.exec()
            self.abort_sender.kill()
            for inst in self.find_instance_by_status('uploading'):
                inst.status = 'error'
                i = self.find_index_by_name(inst.name)
                self.file_table.item(i, 4).setText('传输中断')
            self.ui_pending()
            self.abort_sender.join()
        self.abort_sender.close()
        del self.abort_sender

    def shorten_filename(self, name, width):
        """根据给定的宽度截断文件名并添加...,返回截断后的文件名。"""
        metrics = QFontMetrics(self.font())
        if metrics.width(name) > width - self.reso_width / 128:
            for i in range(4, len(name)):  # 从第4个字符开始计算长度
                if metrics.width(name[:i]) > width - self.reso_width / 128:
                    return name[:i] + '...'
        return name

    def safe_close(self):
        """结束各个子进程及计时器,做好退出准备。"""
        self.settings.beginGroup('Misc')  # 记录关闭时的窗口尺寸
        self.settings.setValue('window_size', (self.geometry().width(), self.geometry().height()))
        self.settings.setValue('frame_width', (self.sender_frame.width(), self.chat_frame.width()))
        self.settings.endGroup()
        self.settings.sync()
        try:  # 关闭后台监控进程和传输子进程
            self.server_timer.stop()
            del self.server_timer
            self.server_starter.terminate()
            self.server_starter.join()
            self.server_starter.close()
            self.prog_timer.stop()
            del self.prog_timer
            self.file_sender.terminate()
            self.file_sender.join()
            self.file_sender.close()
        except ValueError:  # 忽略传输子进程已关闭时的异常
            pass
        except AttributeError:  # 忽略还未声明变量时的异常
            pass
        try:  # 关闭聊天子进程
            self.chat_sender.terminate()
            self.chat_sender.join()
            self.chat_sender.close()
        except ValueError:
            pass
        except AttributeError:
            pass

    def find_instance_by_name(self, name):
        """按文件名在文件列表中查找实例,没有返回None。"""
        for inst in self.files:
            if inst.name == name:
                return inst
        return None

    def find_instance_by_status(self, status):
        """按文件状态在文件列表中查找实例,没有返回空列表。"""
        inst_list = [inst for inst in self.files if inst.status[0] == status]
        return inst_list

    def find_index_by_name(self, name):
        """按文件名在表格视图中查找索引,没有返回None。"""
        row = self.file_table.rowCount()
        for i in range(row):
            if self.file_table.cellWidget(i, 1).layout().widget(1).toolTip() == name:
                return i
        return None

    """对话框实例创建函数"""

    def file_dialog(self):
        """文件选择对话框,制作文件实例。"""
        self.settings.beginGroup('Misc')
        setting_path_history = self.settings.value('path_history', '.')
        self.settings.endGroup()
        fname = QFileDialog.getOpenFileNames(self, '请选择文件', setting_path_history)
        if fname[0]:
            new_list = set(fname[0])
            old_list = {inst.path for inst in self.files}
            old_name = {inst.name for inst in self.files}
            same_list = {path for path in new_list if os.path.split(path)[1] in old_name}
            add_list = new_list - old_list - same_list  # 用集合set求纯新增文件路径列表(剔除重名项)
            add_files = [FileStatus(path) for path in add_list]
            self.Lfile_empty.hide()
            if not self.setting_detail_view:
                self.simple_viewer(add_files)
            else:
                self.detail_viewer(add_files)
            self.Bfile_sender.setDisabled(False)
            self.act_send.setDisabled(False)
            self.settings.beginGroup('Misc')
            self.settings.setValue('path_history', os.path.split(fname[0][-1])[0])
            self.settings.endGroup()
        self.settings.sync()

    def client_setting_dialog(self):
        self.client_setting = def_widget.ClientSettingDialog(self)
        self.client_setting.setAttribute(Qt.WA_DeleteOnClose)
        self.client_setting.destroyed.connect(self.client_setting_checker)
        self.client_setting.show()

    def server_setting_dialog(self):
        self.server_setting = def_widget.ServerSettingDialog(self)
        self.server_setting.setAttribute(Qt.WA_DeleteOnClose)
        self.server_setting.show()

    def ui_setting_dialog(self):
        self.ui_setting = def_widget.UIDialog(self)
        self.ui_setting.setAttribute(Qt.WA_DeleteOnClose)
        self.ui_setting.destroyed.connect(self.ui_setting_checker)
        self.ui_setting.show()

    """事件函数重载"""

    def closeEvent(self, event):
        """关闭事件函数:检查是否有传输中的文件并退出"""
        if self.find_instance_by_status('uploading'):
            msg_box = QMessageBox(self)
            msg_box.setWindowTitle('警告')
            msg_box.setIcon(QMessageBox.Warning)
            msg_box.setText('文件传输中,是否中断并退出?')
            msg_box.addButton('确定', QMessageBox.AcceptRole)
            msg_box.addButton('取消', QMessageBox.DestructiveRole)
            reply = msg_box.exec()
            if reply == QMessageBox.AcceptRole:
                self.abort_trans()
                self.safe_close()
                event.accept()
            else:
                event.ignore()
        else:
            self.safe_close()
            event.accept()

    def keyPressEvent(self, k):
        """按ESC时触发的关闭行为。"""
        if k.key() == Qt.Key_Escape:
            self.close()

    def resizeEvent(self, event):
        """调整窗口尺寸时触发。"""
        for inst in self.files:  # 根据表格列宽调整截断文件名
            changed_text = self.shorten_filename(inst.name, self.file_table.columnWidth(1))
            inst.label.setText(changed_text)
Ejemplo n.º 49
0
    def run(self):
        settings = QSettings()
        pref_target_path = settings.value(Settings.SETTINGS_SAVE_PATH,
                                          Settings.DEFAULT_TARGET_PATH,
                                          type=str)
        pref_max_pool_cnt = settings.value(Settings.SETTINGS_MAX_POOL_CNT,
                                           Settings.DEFAULT_MAX_POOL,
                                           type=int)
        gallery_save_path = pref_target_path + '/' + self.gallery.path
        if not os.path.exists(gallery_save_path):
            os.makedirs(gallery_save_path)

        # Cloudflare Authorization
        self.state.emit('Authorize..')
        Logger.LOGGER.info("Wait for Cloudflare Authorization..")
        self.driver.get(URL_HITOMI)
        while "Just a moment..." in self.driver.page_source:
            pass
        user_agent = self.driver.execute_script("return navigator.userAgent;")

        try:
            cookie_value = '__cfduid=' + self.driver.get_cookie('__cfduid')['value'] + \
                           '; cf_clearance=' + self.driver.get_cookie('cf_clearance')['value']
            headers = {'User-Agent': user_agent}
            cookies = {'session_id': cookie_value}
        except TypeError:
            Logger.LOGGER.warning("Not apply cookies to requests")
            headers = None
            cookies = None

        # Make Download Session
        session = FuturesSession(max_workers=pref_max_pool_cnt)
        if headers is not None:
            session.headers = headers
        if cookies is not None:
            session.cookies = cookies
        responses = {}

        # Fetch image data from gallery page
        download_list = []
        self.state.emit('Fetch..')
        Logger.LOGGER.info("Connect to Gallery page..")
        self.driver.get(self.gallery.url.replace('galleries', 'reader'))
        source = self.driver.page_source
        soup = BeautifulSoup(source, 'html.parser')
        ref_url = soup.find('img')['src']
        ref_key = ref_url[:ref_url.index('.')]
        img_urls = soup.find_all('div', class_='img-url')
        self.total_cnt = len(img_urls)
        for img_url in img_urls:
            download_url = 'https:' + img_url.get_text().replace(
                '//g', ref_key)
            download_name = download_url.split('/')[-1]
            responses[download_name] = session.get(download_url)
            download_list.append(download_url)
        for filename in responses:
            self.response_to_file(response=responses[filename].result(),
                                  name=filename,
                                  path=gallery_save_path)
        session.close()

        # Compress Zip Files
        self.state.emit('Compressing..')
        if self.gallery.original != "":
            zip_path = pref_target_path + '/' + self.gallery.type + '/' + self.gallery.original + '/' + self.gallery.path + '.zip'
        else:
            zip_path = pref_target_path + '/' + self.gallery.type + '/' + self.gallery.path + '.zip'

        try:
            if not os.path.exists(zip_path[:zip_path.rfind('/')]):
                os.makedirs(zip_path[:zip_path.rfind('/')])
            FileUtil.make_zip(gallery_save_path, zip_path)
            shutil.rmtree(gallery_save_path)
        except:
            print(traceback.format_exc())
            Logger.LOGGER.error("Compressing Process Error... pass")
        # Save to Firebase
        # TODO Enable next line on Build
        FirebaseClient.fbclient.insert_data(self.gallery)
Ejemplo n.º 50
0
class TheWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.in_tree_nav = False
        self.font_metrics = QFontMetrics(QApplication.font())

        self.load_settings()
        setup_ui(self)
        self.setAcceptDrops(True)

        # The data model placeholders - to be populated once we read a file
        self.tree_model = None # Recreated between files
        self.die_model = None # Reused between DIEs

        self.findcondition = None
        self.findcucondition = None

        self.show()

        # Command line: if can't open, print the error to console
        # On Mac/Linux, the user will see it. On Windows, they won't.
        if len(sys.argv) > 1:
            try:
                if self.open_file(sys.argv[1]) is None:
                    print("The file contains no DWARF information, or it is in an unsupported format.")
            except Exception as exc:
                print(format(exc))

    def load_settings(self):
        self.sett = QSettings('Seva', 'DWARFExplorer')
        self.prefix = self.sett.value('General/Prefix', False, type=bool)
        self.lowlevel = self.sett.value('General/LowLevel', False, type=bool)
        self.hex = self.sett.value('General/Hex', False, type=bool)
        self.sortcus = self.sett.value('General/SortCUs', True, type=bool)
        self.sortdies = self.sett.value('General/SortDIEs', False, type=bool)
        self.dwarfregnames = self.sett.value('General/DWARFRegNames', False, type=bool)
        self.mru = []
        for i in range(0, 10):
            f = self.sett.value("General/MRU%d" % i, False)
            if f:
                arch = self.sett.value("General/MRUArch%d" % i, None)
                fa = (f,) if arch is None else (f,arch) 
                self.mru.append(fa)        

    ###################################################################
    # Done with init, now file stuff
    ###################################################################

    # Callback for the Mach-O fat binary opening logic
    # Taking a cue from Hopper or IDA, we parse only one slice at a time
    def resolve_arch(self, arches):
        r = QInputDialog.getItem(self, 'Mach-O Fat Binary', 'Choose an architecture:', arches, 0, False, Qt.WindowType.Dialog)
        return arches.index(r[0]) if r[1] else None

    # Can throw an exception
    # Returns None if it doesn't seem to contain DWARF
    # False if the user cancelled
    # True if the DWARF tree was loaded
    def open_file(self, filename, arch = None):
        self.start_wait()
        try:
            di = read_dwarf(filename, self.resolve_arch if arch is None else lambda arches: arches.index(arch))
            if not di: # Covers both False and None
                return di

            # Some cached top level stuff
            # Notably, iter_CUs doesn't cache
            di._ranges = None # Loaded on first use
            def decorate_cu(cu, i):
                cu._i = i
                cu._lineprogram = None
                cu._exprparser = None
                return cu
            di._unsorted_CUs = [decorate_cu(cu, i) for (i, cu) in enumerate(di.iter_CUs())] # We'll need them first thing, might as well load here
            if not len(di._unsorted_CUs):
                return None # Weird, but saw it once - debug sections present, but no CUs
            # For quick CU search by offset within the info section, regardless of sorting
            di._CU_offsets = [cu.cu_offset for cu in di._unsorted_CUs]
            di._CUs = list(di._unsorted_CUs)

            if self.sortcus:
                di._CUs.sort(key = cu_sort_key)
                for (i, cu) in enumerate(di._CUs):
                    cu._i = i
            di._locparser = None # Created on first use

            self.tree_model = DWARFTreeModel(di, self.prefix, self.sortcus, self.sortdies)
            self.the_tree.setModel(self.tree_model)
            self.the_tree.selectionModel().currentChanged.connect(self.on_tree_selection)
            s = os.path.basename(filename)
            if arch is not None:
                s += ' (' + arch + ')'
            self.setWindowTitle("DWARF Explorer - " + s)
            self.back_menuitem.setEnabled(False)
            self.forward_menuitem.setEnabled(False)
            self.followref_menuitem.setEnabled(False)
            self.highlightcode_menuitem.setEnabled(True)
            self.highlightnothing_menuitem.setEnabled(True)
            self.copy_menuitem.setEnabled(False)
            self.copyline_menuitem.setEnabled(False)
            self.copytable_menuitem.setEnabled(False)
            self.findbycondition_menuitem.setEnabled(True)
            self.find_menuitem.setEnabled(True)
            self.findip_menuitem.setEnabled(True)
            self.on_highlight_nothing()
            # Navigation stack - empty
            self.navhistory = []
            self.navpos = -1
            self.save_filename_in_mru(filename, di._fat_arch if '_fat_arch' in dir(di) and di._fat_arch else None)
            return True
        finally:
            self.end_wait()

    def save_mru(self):
        for i, fa in enumerate(self.mru):
            self.sett.setValue("General/MRU%d" % i, fa[0])    
            if len(fa) > 1:
                self.sett.setValue("General/MRUArch%d" % i, fa[1])
            else:
                self.sett.remove("General/MRUArch%d" % i)

    # Open a file, display an error if failure
    def open_file_interactive(self, filename, arch = None):
        try:
            if self.open_file(filename, arch) is None:
                QMessageBox(QMessageBox.Icon.Warning, "DWARF Explorer",
                    "The file contains no DWARF information, or it is in an unsupported format.",
                    QMessageBox.Ok, self).show()
        except Exception as exc:
            QMessageBox(QMessageBox.Icon.Critical, "DWARF Explorer",
                "Error opening the file:\n\n" + format(exc),
                QMessageBox.Ok, self).show()

    # TODO: list the extensions for the open file dialog?
    def on_open(self):
        dir = os.path.dirname(self.mru[0][0]) if len(self.mru) > 0 else ''
        filename = QFileDialog.getOpenFileName(self, None, dir)
        if filename[0]:
            self.open_file_interactive(os.path.normpath(filename[0]))

    def populate_mru_menu(self):
        class MRUHandler(object):
            def __init__(self, fa, win):
                object.__init__(self)
                self.fa = fa
                self.win = win
            def __call__(self):
                self.win.open_file_interactive(*self.fa)

        for i, fa in enumerate(self.mru):
            s = fa[0]
            if len(fa) > 1:
                s += ' (' + fa[1] + ')'
            self.mru_menu.addAction(s).triggered.connect(MRUHandler(fa, self))

    def save_filename_in_mru(self, filename, arch = None):
        mru_record = (filename,) if arch is None else (filename, arch)
        try:
            i = self.mru.index(mru_record)
        except ValueError:
            i = -1
        if i != 0:
            if i > 0:
                self.mru.pop(i)
            self.mru.insert(0, mru_record)
            if len(self.mru) > 10:
                self.mru = self.mru[0:10]
            self.save_mru()
            self.mru_menu.setEnabled(True)
            self.mru_menu.clear()
            self.populate_mru_menu()    

    # File drag/drop handling - equivalent to open
    def dragEnterEvent(self, evt):
        if evt.mimeData() and evt.mimeData().hasUrls() and len(evt.mimeData().urls()) == 1:
            evt.accept()

    def dropEvent(self, evt):
        self.open_file_interactive(os.path.normpath(evt.mimeData().urls()[0].toLocalFile()))               

    #############################################################
    # Done with file stuff, now tree navigation
    #############################################################     

    # Index is a tree index - the DIE is the data object within
    def display_die(self, index):
        die = index.internalPointer()
        die_table = self.die_table
        if not self.die_model:
            self.die_model = DIETableModel(die, self.prefix, self.lowlevel, self.hex, self.dwarfregnames)
            die_table.setModel(self.die_model)
            die_table.selectionModel().currentChanged.connect(self.on_attribute_selection)
        else:
            self.die_model.display_DIE(die)
        self.die_table.resizeColumnsToContents()
        self.details_table.setModel(None)
        self.followref_menuitem.setEnabled(False)
        self.cuproperties_menuitem.setEnabled(True)
        self.die_table.setCurrentIndex(QModelIndex()) # Will cause on_attribute_selection

        #TODO: resize the attribute table vertically dynamically
        #attr_count = self.die_model.rowCount(None)
        #die_table.resize(die_table.size().width(),
        #    die_table.rowViewportPosition(attr_count-1) + 
        #        die_table.rowHeight(attr_count-1) +
        #        die_table.horizontalHeader().size().height() + 1 + attr_count)
        #self.rpane_layout.update()

    # Invoked for tree clicks and keyboard navigation, ref follow, back-forward
    def on_tree_selection(self, index, prev = None):
        if not self.in_tree_nav: # Short out the history population logic for back-forward clicks
            navitem = self.tree_model.get_navitem(index)
            self.navhistory[0:self.navpos] = [navitem]
            self.navpos = 0
            self.back_menuitem.setEnabled(len(self.navhistory) > 1)
            self.forward_menuitem.setEnabled(False)
        self.display_die(index) # Will clear the selection in the attribute table

    # Selection changed in the DIE table - either user or program
    def on_attribute_selection(self, index, prev = None):
        if index.isValid():
            details_model = self.die_model.get_attribute_details(index)
            self.details_table.setModel(details_model)
            if details_model is not None:
                self.details_table.resizeColumnsToContents()
            self.followref_menuitem.setEnabled(self.die_model.ref_target(index) is not None)
            self.copy_menuitem.setEnabled(True)
            self.copyline_menuitem.setEnabled(True)
            self.copytable_menuitem.setEnabled(True)
        else: # Selected nothing
            self.details_table.setModel(None)
            self.copy_menuitem.setEnabled(False)
            self.copyline_menuitem.setEnabled(False)
            self.copytable_menuitem.setEnabled(False)            
            self.followref_menuitem.setEnabled(False)


    def on_attribute_dclick(self, index):
        self.followref(index)

    # For both back and forward, delta=1 for back, -1 for forward
    # Checked because back-forward buttons can't be disabled
    def on_nav(self, delta):
        np = self.navpos + delta
        if np < 0 or np >= len(self.navhistory):
            return
        self.navpos = np
        navitem = self.navhistory[np]
        tree_index = self.tree_model.index_for_navitem(navitem)
        self.in_tree_nav = True
        self.the_tree.setCurrentIndex(tree_index) # Causes on_tree_selection internally
        self.in_tree_nav = False
        self.back_menuitem.setEnabled(np < len(self.navhistory) - 1)
        self.forward_menuitem.setEnabled(np > 0)

    def followref(self, index = None):
        self.start_wait() # TODO: only show the wait cursor if it's indeed time consuming
        if index is None:
            index = self.die_table.currentIndex()
        navitem = self.die_model.ref_target(index)  # Retrieve the ref target from the DIE model...
        if navitem:
            target_tree_index = self.tree_model.index_for_navitem(navitem) # ...and feed it to the tree model.
            self.the_tree.setCurrentIndex(target_tree_index) # Calls on_tree_selection internally
        self.end_wait()

    # Called for double-click on a reference type attribute, and via the menu
    def on_followref(self):
        self.followref()

    # Back-forward mouse buttons are shortcuts for back/forward navigation
    # Qt docs claim capturing is not necessary
    #def mouseReleaseEvent(self, evt):
    #    QMainWindow.mouseReleaseEvent(self, evt)
    #    b = evt.button()
    #    if b == Qt.MouseButton.BackButton:
    #        self.on_nav(1)
    #    elif b == Qt.MouseButton.ForwardButton:
    #        self.on_nav(-1)
        

    ##########################################################################
    # Find/Find next stuff
    ##########################################################################

    def findbytext(self, die, s):
        for k in die.attributes.keys():
            attr = die.attributes[k]
            v = attr.value
            f = attr.form
            all = "\n".join((str(v), str(k), f, hex(v) if isinstance(v, int) else '')).lower()
            if all.find(s) >= 0:
                return True
        return False

    # Exception means false
    def eval_user_condition(self, cond, die):
        try:
            def has_attribute(func):
                for k in die.attributes:
                    if func(k, die.attributes[k].value, die.attributes[k].form):
                        return True

            return eval(cond, {'die' : die, 'has_attribute' : has_attribute})
        except Exception as exc:
            print("Error in user condition: %s" % format(exc))
            return False

    def on_find(self):
        r = QInputDialog.getText(self, 'Find', 'Find what:')
        if r[1] and r[0]:
            s = r[0].lower()
            self.findcondition = lambda die: self.findbytext(die, s)
            self.findcucondision = None
            self.findnext_menuitem.setEnabled(True)
            self.on_findnext()

    def on_findip(self):
        r = QInputDialog.getText(self, "Find code address", "Offset (hex), assuming the module is loaded at its preferred address:")
        if r[1] and r[0]:
            try:
                ip = int(r[0], 16)
                self.findcondition = lambda die: ip_in_range(die, ip)
                self.findcucondition = lambda cu: ip_in_range(cu.get_top_DIE(), ip)
                self.findnext_menuitem.setEnabled(True)
                self.on_findnext()            
            except ValueError:
                pass

    def on_findbycondition(self):
        dlg = ScriptDlg(self)
        if dlg.exec() == QDialog.Accepted:
            cond = dlg.cond
            self.findcondition = lambda die: self.eval_user_condition(cond, die)
            self.findcucondition = None
            self.findnext_menuitem.setEnabled(True)
            self.on_findnext()

    def on_findnext(self):
        index = self.tree_model.find(self.the_tree.currentIndex(), self.findcondition, self.findcucondition)
        if index:
            self.the_tree.setCurrentIndex(index)

    ##########################################################################
    ##########################################################################

    def on_about(self):
        QMessageBox(QMessageBox.Icon.Information, "About...", "DWARF Explorer v." + '.'.join(str(v) for v in version) + "\n\nSeva Alekseyev, 2020\[email protected]\n\ngithub.com/sevaa/dwex",
            QMessageBox.Ok, self).show()

    def on_updatecheck(self):
        from urllib.request import urlopen
        import json
        try:
            self.start_wait()
            resp = urlopen('https://api.github.com/repos/sevaa/dwex/releases')
            if resp.getcode() == 200:
                releases = resp.read()
                self.end_wait()
                releases = json.loads(releases)
                if len(releases) > 0:
                    max_ver = max(tuple(int(v) for v in r['tag_name'].split('.')) for r in releases)
                    max_tag = '.'.join(str(i) for i in max_ver)
                    if max_ver > version:
                        s = "DWARF Explorer v." + max_tag + " is out. Use \"pip install --upgrade dwex\" to update."
                    else: 
                        s = "You have the latest version."
                    QMessageBox(QMessageBox.Icon.Information, "DWARF Explorer", s, QMessageBox.Ok, self).show()
        except:
            self.end_wait()

    def on_exit(self):
        self.destroy()

    # Checkmark toggling is handled by the framework
    def on_view_prefix(self, checked):
        self.prefix = checked
        self.sett.setValue('General/Prefix', self.prefix)
        if self.tree_model:
            self.tree_model.set_prefix(checked)

        if self.die_model:
            self.die_model.set_prefix(checked)
            self.refresh_details()

    # Checkmark toggling is handled by the framework
    def on_view_lowlevel(self, checked):   
        self.lowlevel = checked
        self.sett.setValue('General/LowLevel', self.lowlevel)
        if self.die_model:
            new_sel = self.die_model.set_lowlevel(checked, self.die_table.currentIndex())
            if new_sel:
                self.die_table.setCurrentIndex(new_sel)

    def on_view_hex(self, checked):        
        self.hex = checked
        self.sett.setValue('General/Hex', self.hex)
        if self.die_model:
            self.die_model.set_hex(checked)
            self.refresh_details()

    def on_view_regnames(self, checked):        
        self.dwarfregnames = checked
        self.sett.setValue('General/DWARFRegNames', self.dwarfregnames)
        if self.die_model:
            self.die_model.set_regnames(checked)
            self.refresh_details()            

    def on_sortcus(self, checked):
        self.sortcus = checked
        self.sett.setValue('General/SortCUs', self.sortcus)
        if self.tree_model:
            sel = self.the_tree.currentIndex()
            sel = self.tree_model.set_sortcus(checked, sel) # This will reload the tree
            self.the_tree.setCurrentIndex(sel)

    def on_sortdies(self, checked):
        self.sortdies = checked
        self.sett.setValue('General/SortDIEs', self.sortdies)
        if self.tree_model:
            #Throw away everything we had cached so far
            sel = self.tree_model.set_sortdies(checked)
            #This invalidates the navigation
            self.back_menuitem.setEnabled(False)
            self.forward_menuitem.setEnabled(False)
            self.followref_menuitem.setEnabled(False)
            # Navigation stack - empty
            self.navhistory = []
            self.navpos = -1
            self.the_tree.setCurrentIndex(sel)

    def on_highlight_code(self):
        self.highlightcode_menuitem.setChecked(True)
        self.tree_model.highlight(has_code_location)

    def on_highlight_nothing(self):
        self.highlightcode_menuitem.setChecked(False)
        self.tree_model.highlight(None)

    def on_cuproperties(self):
        cu = self.the_tree.currentIndex().internalPointer().cu
        ver = cu['version']
        if ver > 1:
            props = (ver, cu['unit_length'], cu['debug_abbrev_offset'], cu['address_size'])
            s = "DWARF version:\t%d\nLength:\t%d\nAbbrev table offset: 0x%x\nAddress size:\t%d" % props
        else:
            props = (ver, cu['address_size'])
            s = "DWARF version:\t%d\nAddress size:\t%d" % props
        t = "CU at 0x%x" % cu.cu_offset
        QMessageBox(QMessageBox.Icon.Information, t, s, QMessageBox.Ok, self).show()

    def on_copy(self, v):
        cb = QApplication.clipboard()
        cb.clear()
        cb.setText(v)

    def on_copyvalue(self):
        t = self.details_table if self.details_table.hasFocus() else self.die_table
        m = t.model()
        self.on_copy(m.data(t.currentIndex(), Qt.DisplayRole))

    def on_copyline(self):
        t = self.details_table if self.details_table.hasFocus() else self.die_table
        m = t.model()
        row = t.currentIndex().row()
        line = "\t".join(m.data(m.index(row, c, QModelIndex()), Qt.DisplayRole)
            for c in range(0, m.columnCount(QModelIndex())))
        self.on_copy(line)

    def on_copytable(self):
        t = self.details_table if self.details_table.hasFocus() else self.die_table
        m = t.model()
        table_text = "\n".join(
                "\t".join(m.data(m.index(r, c, QModelIndex()), Qt.DisplayRole)
                for c in range(0, m.columnCount(QModelIndex())))
            for r in range(0, m.rowCount(QModelIndex())))
        self.on_copy(table_text)

    # If the details pane has data - reload that
    def refresh_details(self):
        index = self.die_table.currentIndex()
        if index.isValid():
            details_model = self.die_model.get_attribute_details(index)
            if details_model:
                self.details_table.setModel(details_model)
                self.details_table.resizeColumnsToContents()
        self.die_table.resizeColumnsToContents()

    def on_homepage(self):
        QDesktopServices.openUrl(QUrl('https://github.com/sevaa/dwex'))

    # Doesn't quite work for the delay on tree expansion :(
    def start_wait(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)

    def end_wait(self):
        QApplication.restoreOverrideCursor()
class ApisImageRegistry(QObject):

    loaded = pyqtSignal(object)

    def __init__(self, pluginDir, iface):

        QObject.__init__(self)

        self.iface = iface
        self.registryFile = pluginDir + "\\" + "apis_image_registry.json"  # self.settings.value("APIS/image_registry_file", None)

        # NE ... NoExtension
        self.__imageRegistryNE = None
        self.__hiResRegistryNE = None
        self.__i2cRegistryNE = None
        self.__orthoRegistryNE = None
        self.__mosaicRegistryNE = None
        self.__imageRegistry = None
        self.__hiResRegistry = None
        self.__i2cRegistry = None
        self.__orthoRegistry = None
        self.__mosaicRegistry = None

        self.isLoaded = False
        self.isSetup = False

        self.worker = None

    def setupSettings(self):
        self.settings = QSettings(QSettings().value("APIS/config_ini"), QSettings.IniFormat)

        self.imageDirName = self.settings.value("APIS/image_dir")
        self.orthoDirName = self.settings.value("APIS/ortho_image_dir")
        self.imageDir = QDir(self.imageDirName)
        self.orthoDir = QDir(self.orthoDirName)

        self.imageFormats = self.settings.value("APIS/image_formats", [u'jpg'])
        self.hiResFormats = self.settings.value("APIS/hires_formats", [u'jpg', u'tif', u'sid', u'nef', u'raf', u'cr2', u'dng'])
        self.orthoFormats = self.settings.value("APIS/ortho_formats", [u'jpg', u'tif', u'sid'])

        self.imageFormatsStr = u"|".join(self.imageFormats)
        self.hiResFormatsStr = u"|".join(self.hiResFormats)
        self.orthoFormatsStr = u"|".join(self.orthoFormats)

        self.isSetup = True

    def registryIsSetup(self):
        return self.isSetup

    def setupRegistry(self):
        if self.registryFile and os.path.isfile(self.registryFile):
            if self.isOutdated():
                # If isOutdated > PopUp > Info: Local Image Registry is outdated > Please Update Image Registry
                # ask if update ImageRegistry
                msgBox = QMessageBox()
                msgBox.setWindowTitle(u'Image Registry')
                msgBox.setText(u'Die APIS Image Registry ist älter als ein Monat. Bitte führen Sie ein Update durch!')
                msgBox.addButton(QPushButton(u'Update'), QMessageBox.YesRole)
                msgBox.addButton(QPushButton(u'Abbrechen'), QMessageBox.NoRole)
                ret = msgBox.exec_()

                if ret == 0:
                    self.updateRegistries()
                else:
                    self.loadRegistryFromFile()
            else:
                self.loadRegistryFromFile()
        else:
            # ask if generate ImageRegistry
            msgBox = QMessageBox()
            msgBox.setWindowTitle(u'Image Registry')
            msgBox.setText(u'Für die Verwendung von APIS muss eine Image Registry erstellt werden!')
            msgBox.addButton(QPushButton(u'Jetzt erstellen'), QMessageBox.YesRole)
            msgBox.addButton(QPushButton(u'Abbrechen'), QMessageBox.NoRole)
            ret = msgBox.exec_()
            if ret == 0:
                self.updateRegistries()
            else:
                self.isLoaded = False

    def registryIsLoaded(self):
        return self.isLoaded

    def loadRegistryFromFile(self):
        #load self.__imageRegistry, self.__hiResRegistry, self.__orthoRegistry from JSON File
        if os.path.isfile(self.registryFile):
            with open(self.registryFile, 'rU') as registry:
                registryDict = json.load(registry)
                self.__imageRegistryNE = registryDict["imageRegistryNE"]
                self.__hiResRegistryNE = registryDict["hiResRegistryNE"]
                self.__i2cRegistryNE = registryDict["i2cRegistryNE"]
                self.__orthoRegistryNE = registryDict["orthoRegistryNE"]
                self.__mosaicRegistryNE = registryDict["mosaicRegistryNE"]

                self.__imageRegistry = registryDict["imageRegistry"]
                self.__hiResRegistry = registryDict["hiResRegistry"]
                self.__i2cRegistry = registryDict["i2cRegistry"]
                self.__orthoRegistry = registryDict["orthoRegistry"]
                self.__mosaicRegistry = registryDict["mosaicRegistry"]
            self.isLoaded = True
            self.loaded.emit(True)
        else:
            self.isLoaded = False

    def writeRegistryToFile(self):
        # write self.__imageRegistry, self.__hiResRegistry, self.__orthoRegistry to JSON File
        # does registry file exist
        if os.path.isfile(self.registryFile):
            os.remove(self.registryFile)

        with open(self.registryFile, 'w') as f:
            registryDict = {
                "imageRegistryNE": self.__imageRegistryNE,
                "hiResRegistryNE": self.__hiResRegistryNE,
                "i2cRegistryNE": self.__i2cRegistryNE,
                "orthoRegistryNE": self.__orthoRegistryNE,
                "mosaicRegistryNE": self.__mosaicRegistryNE,
                "imageRegistry": self.__imageRegistry,
                "hiResRegistry": self.__hiResRegistry,
                "i2cRegistry": self.__i2cRegistry,
                "orthoRegistry": self.__orthoRegistry,
                "mosaicRegistry": self.__mosaicRegistry
            }
            json.dump(registryDict, f)

    def isOutdated(self):
        if os.path.isfile(self.registryFile):
            today = datetime.datetime.today()
            creationDate = datetime.datetime.fromtimestamp(os.path.getctime(self.registryFile))
            modificationDate = datetime.datetime.fromtimestamp(os.path.getmtime(self.registryFile))
            if modificationDate > creationDate:
                fileAge = today - modificationDate
            else:
                fileAge = today - creationDate
            #QMessageBox.warning(None, "Zeit", "{0}".format(fileAge.days))
            if fileAge.days > 30:
                return True
            else:
                return False
            # Registry is Outdated > if local File is older than one month!

    def updateRegistries(self):
        self.startWorker()

    def hasImage(self, imageNumber):
        return True if imageNumber in self.__imageRegistryNE else False

    def hasHiRes(self, imageNumber):
        return True if imageNumber in self.__hiResRegistryNE else False

    def hasIns2Cam(self, imageNumber):
        return True if imageNumber in self.__i2cRegistryNE else False

    def hasOrtho(self, imageNumber):
        return True if imageNumber in self.__orthoRegistryNE else False

    def hasMosaic(self, imageNumber):
        filmNumber = imageNumber[:10]
        image = int(imageNumber[11:14])
        r = re.compile(r"^{0}*".format(filmNumber), re.IGNORECASE)
        mosaicCandidates = list(filter(r.match, self.__mosaicRegistryNE))
        mosaicsValid = []
        for mC in mosaicCandidates:
            fromTo = range(int(mC[11:14]), int(mC[15:18]) + 1)
            if image in fromTo:
                mosaicsValid.append(mC)
        return mosaicsValid
        # QMessageBox.information(None, "MosaicInfo", "{0}: {1}".format(imageNumber, ", ".join(mosaicsValid)))

    def hasOrthoOrMosaic(self, imageNumber):
        return self.hasOrtho(imageNumber) or bool(self.hasMosaic(imageNumber))

    def hasImageRE(self, imageNumber):
        r = re.compile(r"^{0}\.({1})$".format(imageNumber, self.imageFormatsStr), re.IGNORECASE)
        r = list(filter(r.match, self.__imageRegistry))
        return len(r)

    def hasHiResRE(self, imageNumber):
        r = re.compile(r"^{0}\.({1})$".format(imageNumber, self.hiResFormatsStr), re.IGNORECASE)
        r = list(filter(r.match, self.__hiResRegistry))
        return len(r)

    def hasIns2CamRE(self, imageNumber):
        pass

    def hasOrthoRE(self, imageNumber):
        r = re.compile(r"^{0}_op.+\.({1})$".format(imageNumber, self.orthoFormatsStr), re.IGNORECASE)
        r = list(filter(r.match, self.__orthoRegistry))
        return len(r)

    def hasMosaicRE(self, imageNumber):
        pass

    #filmNumber = OLD Film Number
    def getImageRegistryForFilm(self, filmNumber):
        imagesForFilm = []
        for imageNumber in self.__imageRegistryNE:
            if filmNumber in imageNumber:
                imagesForFilm.append(imageNumber)
        return imagesForFilm

    def startWorker(self):
        # create a new worker instance

        if self.worker is None:

            worker = UpdateRegistryWorker()

            # configure the QgsMessageBar
            messageBar = self.iface.messageBar().createMessage(u"Update Image Registry", u"Dieser Vorgang kann einige Minute dauern, bitte haben Sie Geduld!")
            progressBar = QProgressBar()
            progressBar.setMinimum(0)
            progressBar.setMaximum(0)
            progressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            cancelButton = QPushButton()
            cancelButton.setText('Cancel')
            cancelButton.clicked.connect(self.killWorker)
            messageBar.layout().addWidget(progressBar)
            self.progressBar = progressBar
            messageBar.layout().addWidget(cancelButton)
            self.iface.messageBar().pushWidget(messageBar, Qgis.Info)
            #self.iface.messageBar().widgetRemoved
            # messageBar

            self.messageBar = messageBar

            # start the worker in a new thread
            thread = QThread()
            worker.moveToThread(thread)
            worker.finished.connect(self.workerFinished)
            worker.error.connect(self.workerError)
            #worker.progress.connect(progressBar.setValue)
            thread.started.connect(worker.run)
            thread.start()
            self.thread = thread
            self.worker = worker

    def killWorker(self):
        self.worker.kill()
        #self.progressBar.setMaximum(100)
        #self.progressBar.setValue(100)

    def workerFinished(self, ret):
        # clean up the worker and thread
        self.worker.deleteLater()
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()
        # remove widget from message bar
        self.iface.messageBar().popWidget(self.messageBar)
        if ret is not None:
            #report the result
            if not self.worker.killed:
                self.__imageRegistryNE = ret["imageRegistryNE"]
                self.__hiResRegistryNE = ret["hiResRegistryNE"]
                self.__i2cRegistryNE = ret["i2cRegistryNE"]
                self.__orthoRegistryNE = ret["orthoRegistryNE"]
                self.__mosaicRegistryNE = ret["mosaicRegistryNE"]

                self.__imageRegistry = ret["imageRegistry"]
                self.__hiResRegistry = ret["hiResRegistry"]
                self.__i2cRegistry = ret["i2cRegistry"]
                self.__orthoRegistry = ret["orthoRegistry"]
                self.__mosaicRegistry = ret["mosaicRegistry"]
                self.writeRegistryToFile()

                self.iface.messageBar().pushMessage(u"Update Image Registry", u"Das Update wurde erfolgreich abgeschloßen!", level=Qgis.Success, duration=5)
                self.isLoaded = True
                self.loaded.emit(True)
            else:
                self.iface.messageBar().pushMessage(u"Update Image Registry", u"Das Update wurde abgebrochen!", level=Qgis.Warning, duration=5)
                if os.path.isfile(self.registryFile):
                    os.remove(self.registryFile)
                self.isLoaded = False
                self.loaded.emit(False)
            #self.iface.messageBar().pushMessage('Result')
        else:
            # notify the user that something went wrong
            self.iface.messageBar().pushMessage('Something went wrong! See the message log for more information.', level=Qgis.Critical, duration=3)
            self.isLoaded = False
        self.worker = None

    def workerError(self, e, exception_string):
        QgsMessageLog.logMessage('APIS UpdateRegistryWorker thread raised an exception:\n'.format(exception_string), tag='APIS', level=Qgis.Critical)
Ejemplo n.º 52
0
class ListWidget(QWidget):
    deviceSelected = pyqtSignal(TasmotaDevice)
    openRulesEditor = pyqtSignal()
    openConsole = pyqtSignal()
    openTelemetry = pyqtSignal()
    openWebUI = pyqtSignal()

    def __init__(self, parent, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)
        self.setWindowTitle("Devices list")
        self.setWindowState(Qt.WindowMaximized)
        self.setLayout(VLayout(margin=0, spacing=0))

        self.mqtt = parent.mqtt
        self.env = parent.env

        self.device = None
        self.idx = None

        self.nam = QNetworkAccessManager()
        self.backup = bytes()

        self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat)
        views_order = self.settings.value("views_order", [])

        self.views = {}
        self.settings.beginGroup("Views")
        views = self.settings.childKeys()
        if views and views_order:
            for view in views_order.split(";"):
                view_list = self.settings.value(view).split(";")
                self.views[view] = base_view + view_list
        else:
            self.views = default_views
        self.settings.endGroup()

        self.tb = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonTextBesideIcon)
        self.tb_relays = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonIconOnly)
        # self.tb_filter = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonTextBesideIcon)
        self.tb_views = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonTextBesideIcon)

        self.pwm_sliders = []

        self.layout().addWidget(self.tb)
        self.layout().addWidget(self.tb_relays)
        # self.layout().addWidget(self.tb_filter)

        self.device_list = TableView()
        self.device_list.setIconSize(QSize(24, 24))
        self.model = parent.device_model
        self.model.setupColumns(self.views["Home"])

        self.sorted_device_model = QSortFilterProxyModel()
        self.sorted_device_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.sorted_device_model.setSourceModel(parent.device_model)
        self.sorted_device_model.setSortRole(Qt.InitialSortOrderRole)
        self.sorted_device_model.setSortLocaleAware(True)
        self.sorted_device_model.setFilterKeyColumn(-1)

        self.device_list.setModel(self.sorted_device_model)
        self.device_list.setupView(self.views["Home"])
        self.device_list.setSortingEnabled(True)
        self.device_list.setWordWrap(True)
        self.device_list.setItemDelegate(DeviceDelegate())
        self.device_list.sortByColumn(self.model.columnIndex("FriendlyName"), Qt.AscendingOrder)
        self.device_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.device_list.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.layout().addWidget(self.device_list)

        self.layout().addWidget(self.tb_views)

        self.device_list.clicked.connect(self.select_device)
        self.device_list.customContextMenuRequested.connect(self.show_list_ctx_menu)

        self.ctx_menu = QMenu()

        self.create_actions()
        self.create_view_buttons()
        # self.create_view_filter()

        self.device_list.doubleClicked.connect(lambda: self.openConsole.emit())

    def create_actions(self):
        actConsole = self.tb.addAction(QIcon(":/console.png"), "Console", self.openConsole.emit)
        actConsole.setShortcut("Ctrl+E")

        actRules = self.tb.addAction(QIcon(":/rules.png"), "Rules", self.openRulesEditor.emit)
        actRules.setShortcut("Ctrl+R")

        actTimers = self.tb.addAction(QIcon(":/timers.png"), "Timers", self.configureTimers)

        actButtons = self.tb.addAction(QIcon(":/buttons.png"), "Buttons", self.configureButtons)
        actButtons.setShortcut("Ctrl+B")

        actSwitches = self.tb.addAction(QIcon(":/switches.png"), "Switches", self.configureSwitches)
        actSwitches.setShortcut("Ctrl+S")

        actPower = self.tb.addAction(QIcon(":/power.png"), "Power", self.configurePower)
        actPower.setShortcut("Ctrl+P")

        actLight = self.tb.addAction(QIcon(":/color.png"), "Light", self.configureLight)

        # setopts = self.tb.addAction(QIcon(":/setoptions.png"), "SetOptions", self.configureSO)
        # setopts.setShortcut("Ctrl+S")

        self.tb.addSpacer()

        actTelemetry = self.tb.addAction(QIcon(":/telemetry.png"), "Telemetry", self.openTelemetry.emit)
        actTelemetry.setShortcut("Ctrl+T")

        actWebui = self.tb.addAction(QIcon(":/web.png"), "WebUI", self.openWebUI.emit)
        actWebui.setShortcut("Ctrl+U")

        self.ctx_menu.addActions([actRules, actTimers, actButtons, actSwitches, actPower, actLight, actTelemetry, actWebui])
        self.ctx_menu.addSeparator()

        self.ctx_menu_cfg = QMenu("Configure")
        self.ctx_menu_cfg.setIcon(QIcon(":/settings.png"))
        self.ctx_menu_cfg.addAction("Module", self.configureModule)
        self.ctx_menu_cfg.addAction("GPIO", self.configureGPIO)
        self.ctx_menu_cfg.addAction("Template", self.configureTemplate)
        # self.ctx_menu_cfg.addAction("Wifi", self.ctx_menu_teleperiod)
        # self.ctx_menu_cfg.addAction("Time", self.cfgTime.emit)
        # self.ctx_menu_cfg.addAction("MQTT", self.ctx_menu_teleperiod)

        # self.ctx_menu_cfg.addAction("Logging", self.ctx_menu_teleperiod)

        self.ctx_menu_cfg_auto = QMenu("Auto Configure")
        self.ctx_menu_cfg_auto.addAction("Module", self.configureModule)

        self.ctx_menu.addMenu(self.ctx_menu_cfg)
        self.ctx_menu.addSeparator()

        self.ctx_menu.addMenu(self.ctx_menu_cfg_auto)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon(":/refresh.png"), "Refresh", self.ctx_menu_refresh)

        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon(":/clear.png"), "Clear retained", self.ctx_menu_clear_retained)
        self.ctx_menu.addAction("Clear Backlog", self.ctx_menu_clear_backlog)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon(":/copy.png"), "Copy", self.ctx_menu_copy)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon(":/restart.png"), "Restart", self.ctx_menu_restart)
        self.ctx_menu.addAction(QIcon(), "Reset", self.ctx_menu_reset)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon(":/delete.png"), "Delete", self.ctx_menu_delete_device)

        # self.tb.addAction(QIcon(), "Multi Command", self.ctx_menu_webui)

        self.agAllPower = QActionGroup(self)
        self.agAllPower.addAction(QIcon(":/P_ON.png"), "All ON")
        self.agAllPower.addAction(QIcon(":/P_OFF.png"), "All OFF")
        self.agAllPower.setEnabled(False)
        self.agAllPower.setExclusive(False)
        self.agAllPower.triggered.connect(self.toggle_power_all)
        self.tb_relays.addActions(self.agAllPower.actions())

        self.agRelays = QActionGroup(self)
        self.agRelays.setVisible(False)
        self.agRelays.setExclusive(False)

        for a in range(1, 9):
            act = QAction(QIcon(":/P{}_OFF.png".format(a)), "")
            act.setShortcut("F{}".format(a))
            self.agRelays.addAction(act)

        self.agRelays.triggered.connect(self.toggle_power)
        self.tb_relays.addActions(self.agRelays.actions())

        self.tb_relays.addSeparator()
        self.actColor = self.tb_relays.addAction(QIcon(":/color.png"), "Color", self.set_color)
        self.actColor.setEnabled(False)

        self.actChannels = self.tb_relays.addAction(QIcon(":/sliders.png"), "Channels")
        self.actChannels.setEnabled(False)
        self.mChannels = QMenu()
        self.actChannels.setMenu(self.mChannels)
        self.tb_relays.widgetForAction(self.actChannels).setPopupMode(QToolButton.InstantPopup)

    def create_view_buttons(self):
        self.tb_views.addWidget(QLabel("View mode: "))
        ag_views = QActionGroup(self)
        ag_views.setExclusive(True)
        for v in self.views.keys():
            a = QAction(v)
            a.triggered.connect(self.change_view)
            a.setCheckable(True)
            ag_views.addAction(a)
        self.tb_views.addActions(ag_views.actions())
        ag_views.actions()[0].setChecked(True)

        stretch = QWidget()
        stretch.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.tb_views.addWidget(stretch)
        # actEditView = self.tb_views.addAction("Edit views...")

    # def create_view_filter(self):
    #     # self.tb_filter.addWidget(QLabel("Show devices: "))
    #     # self.cbxLWT = QComboBox()
    #     # self.cbxLWT.addItems(["All", "Online"d, "Offline"])
    #     # self.cbxLWT.currentTextChanged.connect(self.build_filter_regex)
    #     # self.tb_filter.addWidget(self.cbxLWT)
    #
    #     self.tb_filter.addWidget(QLabel(" Search: "))
    #     self.leSearch = QLineEdit()
    #     self.leSearch.setClearButtonEnabled(True)
    #     self.leSearch.textChanged.connect(self.build_filter_regex)
    #     self.tb_filter.addWidget(self.leSearch)
    #
    # def build_filter_regex(self, txt):
    #     query = self.leSearch.text()
    #     # if self.cbxLWT.currentText() != "All":
    #     #     query = "{}|{}".format(self.cbxLWT.currentText(), query)
    #     self.sorted_device_model.setFilterRegExp(query)

    def change_view(self, a=None):
        view = self.views[self.sender().text()]
        self.model.setupColumns(view)
        self.device_list.setupView(view)

    def ctx_menu_copy(self):
        if self.idx:
            string = dumps(self.model.data(self.idx))
            if string.startswith('"') and string.endswith('"'):
                string = string[1:-1]
            QApplication.clipboard().setText(string)

    def ctx_menu_clear_retained(self):
        if self.device:
            relays = self.device.power()
            if relays and len(relays.keys()) > 0:
                for r in relays.keys():
                    self.mqtt.publish(self.device.cmnd_topic(r), retain=True)
            QMessageBox.information(self, "Clear retained", "Cleared retained messages.")

    def ctx_menu_clear_backlog(self):
        if self.device:
            self.mqtt.publish(self.device.cmnd_topic("backlog"), "")
            QMessageBox.information(self, "Clear Backlog", "Backlog cleared.")

    def ctx_menu_restart(self):
        if self.device:
            self.mqtt.publish(self.device.cmnd_topic("restart"), payload="1")
            for k in list(self.device.power().keys()):
                self.device.p.pop(k)

    def ctx_menu_reset(self):
        if self.device:
            reset, ok = QInputDialog.getItem(self, "Reset device and restart", "Select reset mode", resets, editable=False)
            if ok:
                self.mqtt.publish(self.device.cmnd_topic("reset"), payload=reset.split(":")[0])
                for k in list(self.device.power().keys()):
                    self.device.p.pop(k)

    def ctx_menu_refresh(self):
        if self.device:
            for k in list(self.device.power().keys()):
                self.device.p.pop(k)

            for c in initial_commands():
                cmd, payload = c
                cmd = self.device.cmnd_topic(cmd)
                self.mqtt.publish(cmd, payload, 1)

    def ctx_menu_delete_device(self):
        if self.device:
            if QMessageBox.question(self, "Confirm", "Do you want to remove the following device?\n'{}' ({})"
                    .format(self.device.p['FriendlyName1'], self.device.p['Topic'])) == QMessageBox.Yes:
                self.model.deleteDevice(self.idx)

    def ctx_menu_teleperiod(self):
        if self.device:
            teleperiod, ok = QInputDialog.getInt(self, "Set telemetry period", "Input 1 to reset to default\n[Min: 10, Max: 3600]", self.device.p['TelePeriod'], 1, 3600)
            if ok:
                if teleperiod != 1 and teleperiod < 10:
                    teleperiod = 10
            self.mqtt.publish(self.device.cmnd_topic("teleperiod"), teleperiod)

    def ctx_menu_config_backup(self):
        if self.device:
            self.backup = bytes()
            self.dl = self.nam.get(QNetworkRequest(QUrl("http://{}/dl".format(self.device.p['IPAddress']))))
            self.dl.readyRead.connect(self.get_dump)
            self.dl.finished.connect(self.save_dump)

    def ctx_menu_ota_set_url(self):
        if self.device:
            url, ok = QInputDialog.getText(self, "Set OTA URL", '100 chars max. Set to "1" to reset to default.', text=self.device.p['OtaUrl'])
            if ok:
                self.mqtt.publish(self.device.cmnd_topic("otaurl"), payload=url)

    def ctx_menu_ota_set_upgrade(self):
        if self.device:
            if QMessageBox.question(self, "OTA Upgrade", "Are you sure to OTA upgrade from\n{}".format(self.device.p['OtaUrl']), QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
                self.mqtt.publish(self.device.cmnd_topic("upgrade"), payload="1")

    def show_list_ctx_menu(self, at):
        self.select_device(self.device_list.indexAt(at))
        self.ctx_menu.popup(self.device_list.viewport().mapToGlobal(at))

    def select_device(self, idx):
        self.idx = self.sorted_device_model.mapToSource(idx)
        self.device = self.model.deviceAtRow(self.idx.row())
        self.deviceSelected.emit(self.device)

        relays = self.device.power()

        self.agAllPower.setEnabled(len(relays) >= 1)

        for i, a in enumerate(self.agRelays.actions()):
            a.setVisible(len(relays) > 1 and i < len(relays))

        color = self.device.color().get("Color", False)
        has_color = bool(color)
        self.actColor.setEnabled(has_color and not self.device.setoption(68))

        self.actChannels.setEnabled(has_color)

        if has_color:
            self.actChannels.menu().clear()

            max_val = 100
            if self.device.setoption(15) == 0:
                max_val = 1023

            for k, v in self.device.pwm().items():
                channel = SliderAction(self, k)
                channel.slider.setMaximum(max_val)
                channel.slider.setValue(int(v))
                self.mChannels.addAction(channel)
                channel.slider.valueChanged.connect(self.set_channel)

            dimmer = self.device.color().get("Dimmer")
            if dimmer:
                saDimmer = SliderAction(self, "Dimmer")
                saDimmer.slider.setValue(int(dimmer))
                self.mChannels.addAction(saDimmer)
                saDimmer.slider.valueChanged.connect(self.set_channel)

    def toggle_power(self, action):
        if self.device:
            idx = self.agRelays.actions().index(action)
            relay = sorted(list(self.device.power().keys()))[idx]
            self.mqtt.publish(self.device.cmnd_topic(relay), "toggle")

    def toggle_power_all(self, action):
        if self.device:
            idx = self.agAllPower.actions().index(action)
            for r in sorted(self.device.power().keys()):
                self.mqtt.publish(self.device.cmnd_topic(r), idx ^ 1)

    def set_color(self):
        if self.device:
            color = self.device.color().get("Color")
            if color:
                dlg = QColorDialog()
                new_color = dlg.getColor(QColor("#{}".format(color)))
                if new_color.isValid():
                    new_color = new_color.name()
                    if new_color != color:
                        self.mqtt.publish(self.device.cmnd_topic("color"), new_color)

    def set_channel(self, value=0):
        cmd = self.sender().objectName()

        if self.device:
            self.mqtt.publish(self.device.cmnd_topic(cmd), str(value))

    def configureSO(self):
        if self.device:
            dlg = SetOptionsDialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureModule(self):
        if self.device:
            dlg = ModuleDialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureGPIO(self):
        if self.device:
            dlg = GPIODialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureTemplate(self):
        if self.device:
            dlg = TemplateDialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureTimers(self):
        if self.device:
            self.mqtt.publish(self.device.cmnd_topic("timers"))
            timers = TimersDialog(self.device)
            self.mqtt.messageSignal.connect(timers.parseMessage)
            timers.sendCommand.connect(self.mqtt.publish)
            timers.exec_()

    def configureButtons(self):
        if self.device:
            backlog = []
            buttons = ButtonsDialog(self.device)
            if buttons.exec_() == QDialog.Accepted:
                for c, cw in buttons.command_widgets.items():
                    current_value = self.device.p.get(c)
                    new_value = ""

                    if isinstance(cw.input, SpinBox):
                        new_value = cw.input.value()

                    if isinstance(cw.input, QComboBox):
                        new_value = cw.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("{} {}".format(c, new_value))

                so_error = False
                for so, sow in buttons.setoption_widgets.items():
                    current_value = None
                    try:
                        current_value = self.device.setoption(so)
                    except ValueError:
                        so_error = True

                    new_value = -1

                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if not so_error and current_value and current_value != new_value:
                        backlog.append("SetOption{} {}".format(so, new_value))

                if backlog:
                    backlog.append("status 3")
                    self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def configureSwitches(self):
        if self.device:
            backlog = []
            switches = SwitchesDialog(self.device)
            if switches.exec_() == QDialog.Accepted:
                for c, cw in switches.command_widgets.items():
                    current_value = self.device.p.get(c)
                    new_value = ""

                    if isinstance(cw.input, SpinBox):
                        new_value = cw.input.value()

                    if isinstance(cw.input, QComboBox):
                        new_value = cw.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("{} {}".format(c, new_value))

                so_error = False
                for so, sow in switches.setoption_widgets.items():
                    current_value = None
                    try:
                        current_value = self.device.setoption(so)
                    except ValueError:
                        so_error = True
                    new_value = -1

                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if not so_error and current_value != new_value:
                        backlog.append("SetOption{} {}".format(so, new_value))

                for sw, sw_mode in enumerate(self.device.p['SwitchMode']):
                    new_value = switches.sm.inputs[sw].currentIndex()

                    if sw_mode != new_value:
                        backlog.append("switchmode{} {}".format(sw+1, new_value))

                if backlog:
                    backlog.append("status")
                    backlog.append("status 3")
                self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def configurePower(self):
        if self.device:
            backlog = []
            power = PowerDialog(self.device)
            if power.exec_() == QDialog.Accepted:
                for c, cw in power.command_widgets.items():
                    current_value = self.device.p.get(c)
                    new_value = ""

                    if isinstance(cw.input, SpinBox):
                        new_value = cw.input.value()

                    if isinstance(cw.input, QComboBox):
                        new_value = cw.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("{} {}".format(c, new_value))

                so_error = False
                for so, sow in power.setoption_widgets.items():
                    current_value = None
                    try:
                        current_value = self.device.setoption(so)
                    except ValueError:
                        so_error = True
                    new_value = -1


                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if not so_error and current_value != new_value:
                        backlog.append("SetOption{} {}".format(so, new_value))

                new_interlock_value = power.ci.input.currentData()
                new_interlock_grps = " ".join([grp.text().replace(" ", "") for grp in power.ci.groups]).rstrip()

                if new_interlock_value != self.device.p.get("Interlock", "OFF"):
                    backlog.append("interlock {}".format(new_interlock_value))

                if new_interlock_grps != self.device.p.get("Groups", ""):
                    backlog.append("interlock {}".format(new_interlock_grps))

                for i, pt in enumerate(power.cpt.inputs):
                    ptime = "PulseTime{}".format(i+1)
                    current_ptime = self.device.p.get(ptime)
                    if current_ptime:
                        current_value = list(current_ptime.keys())[0]
                        new_value = str(pt.value())

                        if new_value != current_value:
                            backlog.append("{} {}".format(ptime, new_value))

                if backlog:
                    backlog.append("status")
                    backlog.append("status 3")
                    self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def configureLight(self):
        if self.device:
            backlog = []
            light = LightDialog(self.device)
            if light.exec_() == QDialog.Accepted:
                so_error = False
                for so, sow in light.setoption_widgets.items():
                    current_value = None
                    try:
                        current_value = self.device.setoption(so)
                    except ValueError:
                        so_error = True

                    new_value = -1

                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if not so_error and current_value != new_value:
                        backlog.append("SetOption{} {}".format(so, new_value))
                if backlog:
                    backlog.append("status")
                    backlog.append("status 3")
                    self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def get_dump(self):
        self.backup += self.dl.readAll()

    def save_dump(self):
        fname = self.dl.header(QNetworkRequest.ContentDispositionHeader)
        if fname:
            fname = fname.split('=')[1]
            save_file = QFileDialog.getSaveFileName(self, "Save config backup", "{}/TDM/{}".format(QDir.homePath(), fname))[0]
            if save_file:
                with open(save_file, "wb") as f:
                    f.write(self.backup)

    def check_fulltopic(self, fulltopic):
        fulltopic += "/" if not fulltopic.endswith('/') else ''
        return "%prefix%" in fulltopic and "%topic%" in fulltopic

    def closeEvent(self, event):
        event.ignore()
Ejemplo n.º 53
0
class Proxy(QDialog):
    id_signal = pyqtSignal([tuple])
    city_signal = pyqtSignal([tuple])
    country_signal = pyqtSignal([tuple])

    def __init__(self, parent=None):
        super(Proxy, self).__init__(parent)
        self.settings = QSettings()

        self.layout = QVBoxLayout()

        self.buttonLayout = QHBoxLayout()
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Ok |
                                          QDialogButtonBox.Cancel)
        self.buttonBox.rejected.connect(self.reject)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonLayout.addWidget(self.buttonBox)

        self.proxy_url_label = QLabel(QCoreApplication.translate(
                                'Entry label for the proxy url',
                                'Proxy URL:', 'Proxy settings dialogue'))
        self.proxy_url_line = QLineEdit()
        url = self.settings.value('Proxy_url') or ''
        self.proxy_url_line = QLineEdit(url)
        self.proxy_url_line.setMinimumWidth(300)

        self.proxy_port_label = QLabel(QCoreApplication.translate(
                            'Entry label for the proxy port', 'Port:',
                            'Proxy settings dialogue'))
        port = self.settings.value('Proxy_port') or ''
        self.proxy_port_line = QLineEdit(port)

        self.proxy_auth_label = QLabel(QCoreApplication.translate(
                                    'Checkbox', 'Use proxy authentification',
                                    'Proxy settings dialogue'))
        self.proxy_auth_checkbox = QCheckBox()
        self.proxy_auth_bool = eval(self.settings.value(
                                    'Use_proxy_authentification') or 'False')
        self.proxy_auth_checkbox.setChecked(self.proxy_auth_bool)
        self.proxy_auth_checkbox.stateChanged.connect(self.proxy_auth)

        self.proxy_user_label = QLabel(QCoreApplication.translate(
            'Proxy username authentification',
            'User ID:', 'Proxy configuration dialogue'))
        self.proxy_user_label.setEnabled(self.proxy_auth_bool)
        self.proxy_pass_label = QLabel(QCoreApplication.translate(
            'Proxy password authentification',
            'Password:'******'Proxy configuration dialogue'))
        self.proxy_pass_label.setEnabled(self.proxy_auth_bool)

        user = self.settings.value('Proxy_user') or ''
        self.proxy_user_line = QLineEdit(user)
        self.proxy_user_line.setEnabled(self.proxy_auth_bool)
        password = self.settings.value('Proxy_password') or ''
        self.proxy_pass_line = QLineEdit(password)
        self.proxy_pass_line.setEnabled(self.proxy_auth_bool)
        self.proxy_pass_line.setEchoMode(QLineEdit.Password)

        self.status_layout = QHBoxLayout()
        self.status_label = QLabel()
        self.status_layout.addWidget(self.status_label)

        self.panel = QGridLayout()
        self.panel.addWidget(self.proxy_url_label, 0, 0)
        self.panel.addWidget(self.proxy_url_line, 0, 1)
        self.panel.addWidget(self.proxy_port_label, 0, 2)
        self.panel.addWidget(self.proxy_port_line, 0, 3)
        self.panel.addWidget(self.proxy_auth_label, 1, 0)
        self.panel.addWidget(self.proxy_auth_checkbox, 1, 1)
        self.panel.addWidget(self.proxy_user_label, 2, 0)
        self.panel.addWidget(self.proxy_user_line, 2, 1)
        self.panel.addWidget(self.proxy_pass_label, 3, 0)
        self.panel.addWidget(self.proxy_pass_line, 3, 1)

        self.layout.addLayout(self.panel)
        self.layout.addLayout(self.status_layout)
        self.layout.addLayout(self.buttonLayout)

        self.setLayout(self.layout)

    def proxy_auth(self, state):
        if state == 2:
            self.proxy_auth_bool = True
            self.proxy_user_label.setEnabled(True)
            self.proxy_pass_label.setEnabled(True)
            self.proxy_user_line.setEnabled(True)
            self.proxy_pass_line.setEnabled(True)
        else:
            self.proxy_auth_bool = False
            self.proxy_user_label.setEnabled(False)
            self.proxy_pass_label.setEnabled(False)
            self.proxy_user_line.setEnabled(False)
            self.proxy_pass_line.setEnabled(False)

    def accept(self):
        port = self.proxy_port_line.text()
        if port.isdigit() or port == '':
            self.settings.setValue('Proxy_url', self.proxy_url_line.text())
            self.settings.setValue('Proxy_port', port)
            self.settings.setValue('Use_proxy_authentification',
                                   str(self.proxy_auth_bool))
            if self.proxy_auth_bool:
                self.settings.setValue('Proxy_user',
                                       self.proxy_user_line.text())
                self.settings.setValue('Proxy_password',
                                       self.proxy_pass_line.text())
        else:
            self.status_label.setText('Port number must contain only digits')
            return
        QDialog.accept(self)
Ejemplo n.º 54
0
class Startup():

    def __init__(self):
        menuVector = iface.vectorMenu().actions()
        menuOpciones = iface.settingsMenu().actions()
        menuPlug = iface.pluginMenu().actions()
        helpBar = iface.helpToolBar()

        QSettings().setValue('listaEliminada', [])
        QSettings().setValue('listaEliminadaRef', [])
        QSettings().setValue('capaRefEdicion', 'None')

        QSettings().setValue('xManzana', "None")
        QSettings().setValue('xPredGeom', "None")
        QSettings().setValue('xPredNum', "None")
        QSettings().setValue('xConst', "None")
        QSettings().setValue('xHoriGeom', "None")
        QSettings().setValue('xHoriNum', "None")
        QSettings().setValue('xVert', "None")
        QSettings().setValue('xCvesVert', "None")

        QSettings().setValue('xAreaValor', "None")
        QSettings().setValue('xZonaUno', "None")
        QSettings().setValue('xZonaDos', "None")
        QSettings().setValue('xCP', "None")
        QSettings().setValue('xColonia', "None")
        QSettings().setValue('xCalle', "None")
        QSettings().setValue('xSector', "None")
        QSettings().setValue('xLocal', "None")
        QSettings().setValue('xSeccion', "None")
        QSettings().setValue('xMunicipio', "None")
        QSettings().setValue('xRegion', "None")
        QSettings().setValue('xEstado', "None")
        QSettings().setValue('xManzanasRef', "None")
        QSettings().setValue('xPredRef', "None")

        self.var = QSettings()

        self.tablas = {'manzana': 'e_manzana', 'predios.geom': 'e_predio', 'construcciones': 'e_construccion',  'horizontales.geom':'e_condominio_horizontal', 'verticales':'e_condominio_vertical', 'cves_verticales':'e_condominio_vert_clave'}

        self.modoDesarrollo = True

        clickFuga = QAction(QIcon("C:/AplicacionQGIS/reload.png"), QCoreApplication.translate("Groundwater Modeling", "Cerrar Sesion"), iface.mainWindow())
        clickCerrar = QAction(QIcon("C:/AplicacionQGIS/cerrar.png"), QCoreApplication.translate("Groundwater Modeling", "Cerrar Sesion"), iface.mainWindow())
        clickBorrarTodoAlv = QAction(QIcon("C:/AplicacionQGIS/borrartodoalv.png"), QCoreApplication.translate("Groundwater Modeling", "Cerrar Sesion"), iface.mainWindow())
        clickGuardar = QAction(QIcon("C:/AplicacionQGIS/guardar.png"), QCoreApplication.translate("Groundwater Modeling", "Guardar Cambios"), iface.mainWindow())

        clickFuga.triggered.connect(self.cerrarSinPreguntar)
        clickCerrar.triggered.connect(self.preguntarCerrarSesion)
        clickBorrarTodoAlv.triggered.connect(self.borrarTodoAlv)

        #helpBar.addAction(clickFuga)
        #helpBar.addAction(clickGuardar)
        #helpBar.addAction(clickCerrar)
        self.var.setValue("reglasTopologicas", [])
        self.var.setValue("posibleGuardar", "False")
        self.var.setValue("posibleGuardarRef", "False")

        QSettings().setValue('integrando', 'False') 

    def borrarTodoAlv(self):
        grupoLayers =  QgsProject.instance().layerTreeRoot().findGroup('consulta')
        layers = grupoLayers.findLayers()
        for layer in layers:
            layer.layer().startEditing()
            for f in layer.layer().getFeatures():
                layer.layer().dataProvider().deleteFeatures([f.id()])
            layer.layer().triggerRepaint()
            layer.layer().commitChanges()
        #print(self.capita.id())
        #capa = QgsProject.instance().mapLayer(self.capita.id())
        #print(capa.name())

    def cerrarSinPreguntar(self):
        os.kill(os.getpid(), 9)


    def mostrarConsola(self):

        iface.actionShowPythonDialog().trigger()
        print ('CONSOLA INICIADA')

    def checarLogin(self):
        
        archivo = open('C:/AplicacionQGIS/start.det', 'r') #Abrimos el archivo generado por C#
        
        self.var.setValue("usoLogin", archivo.readline().replace('\n', ''))

        if(self.var.value("usoLogin") == "True"):
            self.var.setValue("token", archivo.readline().replace('\n', ''))
            self.var.setValue("logeado", archivo.readline().replace('\n', ''))
            
        else:
            self.var.setValue("token", "None")
            self.var.setValue("logeado", "False")

        log = self.var.value("logeado")
        sal = self.var.value("salida")

        archivo.close()

        #DESCOMENTAR LO SIGUIENTE
        if(self.var.value("usoLogin") == "True"):
            archivo = open('C:/AplicacionQGIS/start.det', 'r+')
            archivo.truncate()
            archivo.close()
        

        if log == "True" or self.modoDesarrollo:  
            #print('etrnooooo rasaaaa')
            self.cargarCapas()
            #helpBar.addAction(clickFuga)
            #helpBar.addAction(clickGuardar)
            #helpBar.addAction(clickCerrar)
            #self.var.setValue("reglasTopologicas", [])
            #self.var.setValue("posibleGuardar", "False")
            #self.var.setValue("posibleGuardarRef", "False")
            #agregarBotones()


    def preguntarCerrarSesion(self):
        
        mensaje = "Se perderan las capas de los grupos de consulta y referencia, ademas de las reglas de topologia\nDeseas continuar?"
        respuesta = QMessageBox.question(iface.mainWindow(), "Nota de cierre de sesion", mensaje, QMessageBox.Yes, QMessageBox.No)

        if respuesta == QMessageBox.Yes:
            cerrarSesion()


    def preguntarGuardar(self):
        
        if QSettings().value("token") != "None" or self.modoDesarrollo:

            if QSettings().value("posibleGuardar") == "True" or self.modoDesarrollo:
            
                mensaje = "Deseas guardar los cambios?"
                respuesta = QMessageBox.question(iface.mainWindow(), "Guardar Cambios", mensaje, QMessageBox.Yes, QMessageBox.No)

                if respuesta == QMessageBox.Yes:
                    self.guardarCambios()
            
            else:
                createAlert('No es posible guardar los cambios debido a \n que no se ha validado la topologia', QMessageBox.Critical, 'Error al guardar cambios')

        else:
            createAlert('Inicia Sesion', QMessageBox.Critical, 'Guardar Cambios')
            ocultarIconos()

######################################################################################################################

    def cerrarSesion(self):

        if QSettings().value("token") != "None":
                #Creamos instancia del proyecto
            proyecto = QgsProject.instance()

            #Creamos la raiz
            root = QgsProject.instance().layerTreeRoot()

            #Borramos el grupo consulta y sus capas
            group = root.findGroup('consulta')
            if not group is None:
                for child in group.children():
                    dump = child.dump()
                    id = dump.split("=")[-1].strip()
                    QgsProject.instance().removeMapLayer(id)
                root.removeChildNode(group)

            #Borramos el grupo referencia y sus capas
            group = root.findGroup('referencia')
            if not group is None:
                for child in group.children():
                    dump = child.dump()
                    id = dump.split("=")[-1].strip()
                    QgsProject.instance().removeMapLayer(id)
                root.removeChildNode(group)

            #Devolver token
            QSettings().setValue("token", "None")
            QSettings().setValue("logeado", "False")
            #QSettings().setValue("salida", "True")
            ocultarIconos()

        else:
            createAlert('La sesion ya ha sido cerrada', QMessageBox.Critical, 'Cerrar Sesion')
            ocultarIconos()

######################################################################################################################
        
    def ocultarIconos(self):
        contadorHelp = 0
        for item in helpBar.actions():
            if(contadorHelp > 0):
                item.setVisible(False)
            contadorHelp = contadorHelp + 1

########################################################################################################################

    #Metodo que crea un elemento QMessageBox
    def createAlert(self, mensaje, icono, titulo):
        #Create QMessageBox
        msg = QMessageBox()
        #Add message
        msg.setText(mensaje)
        #Add icon of critical error
        msg.setIcon(icono)
        #Add tittle
        msg.setWindowTitle(titulo)
        #Show of message dialog
        msg.show()
        # Run the dialog event loop
        result = msg.exec_()

#######################################################################################################

    def cargarCapas(self):
        
        if self.modoDesarrollo:
            headers = {'Content-Type': 'application/json', 'Authorization' : self.obtenerToken()}


            urlSrid = 'http://192.168.0.40:8080/busquedasimplewkn/api/cat/municipio/'
            
            respuesta = requests.get(urlSrid, headers = headers)


            if respuesta.status_code == 200:
                salida = respuesta.json()
                srid = str(salida[0]['srid'])

                QSettings().setValue('srid', srid)
            else:
                #self.createAlert('No se ha podido cargar el SRID, se establecerá el valor por defecto: 32614', QMessageBox().Critical, 'Cargar SRID')
                print('NO SE PUDO CARGAR SRDI')
                QSettings().setValue('srid', '32614')
            #print(respuesta.json())
            
            
            root = QgsProject.instance().layerTreeRoot() #Obtenemos la raiz para agregar grupos
            root.addGroup('consulta')
            root.addGroup('referencia')

            self.consultarLlenadoDeCapa('areas_inscritas')
            self.consultarLlenadoDeCapa('cves_verticales')
            self.consultarLlenadoDeCapa('verticales')
            self.consultarLlenadoDeCapa('horizontales.num')
            self.consultarLlenadoDeCapa('horizontales.geom')
            self.consultarLlenadoDeCapa('construcciones')
            self.consultarLlenadoDeCapa('predios.num')
            self.consultarLlenadoDeCapa('predios.geom')
            self.consultarLlenadoDeCapa('manzana')
        

#########################################################################################################################
    
    def etiquetarCapa(self, nombreCapa):

        
        capa = QgsProject.instance().mapLayer(self.obtenerIdCapa(nombreCapa))
        
        if capa == None:
            capa = QgsProject.instance().mapLayersByName(nombreCapa)[0]
        
        etiquetaField = ""
        colorCapa = ""
        esExpresion = False
        if nombreCapa == "manzana":
            etiquetaField = "clave"
            colorCapa = QColor(255,0,0)
        elif nombreCapa == "predios.geom":
            etiquetaField = "clave"
            colorCapa = QColor(0,255,0)
        elif nombreCapa == "predios.num":
            etiquetaField = "numExt"
            colorCapa = QColor(0,255,0)
        elif nombreCapa == "construcciones":
            etiquetaField = " if( cve_const_esp is null, concat(nom_volumen, '\n', num_niveles), concat(nom_volumen, '\n', cve_const_esp))"
            esExpresion = True
            colorCapa = QColor(0,0,255)
        elif nombreCapa == "horizontales.geom":
            etiquetaField = "clave"
            colorCapa = QColor(198,140,33)
        elif nombreCapa == "horizontales.num":
            etiquetaField = "num_ofi"
            colorCapa = QColor(198,140,33)
        elif nombreCapa == "verticales":
            etiquetaField = "clave"
            colorCapa = QColor(255,153,0)
        elif nombreCapa == "cves_verticales":
            etiquetaField = "clave"
            colorCapa = QColor(255,153,0)
        elif nombreCapa == "areas_inscritas":
            etiquetaField = "clave"
            colorCapa = QColor(255,153,0)
        else:
            etiquetaField = "mensaje"
            colorCapa = QColor(255,153,0)
        
        settings = QgsPalLayerSettings()
        settings.fieldName = etiquetaField
        settings.enabled = True
        settings.isExpression = esExpresion
        
        settings.centroidWhole = True

        textFormat = QgsTextFormat()
        textFormat.setColor(colorCapa)
        textFormat.setSize(8)
        textFormat.setNamedStyle('Bold')

        settings.setFormat(textFormat)

        #settings.placement= QgsPalLayerSettings.OverPoint
        labeling = QgsVectorLayerSimpleLabeling(settings)

        capa.setLabeling(labeling)
        capa.setLabelsEnabled(True)
        capa.triggerRepaint()

##############################################################################################################################

    def ineditarCapa(self, nombreCapa):
        #Predios ineditables

        capa = QgsProject.instance().mapLayer(self.obtenerIdCapa(nombreCapa))

        campos = capa.fields()   
        nombres = [field.name() for field in campos]

        for i in range (0, len(nombres)):
            config = capa.editFormConfig()
            config.setReadOnly(i, True)
            capa.setEditFormConfig(config)

#####################################################################################################################################

    def cargarWebService(self):

        #token = "bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5pZCJdLCJleHAiOjE1MTk2ODcwMzgsImlhdCI6MTUxOTY4NjczOCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiJkMzI1ZWQ4Ni1iNmVlLTQ3ZDktYWI4YS1lYzE3YjgwMDNjNzEiLCJjbGllbnRfaWQiOiJ3ZWJfYXBwIn0.TMXvDbwtfMJLK_1UnnoTySt0TZ4o_4H1tp-A1CHBDlnU0aCQWnfcj3ja6jD5RAsNKmlc-5HD1gvRlFdKt5D9BBbLxykswD161N2690iurCV0NkjhmMia7yt8-X0EOI1vQoEyf7GdnzFo6Bi5fpmFE6YSS9LMZaxCy4NVhLRfqgNgq9ExKd4igedRSMmSVpQ5oV0u-5__7-sB07n3KCwGZjXeRJgVZQuKUiNI8pPljsY8X56vqYPlJlBNLzIjGjjxR_bkiYaXTdytd9LckbwSnm0914KHkIEhr66PS548V58nZfSAovLofaqV7-VCWViPo3SgvmuLDVxwdFem4b4ipQ"
        token = "bearer " + QSettings().value("token")

        cabecera = {'Content-type': 'application/json', 'Authorization' : token}

            #url del service
        url = 'http://192.168.0.50:8080/configuracion/api/adm-capas/getAllCapasConfiguration'

        respuesta = requests.get(url, headers = cabecera)

        if respuesta.status_code == 200:

            datos = respuesta.json()


            #Leemos los objetos capa del json
            for objeto in datos:

                stringDatos = "" #Los datos de la capa los guardamemos en un string

                #obtenemos el srid
                srid = objeto["catDependencia"]["municipio"]["srid"]

                #cargamos el srid en el string de los datos
                #stringDatos += objeto["layerType"]
                stringDatos += "Polygon"
                stringDatos += '?crs=epsg:'
                stringDatos += str(srid)

                #Obtenemos la dependencia de del objeto capa
                dependencia = objeto["catDependencia"]["nombre"]

                #Obtenemos los atributos de la capa y los pasamos al string
                for atributo in objeto["atributos"]:
                    stringDatos += '&field='
                    stringDatos += atributo["internalName"]
                    tipo = atributo["attrType"]
                    if tipo == 'STRING':
                        stringDatos += ':string(15)'
                    
                stringDatos += '&index=yes'    
                stringNombre = objeto["name"] #Obtenemos el nombre la capa

                #Creamos la capa con el string
                capa = QgsVectorLayer(stringDatos, stringNombre, "memory")

                capa.startEditing() #Iniciamos edicion de la capa

                root = QgsProject.instance().layerTreeRoot() #Obtenemos la raiz para agregar grupos

                QgsProject.instance().addMapLayer(capa, False) #Agregamos la capa en falso para que no se refleje hasta agregarla al grupo

                group = root.findGroup(dependencia) #Buscamos el grupo de la dependencia

                capaReinst = QgsLayerTreeLayer(capa) #La necesitamos para agregar al grupo

                renderer = capa.renderer() #Obtenemos el rendered
                colorRelleno = "#" + objeto["fillColor"] #Obtenemos el color de relleno de la capa
                colorBorde = "#" + objeto["strokeColor"] #Obtenemos el color del borde de la capa
                anchoBorde = str(objeto["strokeWidth"]) #Obtenemos el ancho del bode de la capa

                if capa.wkbType() == 3: #Si la capa es poligono aplicamos el estilo
                    symbol = QgsFillSymbol.createSimple({'color': colorRelleno, 'color_border': colorBorde, 'width_border': anchoBorde})
                    renderer.setSymbol(symbol)
                    capa.triggerRepaint()
                    capa.commitChanges() #Aceptamos los cambios

                if not group is None: #Si el grupo dependencia existe agregamos la capa
                    group.insertChildNode(0, capaReinst)
                else: #Si el grupo dependencia no existe lo creamos y luego agregamos la capa
                    root.insertGroup(0, dependencia)
                    group = root.findGroup(dependencia)
                    group.insertChildNode(0, capaReinst)

                #print QSettings().value("reglasTopologicas", [])
                reglas = QSettings().value("reglasTopologicas") #obtenemos las reglas ya almacenadas en qgis

                #print reglas
                reglasObjeto = objeto["topologias"]
                
                for regla in reglasObjeto:
                    descRegla = regla["reglaTopolia"]
                    capa1 = stringNombre
                    capa2 = regla["capa"]
                    tupla = (descRegla, capa1, capa2)
                    reglas.append(tupla)
                
                QSettings().setValue("reglasTopologicas", reglas)

##########################################################################################################################

    def obtenerToken(self):
        url= 'http://192.168.0.40:8080/auth/login'
        payload = {"username" : "user", "password" : "user"}
        payload = json.dumps(payload)
        headers = {'Content-Type': 'application/json'}

        response = requests.post(url, headers = headers, data = payload)
        if response.status_code == 200:
            #print('habemus token')
            data = response.content
        else:
            print(response)
            self.createAlert('No se ha conseguido token de startup', QMessageBox().Critical, 'Autenticacion')
            return

        return 'bearer ' + json.loads(data)['access_token']




#################################################################################################################

    def cargarJSON(self):


        stringJson = """ [
    {
    "name": "PRUEBA1",
    "layerType": "WMS",
    "strokeColor": "ABCDEF",
    "strokeWidth": 0.5,
    "fillColor": "FFBF00",
    "atributos": [
        {
        "internalName": "clv",
        "attrType": "STRING"
        }
    ],
    "catDependencia": {
        "nombre": "catastro",
        "municipio": {
        "srid": 32614
        },
        "topologias": [
        {
            "reglaTopolia": "interseccion",
            "capa": "PRUEBA1"
        },
        {
            "reglaTopolia": "inclusion",
            "capa": "PRUEBA2"
        }
        ]
    }
    },
    {
    "name": "PRUEBA2",
    "layerType": "WMS",
    "strokeColor": "A9F5A9",
    "strokeWidth": 0.3,
    "fillColor": "A9F5A9",
    "atributos": [
        {
        "internalName": "clv",
        "attrType": "STRING"
        }
    ],
    "catDependencia": {
        "nombre": "catastro",
        "municipio": {
        "srid": 32614
        },
        "topologias": [
        {
            "reglaTopolia": "interseccion",
            "capa": "PRUEBA2"
        }
        ]
    }
    }

    ] """

        datos = json.loads(stringJson)


        #Leemos los objetos capa del json
        for objeto in datos:

            stringDatos = "" #Los datos de la capa los guardamemos en un string

            #obtenemos el srid
            srid = objeto["catDependencia"]["municipio"]["srid"]

            #cargamos el srid en el string de los datos
            #stringDatos += objeto["layerType"]
            stringDatos += "Polygon"
            stringDatos += '?crs=epsg:'
            stringDatos += str(srid)

            #Obtenemos la dependencia de del objeto capa
            dependencia = objeto["catDependencia"]["nombre"]

            #Obtenemos los atributos de la capa y los pasamos al string
            for atributo in objeto["atributos"]:
                stringDatos += '&field='
                stringDatos += atributo["internalName"]
                tipo = atributo["attrType"]
                if tipo == 'STRING':
                    stringDatos += ':string(15)'
                
            stringDatos += '&index=yes'    
            stringNombre = objeto["name"] #Obtenemos el nombre la capa

            #Creamos la capa con el string
            capa = QgsVectorLayer(stringDatos, stringNombre, "memory")

            capa.startEditing() #Iniciamos edicion de la capa

            root = QgsProject.instance().layerTreeRoot() #Obtenemos la raiz para agregar grupos

            QgsProject.instance().addMapLayer(capa, False) #Agregamos la capa en falso para que no se refleje hasta agregarla al grupo

            group = root.findGroup(dependencia) #Buscamos el grupo de la dependencia

            capaReinst = QgsLayerTreeLayer(capa) #La necesitamos para agregar al grupo

            renderer = capa.renderer() #Obtenemos el rendered
            colorRelleno = "#" + objeto["fillColor"] #Obtenemos el color de relleno de la capa
            colorBorde = "#" + objeto["strokeColor"] #Obtenemos el color del borde de la capa
            anchoBorde = str(objeto["strokeWidth"]) #Obtenemos el ancho del bode de la capa

            if capa.wkbType() == 3: #Si la capa es poligono aplicamos el estilo
                symbol = QgsFillSymbol.createSimple({'color': colorRelleno, 'color_border': colorBorde, 'width_border': anchoBorde})
                renderer.setSymbol(symbol)
                capa.triggerRepaint()
                capa.commitChanges() #Aceptamos los cambios

            if not group is None: #Si el grupo dependencia existe agregamos la capa
                group.insertChildNode(0, capaReinst)
            else: #Si el grupo dependencia no existe lo creamos y luego agregamos la capa
                root.insertGroup(0, dependencia)
                group = root.findGroup(dependencia)
                group.insertChildNode(0, capaReinst)

            #print QSettings().value("reglasTopologicas", [])
            reglas = QSettings().value("reglasTopologicas") #obtenemos las reglas ya almacenadas en qgis

            #print reglas
            reglasObjeto = objeto["catDependencia"]["topologias"]
            
            for regla in reglasObjeto:
                descRegla = regla["reglaTopolia"]
                capa1 = stringNombre
                capa2 = regla["capa"]
                tupla = (descRegla, capa1, capa2)
                reglas.append(tupla)
            
            QSettings().setValue("reglasTopologicas", reglas)

##############################################################################################################

        
    def obtenerIdCapa(self, nombreCapa):

        if nombreCapa == "manzana":
            return QSettings().value('xManzana')
        elif nombreCapa == "predios.geom":
            return QSettings().value('xPredGeom')
        elif nombreCapa == "predios.num":
            return QSettings().value('xPredNum')
        elif nombreCapa == "construcciones":
            return QSettings().value('xConst')
        elif nombreCapa == "horizontales.geom":
            return QSettings().value('xHoriGeom')
        elif nombreCapa == "horizontales.num":
            return QSettings().value('xHoriNum')
        elif nombreCapa == "verticales":
            return QSettings().value('xVert')
        elif nombreCapa == "cves_verticales":
            return QSettings().value('xCvesVert')
        elif nombreCapa == "Area de Valor":
            return QSettings().value('xAreaValor')
        elif nombreCapa == "Zona Uno":
            return QSettings().value('xZonaUno')
        elif nombreCapa == "Zona Dos":
            return QSettings().value('xZonaDos')
        elif nombreCapa == "Codigo Postal":
            return QSettings().value('xCP')
        elif nombreCapa == "Colonias":
            return QSettings().value('xColonia')
        elif nombreCapa == "Calles":
            return QSettings().value('xCalle')
        elif nombreCapa == "Sectores":
            return QSettings().value('xSector')
        elif nombreCapa == "Localidades":
            return QSettings().value('xLocal')
        elif nombreCapa == "Secciones":
            return QSettings().value('xSeccion')
        elif nombreCapa == "Municipios":
            return QSettings().value('xMunicipio')
        elif nombreCapa == "Region Catastral":
            return QSettings().value('xRegion')
        elif nombreCapa == "Estado":
            return QSettings().value('xEstado')
        
        return 'None'

################################################################################################################################

    def consultarLlenadoDeCapa(self,capaParam):
        
        #capaParam = 'manzana'

        diccionarioTabla = {}
        diccionarioTabla['manzana'] = 'sig:e_manzana'
        diccionarioTabla['predios.geom'] = 'sig:e_predio'
        #diccionarioTabla['predios.num'] = 'sig:e_manzana'
        diccionarioTabla['construcciones'] = 'sig:e_construccion'
        diccionarioTabla['horizontales.geom'] = 'sig:e_condominio_horizontal'
        #diccionarioTabla['horizontales.num'] = 'sig:e_manzana'
        diccionarioTabla['verticales'] = 'sig:e_condominio_vertical'
        diccionarioTabla['cves_verticales'] = 'sig:e_condominio_vert_clave'

        

        #stringCapa = "sig:e_manzana"
        
        headers = {'Content-Type': 'application/json', 'Authorization' : self.obtenerToken()}

        
        diccionarioTipo = {}
        diccionarioTipo["STRING"] = 'string'
        diccionarioTipo["INTEGER"] = 'integer'
        diccionarioTipo["DATETIME"] = 'date'
        diccionarioTipo["NUMERIC"] = 'real'
        diccionarioTipo["SMALLINT"] = 'integer'
        diccionarioTipo["BOOLEAN"] = 'string'

        diccionarioGeom = {}
        diccionarioGeom["manzana"] = 'Polygon'
        diccionarioGeom["predios.geom"] = 'Polygon'
        diccionarioGeom["predios.num"] = 'Point'
        diccionarioGeom["construcciones"] = 'Polygon'
        diccionarioGeom["horizontales.geom"] = 'Polygon'
        diccionarioGeom["horizontales.num"] = 'Point'
        diccionarioGeom["verticales"] = 'Polygon'
        diccionarioGeom["cves_verticales"] = 'Point'
        diccionarioGeom["areas_inscritas"] = 'Polygon'

        tipoGeom = diccionarioGeom[capaParam]



        if capaParam == 'predios.num':
            stringCapa = 'Point?crs=epsg:' +str(QSettings().value('srid')) +'&field=numExt:string(50)'
        elif capaParam == 'horizontales.num':
            stringCapa = 'Point?crs=epsg:' +str(QSettings().value('srid')) +'&field=num_ofi:string(50)'
        elif capaParam == 'areas_inscritas':
            stringCapa = 'Polygon?crs=epsg:32614&field=id:string(15)&field=valor:integer(15)&field=descripcion:string(15)&field=clave:string(15)&index=yes'

        else:
            stringTabla = diccionarioTabla[capaParam]
            urlCapas = '******' + stringTabla + '/' + 'false'
            #urlCapas = '******' + stringTabla
            respuesta = requests.post(urlCapas, headers = headers)
            
            stringCapa = tipoGeom + "?crs=epsg:" + str(QSettings().value('srid'))

            if respuesta.status_code == 200:
                datos = respuesta.json()
                
                for campo in datos:

                    longitud = campo['longitud']

                    name = campo['name']
                    tipo = diccionarioTipo[campo['type']]

                    stringCapa += '&field='
                    stringCapa += name + ':'
                    stringCapa += tipo

                    if longitud != None:
                        stringCapa += "("+str(longitud)+")"
                    
                stringCapa += '&index=yes'
                
            else:
            
                print(respuesta.status_code)
            
        nuevaCapa = QgsVectorLayer(stringCapa, capaParam, 'memory')

        if capaParam == 'manzana':
            QSettings().setValue('xManzana', nuevaCapa.id())
            render = nuevaCapa.renderer()
            symbol = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#F5A9F2', 'width_border':'0.5'})
            render.setSymbol(symbol)
        
        elif capaParam == 'predios.geom':
            QSettings().setValue('xPredGeom', nuevaCapa.id())
            render = nuevaCapa.renderer()
            symbol = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#00ff00', 'width_border':'0.5'})
            render.setSymbol(symbol)

        elif capaParam == 'predios.num':
            QSettings().setValue('xPredNum', nuevaCapa.id())
            props = nuevaCapa.renderer().symbol().symbolLayer(0).properties()
            props['color'] = '#00FF00'
            nuevaCapa.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))

        elif capaParam == 'construcciones':
            QSettings().setValue('xConst', nuevaCapa.id())
            road_rules = (
                ('Const_Esp',  'NOT "cve_const_esp" is NULL '),
                ('Construccion', ' "cve_const_esp" is NULL '),
                
            )

            symbolConst = QgsSymbol.defaultSymbol(nuevaCapa.geometryType())
            rendererConst = QgsRuleBasedRenderer(symbolConst)
            fillConst = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#000000', 'width_border':'0.5'})
            fillEsp = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#00FFFF', 'width_border':'0.5'})

            # get the "root" rule
            root_rule = rendererConst.rootRule()
            for label, expression in road_rules:
                # create a clone (i.e. a copy) of the default rule
                rule = root_rule.children()[0].clone()
                # set the label, expression and color
                rule.setLabel(label)
                rule.setFilterExpression(expression)

                if label == "Const_Esp":
                    rule.setSymbol(fillEsp)
                else:
                    rule.setSymbol(fillConst)

                root_rule.appendChild(rule)
            root_rule.removeChildAt(0)
            #apply the renderer to the layer
            nuevaCapa.setRenderer(rendererConst)

        elif capaParam == 'horizontales.geom':
            QSettings().setValue('xHoriGeom', nuevaCapa.id())
            render = nuevaCapa.renderer()
            symbol = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#C68C21', 'width_border':'0.5'})
            render.setSymbol(symbol)

        elif capaParam == 'areas_inscritas':
            QSettings().setValue('xAreasInscritas', nuevaCapa.id())
            render = nuevaCapa.renderer()
            symbol = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#F646F3', 'width_border':'0.5'})
            render.setSymbol(symbol)

        elif capaParam == 'horizontales.num':
            QSettings().setValue('xHoriNum', nuevaCapa.id())
            props = nuevaCapa.renderer().symbol().symbolLayer(0).properties()
            props['color'] = '#C68C21'
            nuevaCapa.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))

        elif capaParam == 'verticales':
            QSettings().setValue('xVert', nuevaCapa.id())
            render = nuevaCapa.renderer()
            symbol = QgsFillSymbol.createSimple({'color':'255,0,0,0', 'color_border':'#ff9900', 'width_border':'0.5'})
            render.setSymbol(symbol)
            
        elif capaParam == 'cves_verticales':
            QSettings().setValue('xCvesVert', nuevaCapa.id())
            props = nuevaCapa.renderer().symbol().symbolLayer(0).properties()
            props['color'] = '#ff9900'
            nuevaCapa.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))

        QgsProject.instance().addMapLayers([nuevaCapa], False)

        root = QgsProject.instance().layerTreeRoot()
        group = root.findGroup('consulta')
        capaArbol = QgsLayerTreeLayer(nuevaCapa)

        group.insertChildNode(0, capaArbol)

        self.etiquetarCapa(capaParam)
        #self.ineditarCapa(capaParam)

##################################################################################################################

    def cargarWorkstation(self):
        GAzu = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'Polis Azules', 'memory')
        GVer = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'Polis Verdes', 'memory')
        GRoj = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'Polis Rojos', 'memory')
        GAma = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'Polis Amarillos', 'memory')

        capaManzana = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'manzana', 'memory')
        capaPredios = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'predios.geom', 'memory')
        capaConst = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'construcciones', 'memory')
        capaHorizontalesG = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'horizontales.geom', 'memory')
        capaVerticalesG = QgsVectorLayer('Polygon?crs=epsg:32614&field=clave:string(15)&index=yes', 'verticales', 'memory')
        capaVerticalesCve = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'cves_verticales', 'memory')
        capaHorizontalesnum = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'horizontales.num', 'memory')
        capaPrediosNum = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'predios.num', 'memory')

        LAzu = QgsVectorLayer('LineString?crs=epsg:32614&field=clave:string(15)&index=yes', 'Lineas Azules', 'memory')
        LVer = QgsVectorLayer('LineString?crs=epsg:32614&field=clave:string(15)&index=yes', 'Lineas Verdes', 'memory')
        LRoj = QgsVectorLayer('LineString?crs=epsg:32614&field=clave:string(15)&index=yes', 'Lineas Rojos', 'memory')
        LAma = QgsVectorLayer('LineString?crs=epsg:32614&field=clave:string(15)&index=yes', 'Lineas Amarillos', 'memory')

        PAzu = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'Puntos Azules', 'memory')
        PVer = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'Puntos Verdes', 'memory')
        PRoj = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'Puntos Rojos', 'memory')
        PAma = QgsVectorLayer('Point?crs=epsg:32614&field=clave:string(15)&index=yes', 'Puntos Amarillos', 'memory')
        
        self.colorearPuntos(PAzu, '0,181,255,255')
        self.colorearPuntos(PVer, '0,237,32,255')
        self.colorearPuntos(PRoj, '255,67,0,255')
        self.colorearPuntos(PAma, '255,255,0,255')
        
        self.colorearPuntos(capaPrediosNum, '0,255,0,255')
        self.colorearPuntos(capaHorizontalesnum, '198,140,33,255')
        self.colorearPuntos(capaVerticalesCve, '255,153,0,255')

        self.colorearLineas(LAzu, QColor.fromRgb(0,181,255))
        self.colorearLineas(LVer, QColor.fromRgb(0,237,32))
        self.colorearLineas(LRoj, QColor.fromRgb(255,67,0))
        self.colorearLineas(LAma, QColor.fromRgb(255,255,0))
        

        self.colorearPolis(GAzu, '0,181,255,255', '0,181,255,127')
        self.colorearPolis(GVer, '0,237,32,255', '0,237,32,127')
        self.colorearPolis(GRoj, '255,67,0,255', '255,67,0,127')
        self.colorearPolis(GAma, '255,255,0,255', '225,255,0,127')
        

        self.colorearPolis(capaManzana, '#F5A9F2', '255,0,0,0')
        self.colorearPolis(capaPredios, '#00ff00', '255,0,0,0')
        self.colorearPolis(capaConst, '#000000', '255,0,0,0')
        self.colorearPolis(capaHorizontalesG, '#C68C21', '255,0,0,0')
        self.colorearPolis(capaVerticalesG, '#ff9900', '255,0,0,0')

        self.agregarCapaArbol(capaVerticalesCve)
        self.agregarCapaArbol(capaVerticalesG)
        self.agregarCapaArbol(capaHorizontalesnum)
        self.agregarCapaArbol(capaHorizontalesG)
        self.agregarCapaArbol(capaConst)
        self.agregarCapaArbol(capaPrediosNum)
        self.agregarCapaArbol(capaPredios)
        self.agregarCapaArbol(capaManzana) #P
        
        
        
        
        
        

        #self.agregarCapaArbol(GAzu) #P
        #self.agregarCapaArbol(GVer)
        #self.agregarCapaArbol(GRoj)
        #self.agregarCapaArbol(GAma)

        self.agregarCapaArbol(LAzu) #P
        #self.agregarCapaArbol(LVer)
        self.agregarCapaArbol(LRoj)
        #self.agregarCapaArbol(LAma)

        #self.agregarCapaArbol(PAzu)
        #self.agregarCapaArbol(PVer)
        #self.agregarCapaArbol(PRoj)
        #self.agregarCapaArbol(PAma)


    def colorearPuntos(self, capa, color):
        props = capa.renderer().symbol().symbolLayer(0).properties()
        props['color'] = color
        capa.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))

    def colorearPolis(self, capa, contorno, relleno):
        render = capa.renderer()
        symbol = QgsFillSymbol.createSimple({'color':relleno, 'color_border':contorno, 'width_border':'0.2'})
        render.setSymbol(symbol)

    def colorearLineas(self, capa, color):
        symbols = capa.renderer().symbols(QgsRenderContext())
        symbol = symbols[0]
        symbol.setColor(color)

    def agregarCapaArbol(self, capa):
        QgsProject.instance().addMapLayer(capa, True)
Ejemplo n.º 55
0
class App(QWidget):
    def __init__(self, db, img_path):
        super().__init__()
        self.setWindowTitle('Kochbuch Tag-O-Matic by jaseg')
        self.resize(1200, 1200)
        self.db = db
        self.img_path = img_path
        self.tag_validator = TagValidator(self.db)
        self.tag_box = None
        self.refresh_timer = QTimer(self)
        self.refresh_timer.timeout.connect(self.refresh_pic)
        self.refresh_timer.setSingleShot(True)
        self.refresh_timer.setInterval(0)
        self.tag_edits = {}
        self.focused_tag_tid = None
        self.settings = QSettings()

        tags = [tag for tag, in self.db.execute('SELECT name FROM tags')]
        self.new_completer = QCompleter(tags, self)
        self.new_completer.setCompletionMode(QCompleter.InlineCompletion)
        self.new_completer.setCaseSensitivity(Qt.CaseInsensitive)

        pics = self.db.execute(
            'SELECT filename, id FROM pics WHERE valid=1 ORDER BY filename'
        ).fetchall()
        self.pid_for_fn = {k: v for k, v in pics}
        self.fn_for_pid = {v: k for k, v in pics}
        _, first_pid = pics[0]
        self.current_pic = int(
            self.settings.value("current_picture", first_pid))
        #self.shown = list(self.fn_for_pid.keys())

        zoom = float(self.settings.value('zoom', 1.0))

        def save_zoom(zoom):
            self.settings.setValue('zoom', zoom)

        self.zoom_view = ZoomView(zoom, save_zoom)

        self.pic_layout = QVBoxLayout()
        self.pic_layout.addWidget(self.zoom_view)

        self.refresh_pic()

        self.pic_list = QListWidget()
        self.pic_list.setSelectionMode(QAbstractItemView.SingleSelection)
        show_item = lambda item: self.set_pic(self.pid_for_fn[item.text()])
        self.pic_list.itemActivated.connect(show_item)
        self.pic_list.itemClicked.connect(show_item)
        for fn, _ in pics:
            self.pic_list.addItem(fn)
        # Use row-based indexing since at this point there's no filters yet.
        idx = next(i for i, (_fn, pid) in enumerate(pics)
                   if pid == self.current_pic)
        self.pic_list.setCurrentRow(idx, QItemSelectionModel.SelectCurrent)

        nav_prev = QPushButton('Previous')
        nav_prev.clicked.connect(self.prev)
        nav_next = QPushButton('Next')
        nav_next.clicked.connect(self.next)

        shortcut1 = QShortcut(QKeySequence('Ctrl+N'), self)
        shortcut1.activated.connect(self.next)
        shortcut2 = QShortcut(QKeySequence('Ctrl+P'), self)
        shortcut2.activated.connect(self.prev)
        shortcut3 = QShortcut(QKeySequence(QKeySequence.MoveToNextPage), self)
        shortcut3.activated.connect(self.next)
        shortcut4 = QShortcut(QKeySequence(QKeySequence.MoveToPreviousPage),
                              self)
        shortcut4.activated.connect(self.prev)
        shortcut4 = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self)
        shortcut4.activated.connect(self.remove_current_tag)
        #shortcut5 = QShortcut(QKeySequence(Qt.CTRL+ Qt.Key_Return), self)
        #shortcut5.activated.connect(self.next)

        nav_buttons = QHBoxLayout()
        nav_buttons.addWidget(nav_prev)
        nav_buttons.addWidget(nav_next)
        list_layout = QVBoxLayout()
        list_layout.addWidget(self.pic_list)
        list_layout.addLayout(nav_buttons)
        main_layout = QHBoxLayout()
        main_layout.addLayout(list_layout)
        main_layout.addLayout(self.pic_layout)
        self.setLayout(main_layout)
        QApplication.instance().focusChanged.connect(self.focusChanged)
        self.show()

    def navigate(self, movement):
        idx = self.pic_list.moveCursor(movement, Qt.NoModifier)
        cmd = self.pic_list.selectionCommand(idx, None)
        self.pic_list.selectionModel().setCurrentIndex(idx, cmd)
        items = self.pic_list.selectedItems()
        if len(items) != 1:
            return
        item, = items
        self.set_pic(self.pid_for_fn[item.text()])

    def prev(self):
        self.navigate(QAbstractItemView.MovePrevious)

    def next(self):
        self.navigate(QAbstractItemView.MoveDown)

    def schedule_refresh(self):
        self.refresh_timer.start()

    def set_pic(self, pid):
        self.current_pic = pid
        self.settings.setValue("current_picture", pid)
        self.schedule_refresh()

    def refresh_list(self):
        for i in range(self.pic_list.count()):
            item = self.pic_list.itemAt(i)
            pid = self.pid_for_fn[item.text()]

    def tags(self):
        return {
            name: (type, desc)
            for name, type, desc in self.db.execute(
                '''SELECT name, type, description FROM tags''').fetchall()
        }

    def focusChanged(self, _old, new):
        if new in self.tag_edits:
            tid, _lid = self.tag_edits[new]
            self.focused_tag_tid = tid

    def remove_current_tag(self):
        obj = QApplication.instance().focusWidget()
        if obj in self.tag_edits:
            _tid, lid = self.tag_edits[obj]
            self.remove_tag(lid)

    def remove_tag(self, lid):
        with self.db:
            self.db.execute('DELETE FROM pic_tags WHERE id=?', (lid, ))
        self.refresh_pic()

    def update_tag(self, edit, lid):
        with self.db:
            self.db.execute('UPDATE pic_tags SET value=? WHERE id=?',
                            (edit.text(), lid))

    def add_tag(self):
        if not self.new_edit.hasAcceptableInput():
            return
        name = self.new_edit.text()
        with self.db:
            tid, = self.db.execute('SELECT id FROM tags WHERE name=?',
                                   (name, )).fetchone()
            self.db.execute('INSERT INTO pic_tags (pic, tag) VALUES (?, ?)',
                            (self.current_pic, tid))
            self.focused_tag_tid = tid
        self.refresh_pic()

    def refresh_pic(self):
        pid = self.current_pic
        le_path = path.join(
            self.img_path,
            *self.db.execute('SELECT path FROM pics WHERE id=?',
                             (pid, )).fetchone(), self.fn_for_pid[pid])
        self.zoom_view.set_pic(le_path)

        tag_layout = QGridLayout()

        self.tag_edits = {}
        tag_edit_for_tid = {}
        for i, (
                lid, tid, tag_type, name, desc, value
        ) in enumerate(self.db.execute(
                '''SELECT pic_tags.id, tags.id, tags.type, name, description, value
                FROM pic_tags JOIN tags ON pic_tags.tag = tags.id
                WHERE pic_tags.pic = ?''', (pid, )),
                       start=1):
            is_bool = tag_type == 'bool'
            remove_btn = QPushButton('Remove')
            remove_btn.clicked.connect(lambda: self.remove_tag(lid))
            tag_layout.addWidget(remove_btn, i, 0)
            tag_layout.addWidget(QLabel(f'{desc}:' if not is_bool else desc),
                                 i, 1)

            if not is_bool:
                tag_edit = QLineEdit()
                self.tag_edits[tag_edit] = tid, lid
                tag_edit_for_tid[tid] = tag_edit
                tag_edit.setText(value)
                tag_edit.editingFinished.connect(
                    lambda: self.update_tag(tag_edit, lid))
                tag_edit.returnPressed.connect(self.next)
                tag_layout.addWidget(tag_edit, i, 2)

        add_button = QPushButton('Add')
        manage_button = QPushButton('Manage')
        add_button.setEnabled(False)
        add_button.clicked.connect(self.add_tag)
        self.new_edit = QLineEdit()
        self.new_edit.setCompleter(self.new_completer)

        def edited(text):
            ok = self.new_edit.hasAcceptableInput()
            add_button.setEnabled(ok)
            color = '#c4df9b' if ok else ('#fff79a' if text else '#ffffff')
            self.new_edit.setStyleSheet(
                f'QLineEdit {{ background-color: {color} }}')

        self.new_edit.textEdited.connect(edited)
        new_layout = QHBoxLayout()
        new_layout.addWidget(QLabel('New:'))
        new_layout.addWidget(self.new_edit)
        new_layout.addWidget(add_button)
        new_layout.addWidget(manage_button)
        self.new_edit.returnPressed.connect(self.add_tag)
        self.new_edit.setValidator(self.tag_validator)

        if self.tag_box:
            self.pic_layout.removeWidget(self.tag_box)
            self.tag_box.close()
        self.tag_box = QGroupBox('Image tags')
        self.tag_box.setAttribute(Qt.WA_DeleteOnClose, True)
        box_layout = QVBoxLayout()
        box_layout.addLayout(tag_layout)
        box_layout.addLayout(new_layout)
        self.tag_box.setLayout(box_layout)
        self.pic_layout.addWidget(self.tag_box)

        if self.focused_tag_tid in tag_edit_for_tid:
            tag_edit_for_tid[self.focused_tag_tid].setFocus()
Ejemplo n.º 56
0
    def __init__(self):
        super(QMainWindow, self).__init__()
        # Set up the user interface from Designer.
        self.setupUi(self)
        
        self.setAccessibleName("Hive Desktop")
        self.redrawLock = Lock()
        self.updateLock = Lock()
        
        self.optionsDialog = dialogs.Options(self)
        self.aboutDialog = dialogs.About(self,
            copyright='holger80',
            programName='Hive Desktop',
            version=VERSION,
            website='https://github.com/holgern/hivedesktop',
            websiteLabel='Github',
            comments='"Welcome to Hive desktop!\n This is the first release for testing qt5.\n Please vote for holger80 as witness, if you like this :).',
            licenseName='GPL-3.0',
            # licenseUrl=helpers.joinpath_to_cwd('LICENSE').as_uri(),
            authors=('holger80',),
            # dependencies=[l.strip() for l in requirements.readlines()],
        )		
        self.mdrenderer = MDRenderer(str(helpers.joinpath_to_cwd('themes')))

        # tmpfile = helpers.mktemp(prefix='hivedesktop', suffix='.html')
        
        self.post = {"body": "##test", "authorperm": "@test/test"}
        self.thread = threads.MDThread(self)
        
        
        # self.webview.url = tmpfile.as_uri()
        
        
        self.feedListWidget.currentRowChanged.connect(self.change_displayed_post, Qt.QueuedConnection)
        
        self.timer = QTimer()
        self.timer.timeout.connect(self.refresh_account_thread)
        
        self.timer2 = QTimer()
        self.timer2.timeout.connect(self.update_account_hist_thread)

        self.timer3 = QTimer()
        self.timer3.timeout.connect(self.update_account_feed_thread)
        
        self.cache_path = QStandardPaths.writableLocation(QStandardPaths.CacheLocation)
        self.db_type = "shelve"
        self.db_type = "sqlite"
        self.feed = []
        self.post = None
        # Get settings
        settings = QSettings()
        # Get checkbox state with speciying type of checkbox:
        # type=bool is a replacement of toBool() in PyQt5
        check_state = settings.value(SETTINGS_TRAY, True, type=bool)
        hist_info_check_state = settings.value(SETTINGS_HIST_INFO, True, type=bool)
        account_state = settings.value(SETTINGS_ACCOUNT, "", type=str)
        self.resize(settings.value(SETTINGS_SIZE, QSize(1053, 800)))
        self.move(settings.value(SETTINGS_POS, QPoint(50, 50)))
        
        #self.accountHistTableWidget.setColumnCount(5)
        #self.accountHistTableWidget.setHorizontalHeaderLabels(["type", "1", "2", "3", "timestamp"])
        
        self.update_account_refreshtime = 5000
        
        # Set state
        self.accountHistNotificationCheckBox.setChecked(hist_info_check_state)
        self.autoRefreshCheckBox.setChecked(check_state)
        if check_state:
            self.timer.start(self.update_account_refreshtime)
            self.timer2.start(15000)
            self.timer3.start(60000)
        self.accountLineEdit.setText(account_state)
        # connect the slot to the signal by clicking the checkbox to save the state settings
        self.autoRefreshCheckBox.clicked.connect(self.save_check_box_settings)   
        self.accountHistNotificationCheckBox.clicked.connect(self.save_check_box_settings)  
        self.accountLineEdit.editingFinished.connect(self.save_account_settings)
        self.actionAbout.triggered.connect(self.about)
        self.actionOptions.triggered.connect(self.options)
        self.threadpool = QThreadPool()
        
        self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide)
        self.maximizeAction = QAction("Ma&ximize", self,
                triggered=self.showMaximized)
        self.restoreAction = QAction("&Restore", self,
                triggered=self.showNormal)        
        
        menu = QMenu()
        menu.addAction(self.minimizeAction)
        menu.addAction(self.maximizeAction)
        menu.addAction(self.restoreAction)
        menu.addSeparator()        
        # aboutAction = menu.addAction("about")
        # aboutAction.triggered.connect(self.about)
        exitAction = menu.addAction("Exit")
        exitAction.triggered.connect(self.closeApp)
        
        self.tray = QSystemTrayIcon(QIcon(':/icons/icon.ico'))
        
        self.tray.setContextMenu(menu)
        self.tray.activated.connect(self.trayAction)
        
        self.tray.setToolTip("Hive Desktop!")
        self.tray.setObjectName("Hive Desktop")
        self.setWindowTitle("Hive Desktop")
        self.tray.show()
        
        splash_pix = QPixmap(':/icons/splash.png')
        splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint)
        splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        splash.setEnabled(False)
        
        #splash.show()
        #splash.showMessage("<h1><font color='green'>starting...</font></h1>", Qt.AlignTop | Qt.AlignCenter, Qt.black)        
        
        account = account_state
        nodelist = NodeList()
        nodelist.update_nodes()
        self.stm = Steem(node=nodelist.get_nodes(hive=True))
        set_shared_blockchain_instance(self.stm)
        if account != "":
            try:
                self.hist_account = Account(account, steem_instance=self.stm)
            except:
                self.hist_account = None
        else:
            self.hist_account = None
            
        self.refreshPushButton.clicked.connect(self.refresh_account)
        self.refreshPushButton.clicked.connect(self.update_account_hist_thread)
        self.accountLineEdit.editingFinished.connect(self.update_account_info)        
        if self.hasFocus is not None:
            self.init_new_account()
            self.init_new_blocks()
        splash.deleteLater()
Ejemplo n.º 57
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.chunk_directory = Directory(
            "CHUNK",
            QIcon(Assets.get_asset_path("document_a4_locked.png")),
            None)
        self.mod_directory = Directory(
            "MOD",
            QIcon(Assets.get_asset_path("document_a4.png")),
            None)
        self.workspace = Workspace([self.mod_directory, self.chunk_directory],
                                   parent=self)
        self.workspace.fileOpened.connect(self.handle_workspace_file_opened)
        self.workspace.fileClosed.connect(self.handle_workspace_file_closed)
        self.workspace.fileActivated.connect(self.handle_workspace_file_activated)
        self.workspace.fileLoadError.connect(self.handle_workspace_file_load_error)
        self.init_actions()
        self.init_menu_bar()
        self.init_toolbar()
        self.setStatusBar(QStatusBar())
        self.setWindowTitle("MHW-Editor-Suite")
        self.init_file_tree(
            self.chunk_directory, "Chunk directory",
            self.open_chunk_directory_action,
            filtered=True)
        self.init_file_tree(
            self.mod_directory,
            "Mod directory",
            self.open_mod_directory_action)
        self.setCentralWidget(self.init_editor_tabs())
        self.load_settings()

    def closeEvent(self, event):
        self.write_settings()

    def load_settings(self):
        self.settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                                  "fre-sch.github.com",
                                  "MHW-Editor-Suite")
        self.settings.beginGroup("MainWindow")
        size = self.settings.value("size", QSize(1000, 800))
        position = self.settings.value("position", QPoint(300, 300))
        self.settings.endGroup()
        self.settings.beginGroup("Application")
        chunk_directory = self.settings.value("chunk_directory", None)
        mod_directory = self.settings.value("mod_directory", None)
        lang = self.settings.value("lang", None)
        self.settings.endGroup()
        self.resize(size)
        self.move(position)
        if chunk_directory:
            self.chunk_directory.set_path(chunk_directory)
        if mod_directory:
            self.mod_directory.set_path(mod_directory)
        if lang:
            self.handle_set_lang_action(lang)

    def write_settings(self):
        self.settings.beginGroup("MainWindow")
        self.settings.setValue("size", self.size())
        self.settings.setValue("position", self.pos())
        self.settings.endGroup()
        self.settings.beginGroup("Application")
        self.settings.setValue("chunk_directory", self.chunk_directory.path)
        self.settings.setValue("mod_directory", self.mod_directory.path)
        self.settings.setValue("lang", FilePluginRegistry.lang)
        self.settings.endGroup()

    def get_icon(self, name):
        return self.style().standardIcon(name)

    def init_actions(self):
        self.open_chunk_directory_action = create_action(
            self.get_icon(QStyle.SP_DirOpenIcon),
            "Open chunk_directory ...",
            self.handle_open_chunk_directory,
            None)
        self.open_mod_directory_action = create_action(
            self.get_icon(QStyle.SP_DirOpenIcon),
            "Open mod directory ...",
            self.handle_open_mod_directory,
            QKeySequence.Open)
        self.save_file_action = create_action(
            self.get_icon(QStyle.SP_DriveHDIcon),
            "Save file",
            self.handle_save_file_action,
            QKeySequence.Save)
        self.save_file_action.setDisabled(True)
        self.export_csv_action = create_action(
            self.get_icon(QStyle.SP_FileIcon),
            "Export file to CSV...",
            self.handle_export_file_action)
        self.export_csv_action.setDisabled(True)
        self.about_action = create_action(
            None, "About", self.handle_about_action)
        self.lang_actions = {
            lang: create_action(
                None, name, partial(self.handle_set_lang_action, lang),
                checkable=True)
            for lang, name in LANG
        }

    def init_menu_bar(self):
        menubar = self.menuBar()
        file_menu = menubar.addMenu("File")
        file_menu.insertAction(None, self.open_chunk_directory_action)
        file_menu.insertAction(None, self.open_mod_directory_action)
        file_menu.insertAction(None, self.export_csv_action)
        file_menu.insertAction(None, self.save_file_action)
        lang_menu = menubar.addMenu("Language")
        for action in self.lang_actions.values():
            lang_menu.insertAction(None, action)
        help_menu = menubar.addMenu("Help")
        help_menu.insertAction(None, self.about_action)

    def init_toolbar(self):
        toolbar = self.addToolBar("Main")
        toolbar.setIconSize(QSize(16, 16))
        toolbar.setFloatable(False)
        toolbar.setMovable(False)
        toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        toolbar.insertAction(None, self.open_mod_directory_action)
        toolbar.insertAction(None, self.save_file_action)

    def init_file_tree(self, directory, title, action, filtered=False):
        widget = DirectoryDockWidget(directory, filtered=filtered, parent=self)
        widget.path_label.addAction(action, QLineEdit.LeadingPosition)
        widget.tree_view.activated.connect(
            partial(self.handle_directory_tree_view_activated, directory))
        dock = QDockWidget(title, self)
        dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dock.setFeatures(QDockWidget.DockWidgetMovable)
        dock.setWidget(widget)
        self.addDockWidget(Qt.LeftDockWidgetArea, dock)

    def handle_directory_tree_view_activated(self, directory, qindex: QModelIndex):
        if qindex.model().isDir(qindex):
            return
        file_path = qindex.model().filePath(qindex)
        self.workspace.open_file(directory, file_path)

    def init_editor_tabs(self):
        self.editor_tabs = QTabWidget()
        self.editor_tabs.setDocumentMode(True)
        self.editor_tabs.setTabsClosable(True)
        self.editor_tabs.tabCloseRequested.connect(
            self.handle_editor_tab_close_requested)
        return self.editor_tabs

    def handle_workspace_file_opened(self, path, rel_path):
        ws_file = self.workspace.files[path]
        editor_view = EditorView.factory(self.editor_tabs, ws_file)
        editor_view.setObjectName(path)
        self.editor_tabs.addTab(editor_view,
                                ws_file.directory.file_icon,
                                f"{ws_file.directory.name}: {rel_path}")
        self.editor_tabs.setCurrentWidget(editor_view)
        self.save_file_action.setDisabled(False)
        self.export_csv_action.setDisabled(False)

    def handle_workspace_file_activated(self, path, rel_path):
        widget = self.editor_tabs.findChild(QWidget, path)
        self.editor_tabs.setCurrentWidget(widget)

    def handle_workspace_file_closed(self, path, rel_path):
        widget = self.editor_tabs.findChild(QWidget, path)
        widget.deleteLater()
        self.save_file_action.setDisabled(not self.workspace.files)
        self.export_csv_action.setDisabled(not self.workspace.files)

    def handle_workspace_file_load_error(self, path, rel_path, error):
        QMessageBox.warning(self, f"Error loading file `{rel_path}`",
                            f"Error while loading\n{path}:\n\n{error}",
                            QMessageBox.Ok, QMessageBox.Ok)

    def handle_editor_tab_close_requested(self, tab_index):
        editor_view = self.editor_tabs.widget(tab_index)
        self.workspace.close_file(editor_view.workspace_file)

    def handle_open_chunk_directory(self):
        path = QFileDialog.getExistingDirectory(parent=self,
                                                caption="Open chunk directory")
        if path:
            self.chunk_directory.set_path(os.path.normpath(path))

    def handle_open_mod_directory(self):
        path = QFileDialog.getExistingDirectory(parent=self,
                                                caption="Open mod directory")
        if path:
            self.mod_directory.set_path(os.path.normpath(path))

    def handle_save_file_action(self):
        editor = self.editor_tabs.currentWidget()
        main_ws_file = editor.workspace_file
        for ws_file in main_ws_file.get_files_modified():
            if ws_file.directory is self.chunk_directory:
                if self.mod_directory.is_valid:
                    self.transfer_file_to_mod_workspace(
                        ws_file, ws_file is main_ws_file)
                else:
                    self.save_base_content_file(ws_file)
            else:
                with show_error_dialog(self, "Error writing file"):
                    self.save_workspace_file(ws_file)

    def handle_export_file_action(self):
        editor = self.editor_tabs.currentWidget()
        ws_file = editor.workspace_file
        file_name, file_type = QFileDialog.getSaveFileName(self, "Export file as CSV")
        if file_name:
            if not file_name.endswith(".csv"):
                file_name += ".csv"
            with show_error_dialog(self, "Error exporting file"):
                self.write_csv(ws_file, file_name)
                self.statusBar().showMessage(
                    f"Export '{file_name}' finished.", STATUSBAR_MESSAGE_TIMEOUT)

    def handle_set_lang_action(self, lang):
        FilePluginRegistry.lang = lang
        for act in self.lang_actions.values():
            act.setChecked(False)
        self.lang_actions[lang].setChecked(True)

    def write_csv(self, ws_file, file_name):
        with open(file_name, "w") as fp:
            csv_writer = csv.writer(
                fp, delimiter=",", doublequote=False, escapechar='\\',
                lineterminator="\n")
            cls = type(ws_file.data)
            fields = cls.EntryFactory.fields()
            csv_writer.writerow(fields)
            for entry in ws_file.data.entries:
                csv_writer.writerow(entry.values())

    def save_base_content_file(self, ws_file):
        result = QMessageBox.question(
            self, "Save base content file?",
            "Do you really want to update this chunk file?",
            QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel)
        if result == QMessageBox.Ok:
            with show_error_dialog(self, "Error writing file"):
                self.save_workspace_file(ws_file)

    def transfer_file_to_mod_workspace(self, ws_file, reopen=False):
        mod_abs_path, exists = self.mod_directory.get_child_path(ws_file.rel_path)
        if not exists:
            return self.transfer_file(ws_file, self.mod_directory, reopen)

        result = QMessageBox.question(
            self,
            "File exists, overwrite?",
            f"File '{ws_file.rel_path}' already found in mod directory, overwrite?",
            QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok)
        if result == QMessageBox.Ok:
            self.transfer_file(ws_file, self.mod_directory, reopen)

    def transfer_file(self, ws_file, target_directory, reopen=False):
        if target_directory is ws_file.directory:
            return
        self.workspace.close_file(ws_file)
        ws_file.set_directory(target_directory)
        self.save_workspace_file(ws_file)
        if reopen:
            self.workspace.open_file(target_directory, ws_file.abs_path)

    def save_workspace_file(self, ws_file):
        ws_file.save()
        self.statusBar().showMessage(
            f"File '{ws_file.abs_path}' saved.", STATUSBAR_MESSAGE_TIMEOUT)

    def handle_about_action(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("About MHW Editor Suite")
        layout = QVBoxLayout()
        dialog.setLayout(layout)
        about_text = QLabel(ABOUT_TEXT)
        about_text.setTextFormat(Qt.RichText)
        about_text.setTextInteractionFlags(Qt.TextBrowserInteraction)
        about_text.setOpenExternalLinks(True)
        layout.addWidget(about_text)
        dialog.exec()
Ejemplo n.º 58
0
class MyMainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self.setupUi(self)
        # 设置ini文件
        self.settings = QSettings("./QtPad.ini", QSettings.IniFormat)
        self.lineEdit_dirSaveImage.setText(self.settings.value('dirSaveImage'))
        self.lineEdit_dirSaveAnnotation.setText(
            self.settings.value('dirSaveAnnotation'))
        self.lineEdit_RGBdirSaveImage.setText(
            self.settings.value('RGBdirSaveImage'))

        self.listWidget_image_list.clear()
        self.listWidget_image_list.addItems(
            self.getFileList(self.settings.value('dirSaveImage')))
        self.cap_depth = None
        self.jump_frame = 1
        self.last_choice = None
        self.dir_depth_video = None
        self.initUI()
        self.action_last_page.triggered.connect(self.last_page)
        self.action_next_page.triggered.connect(self.next_page)
        self.pushButton_delete_files.clicked.connect(self.delete_files)
        self.pushButton_dirSaveAnnotation.clicked.connect(
            self.change_dir_annotation)
        self.pushButton_dirSaveImage.clicked.connect(self.change_dir_image)
        self.pushButton_RGBdirSaveImage.clicked.connect(
            self.change_dir_RGBimage)
        self.pushButton_Head.clicked.connect(self.change_body_part)
        self.pushButton_LShouder.clicked.connect(self.change_body_part)
        self.pushButton_LAncon.clicked.connect(self.change_body_part)
        self.pushButton_LHand.clicked.connect(self.change_body_part)
        self.pushButton_RShouder.clicked.connect(self.change_body_part)
        self.pushButton_RAncon.clicked.connect(self.change_body_part)
        self.pushButton_RHand.clicked.connect(self.change_body_part)
        self.action_openImageFolder.triggered.connect(self.openImageFolder)
        self.action_openDepthVideo.triggered.connect(self.openDepthVideo)
        self.listWidget_image_list.itemClicked.connect(self.listChange)
        self.listWidget_image_list.setCurrentRow(0)
        self.listWidget_image_list.itemClicked.emit(
            self.listWidget_image_list.item(0))
        self.update()

    def initUI(self):

        self.frame1 = MyShowWidget()
        self.frame1.setMinimumSize(QtCore.QSize(500, 500))
        self.frame1.setObjectName("frame1")
        self.horizontalLayout.addWidget(self.frame1)
        self.frame1.myclicked.connect(self.update_frame2_annotation)
        self.frame2 = MyShowWidget()
        self.frame2.setMinimumSize(QtCore.QSize(500, 500))
        self.frame2.setObjectName("frame2")
        self.horizontalLayout.addWidget(self.frame2)
        self.frame2.myclicked.connect(self.update_frame1_annotation)

    def last_page(self):
        if (self.listWidget_image_list.currentRow() > 0):
            self.listWidget_image_list.itemClicked.emit(
                self.listWidget_image_list.item(
                    self.listWidget_image_list.currentRow() - 1))
            self.listWidget_image_list.setCurrentRow(
                self.listWidget_image_list.currentRow() - 1)

    def next_page(self):
        self.listWidget_image_list.itemClicked.emit(
            self.listWidget_image_list.item(
                self.listWidget_image_list.currentRow() + 1))
        self.listWidget_image_list.setCurrentRow(
            self.listWidget_image_list.currentRow() + 1)

    def delete_files(self):
        file = self.listWidget_image_list.currentItem().text()
        self.listWidget_image_list.takeItem(
            self.listWidget_image_list.currentRow())
        self.listWidget_image_list.removeItemWidget(
            self.listWidget_image_list.currentItem())
        self.listWidget_image_list.itemClicked.emit(
            self.listWidget_image_list.currentItem())

        os.remove(self.lineEdit_RGBdirSaveImage.text() + '/' +
                  file.split('/')[-1])
        os.remove(file)

    def update_frame1_annotation(self, ann, x, y):
        self.frame1.dict_body[ann] = x, y
        self.frame1.update()

    def update_frame2_annotation(self, ann, x, y):
        self.frame2.dict_body[ann] = x, y
        self.frame2.update()

    def change_dir_annotation(self):
        dir = QFileDialog.getExistingDirectory(
            self, "选择标注保存路径", '/home/learn/PycharmProjects/test/')
        self.lineEdit_dirSaveAnnotation.setText(dir)

    def change_dir_image(self):
        dir = QFileDialog.getExistingDirectory(
            self, "选择图片保存路径", '/home/learn/PycharmProjects/test/')
        self.lineEdit_dirSaveImage.setText(dir)

    def change_dir_RGBimage(self):
        dir = QFileDialog.getExistingDirectory(
            self, "选择RGB图片保存路径", '/home/learn/PycharmProjects/test/')
        self.lineEdit_RGBdirSaveImage.setText(dir)

    def change_body_part(self):
        sender = self.sender()
        if not self.last_choice:
            self.last_choice = sender
            self.frame1.str_body = sender.objectName()[11:]
            self.frame2.str_body = sender.objectName()[11:]

        elif self.last_choice == sender:
            self.last_choice = None
            self.frame1.str_body = None
            self.frame2.str_body = None
        else:
            self.last_choice.setChecked(False)
            self.last_choice = sender
            self.frame1.str_body = sender.objectName()[11:]
            self.frame2.str_body = sender.objectName()[11:]

        self.frame1.update()
        self.frame2.update()

    def openImageFolder(self):
        dir = QFileDialog.getExistingDirectory(self, "打开", '/')

    def openDepthVideo(self):
        self.dir_depth_video, ok = QFileDialog.getOpenFileName(
            self, "打开", '/home/learn/PycharmProjects/test/',
            "All Files (*);;Video Files(*.avi)")
        if not self.cap_depth:
            self.cap_depth = cv2.VideoCapture(self.dir_depth_video)

    def closeEvent(self, event):
        self.settings.setValue("dirSaveImage",
                               self.lineEdit_dirSaveImage.text())
        self.settings.setValue("dirSaveAnnotation",
                               self.lineEdit_dirSaveAnnotation.text())
        self.settings.setValue("RGBdirSaveImage",
                               self.lineEdit_RGBdirSaveImage.text())
        del self.settings

    def getFileList(self, fileDir):
        _list = os.listdir(fileDir)  # 列出文件夹下所有的目录与文件
        dirlist = []
        for i in range(0, len(_list)):
            path = os.path.join(fileDir, _list[i])
            if os.path.isfile(path):
                dirlist.append(path)
        dirlist.sort()
        return dirlist

    def listChange(self, imageChoice):
        self.frame2.showImageFromDir(
            imageChoice.text(),
            self.lineEdit_RGBdirSaveImage.text() + '/' +
            imageChoice.text().split('/')[-1],
            self.lineEdit_dirSaveAnnotation.text() + '/' +
            imageChoice.text().split('/')[-1][:-4] + '.xml', 'depth')

        self.frame1.showImageFromDir(
            imageChoice.text(),
            self.lineEdit_RGBdirSaveImage.text() + '/' +
            imageChoice.text().split('/')[-1],
            self.lineEdit_dirSaveAnnotation.text() + '/' +
            imageChoice.text().split('/')[-1][:-4] + '.xml', 'color')
Ejemplo n.º 59
0
def chunks(l, n):
    for i in range(0, len(l), n):
        yield l[i:i + n]


QCoreApplication.setOrganizationName("QGIS")
QCoreApplication.setOrganizationDomain("qgis.org")
QCoreApplication.setApplicationName("QGIS3")

if len(sys.argv) == 1:
    print("Usage: ./scripts/mkuidefaults.py \"location_to_ini\"")
    sys.exit(1)

s = QSettings(sys.argv[1], QSettings.IniFormat)

ba = bytes(s.value("/UI/geometry"))
print

with open("src/app/ui_defaults.h", "w") as f:

    f.write("#ifndef UI_DEFAULTS_H\n#define UI_DEFAULTS_H\n" +
            "\nstatic const unsigned char defaultUIgeometry[] =\n{\n")

    for chunk in chunks(ba, 16):
        f.write('  {},\n'.format(', '.join(
            map(hex, struct.unpack('B' * len(chunk), chunk)))))

    f.write("};\n\nstatic const unsigned char defaultUIstate[] =\n{\n")

    ba = bytes(s.value("/UI/state"))
Ejemplo n.º 60
0
b = QVariant(231)

# settings.beginGroup('isp_default_parameter_values')
# settings.setValue('qp', p)
# settings.setValue('status', True)
# settings.setValue('int_value', 321)
# settings.setValue('float_value', float_value)
# settings.setValue('leve1/int_value', 567)
# settings.setValue('list_value', list_data)
# settings.endGroup()
# settings.sync()

time.sleep(1)

settings.beginGroup('isp_default_parameter_values')
set_bool_qp = settings.value('qp', QPoint(1, 1))
set_bool = settings.value('status', False)
set_value = settings.value('int_value', 1)
set_flaot_value = settings.value('float_value', type(float_value))
set_lv_value = settings.value('leve1/int_value', 1, int)
set_list = settings.value('list_value', list_data_default,
                          type(list_data_default[0]))
settings.endGroup()

print('set_flaot_value')
print(set_flaot_value)
print(type(set_flaot_value))

print(set_bool)
print(type(set_bool))