class CommandDropBox(QGroupBox): _instances = set() _todelete = set() def __init__(self, controller, cmd="", msg="", parent=None): super().__init__(parent) self.controller = controller self._instances.add(weakref.ref(self)) self.ident = len(self._instances) layout = QHBoxLayout() self.command = MonitoredLineEdit() self.command.setText(cmd) self.command.setAlignment(Qt.AlignCenter) self.command.setPlaceholderText(("!command")) layout.addWidget(self.command) self.message = MonitoredLineEdit() self.message.setText(msg) self.message.setAlignment(Qt.AlignCenter) self.message.setPlaceholderText(_('message, e.g.,') + ' (URL)') self.message.setToolTip( _('Available placeholders:') + ' ' + ', '.join(self.controller.placeholders.available())) completer = Completer( self.controller.placeholders.available(), self.message) self.message.setCompleter(completer) layout.addWidget(self.message) self.pushButton1 = QPushButton(_('Test')) self.pushButton1.clicked.connect( lambda: self.testPlaceholder(self.message.text())) layout.addWidget(self.pushButton1) self.pushButton2 = QPushButton(_('Delete')) self.pushButton2.clicked.connect(self.remove) layout.addWidget(self.pushButton2) self.setLayout(layout) for ref in self._instances: obj = ref() if obj is not None: obj.setTitle() def connect(self, handler): self.command.textModified.connect(handler) self.message.textModified.connect(handler) def setTitle(self): title = "Command {}".format(self.ident) super().setTitle(title) self.pushButton2.setDisabled(len(self._instances) == 1) def adjustIdent(self, removedIdent): if removedIdent < self.ident: self.ident -= 1 self.setTitle() def remove(self): self.parent().layout().removeWidget(self) cmd = self.command.text().strip() if cmd: self._todelete.add(cmd) self.deleteLater() self._instances.remove(weakref.ref(self)) for ref in self._instances: obj = ref() if obj is not None: obj.adjustIdent(self.ident) def testPlaceholder(self, string): """Test placeholders.""" string = self.controller.placeholders.replace(string) QMessageBox.information(self, _("Output:"), string) @classmethod def addDeletedCommand(cls, cmd): cls._todelete.add(cmd.strip()) @classmethod def getData(cls): data = dict() for cmd in cls._todelete: data[cmd] = "__DELETE__" for inst_ref in cls._instances: inst = inst_ref() if inst is not None: cmd = inst.command.text().strip() msg = inst.message.text().strip() if cmd and msg: data[cmd] = msg return data @classmethod def clean(cls): cls._instances = set() cls._todelete = set()
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 with self.tlock: self.createFormMatchDataBox() self.createTabs() self.createHorizontalGroupBox() self.createBackgroundTasksBox() self.createMenuBar() mainLayout = QVBoxLayout() mainLayout.addWidget(self.tabs, 0) mainLayout.addWidget(self.fromMatchDataBox, 1) mainLayout.addWidget(self.backgroundTasksBox, 0) mainLayout.addWidget(self.horizontalGroupBox, 0) self.setWindowTitle("Halo Wars Casting Tool v{}".format( hwctool.__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(hwctool.settings.getResFile("about.md")) html = html.replace("%VERSION%", hwctool.__version__) if (not hwctool.__new_version__): new_version = _("Halo Wars Casting Tool is up to date.") else: new_version = _("The new version {} is available!").format( hwctool.__latest_version__) html = html.replace('%NEW_VERSION%', new_version) # use self as parent here QMessageBox.about(self, _("Halo Wars 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(hwctool.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(hwctool.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(hwctool.settings.getResFile('pantone.png')), _('Styles'), self) styleAct.setToolTip('') styleAct.triggered.connect(self.openStyleDialog) settingsMenu.addAction(styleAct) self.createBrowserSrcMenu() ProfileMenu(self, self.controller) infoMenu = menubar.addMenu(_('Info && Links')) myAct = QAction(QIcon(hwctool.settings.getResFile('about.png')), _('About'), self) myAct.triggered.connect(self.showAbout) infoMenu.addAction(myAct) myAct = QAction(QIcon(hwctool.settings.getResFile('readme.ico')), _('Readme'), self) myAct.triggered.connect(self.openReadme) infoMenu.addAction(myAct) myAct = QAction(QIcon(hwctool.settings.getResFile('update.png')), _('Check for new version'), self) myAct.triggered.connect(lambda: self.controller.checkVersion(True)) infoMenu.addAction(myAct) myAct = QAction( QIcon(hwctool.settings.getResFile('changelog.png')), _('Changelog'), self) myAct.triggered.connect(self.openChangelog) infoMenu.addAction(myAct) myAct = QAction(QIcon(hwctool.settings.getResFile('folder.png')), _('Open log folder'), self) myAct.triggered.connect(lambda: os.startfile( hwctool.settings.getAbsPath(hwctool.settings.getLogDir()))) infoMenu.addAction(myAct) infoMenu.addSeparator() websiteAct = QAction( QIcon(hwctool.settings.getResFile('hwct.ico')), 'Halo Wars Casting Tool', self) websiteAct.triggered.connect(lambda: self.controller.openURL( "https://teampheenix.github.io/StarCraft-Casting-Tool/")) infoMenu.addAction(websiteAct) discordAct = QAction( QIcon(hwctool.settings.getResFile('discord.png')), 'Discord', self) discordAct.triggered.connect( lambda: self.controller.openURL("https://discord.gg/G9hFEfh")) infoMenu.addAction(discordAct) ixAct = QAction(QIcon(hwctool.settings.getResFile('icon.png')), 'team pheeniX', self) ixAct.triggered.connect( lambda: self.controller.openURL("http://team-pheenix.de")) infoMenu.addAction(ixAct) yodeslaAct = QAction( QIcon(hwctool.settings.getResFile('twitch.png')), 'Yodesla', self) yodeslaAct.triggered.connect(lambda: self.controller.openURL( "https://www.twitch.tv/yodesla")) infoMenu.addAction(yodeslaAct) except Exception as e: module_logger.exception("message") 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': _('Score'), 'file': 'score.html'}) act = QAction(QIcon(hwctool.settings.getResFile('folder.png')), _('Open Folder'), self) act.triggered.connect(lambda: os.startfile( hwctool.settings.getAbsPath(hwctool.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( hwctool.settings.casting_html_dir, icon['file']) act = QAction( QIcon(hwctool.settings.getResFile('html.png')), _('Open in Browser'), self) act.triggered.connect( lambda x, file=icon['file']: self.controller.openURL( hwctool.settings.getAbsPath(file))) mySubMenu.addAction(act) act = QAction( QIcon(hwctool.settings.getResFile('copy.png')), _('Copy URL to Clipboard'), self) act.triggered.connect( lambda x, file=icon['file']: QApplication.clipboard( ).setText(hwctool.settings.getAbsPath(file))) mySubMenu.addAction(act) if icon.get('settings', None) is not None: act = QAction( QIcon(hwctool.settings.getResFile('browser.png')), _('Settings'), self) act.triggered.connect(icon['settings']) mySubMenu.addAction(act) myMenu.addMenu(mySubMenu) else: src['file'] = os.path.join(hwctool.settings.casting_html_dir, src['file']) act = QAction(QIcon(hwctool.settings.getResFile('html.png')), _('Open in Browser'), self) act.triggered.connect( lambda x, file=src['file']: self.controller.openURL( hwctool.settings.getAbsPath(file))) myMenu.addAction(act) act = QAction(QIcon(hwctool.settings.getResFile('copy.png')), _('Copy URL to Clipboard'), self) act.triggered.connect( lambda x, file=src['file']: QApplication.clipboard( ).setText(hwctool.settings.getAbsPath(file))) myMenu.addAction(act) if src.get('settings', None) is not None: act = QAction( QIcon(hwctool.settings.getResFile('browser.png')), _('Settings'), self) act.triggered.connect(src['settings']) myMenu.addAction(act) main_menu.addMenu(myMenu) main_menu.addSeparator() apiAct = QAction(QIcon(hwctool.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(hwctool.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 openBrowserSourcesDialog(self, tab=''): """Open subwindow with browser sources 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"), hwctool.settings.getResFile('readme.ico'), hwctool.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, "Halo Wars Casting Tool " + _("Changelog"), hwctool.settings.getResFile("changelog.png"), hwctool.settings.getResFile("../CHANGELOG.md")) self.mysubwindows['changelog'].show() def changeLanguage(self, language): """Change the language.""" hwctool.settings.config.parser.set("SCT", "language", language) self.restart() def createTabs(self): """Create tabs in main window.""" try: # Initialize tab screen self.tabs = QTabWidget() self.tab2 = QWidget() # self.tabs.resize(300,200) # Add tabs self.tabs.addTab(self.tab2, _("Custom Match")) # 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, hwctool.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(40) 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(40) container.addWidget(self.cb_minSets, 0) container.addWidget(QLabel(" " + _("maps") + " "), 0) self.cb_minSets.currentIndexChanged.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.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 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 updatePlayerCompleters(self): """Refresh the completer for the player line edits.""" list = ["TBD"] + self.controller.historyManager.getPlayerList() for player_idx in range(self.max_no_sets): for team_idx in range(2): completer = QCompleter(list, self.le_player[team_idx][player_idx]) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.InlineCompletion) completer.setWrapAround(True) self.le_player[team_idx][player_idx].setCompleter(completer) def createFormMatchDataBox(self): """Create the froms for the match data.""" try: self.max_no_sets = hwctool.settings.max_no_sets self.scoreWidth = 35 self.raceWidth = 100 self.labelWidth = 25 self.mimumLineEditWidth = 130 self.fromMatchDataBox = QGroupBox(_("Match Data")) layout2 = QVBoxLayout() self.le_league = MonitoredLineEdit() self.le_league.setText("League TBD") self.le_league.setAlignment(Qt.AlignCenter) self.le_league.setPlaceholderText("League TBD") self.le_league.textModified.connect(self.league_changed) policy = QSizePolicy() policy.setHorizontalStretch(3) policy.setHorizontalPolicy(QSizePolicy.Expanding) policy.setVerticalStretch(1) policy.setVerticalPolicy(QSizePolicy.Fixed) self.le_league.setSizePolicy(policy) self.le_player = [[ MonitoredLineEdit() for x in range(self.max_no_sets) ] for y in range(2)] self.cb_race = [[QComboBox() for x in range(self.max_no_sets)] for y in range(2)] self.sl_score = [ QSlider(Qt.Horizontal) for y in range(self.max_no_sets) ] self.label_set = [ QLabel('#{}'.format(y + 1), self) for y in range(self.max_no_sets) ] self.setContainer = [ QHBoxLayout() for y in range(self.max_no_sets) ] container = QGridLayout() button = QPushButton() pixmap = QIcon(hwctool.settings.getResFile('update.png')) button.setIcon(pixmap) button.clicked.connect(lambda: self.controller.swapTeams()) button.setFixedWidth(self.labelWidth) button.setToolTip(_("Swap players.")) container.addWidget(button, 0, 0, 1, 1) label = QLabel(_("League:")) label.setAlignment(Qt.AlignCenter) label.setFixedWidth(self.raceWidth) container.addWidget(label, 0, 1, 1, 1) container.addWidget(self.le_league, 0, 2, 1, 3) label = QLabel("") label.setFixedWidth(self.raceWidth) container.addWidget(label, 0, 5, 1, 1) layout2.addLayout(container) for player_idx in range(self.max_no_sets): for team_idx in range(2): self.cb_race[team_idx][player_idx].\ currentIndexChanged.connect( lambda idx, t=team_idx, p=player_idx: self.race_changed(t, p)) self.le_player[team_idx][player_idx].textModified.connect( lambda t=team_idx, p=player_idx: self.player_changed( t, p)) self.le_player[team_idx][player_idx].setText("TBD") self.le_player[team_idx][player_idx].setAlignment( Qt.AlignCenter) self.le_player[team_idx][player_idx].setPlaceholderText( _("Player {} of team {}").format( player_idx + 1, team_idx + 1)) self.le_player[team_idx][player_idx].setMinimumWidth( self.mimumLineEditWidth) for race in hwctool.settings.races: self.cb_race[team_idx][player_idx].addItem(race) self.cb_race[team_idx][player_idx].setFixedWidth( self.raceWidth) self.sl_score[player_idx].setMinimum(-1) self.sl_score[player_idx].setMaximum(1) self.sl_score[player_idx].setValue(0) self.sl_score[player_idx].setTickPosition( QSlider.TicksBothSides) self.sl_score[player_idx].setTickInterval(1) self.sl_score[player_idx].setTracking(False) self.sl_score[player_idx].valueChanged.connect( lambda x, player_idx=player_idx: self.sl_changed( player_idx, x)) self.sl_score[player_idx].setToolTip(_('Set the score')) self.sl_score[player_idx].setFixedWidth(self.scoreWidth) self.setContainer[player_idx] = QHBoxLayout() # self.label_set[player_idx].setText("#" + str(player_idx + 1)) self.label_set[player_idx].setAlignment(Qt.AlignCenter) self.label_set[player_idx].setFixedWidth(self.labelWidth) self.setContainer[player_idx].addWidget( self.label_set[player_idx], 0) self.setContainer[player_idx].addWidget( self.cb_race[0][player_idx], 0) self.setContainer[player_idx].addWidget( self.le_player[0][player_idx], 4) self.setContainer[player_idx].addWidget( self.sl_score[player_idx], 0) self.setContainer[player_idx].addWidget( self.le_player[1][player_idx], 4) self.setContainer[player_idx].addWidget( self.cb_race[1][player_idx], 0) layout2.addLayout(self.setContainer[player_idx]) layout2.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.fromMatchDataBox.setLayout(layout2) self.updatePlayerCompleters() except Exception as e: module_logger.exception("message") 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 createBackgroundTasksBox(self): """Create group box for background tasks.""" try: self.backgroundTasksBox = QGroupBox(_("Background Tasks")) 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) layout = QGridLayout() layout.addWidget(self.cb_autoTwitch, 0, 0) layout.addWidget(self.cb_autoNightbot, 0, 1) self.backgroundTasksBox.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.matchData.applyCustomFormat(format) self.controller.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()), False, True, 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 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: for set_idx in range(self.max_no_sets): self.sl_score[set_idx].setValue(0) self.controller.matchData.setMapScore(set_idx, 0, overwrite=True) if myteam: self.sl_team.setValue(0) self.controller.matchData.setMyTeam(0) if not self.controller.resetWarning(): self.statusBar().showMessage('') except Exception as e: module_logger.exception("message") def setScore(self, idx, score, allkill=True): """Handle change of the score.""" try: if (self.sl_score[idx].value() == 0): self.statusBar().showMessage(_('Updating Score...')) with self.tlock: self.sl_score[idx].setValue(score) self.controller.matchData.setMapScore(idx, score, True) if not self.controller.resetWarning(): self.statusBar().showMessage('') return True else: return False except Exception as e: module_logger.exception("message") def league_changed(self): if not self.tlock.trigger(): return self.controller.matchData.setLeague(self.le_league.text()) def sl_changed(self, set_idx, value): """Handle a new score value.""" try: if self.tlock.trigger(): if set_idx == -1: self.controller.matchData.setMyTeam(value) else: self.controller.matchData.setMapScore(set_idx, value, True) except Exception as e: module_logger.exception("message") def player_changed(self, team_idx, player_idx): """Handle a change of player names.""" if not self.tlock.trigger(): return try: player = self.le_player[team_idx][player_idx].text().strip() race = self.cb_race[team_idx][player_idx].currentText() if (player_idx == 0 and self.controller.matchData.getSolo()): for p_idx in range(1, self.max_no_sets): self.le_player[team_idx][p_idx].setText(player) self.player_changed(team_idx, p_idx) self.controller.historyManager.insertPlayer(player, race) self.controller.matchData.setPlayer( team_idx, player_idx, self.le_player[team_idx][player_idx].text()) if race == "Random": new_race = self.controller.historyManager.getRace(player) if new_race != "Random": index = self.cb_race[team_idx][player_idx].findText( new_race, Qt.MatchFixedString) if index >= 0: self.cb_race[team_idx][player_idx].setCurrentIndex( index) elif player.lower() == "tbd": self.cb_race[team_idx][player_idx].setCurrentIndex(0) self.updatePlayerCompleters() except Exception as e: module_logger.exception("message") def race_changed(self, team_idx, player_idx): """Handle a change of player names.""" if not self.tlock.trigger(): return player = self.le_player[team_idx][player_idx].text().strip() race = self.cb_race[team_idx][player_idx].currentText() self.controller.historyManager.insertPlayer(player, race) self.controller.matchData.setRace( team_idx, player_idx, self.cb_race[team_idx][player_idx].currentText()) try: if (player_idx == 0 and self.controller.matchData.getSolo()): race = self.cb_race[team_idx][0].currentText() for player_idx in range(1, self.max_no_sets): index = self.cb_race[team_idx][player_idx].findText( race, Qt.MatchFixedString) if index >= 0: self.cb_race[team_idx][player_idx].setCurrentIndex( index) 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.darkRed) myPalette.setColor(QPalette.ButtonText, Qt.darkRed) self.pb_applycustom.setPalette(myPalette) else: self.pb_applycustom.setPalette(self.defaultButtonPalette) self.applycustom_is_highlighted = highlight return highlight 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 SubwindowConnections(QWidget): """Show connections settings sub window.""" def createWindow(self, mainWindow): """Create window.""" try: parent = None super().__init__(parent) # self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowIcon( QIcon(hwctool.settings.getResFile('twitch.png'))) self.setWindowModality(Qt.ApplicationModal) self.mainWindow = mainWindow self.passEvent = False self.controller = mainWindow.controller self.__dataChanged = False self.createButtonGroup() self.createTabs() mainLayout = QVBoxLayout() mainLayout.addWidget(self.tabs) mainLayout.addLayout(self.buttonGroup) self.setLayout(mainLayout) self.resize(QSize(mainWindow.size().width() * 0.8, self.sizeHint().height())) relativeChange = QPoint(mainWindow.size().width() / 2, mainWindow.size().height() / 3) -\ QPoint(self.size().width() / 2, self.size().height() / 3) self.move(mainWindow.pos() + relativeChange) self.setWindowTitle(_("Twitch")) except Exception as e: module_logger.exception("message") def createTabs(self): """Create tabs.""" self.tabs = QTabWidget() self.createFormGroupTwitch() self.createFormGroupNightbot() # Add tabs self.tabs.addTab(self.formGroupTwitch, QIcon( hwctool.settings.getResFile('twitch.png')), _("Twitch")) # self.tabs.addTab(self.formGroupNightbot, QIcon( # hwctool.settings.getResFile('nightbot.ico')), _("Nightbot")) def createFormGroupTwitch(self): """Create forms for twitch.""" self.formGroupTwitch = QWidget() layout = QFormLayout() self.twitchChannel = MonitoredLineEdit() self.twitchChannel.textModified.connect(self.changed) self.twitchChannel.setText( hwctool.settings.config.parser.get("Twitch", "channel")) self.twitchChannel.setAlignment(Qt.AlignCenter) self.twitchChannel.setPlaceholderText( _("Name of the Twitch channel that should be updated")) self.twitchChannel.setToolTip( _('The connected twitch user needs to have editor' ' rights for this channel.')) layout.addRow(QLabel( "Twitch-Channel:"), self.twitchChannel) container = QHBoxLayout() self.twitchToken = MonitoredLineEdit() self.twitchToken.textModified.connect(self.changed) self.twitchToken.setText( hwctool.settings.config.parser.get("Twitch", "oauth")) self.twitchToken.setAlignment(Qt.AlignCenter) self.twitchToken.setPlaceholderText( _("Press 'Get' to generate a token")) self.twitchToken.setEchoMode(QLineEdit.Password) self.twitchToken.setToolTip(_("Press 'Get' to generate a new token.")) container.addWidget(self.twitchToken) self.pb_getTwitch = QPushButton(_('Get')) self.pb_getTwitch.setFixedWidth(100) self.pb_getTwitch.clicked.connect( lambda: self.controller.authThread.requestToken('twitch')) container.addWidget(self.pb_getTwitch) layout.addRow(QLabel(_("Access-Token:")), container) container = QHBoxLayout() self.twitchTemplate = MonitoredLineEdit() self.twitchTemplate.textModified.connect(self.changed) self.twitchTemplate.setText( hwctool.settings.config.parser.get("Twitch", "title_template")) self.twitchTemplate.setAlignment(Qt.AlignCenter) self.twitchTemplate.setPlaceholderText("(League) – (Team1) vs (Team2)") self.twitchTemplate.setToolTip( _('Available placeholders:') + " " + ', '.join(self.controller.placeholders.available())) completer = Completer( self.controller.placeholders.available(), self.twitchTemplate) self.twitchTemplate.setCompleter(completer) container.addWidget(self.twitchTemplate) button = QPushButton(_('Test')) button.setFixedWidth(100) button.clicked.connect( lambda: self.testPlaceholder(self.twitchTemplate.text())) container.addWidget(button) label = QLabel(_("Title Template:")) label.setFixedWidth(100) layout.addRow(label, container) container = QVBoxLayout() self.cb_set_game = QCheckBox(_("Set twitch game to the selected game")) self.cb_set_game.setChecked( hwctool.settings.config.parser.getboolean("Twitch", "set_game")) self.cb_set_game.stateChanged.connect(self.changed) container.addWidget(self.cb_set_game) self.cb_set_community = QCheckBox( _("Add to Community 'StarCraft Casting Tool'")) self.cb_set_community.setChecked( hwctool.settings.config.parser.getboolean( "Twitch", "set_community")) self.cb_set_community.stateChanged.connect(self.changed) # container.addWidget(self.cb_set_community) label = QLabel(_("Options:") + " ") label.setMinimumWidth(120) layout.addRow(label, container) layout.addItem(QSpacerItem( 0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.formGroupTwitch.setLayout(layout) def createFormGroupNightbot(self): """Create forms for nightbot.""" self.formGroupNightbot = QWidget() mainLayout = QVBoxLayout() tokenBox = QGroupBox("Access-Token") container = QHBoxLayout() self.nightbotToken = MonitoredLineEdit() self.nightbotToken.textModified.connect(self.changed) self.nightbotToken.setText( hwctool.settings.config.parser.get("Nightbot", "token")) self.nightbotToken.setAlignment(Qt.AlignCenter) self.nightbotToken.setEchoMode(QLineEdit.Password) self.nightbotToken.setPlaceholderText( _("Press 'Get' to generate a token")) self.nightbotToken.setToolTip( _("Press 'Get' to generate a token.")) container.addWidget(self.nightbotToken) self.pb_getNightbot = QPushButton(_('Get')) self.pb_getNightbot.clicked.connect( lambda: self.controller.authThread.requestToken('nightbot')) self.pb_getNightbot.setFixedWidth(100) # self.pb_getNightbot.setEnabled(False) container.addWidget(self.pb_getNightbot) tokenBox.setLayout(container) mainLayout.addWidget(tokenBox, 0) # scroll area widget contents - layout self.scrollLayout = QVBoxLayout() self.scrollLayout.setDirection(QBoxLayout.BottomToTop) self.scrollLayout.addStretch(0) buttonLayout = QHBoxLayout() buttonLayout.addStretch(0) self.scrollLayout.addLayout(buttonLayout) # scroll area widget contents self.scrollWidget = QWidget() self.scrollWidget.setLayout(self.scrollLayout) # scroll area self.scrollArea = QScrollArea() self.scrollArea.setWidgetResizable(True) self.scrollArea.setWidget(self.scrollWidget) self.scrollArea.setFixedHeight(180) mainLayout.addWidget(self.scrollArea, 1) layout = QHBoxLayout() layout.addWidget(QLabel("")) addButton = QPushButton(_('Add Command')) addButton.clicked.connect(lambda: self.addCommand()) layout.addWidget(addButton) mainLayout.addLayout(layout, 0) data = hwctool.settings.nightbot_commands if len(data) == 0: self.addCommand() else: for cmd, msg in data.items(): self.addCommand(cmd, msg) mainLayout.addItem(QSpacerItem( 0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.formGroupNightbot.setLayout(mainLayout) def addCommand(self, cmd="", msg=""): if msg != "__DELETE__": dropbox = CommandDropBox(self.controller, cmd=cmd, msg=msg) dropbox.connect(self.changed) self.scrollLayout.insertWidget(1, dropbox) else: CommandDropBox.addDeletedCommand(cmd) def createButtonGroup(self): """Create buttons.""" try: layout = QHBoxLayout() layout.addWidget(QLabel("")) buttonCancel = QPushButton(_('Cancel')) buttonCancel.clicked.connect(self.closeWindow) layout.addWidget(buttonCancel) buttonSave = QPushButton(_('&Save && Close')) buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S")) self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self) self.shortcut.setAutoRepeat(False) self.shortcut.activated.connect(self.saveCloseWindow) buttonSave.clicked.connect(self.saveCloseWindow) layout.addWidget(buttonSave) self.buttonGroup = layout except Exception as e: module_logger.exception("message") def changed(self, *values): """Handle changed data.""" self.__dataChanged = True def saveData(self): """Save the data to config.""" if(self.__dataChanged): hwctool.settings.config.parser.set( "Twitch", "channel", self.twitchChannel.text().strip()) hwctool.settings.config.parser.set( "Twitch", "oauth", self.twitchToken.text().strip()) hwctool.settings.config.parser.set( "Twitch", "title_template", self.twitchTemplate.text().strip()) hwctool.settings.config.parser.set( "Twitch", "set_game", str(self.cb_set_game.isChecked())) # hwctool.settings.config.parser.set( # "Twitch", # "set_community", # str(self.cb_set_community.isChecked())) hwctool.settings.config.parser.set( "Nightbot", "token", self.nightbotToken.text().strip()) self.__dataChanged = False self.controller.refreshButtonStatus() hwctool.settings.nightbot_commands = CommandDropBox.getData() def saveCloseWindow(self): """Save and close window.""" self.saveData() self.closeWindow() def closeWindow(self): """Close window without save.""" self.passEvent = True self.close() def closeEvent(self, event): """Handle close event.""" try: if(not self.__dataChanged): CommandDropBox.clean() event.accept() return if(not self.passEvent): if(self.isMinimized()): self.showNormal() buttonReply = QMessageBox.question( self, _('Save data?'), _("Do you want to save the data?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.Yes: self.saveData() CommandDropBox.clean() event.accept() except Exception as e: module_logger.exception("message") def testPlaceholder(self, string): """Test placeholders.""" string = self.controller.placeholders.replace(string) QMessageBox.information(self, _("Output:"), string)