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'))
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)
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 ""
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
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))
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)
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)
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))
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)
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)
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
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)))
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')
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)
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)))
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())
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))
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
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
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)
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_())
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)
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)))
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))
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)
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() + '/'
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)
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()
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()
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))
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()
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()
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()
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]
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()
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'
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)
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()
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
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()
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()
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)
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
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)
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)
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()
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)
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)
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)
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()
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)
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)
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()
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()
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()
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')
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"))
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))