Пример #1
0
    def createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/intro",
            "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/intro.html"))
        layout.addWidget(styleqb, 2)
        layout.addWidget(button, 1)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.hotkeyBox = QGroupBox(_("Hotkeys"))
        layout = QVBoxLayout()
        self.controller.websocketThread.unregister_hotkeys(force=True)

        self.cb_single_hotkey = QCheckBox(
            _("Use a single hotkey for both players"))
        self.cb_single_hotkey.stateChanged.connect(self.singleHotkeyChanged)
        layout.addWidget(self.cb_single_hotkey)

        self.hotkeys = dict()
        layout.addLayout(self.addHotkey("hotkey_player1", _("Player 1")))
        layout.addLayout(self.addHotkey("hotkey_player2", _("Player 2")))
        layout.addLayout(self.addHotkey("hotkey_debug", _("Debug")))

        self.cb_single_hotkey.setChecked(
            self.hotkeys['hotkey_player1'].getKey() ==
            self.hotkeys['hotkey_player2'].getKey())
        self.connectHotkeys()
        label = QLabel(_("Player 1 is always the player your observer"
                         " camera is centered on at start of a game."))
        layout.addWidget(label)
        self.hotkeyBox.setLayout(layout)
        mainLayout.addWidget(self.hotkeyBox)

        self.introBox = QGroupBox(_("Animation"))
        layout = QFormLayout()
        self.cb_animation = QComboBox()
        animation = scctool.settings.config.parser.get("Intros", "animation")
        currentIdx = 0
        idx = 0
        options = dict()
        options['Fly-In'] = _("Fly-In")
        options['Slide'] = _("Slide")
        options['Fanfare'] = _("Fanfare")
        for key, item in options.items():
            self.cb_animation.addItem(item, key)
            if(key == animation):
                currentIdx = idx
            idx += 1
        self.cb_animation.setCurrentIndex(currentIdx)
        self.cb_animation.currentIndexChanged.connect(self.changed)
        label = QLabel(_("Animation:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_animation)
        self.sb_displaytime = QDoubleSpinBox()
        self.sb_displaytime.setRange(0, 10)
        self.sb_displaytime.setDecimals(1)
        self.sb_displaytime.setValue(
            scctool.settings.config.parser.getfloat("Intros", "display_time"))
        self.sb_displaytime.setSuffix(" " + _("Seconds"))
        self.sb_displaytime.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Display Duration:") + " "), self.sb_displaytime)
        self.sl_sound = QSlider(Qt.Horizontal)
        self.sl_sound.setMinimum(0)
        self.sl_sound.setMaximum(20)
        self.sl_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "sound_volume"))
        self.sl_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_sound.setTickInterval(1)
        self.sl_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_sound)
        self.introBox.setLayout(layout)
        mainLayout.addWidget(self.introBox)

        self.ttsBox = QGroupBox(_("Text-to-Speech"))
        layout = QFormLayout()

        self.cb_tts_active = QCheckBox()
        self.cb_tts_active.setChecked(
            scctool.settings.config.parser.getboolean("Intros", "tts_active"))
        self.cb_tts_active.stateChanged.connect(self.changed)
        label = QLabel(_("Activate Text-to-Speech:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_tts_active)

        self.icons = {}
        self.icons['MALE'] = QIcon(scctool.settings.getResFile('male.png'))
        self.icons['FEMALE'] = QIcon(scctool.settings.getResFile('female.png'))
        self.cb_tts_voice = QComboBox()

        currentIdx = 0
        idx = 0
        tts_voices = self.controller.tts.getVoices()
        tts_voice = scctool.settings.config.parser.get("Intros", "tts_voice")
        for voice in tts_voices:
            self.cb_tts_voice.addItem(
                self.icons[voice['ssmlGender']],
                '   ' + voice['name'],
                voice['name'])
            if(voice['name'] == tts_voice):
                currentIdx = idx
            idx += 1
        self.cb_tts_voice.setCurrentIndex(currentIdx)
        self.cb_tts_voice.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Voice:") + " "), self.cb_tts_voice)
        self.ttsBox.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.ttsBox.setLayout(layout)
        mainLayout.addWidget(self.ttsBox)

        self.sb_tts_pitch = QDoubleSpinBox()
        self.sb_tts_pitch.setRange(-20, 20)
        self.sb_tts_pitch.setDecimals(2)
        self.sb_tts_pitch.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_pitch"))
        self.sb_tts_pitch.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Pitch:") + " "), self.sb_tts_pitch)

        self.sb_tts_rate = QDoubleSpinBox()
        self.sb_tts_rate.setRange(0.25, 4.00)
        self.sb_tts_rate.setSingleStep(0.1)
        self.sb_tts_rate.setDecimals(2)
        self.sb_tts_rate.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_rate"))
        self.sb_tts_rate.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Rate:") + " "), self.sb_tts_rate)

        self.cb_tts_scope = QComboBox()
        scope = scctool.settings.config.parser.get("Intros", "tts_scope")
        currentIdx = 0
        idx = 0
        options = self.controller.tts.getOptions()
        for key, item in options.items():
            self.cb_tts_scope.addItem(item['desc'], key)
            if(key == scope):
                currentIdx = idx
            idx += 1
        self.cb_tts_scope.setCurrentIndex(currentIdx)
        self.cb_tts_scope.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Line:") + " "), self.cb_tts_scope)

        self.sl_tts_sound = QSlider(Qt.Horizontal)
        self.sl_tts_sound.setMinimum(0)
        self.sl_tts_sound.setMaximum(20)
        self.sl_tts_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "tts_volume"))
        self.sl_tts_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_tts_sound.setTickInterval(1)
        self.sl_tts_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_tts_sound)

        text = _(
            "Text-to-Speech provided by Google-Cloud is paid for "
            "by StarCraft Casting Tool Patrons. To keep this service up "
            "consider becoming a <a href='{patreon}'>Patron</a> yourself. "
            "You can test all voices at {tts}.")

        patreon = 'https://www.patreon.com/StarCraftCastingTool'

        url = 'https://cloud.google.com/text-to-speech/'
        tts = "<a href='{}'>cloud.google.com/text-to-speech</a>"
        tts = tts.format(url)

        label = QLabel(text.format(patreon=patreon, tts=tts))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addRow(label)

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupIntro.setLayout(mainLayout)
class SubwindowStyles(QWidget):
    """Show styles settings sub window."""
    current_tab = -1

    def createWindow(self, mainWindow, tab=''):
        """Create styles settings sub window."""
        try:
            parent = None
            super().__init__(parent)

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('pantone.png')))
            self.setWindowModality(Qt.ApplicationModal)
            self.mainWindow = mainWindow
            self.passEvent = False
            self.controller = mainWindow.controller
            self.__dataChanged = False

            self.createButtonGroup()
            self.createColorBox()
            self.createStyleBox()
            self.createFontBox()

            self.tabs = QTabWidget()
            self.tabs.addTab(self.styleBox, _("Styles"))
            self.tabs.addTab(self.colorBox, _("Colors"))
            self.tabs.addTab(self.fontBox, _("Font"))

            table = dict()
            table['styles'] = 0
            table['colors'] = 1
            table['font'] = 2
            self.tabs.setCurrentIndex(
                table.get(tab, SubwindowStyles.current_tab))
            self.tabs.currentChanged.connect(self.tabChanged)

            mainLayout = QVBoxLayout()
            mainLayout.addWidget(self.tabs)
            mainLayout.addItem(QSpacerItem(
                0, 0, QSizePolicy.Minimum,
                QSizePolicy.Expanding))
            mainLayout.addLayout(self.buttonGroup)
            self.setLayout(mainLayout)

            self.resize(QSize(mainWindow.size().width() * .80,
                              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(_("Style Settings"))

        except Exception:
            module_logger.exception("message")

    @classmethod
    def tabChanged(cls, idx):
        """Save the current tab."""
        SubwindowStyles.current_tab = idx

    def changed(self):
        """Handle data change."""
        self.__dataChanged = True

    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:
            module_logger.exception("message")

    def createStyleBox(self):
        """Create style box."""
        self.styleBox = QWidget()
        layout = QFormLayout()

        try:
            container = QHBoxLayout()
            self.qb_boxStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/mapicons_box",
                "mapicons_box")
            self.qb_boxStyle.connect2WS(self.controller, 'mapicons_box')
            label = QLabel(_("Box Map Icons:"))
            label.setMinimumWidth(110)
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/mapicons_box_1.html"))
            container.addWidget(self.qb_boxStyle)
            container.addWidget(button)
            layout.addRow(label, container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_landscapeStyle = StyleComboBox(
                scctool.settings.casting_html_dir
                + "/src/css/mapicons_landscape",
                "mapicons_landscape")
            self.qb_landscapeStyle.connect2WS(
                self.controller, 'mapicons_landscape')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir
                + "/mapicons_landscape_1.html"))
            container.addWidget(self.qb_landscapeStyle)
            container.addWidget(button)
            layout.addRow(QLabel(
                _("Landscape Map Icons:")), container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_scoreStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/score",
                "score")
            self.qb_scoreStyle.connect2WS(self.controller, 'score')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/score.html"))
            container.addWidget(self.qb_scoreStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Score:")), container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_introStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/intro",
                "intro")
            self.qb_introStyle.connect2WS(self.controller, 'intro')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/intro.html"))
            container.addWidget(self.qb_introStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Intros:")), container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_mapstatsStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/mapstats",
                "mapstats")
            self.qb_mapstatsStyle.connect2WS(self.controller, 'mapstats')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/mapstats.html"))
            container.addWidget(self.qb_mapstatsStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Map Stats:")), container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_aligulacStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/aligulac",
                "aligulac")
            self.qb_aligulacStyle.connect2WS(self.controller, 'aligulac')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/aligulac.html"))
            container.addWidget(self.qb_aligulacStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Aligulac:")), container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_countdownStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/countdown",
                "countdown")
            self.qb_countdownStyle.connect2WS(self.controller, 'countdown')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/countdown.html"))
            container.addWidget(self.qb_countdownStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Countdown:")), container)
        except Exception:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_countdownStyle = StyleComboBox(
                scctool.settings.casting_html_dir + "/src/css/vetoes",
                "vetoes")
            self.qb_countdownStyle.connect2WS(self.controller, 'vetoes')
            button = QPushButton(_("Show in Browser"))
            button.clicked.connect(lambda: self.openHTML(
                scctool.settings.casting_html_dir + "/vetoes.html"))
            container.addWidget(self.qb_countdownStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Vetoes") + ':'), container)
        except Exception:
            module_logger.exception("message")

        layout.addRow(QLabel(''))
        stylesDisc = _(
            'StarCraft Casting Tools allows you to make your own skins/styles'
            ' via CSS by placing an alternative CSS-files into '
            'casting_html/src/css/{browser-source}. If you do so, please'
            ' share these skins with this project. In case you need help'
            ' or just want to share your ideas for new skins'
            ' join our Discord Server.')
        label = QLabel(stylesDisc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)
        layout.addRow(label)

        self.styleBox.setLayout(layout)

    def openHTML(self, file):
        """Open file in browser."""
        self.controller.openURL(scctool.settings.getAbsPath(file))

    def createColorBox(self):
        """Create box for color selection."""
        self.colorBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Map and Score Icons"))
        layout = QVBoxLayout()
        self.default_color = ColorLayout(
            self, _("Default Border:"),
            scctool.settings.config.parser.get(
                "MapIcons", "default_border_color"),
            "#f29b00")
        layout.addLayout(self.default_color)
        self.winner_color = ColorLayout(
            self, _("Winner Highlight:"),
            scctool.settings.config.parser.get(
                "MapIcons", "winner_highlight_color"),
            "#f29b00")
        layout.addLayout(self.winner_color)
        self.win_color = ColorLayout(
            self, _("Win:"),
            scctool.settings.config.parser.get("MapIcons", "win_color"),
            "#008000")
        layout.addLayout(self.win_color)
        self.lose_color = ColorLayout(
            self, _("Lose:"),
            scctool.settings.config.parser.get("MapIcons", "lose_color"),
            "#f22200")
        layout.addLayout(self.lose_color)
        self.undecided_color = ColorLayout(
            self, _("Undecided:"),
            scctool.settings.config.parser.get("MapIcons", "undecided_color"),
            "#aaaaaa")
        layout.addLayout(self.undecided_color)
        self.notplayed_color = ColorLayout(
            self, _("Not played:"),
            scctool.settings.config.parser.get("MapIcons", "notplayed_color"),
            "#aaaaaa")
        layout.addLayout(self.notplayed_color)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        box = QGroupBox(_("Map Stats"))
        layout = QVBoxLayout()
        self.mapstats_color1 = ColorLayout(
            self, _("Color 1:"),
            scctool.settings.config.parser.get("Mapstats", "color1"),
            "#6495ed")
        layout.addLayout(self.mapstats_color1)
        self.mapstats_color2 = ColorLayout(
            self, _("Color 2:"),
            scctool.settings.config.parser.get("Mapstats", "color2"),
            "#000000")
        layout.addLayout(self.mapstats_color2)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.colorBox.setLayout(mainLayout)

    def createFontBox(self):
        """Create box for font selection."""
        self.fontBox = QWidget()
        layout = QGridLayout()

        label = QLabel(
            _("Warning: Using a custom font instead of the regular font"
              " defined in the Icon Styles can lead to unitentional"
              " appereance.")
            + _("The proper way is to create a custom skin."))
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)
        layout.addWidget(label, 1, 0, 1, 2)
        label = QLabel(_("Activate Custom Font") + ":")
        label.setMinimumWidth(110)
        self.cb_usefont = QCheckBox(" ")
        self.cb_usefont.setChecked(
            scctool.settings.config.parser.getboolean(
                "Style",
                "use_custom_font"))
        self.cb_usefont.stateChanged.connect(self.changed)
        layout.addWidget(label, 0, 0, alignment=Qt.AlignVCenter)
        layout.addWidget(self.cb_usefont, 0, 1,
                         alignment=Qt.AlignVCenter)
        label = QLabel(_("Custom Font") + ":")
        label.setMinimumWidth(110)
        layout.addWidget(label, 2, 0)
        self.cb_font = QComboBox()
        my_font = scctool.settings.config.parser.get(
            "Style", "custom_font")
        fonts = QFontDatabase().families()
        for idx, font in enumerate(fonts):
            self.cb_font.addItem(str(font))
            if str(font).lower().strip() == my_font.lower():
                self.cb_font.setCurrentIndex(idx)
        self.cb_font.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.cb_font.currentIndexChanged.connect(self.changed)
        self.cb_font.currentIndexChanged.connect(self.updateFontPreview)
        layout.addWidget(self.cb_font, 2, 1)
        layout.setColumnStretch(1, 1)
        self.previewer = TextPreviewer()
        layout.addWidget(self.previewer, 3, 0, 1, 2)
        layout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding),
            4, 0)
        self.fontBox.setLayout(layout)
        self.updateFontPreview()

    def updateFontPreview(self):
        """Update the font preview."""
        font = self.cb_font.currentText().strip()
        self.previewer.setFont(font)

    def saveData(self):
        """Save data."""
        if(self.__dataChanged):
            scctool.settings.config.parser.set(
                "MapIcons", "default_border_color",
                self.default_color.getColor())
            scctool.settings.config.parser.set(
                "MapIcons", "undecided_color",
                self.undecided_color.getColor())
            scctool.settings.config.parser.set(
                "MapIcons", "winner_highlight_color",
                self.winner_color.getColor())
            scctool.settings.config.parser.set(
                "MapIcons", "win_color",
                self.win_color.getColor())
            scctool.settings.config.parser.set(
                "MapIcons", "lose_color",
                self.lose_color.getColor())
            scctool.settings.config.parser.set(
                "MapIcons", "notplayed_color",
                self.notplayed_color.getColor())

            scctool.settings.config.parser.set(
                "Mapstats", "color1",
                self.mapstats_color1.getColor())
            scctool.settings.config.parser.set(
                "Mapstats", "color2",
                self.mapstats_color2.getColor())

            scctool.settings.config.parser.set(
                "Style", "use_custom_font",
                str(self.cb_usefont.isChecked()))

            scctool.settings.config.parser.set(
                "Style", "custom_font",
                self.cb_font.currentText().strip())

            colors = {'color1': self.mapstats_color1.getColor(),
                      'color2': self.mapstats_color2.getColor()}
            self.controller.websocketThread.changeColors('mapstats', colors)
            self.controller.websocketThread.changeFont()
            self.controller.matchMetaDataChanged()
            self.__dataChanged = False

    def saveCloseWindow(self):
        """Save and close window."""
        self.saveData()
        self.passEvent = True
        self.close()

    def closeWindow(self):
        """Close window."""
        self.passEvent = True
        self.close()

    def closeEvent(self, event):
        """Handle close event."""
        try:
            if(not self.__dataChanged):
                event.accept()
                return
            if(not self.passEvent):
                if(self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Save data?"),
                    QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            event.accept()
        except Exception:
            module_logger.exception("message")
Пример #3
0
class SubwindowBrowserSources(QWidget):
    """Show connections settings sub window."""
    current_tab = -1

    def createWindow(self, mainWindow, tab=''):
        """Create window."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('browser.png')))
            self.setWindowModality(Qt.ApplicationModal)
            self.mainWindow = mainWindow
            self.passEvent = False
            self.controller = mainWindow.controller
            self.__dataChanged = False

            self.createButtonGroup()
            self.createTabs(tab)

            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(_("Browser Sources"))

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

    def createTabs(self, tab):
        """Create tabs."""
        self.tabs = QTabWidget()

        self.createFormGroupIntro()
        self.createFormGroupMapstats()
        self.createFormGroupMapBox()
        self.createFormGroupMapLandscape()

        # Add tabs
        self.tabs.addTab(self.formGroupIntro, _("Intros"))
        self.tabs.addTab(self.formGroupMapstats, _("Mapstats"))

        self.tabs.addTab(self.formGroupMapBox, _("Box Map Icons"))
        self.tabs.addTab(self.formGroupMapLandscape, _("Landscape Map Icons"))

        table = dict()
        table['intro'] = 0
        table['mapstats'] = 1
        table['mapicons_box'] = 2
        table['mapicons_landscape'] = 3
        self.tabs.setCurrentIndex(
            table.get(tab, SubwindowBrowserSources.current_tab))
        self.tabs.currentChanged.connect(self.tabChanged)

    def tabChanged(self, idx):
        SubwindowBrowserSources.current_tab = idx

    def addHotkey(self, ident, label):
        element = HotkeyLayout(
            self, ident, label,
            scctool.settings.config.parser.get("Intros", ident))
        self.hotkeys[ident] = element
        return element

    def connectHotkeys(self):
        for ident, key in self.hotkeys.items():
            if ident == 'hotkey_debug':
                for ident2, key2 in self.hotkeys.items():
                    if ident == ident2:
                        continue
                    key.modified.connect(key2.check_dublicate)
            key.modified.connect(self.hotkeyChanged)

    def hotkeyChanged(self, key, ident):
        self.changed()

        if(ident == 'hotkey_player1' and self.cb_single_hotkey.isChecked()):
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())

        if not key:
            return

        if((ident == 'hotkey_player1' and
            key == self.hotkeys['hotkey_player2'].getKey()['name']) or
           (ident == 'hotkey_player2' and
                key == self.hotkeys['hotkey_player1'].getKey()['name'])):
            self.cb_single_hotkey.setChecked(True)

        if(ident in ['hotkey_player1', 'hotkey_player2'] and
           key == self.hotkeys['hotkey_debug'].getKey()['name']):
            self.hotkeys['hotkey_debug'].clear()

    def singleHotkeyChanged(self):
        checked = self.cb_single_hotkey.isChecked()
        self.hotkeys['hotkey_player2'].setDisabled(checked)
        if checked:
            self.hotkeys['hotkey_player2'].setData(
                self.hotkeys['hotkey_player1'].getKey())
        elif(self.hotkeys['hotkey_player1'].getKey() ==
             self.hotkeys['hotkey_player2'].getKey()):
            self.hotkeys['hotkey_player2'].clear()

    def createFormGroupMapstats(self):
        self.formGroupMapstats = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapstats",
            "mapstats")
        self.qb_boxStyle.connect2WS(self.controller, 'mapstats')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapstats.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.cb_mappool = QComboBox()
        self.cb_mappool.addItem(_("Current Ladder Map Pool"))
        self.cb_mappool.addItem(_("Custom Map Pool (defined below)"))
        self.cb_mappool.addItem(_("Currently entered Maps only"))
        self.cb_mappool.setCurrentIndex(
            self.controller.mapstatsManager.getMapPoolType())
        self.cb_mappool.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(_("Map Pool:")), self.cb_mappool)

        self.cb_autoset_map = QCheckBox(_("Select the next map automatically"))
        self.cb_autoset_map.setChecked(
            scctool.settings.config.parser.getboolean(
                "Mapstats", "autoset_next_map"))
        self.cb_autoset_map.stateChanged.connect(self.changed)
        label = QLabel(_("Next Map:"))
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_autoset_map)

        self.cb_mark_played = QCheckBox(_("Mark already played maps"))
        self.cb_mark_played.setChecked(
            scctool.settings.config.parser.getboolean(
                "Mapstats", "mark_played"))
        self.cb_mark_played.stateChanged.connect(self.changed)
        label = QLabel(_("Mark:"))
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_mark_played)

        box.setLayout(layout)
        mainLayout.addWidget(box)

        box = QGroupBox(_("Custom Map Pool"))
        layout = QGridLayout()
        self.maplist = QListWidget()
        self.maplist.setSortingEnabled(True)

        ls = list(self.controller.mapstatsManager.getCustomMapPool())
        self.maplist.addItems(ls)
        self.maplist.setCurrentItem(self.maplist.item(0))

        layout.addWidget(self.maplist, 0, 0, 3, 1)

        qb_add = QPushButton()
        pixmap = QIcon(
            scctool.settings.getResFile('add.png'))
        qb_add.setIcon(pixmap)
        qb_add.clicked.connect(self.addMap)
        layout.addWidget(qb_add, 0, 1)

        qb_remove = QPushButton()
        pixmap = QIcon(
            scctool.settings.getResFile('delete.png'))
        qb_remove.clicked.connect(self.removeMap)
        qb_remove.setIcon(pixmap)
        layout.addWidget(qb_remove, 1, 1)

        self.sc_removeMap = QShortcut(QKeySequence("Del"), self)
        self.sc_removeMap.setAutoRepeat(False)
        self.sc_removeMap.activated.connect(self.removeMap)

        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapstats.setLayout(mainLayout)

    def addMap(self):
        maplist = list(scctool.settings.maps)
        for i in range(self.maplist.count()):
            map = str(self.maplist.item(i).text())
            if map in maplist:
                maplist.remove(map)

        if len(maplist) > 0:
            map, ok = QInputDialog.getItem(
                self, _('Add Map'),
                _('Please select a map') + ':',
                maplist, editable=False)

            if ok:
                self.__dataChanged = True
                item = QListWidgetItem(map)
                self.maplist.addItem(item)
                self.maplist.setCurrentItem(item)
        else:
            QMessageBox.information(
                self,
                _("No maps available"),
                _('All available maps have already been added.'))

    def removeMap(self):
        item = self.maplist.currentItem()
        if item:
            self.maplist.takeItem(self.maplist.currentRow())
            self.__dataChanged = True

    def createFormGroupMapBox(self):
        self.formGroupMapBox = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapicons_box",
            "mapicons_box")
        self.qb_boxStyle.connect2WS(self.controller, 'mapicons_box')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapicons_box_1.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_box = QDoubleSpinBox()
        self.sb_padding_box.setRange(0, 50)
        self.sb_padding_box.setDecimals(1)
        self.sb_padding_box.setValue(
            scctool.settings.config.parser.getfloat("MapIcons", "padding_box"))
        self.sb_padding_box.setSuffix(" " + _("Pixel"))
        self.sb_padding_box.valueChanged.connect(
            lambda x: self.changePadding('box', x))
        layout.addRow(QLabel(
            _("Icon Padding:") + " "), self.sb_padding_box)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        options = self.controller.matchControl.scopes
        self.scope_box = dict()
        for idx in range(0, 3):
            self.scope_box[idx] = ScopeGroupBox(
                _("Icon Set {} Scope".format(idx + 1)),
                options,
                scctool.settings.config.parser.get(
                    "MapIcons", "scope_box_{}".format(idx + 1)),
                self)
            self.scope_box[idx].dataModified.connect(self.changed)
            mainLayout.addWidget(self.scope_box[idx])

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapBox.setLayout(mainLayout)

    def createFormGroupMapLandscape(self):
        self.formGroupMapLandscape = QWidget()
        mainLayout = QVBoxLayout()
        box = QGroupBox(_("General"))
        layout = QFormLayout()

        container = QHBoxLayout()
        self.qb_boxStyle = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/mapicons_landscape",
            "mapicons_landscape")
        self.qb_boxStyle.connect2WS(self.controller, 'mapicons_landscape')
        label = QLabel(_("Style:"))
        label.setMinimumWidth(120)
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/mapicons_landscape_1.html"))
        container.addWidget(self.qb_boxStyle, 2)
        container.addWidget(button, 1)
        layout.addRow(label, container)

        self.sb_padding_landscape = QDoubleSpinBox()
        self.sb_padding_landscape.setRange(0, 50)
        self.sb_padding_landscape.setDecimals(1)
        self.sb_padding_landscape.setValue(
            scctool.settings.config.parser.getfloat(
                "MapIcons", "padding_landscape"))
        self.sb_padding_landscape.setSuffix(" " + _("Pixel"))
        self.sb_padding_landscape.valueChanged.connect(
            lambda x: self.changePadding('landscape', x))
        layout.addRow(QLabel(
            _("Icon Padding:") + " "), self.sb_padding_landscape)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        options = self.controller.matchControl.scopes
        self.scope_landscape = dict()
        for idx in range(0, 3):
            self.scope_landscape[idx] = ScopeGroupBox(
                _("Icon Set {} Scope".format(idx + 1)),
                options,
                scctool.settings.config.parser.get(
                    "MapIcons", "scope_landscape_{}".format(idx + 1)),
                self)
            self.scope_landscape[idx].dataModified.connect(self.changed)
            mainLayout.addWidget(self.scope_landscape[idx])

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupMapLandscape.setLayout(mainLayout)

    def createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/intro",
            "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/intro.html"))
        layout.addWidget(styleqb, 2)
        layout.addWidget(button, 1)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.hotkeyBox = QGroupBox(_("Hotkeys"))
        layout = QVBoxLayout()
        self.controller.websocketThread.unregister_hotkeys(force=True)

        self.cb_single_hotkey = QCheckBox(
            _("Use a single hotkey for both players"))
        self.cb_single_hotkey.stateChanged.connect(self.singleHotkeyChanged)
        layout.addWidget(self.cb_single_hotkey)

        self.hotkeys = dict()
        layout.addLayout(self.addHotkey("hotkey_player1", _("Player 1")))
        layout.addLayout(self.addHotkey("hotkey_player2", _("Player 2")))
        layout.addLayout(self.addHotkey("hotkey_debug", _("Debug")))

        self.cb_single_hotkey.setChecked(
            self.hotkeys['hotkey_player1'].getKey() ==
            self.hotkeys['hotkey_player2'].getKey())
        self.connectHotkeys()
        label = QLabel(_("Player 1 is always the player your observer"
                         " camera is centered on at start of a game."))
        layout.addWidget(label)
        self.hotkeyBox.setLayout(layout)
        mainLayout.addWidget(self.hotkeyBox)

        self.introBox = QGroupBox(_("Animation"))
        layout = QFormLayout()
        self.cb_animation = QComboBox()
        animation = scctool.settings.config.parser.get("Intros", "animation")
        currentIdx = 0
        idx = 0
        options = dict()
        options['Fly-In'] = _("Fly-In")
        options['Slide'] = _("Slide")
        options['Fanfare'] = _("Fanfare")
        for key, item in options.items():
            self.cb_animation.addItem(item, key)
            if(key == animation):
                currentIdx = idx
            idx += 1
        self.cb_animation.setCurrentIndex(currentIdx)
        self.cb_animation.currentIndexChanged.connect(self.changed)
        label = QLabel(_("Animation:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_animation)
        self.sb_displaytime = QDoubleSpinBox()
        self.sb_displaytime.setRange(0, 10)
        self.sb_displaytime.setDecimals(1)
        self.sb_displaytime.setValue(
            scctool.settings.config.parser.getfloat("Intros", "display_time"))
        self.sb_displaytime.setSuffix(" " + _("Seconds"))
        self.sb_displaytime.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Display Duration:") + " "), self.sb_displaytime)
        self.sl_sound = QSlider(Qt.Horizontal)
        self.sl_sound.setMinimum(0)
        self.sl_sound.setMaximum(20)
        self.sl_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "sound_volume"))
        self.sl_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_sound.setTickInterval(1)
        self.sl_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_sound)
        self.introBox.setLayout(layout)
        mainLayout.addWidget(self.introBox)

        self.ttsBox = QGroupBox(_("Text-to-Speech"))
        layout = QFormLayout()

        self.cb_tts_active = QCheckBox()
        self.cb_tts_active.setChecked(
            scctool.settings.config.parser.getboolean("Intros", "tts_active"))
        self.cb_tts_active.stateChanged.connect(self.changed)
        label = QLabel(_("Activate Text-to-Speech:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_tts_active)

        self.icons = {}
        self.icons['MALE'] = QIcon(scctool.settings.getResFile('male.png'))
        self.icons['FEMALE'] = QIcon(scctool.settings.getResFile('female.png'))
        self.cb_tts_voice = QComboBox()

        currentIdx = 0
        idx = 0
        tts_voices = self.controller.tts.getVoices()
        tts_voice = scctool.settings.config.parser.get("Intros", "tts_voice")
        for voice in tts_voices:
            self.cb_tts_voice.addItem(
                self.icons[voice['ssmlGender']],
                '   ' + voice['name'],
                voice['name'])
            if(voice['name'] == tts_voice):
                currentIdx = idx
            idx += 1
        self.cb_tts_voice.setCurrentIndex(currentIdx)
        self.cb_tts_voice.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Voice:") + " "), self.cb_tts_voice)
        self.ttsBox.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.ttsBox.setLayout(layout)
        mainLayout.addWidget(self.ttsBox)

        self.sb_tts_pitch = QDoubleSpinBox()
        self.sb_tts_pitch.setRange(-20, 20)
        self.sb_tts_pitch.setDecimals(2)
        self.sb_tts_pitch.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_pitch"))
        self.sb_tts_pitch.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Pitch:") + " "), self.sb_tts_pitch)

        self.sb_tts_rate = QDoubleSpinBox()
        self.sb_tts_rate.setRange(0.25, 4.00)
        self.sb_tts_rate.setSingleStep(0.1)
        self.sb_tts_rate.setDecimals(2)
        self.sb_tts_rate.setValue(
            scctool.settings.config.parser.getfloat("Intros", "tts_rate"))
        self.sb_tts_rate.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Rate:") + " "), self.sb_tts_rate)

        self.cb_tts_scope = QComboBox()
        scope = scctool.settings.config.parser.get("Intros", "tts_scope")
        currentIdx = 0
        idx = 0
        options = self.controller.tts.getOptions()
        for key, item in options.items():
            self.cb_tts_scope.addItem(item['desc'], key)
            if(key == scope):
                currentIdx = idx
            idx += 1
        self.cb_tts_scope.setCurrentIndex(currentIdx)
        self.cb_tts_scope.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Line:") + " "), self.cb_tts_scope)

        self.sl_tts_sound = QSlider(Qt.Horizontal)
        self.sl_tts_sound.setMinimum(0)
        self.sl_tts_sound.setMaximum(20)
        self.sl_tts_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "tts_volume"))
        self.sl_tts_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_tts_sound.setTickInterval(1)
        self.sl_tts_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_tts_sound)

        text = _(
            "Text-to-Speech provided by Google-Cloud is paid for "
            "by StarCraft Casting Tool Patrons. To keep this service up "
            "consider becoming a <a href='{patreon}'>Patron</a> yourself. "
            "You can test all voices at {tts}.")

        patreon = 'https://www.patreon.com/StarCraftCastingTool'

        url = 'https://cloud.google.com/text-to-speech/'
        tts = "<a href='{}'>cloud.google.com/text-to-speech</a>"
        tts = tts.format(url)

        label = QLabel(text.format(patreon=patreon, tts=tts))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addRow(label)

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupIntro.setLayout(mainLayout)

    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):
            self.saveWebsocketdata()

            maps = list()
            for i in range(self.maplist.count()):
                maps.append(str(self.maplist.item(i).text()).strip())

            self.controller.mapstatsManager.setCustomMapPool(maps)
            self.controller.mapstatsManager.setMapPoolType(
                self.cb_mappool.currentIndex())

            scctool.settings.config.parser.set(
                "Mapstats",
                "autoset_next_map",
                str(self.cb_autoset_map.isChecked()))

            scctool.settings.config.parser.set(
                "Mapstats",
                "mark_played",
                str(self.cb_mark_played.isChecked()))

            self.controller.mapstatsManager.sendMapPool()
            self.mainWindow.updateAllMapButtons()

            for idx in range(0, 3):
                scctool.settings.config.parser.set(
                    "MapIcons", "scope_box_{}".format(idx + 1),
                    self.scope_box[idx].getScope())
                scctool.settings.config.parser.set(
                    "MapIcons", "scope_landscape_{}".format(idx + 1),
                    self.scope_landscape[idx].getScope())
            self.controller.matchMetaDataChanged()
            self.__dataChanged = False
            # self.controller.refreshButtonStatus()

    def saveWebsocketdata(self):
        """Save Websocket data."""
        for ident, key in self.hotkeys.items():
            string = scctool.settings.config.dumpHotkey(key.getKey())
            scctool.settings.config.parser.set("Intros", ident, string)
        scctool.settings.config.parser.set(
            "Intros", "display_time", str(self.sb_displaytime.value()))
        scctool.settings.config.parser.set(
            "Intros", "sound_volume", str(self.sl_sound.value()))
        scctool.settings.config.parser.set(
            "Intros", "animation", self.cb_animation.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_voice", self.cb_tts_voice.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_scope", self.cb_tts_scope.currentData().strip())
        scctool.settings.config.parser.set(
            "Intros", "tts_active", str(self.cb_tts_active.isChecked()))
        scctool.settings.config.parser.set(
            "Intros", "tts_volume", str(self.sl_tts_sound.value()))
        scctool.settings.config.parser.set(
            "Intros", "tts_pitch", str(self.sb_tts_pitch.value()))
        scctool.settings.config.parser.set(
            "Intros", "tts_rate", str(self.sb_tts_rate.value()))

    def openHTML(self, file):
        """Open file in browser."""
        self.controller.openURL(scctool.settings.getAbsPath(file))

    def changePadding(self, scope, padding):
        scctool.settings.config.parser.set(
            "MapIcons", "padding_{}".format(scope),
            str(padding))
        self.controller.websocketThread.changePadding(
            "mapicons_{}".format(scope), padding)

    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):
                self.controller.updateHotkeys()
                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()
            self.controller.updateHotkeys()
            event.accept()
        except Exception as e:
            module_logger.exception("message")
Пример #4
0
    def createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            scctool.settings.casting_html_dir + "/src/css/intro",
            "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            scctool.settings.casting_html_dir + "/intro.html"))
        layout.addWidget(styleqb, 2)
        layout.addWidget(button, 1)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        self.hotkeyBox = QGroupBox(_("Hotkeys"))
        layout = QVBoxLayout()
        try:
            keyboard.unhook_all()
        except AttributeError:
            pass

        self.cb_single_hotkey = QCheckBox(
            _("Use a single hotkey for both players"))
        self.cb_single_hotkey.stateChanged.connect(self.singleHotkeyChanged)
        layout.addWidget(self.cb_single_hotkey)

        self.hotkeys = dict()
        layout.addLayout(self.addHotkey("hotkey_player1", _("Player 1")))
        layout.addLayout(self.addHotkey("hotkey_player2", _("Player 2")))
        layout.addLayout(self.addHotkey("hotkey_debug", _("Debug")))

        self.cb_single_hotkey.setChecked(
            self.hotkeys['hotkey_player1'].getKey() ==
            self.hotkeys['hotkey_player2'].getKey())
        self.connectHotkeys()
        label = QLabel(_("Player 1 is always the player your observer"
                         " camera is centered on at start of a game."))
        layout.addWidget(label)
        self.hotkeyBox.setLayout(layout)
        mainLayout.addWidget(self.hotkeyBox)

        self.introBox = QGroupBox(_("Animation"))
        layout = QFormLayout()
        self.cb_animation = QComboBox()
        animation = scctool.settings.config.parser.get("Intros", "animation")
        currentIdx = 0
        idx = 0
        options = dict()
        options['Fly-In'] = _("Fly-In")
        options['Slide'] = _("Slide")
        options['Fanfare'] = _("Fanfare")
        for key, item in options.items():
            self.cb_animation.addItem(item, key)
            if(key == animation):
                currentIdx = idx
            idx += 1
        self.cb_animation.setCurrentIndex(currentIdx)
        self.cb_animation.currentIndexChanged.connect(self.changed)
        label = QLabel(_("Animation:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_animation)
        self.sb_displaytime = QDoubleSpinBox()
        self.sb_displaytime.setRange(0, 10)
        self.sb_displaytime.setDecimals(1)
        self.sb_displaytime.setValue(
            scctool.settings.config.parser.getfloat("Intros", "display_time"))
        self.sb_displaytime.setSuffix(" " + _("Seconds"))
        self.sb_displaytime.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Display Duration:") + " "), self.sb_displaytime)
        self.sl_sound = QSlider(Qt.Horizontal)
        self.sl_sound.setMinimum(0)
        self.sl_sound.setMaximum(20)
        self.sl_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "sound_volume"))
        self.sl_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_sound.setTickInterval(1)
        self.sl_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_sound)
        self.introBox.setLayout(layout)
        mainLayout.addWidget(self.introBox)

        self.ttsBox = QGroupBox(_("Text-to-Speech"))
        layout = QFormLayout()

        self.cb_tts_active = QCheckBox()
        self.cb_tts_active.setChecked(
            scctool.settings.config.parser.getboolean("Intros", "tts_active"))
        self.cb_tts_active.stateChanged.connect(self.changed)
        label = QLabel(_("Activate Text-to-Speech:") + " ")
        label.setMinimumWidth(120)
        layout.addRow(label, self.cb_tts_active)

        self.cb_tts_lang = QComboBox()

        currentIdx = 0
        idx = 0
        tts_langs = gtts.lang.tts_langs()
        tts_lang = scctool.settings.config.parser.get("Intros", "tts_lang")
        for key, name in tts_langs.items():
            self.cb_tts_lang.addItem(name, key)
            if(key == tts_lang):
                currentIdx = idx
            idx += 1
        self.cb_tts_lang.setCurrentIndex(currentIdx)
        self.cb_tts_lang.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Language:") + " "), self.cb_tts_lang)
        self.ttsBox.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.ttsBox.setLayout(layout)
        mainLayout.addWidget(self.ttsBox)

        self.cb_tts_scope = QComboBox()
        scope = scctool.settings.config.parser.get("Intros", "tts_scope")
        currentIdx = 0
        idx = 0
        options = dict()
        options['team_player'] = _("Team & Player")
        options['player'] = _("Player")
        for key, item in options.items():
            self.cb_tts_scope.addItem(item, key)
            if(key == scope):
                currentIdx = idx
            idx += 1
        self.cb_tts_scope.setCurrentIndex(currentIdx)
        self.cb_tts_scope.currentIndexChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Scope:") + " "), self.cb_tts_scope)

        self.sl_tts_sound = QSlider(Qt.Horizontal)
        self.sl_tts_sound.setMinimum(0)
        self.sl_tts_sound.setMaximum(20)
        self.sl_tts_sound.setValue(
            scctool.settings.config.parser.getint("Intros", "tts_volume"))
        self.sl_tts_sound.setTickPosition(QSlider.TicksBothSides)
        self.sl_tts_sound.setTickInterval(1)
        self.sl_tts_sound.valueChanged.connect(self.changed)
        layout.addRow(QLabel(
            _("Sound Volume:") + " "), self.sl_tts_sound)

        mainLayout.addItem(QSpacerItem(
            0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.formGroupIntro.setLayout(mainLayout)