Пример #1
0
class MovablePushButton(QPushButton, MovableWidget):
    def __init__(self, name, valueOn, shortcutKey, **kwargs):
        MovableWidget.__init__(self, name)
        QPushButton.__init__(self, name=name)
        self.valueOn = valueOn

        self.parameter = kwargs.get('parameter', None)
        self.module = kwargs.get('module', None)

        self.shortcut = QShortcut(self)
        self.shortcut.setKey(shortcutKey)
        self.shortcut.setAutoRepeat(False)
        self.shortcut.activated.connect(lambda: self.animateClick())

        self.updateData()

    def getData(self):
        data = dict()

        data['widgetType'] = 'PushButton'
        data['name'] = self.widgetName
        data['valueOn'] = self.valueOn
        data['module'] = self.module
        data['parameter'] = self.parameter
        data['shortcut'] = self.shortcut.key().toString()

        return data

    def updateData(self):
        self.setText(self.widgetName + '\n' + self.valueOn)
Пример #2
0
    def __init__(self, parent):
        """Init table list."""
        super().__init__()
        self.parent = parent
        self.items = dict()
        self.header().hide()
        self.setAnimated(True)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.openMenu)

        shortcut = QShortcut(QKeySequence("DEL"), self)
        shortcut.setAutoRepeat(False)
        shortcut.setContext(Qt.WidgetWithChildrenShortcut)
        shortcut.activated.connect(self.delClicked)

        shortcut = QShortcut(QKeySequence("Enter"), self)
        shortcut.setAutoRepeat(False)
        shortcut.setContext(Qt.WidgetWithChildrenShortcut)
        shortcut.activated.connect(self.enterClicked)
Пример #3
0
class LoggerTab(*LoggerTabBase):
    def __init__(self, name, tab_closed_signal, log, loop, main_window,
                 parent_widget):
        super().__init__()
        self.log = log.getChild(name)
        self.log.debug(f'Starting a logger named {name}')
        self.name = name
        self.main_window = main_window
        self.parent_widget = parent_widget
        self.loop = loop
        self.level_filter = LevelFilter()
        self.level_filter.set_all_pass(False)
        self.filter_model_enabled = True
        self.detail_model = DetailTableModel()
        self.namespace_tree_model = LogNamespaceTreeModel()
        self.popped_out = False
        self.autoscroll = True
        self.scroll_max = 0
        self.record_count = 0
        self.monitor_count = 0  # for monitoring
        self.tab_closed_signal = tab_closed_signal
        self.last_status_update_time = 0

        self.search_bar_visible = CONFIG['search_open_default']
        self.search_regex = CONFIG['search_regex_default']
        self.search_casesensitive = CONFIG['search_casesensitive_default']
        self.search_wildcard = CONFIG['search_wildcard_default']

        self.search_start = 0
        self.search_filter = False
        self.setupUi()

    def setupUi(self):
        super().setupUi(self.parent_widget)
        self.table_header = LoggerTableHeader(
            self.loggerTable.horizontalHeader())
        self.record_model = LogRecordModel(self, self.level_filter.levels,
                                           self.table_header)

        self.createLevelButton.clicked.connect(self.create_level)

        self.loggerTable.setMouseTracking(False)
        self.loggerTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.loggerTable.verticalScrollBar().rangeChanged.connect(
            self.onRangeChanged)
        self.loggerTable.verticalScrollBar().valueChanged.connect(
            self.onScroll)
        self.loggerTable.setContextMenuPolicy(Qt.CustomContextMenu)
        self.loggerTable.customContextMenuRequested.connect(
            self.open_logger_table_menu)

        self.loggerTable.setStyleSheet("QTableView { border: 0px;}")

        self.loggerTable.verticalHeader().setDefaultSectionSize(
            CONFIG['logger_row_height'])

        self.namespaceTreeView.setModel(self.namespace_tree_model)
        self.namespaceTreeView.selectionModel().selectionChanged.connect(
            self.reset_master)
        self.namespaceTreeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.namespaceTreeView.customContextMenuRequested.connect(
            self.open_namespace_table_menu)
        self.namespace_tree_model.rowsInserted.connect(
            self.on_tree_rows_inserted)

        for levelno, level in self.level_filter.levels.items():
            self.add_level_to_table(level)
        self.levelsTable.doubleClicked.connect(self.level_double_clicked)
        self.levelsTable.installEventFilter(self)
        self.levelsTable.setContextMenuPolicy(Qt.CustomContextMenu)
        self.levelsTable.customContextMenuRequested.connect(
            self.open_levels_table_menu)

        if self.filter_model_enabled:
            self.filter_model = RecordFilter(self, self.namespaceTreeView,
                                             self.level_filter)
            self.filter_model.setSourceModel(self.record_model)
            self.loggerTable.setModel(self.filter_model)
        else:
            self.loggerTable.setModel(self.record_model)

        self.loggerTable.selectionModel().selectionChanged.connect(
            self.update_detail)
        self.detailTable.setModel(self.detail_model)

        self.table_header_view = header = self.loggerTable.horizontalHeader()
        header.setStretchLastSection(True)
        # header.sectionResized.connect(self.table_header.column_resized)
        header.viewport().installEventFilter(
            self.table_header)  # read the docstring
        header.setContextMenuPolicy(Qt.CustomContextMenu)
        header.customContextMenuRequested.connect(self.open_header_menu)

        self.searchSC = QShortcut('Ctrl+F', self.parent_widget)
        self.searchSC.activated.connect(self.toggle_search)
        self.searchSC.setAutoRepeat(False)

        self.searchSC_F3 = QShortcut('F3', self.parent_widget)
        self.searchSC_F3.activated.connect(self.search_down_or_close)
        self.searchSC_F3.setAutoRepeat(True)

        self.searchSC_Esc = QShortcut('Esc', self.parent_widget)
        self.searchSC_Esc.activated.connect(
            partial(self.set_search_visible, False))
        self.searchSC_Esc.setAutoRepeat(False)

        self.searchLine.returnPressed.connect(self.search_down)
        self.searchDownButton.clicked.connect(self.search_down)
        self.searchDownButton.setMenu(self.setup_search_button_menu())

        self.searchWidget.setVisible(self.search_bar_visible)
        self.filterButton.clicked.connect(self.filter_or_clear)
        self.filterButton.setToolTip(
            'Adheres to the same settings as the search')

        # @NextVersion: make this happen
        self.levelButtonsLayout.setParent(None)
        self.createLevelButton.setVisible(False)
        self.presetsButton.setVisible(False)

        self.setup_internal_connections()
        self.set_columns_sizes()

    def setup_search_button_menu(self):
        smenu = QMenu(self.searchDownButton)
        action_regex = smenu.addAction('Regex')
        action_regex.setCheckable(True)
        action_regex.setChecked(self.search_regex)
        action_regex.triggered.connect(partial(setattr, self, 'search_regex'))
        action_case = smenu.addAction('Case sensitive')
        action_case.setCheckable(True)
        action_case.setChecked(self.search_casesensitive)
        action_case.triggered.connect(
            partial(setattr, self, 'search_casesensitive'))
        action_wild = smenu.addAction('Wildcard')
        action_wild.setCheckable(True)
        action_wild.setChecked(self.search_wildcard)
        action_wild.triggered.connect(partial(setattr, self,
                                              'search_wildcard'))
        return smenu

    def setup_internal_connections(self):
        CONFIG.row_height_changed.connect(self.row_height_changed)

    def filter_or_clear(self):
        self.search_filter = not self.search_filter
        if self.search_filter:
            self.filterButton.setText('Clear filter')
            self.filter_model.search_filter = True
            self.filter_model.set_filter(self.searchLine.text(),
                                         self.search_regex,
                                         self.search_wildcard,
                                         self.search_casesensitive)
        else:
            self.filterButton.setText('Filter')
            self.filter_model.clear_filter()

    def set_columns_sizes(self):
        # self.table_header.ignore_resizing = True
        cols = self.table_header.visible_columns
        for i, col in enumerate(cols):
            self.table_header_view.resizeSection(i, col.width)
        # self.table_header.ignore_resizing = False

    def set_max_capacity(self, max_capacity):
        self.record_model.max_capacity = max_capacity
        self.record_model.trim_if_needed()

    def header_section_resized(self, index, oldw, neww):
        if index == 4:
            return
        self.log.warn(f"index = {index}, {oldw} -> {neww}")

    def eventFilter(self, object, event):
        if event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
                self.toggle_selected_levels()
                return True
        return False

    def toggle_selected_levels(self):
        selected = self.levelsTable.selectedIndexes()
        for index in selected:
            if index.column() == 0:
                checkbox = self.levelsTable.cellWidget(
                    index.row(), index.column()).children()[1]
                checkbox.toggle()
        self.reset_master()

    def search_down(self):
        start = self.filter_model.index(self.search_start, 0, INVALID_INDEX)
        s = self.searchLine.text()

        if not self.search_regex:
            search_flags = Qt.MatchContains
        else:
            search_flags = Qt.MatchRegExp
        if self.search_casesensitive:
            search_flags = search_flags | Qt.MatchCaseSensitive
        if self.search_wildcard:
            search_flags = search_flags | Qt.MatchWildcard

        hits = self.filter_model.match(start, SearchRole, s, 1,
                                       Qt.MatchWrap | search_flags)
        if not hits:
            self.log.warn(f'No matches for {s}')
            self.search_start = 0
        else:
            result = hits[0]
            self.search_start = result.row() + 1
            self.loggerTable.scrollTo(result)
            self.loggerTable.setCurrentIndex(result)

    def search_down_or_close(self):
        if self.search_bar_visible is False:
            self.set_search_visible(True)
        elif self.searchLine.text() == "":
            self.set_search_visible(False)
        else:
            self.search_down()

    def set_search_visible(self, visible):
        self.search_bar_visible = visible
        self.searchWidget.setVisible(self.search_bar_visible)
        if self.search_bar_visible:
            self.searchLine.setFocus()
        else:
            self.searchLine.clear()

    def toggle_search(self):
        self.search_bar_visible = not self.search_bar_visible
        self.set_search_visible(self.search_bar_visible)

    def on_record(self, record):
        level = self.process_level(record.levelno, record.levelname)
        record.levelname = level.levelname
        self.record_model.add_record(record)
        if self.autoscroll:
            self.loggerTable.scrollToBottom()
        self.register_logger(record.name)
        self.record_count += 1
        self.monitor_count += 1
        # self.loggerTable.resizeRowsToContents()

    def get_record(self, index):
        if self.filter_model_enabled:
            source_index = self.filter_model.mapToSource(index)
            record = self.record_model.get_record(source_index)
        else:
            record = self.record_model.get_record(index)
        return record

    def register_logger(self, name):
        self.namespace_tree_model.register_logger(name)

    def process_level(self, levelno, levelname):
        level = self.level_filter.levels.get(levelno)
        if level:
            level.msg_count += 1
            return level
        new_level = LogLevel(levelno, levelname)
        self.level_filter.add_level(new_level)
        self.add_level_to_table(new_level)
        return new_level

    def add_level_to_table(self, level):
        row_count = self.levelsTable.rowCount()
        self.levelsTable.setRowCount(row_count + 1)

        checkbox = QCheckBox()
        checkbox.setStyleSheet(
            "QCheckBox::indicator { width: 15px; height: 15px;}")
        checkbox.toggle()
        checkbox.clicked.connect(level.set_enabled)
        checkbox.clicked.connect(self.reset_master)
        checkbox.toggled.connect(level.set_enabled)

        checkbox_layout = QHBoxLayout()
        checkbox_layout.setAlignment(Qt.AlignCenter)
        checkbox_layout.setContentsMargins(0, 0, 0, 0)
        checkbox_layout.addWidget(checkbox)

        checkbox_widget = QWidget()
        checkbox_widget.setLayout(checkbox_layout)
        checkbox_widget.setStyleSheet("QWidget { background-color:none;}")

        self.levelsTable.setCellWidget(row_count, 0, checkbox_widget)
        self.levelsTable.setItem(row_count, 1,
                                 QTableWidgetItem(str(level.levelno)))
        self.levelsTable.setItem(row_count, 2,
                                 QTableWidgetItem(level.levelname))

        self.levelsTable.sortItems(1, Qt.SortOrder(Qt.AscendingOrder))
        self.levelsTable.resizeColumnToContents(1)

    def open_namespace_table_menu(self, position):
        menu = QMenu(self)
        include_children_action = menu.addAction("Selection includes children")
        include_children_action.setCheckable(True)
        if self.filter_model_enabled:
            include_children_action.setChecked(
                self.filter_model.selection_includes_children)
        else:
            include_children_action.set_enabled(False)
        include_children_action.triggered.connect(
            self.toggle_selection_includes_children)
        menu.popup(self.namespaceTreeView.viewport().mapToGlobal(position))

    def toggle_selection_includes_children(self, val):
        self.filter_model.selection_includes_children = val
        self.reset_master()

    def open_levels_table_menu(self, position):
        menu = QMenu(self)
        enable_all_action = menu.addAction("Enable all")
        enable_all_action.triggered.connect(self.enable_all_levels)
        disable_all_action = menu.addAction("Disable all")
        disable_all_action.triggered.connect(self.disable_all_levels)
        menu.addSeparator()
        edit_action = menu.addAction("Edit selected level")
        edit_action.triggered.connect(self.open_edit_level_dialog)
        menu.popup(self.levelsTable.viewport().mapToGlobal(position))

    def open_logger_table_menu(self, position):
        # Needed as a workaround for when the header column count is 0 and it becomes invisible
        if self.table_header.column_count == 0:
            self.open_header_menu(position)
            return
        selected = self.loggerTable.selectedIndexes()
        if not selected:
            return
        row_index = selected[0]
        record = self.get_record(row_index)
        menu = QMenu(self)
        view_message = menu.addAction("View message")
        view_message.triggered.connect(
            partial(self.open_text_view_dialog, row_index, False))
        if record.exc_text:
            view_traceback = menu.addAction("View traceback")
            view_traceback.triggered.connect(
                partial(self.open_text_view_dialog, row_index, True))
        menu.popup(self.table_header_view.viewport().mapToGlobal(position))

    def open_header_menu(self, position):
        menu = QMenu(self)
        customize_header = menu.addAction("Customize header")
        customize_header.triggered.connect(self.open_header_dialog)
        reset_header_action = menu.addAction("Reset header")
        reset_header_action.triggered.connect(self.table_header.reset_columns)
        reset_header_action.triggered.connect(self.set_columns_sizes)
        menu.popup(self.table_header_view.viewport().mapToGlobal(position))

    def open_header_dialog(self):
        d = HeaderEditDialog(self.main_window, self.table_header.columns)
        d.header_changed.connect(self.header_changed)
        d.setWindowTitle('Header editor')
        d.open()

    def header_changed(self, action, data):
        if action == 'rearrange':
            self.table_header.replace_columns(data)
        elif action == 'load':
            loaded = CONFIG.load_columns_preset(data)
            self.table_header.replace_columns(loaded)
        elif action == 'save':
            CONFIG.save_columns_preset(data, self)
        elif action == 'save new':
            pass
        self.set_columns_sizes()

    def merge_with_records(self, new_records):
        self.record_model.merge_with_records(new_records)
        for record in new_records:
            self.register_logger(record.name)
            level = self.process_level(record.levelno, record.levelname)
            record.levelname = level.levelname

    def update_detail(self, sel, desel):
        indexes = sel.indexes()
        if len(indexes) <= 0:
            self.detail_model.clear()
            return
        index = indexes[0]
        record = self.get_record(index)
        self.detail_model.set_record(record)

    def open_text_view_dialog(self, index, exception=False):
        record = self.get_record(index)
        d = TextViewDialog(self.main_window,
                           record.exc_text if exception else record.message)
        d.setWindowModality(Qt.NonModal)
        d.setAttribute(Qt.WA_DeleteOnClose, True)
        d.setWindowTitle(
            'Exception traceback' if exception else 'View message')
        d.open()

    def enable_all_levels(self):
        for row in range(self.levelsTable.rowCount()):
            checkbox = self.levelsTable.cellWidget(row, 0).children()[1]
            if not checkbox.isChecked():
                checkbox.setChecked(True)
        self.reset_master()

    def disable_all_levels(self):
        for row in range(self.levelsTable.rowCount()):
            checkbox = self.levelsTable.cellWidget(row, 0).children()[1]
            if checkbox.isChecked():
                checkbox.setChecked(False)
        self.reset_master()

    def set_dark_theme(self, value):
        self.record_model.dark_theme = value

    def level_double_clicked(self, index):
        row, column = index.row(), index.column()
        if column == 0:  # if you're clicking at the checkbox widget, just toggle it instead
            checkbox = self.levelsTable.cellWidget(row, column).children()[1]
            checkbox.toggle()
            self.reset_master()
        else:
            self.open_edit_level_dialog(row)

    def open_edit_level_dialog(self, row=None):
        if not row:
            selected = self.levelsTable.selectedIndexes()
            if not selected:
                return
            row = selected[0].row()
        levelno = self.levelsTable.item(row, 1).data(Qt.DisplayRole)
        level = self.level_filter.levels[int(levelno)]
        d = LevelEditDialog(self.main_window, level)
        d.setWindowModality(Qt.NonModal)
        d.setWindowTitle('Level editor')
        d.open()

    def create_level(self):
        self.log.warn('Creating level')
        d = LevelEditDialog(creating_new_level=True)
        d.setWindowModality(Qt.NonModal)
        d.setWindowTitle('Level editor')
        d.open()

    def reset_master(self, sel=None, desel=None):
        self.record_model.beginResetModel()
        self.record_model.endResetModel()
        if self.autoscroll:
            self.loggerTable.scrollToBottom()

    def onScroll(self, pos):
        if pos < self.scroll_max:
            self.autoscroll = False
        else:
            self.autoscroll = True

    def on_tree_rows_inserted(self, pindex, start, end):
        tree = self.namespaceTreeView
        tmodel = self.namespace_tree_model
        tree.expand(pindex)
        while start <= end:
            index = tmodel.index(start, 0, pindex)
            if not index.isValid():
                self.log.error('Invalid index!')
            else:
                tree.expand(index)
            start += 1

    def onRangeChanged(self, min, max):
        self.scroll_max = max

    def closeEvent(self, event=None):
        self.log.debug('Tab close event!')
        self.tab_closed_signal.set()
        if self.popped_out:
            self.main_window.close_popped_out_logger(self)

    def row_height_changed(self, new_height):
        self.loggerTable.verticalHeader().setDefaultSectionSize(new_height)

    async def monitor(self):
        "Used only when benchmark parameter of LogServer is True"
        records = []
        while True:
            await asyncio.sleep(0.5)
            status = f"{self.monitor_count * 2} rows/s"
            if self.monitor_count == 0:
                break
            records.append(self.monitor_count)
            print(status, int(sum(records) / len(records)) * 2, 'average')
            self.main_window.set_status(status)
            self.monitor_count = 0
        print('Result:', int(sum(records) / len(records)) * 2, 'average')
Пример #4
0
    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(WatchpointsWidget, self).__init__(parent=parent)
        self._app_window = parent

        if self._app_window.dwarf is None:
            print('WatchpointsWidget created before Dwarf exists')
            return

        self._uppercase_hex = True
        self.setAutoFillBackground(True)

        # connect to dwarf
        self._app_window.dwarf.onWatchpointAdded.connect(self._on_watchpoint_added)
        self._app_window.dwarf.onWatchpointRemoved.connect(
            self._on_watchpoint_removed)

        # setup our model
        self._watchpoints_model = QStandardItemModel(0, 5)
        self._watchpoints_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._watchpoints_model.setHeaderData(1, Qt.Horizontal, 'R')
        self._watchpoints_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchpoints_model.setHeaderData(2, Qt.Horizontal, 'W')
        self._watchpoints_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchpoints_model.setHeaderData(3, Qt.Horizontal, 'X')
        self._watchpoints_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchpoints_model.setHeaderData(4, Qt.Horizontal, 'S')
        self._watchpoints_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)

        # setup ui
        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        self.list_view = DwarfListView()
        self.list_view.setModel(self._watchpoints_model)
        self.list_view.header().setSectionResizeMode(0, QHeaderView.Stretch)
        self.list_view.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setSectionResizeMode(
            3, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setSectionResizeMode(
            4, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setStretchLastSection(False)
        self.list_view.doubleClicked.connect(self._on_item_dblclick)
        self.list_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.list_view.customContextMenuRequested.connect(self._on_contextmenu)

        v_box.addWidget(self.list_view)
        #header = QHeaderView(Qt.Horizontal, self)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        btn1.setFixedSize(20, 20)
        btn1.clicked.connect(self._on_additem_clicked)
        btn2 = QPushButton(
            QIcon(utils.resource_path('assets/icons/dash.svg')), '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)

        # create a centered dot icon
        _section_width = self.list_view.header().sectionSize(2)
        self._new_pixmap = QPixmap(_section_width, 20)
        self._new_pixmap.fill(Qt.transparent)
        painter = QPainter(self._new_pixmap)
        rect = QRect((_section_width * 0.5), 0, 20, 20)
        painter.setBrush(QColor('#666'))
        painter.setPen(QColor('#666'))
        painter.drawEllipse(rect)
        self._dot_icon = QIcon(self._new_pixmap)

        # shortcuts
        shortcut_add = QShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_W), self._app_window,
            self._on_additem_clicked)
        shortcut_add.setAutoRepeat(False)

        self.setLayout(v_box)
Пример #5
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")
Пример #6
0
class SubwindowBrowserSources(QWidget):
    """Show connections settings sub window."""
    def createWindow(self, mainWindow, tab=''):
        """Create window."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(hwctool.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"))

            self.controller.websocketThread.unregister_hotkeys(force=True)

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

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

        self.createFormGroupIntro()

        # Add tabs
        self.tabs.addTab(self.formGroupIntro, _("Intros"))
        table = dict()
        table['intro'] = 0
        self.tabs.setCurrentIndex(table.get(tab, -1))

    def addHotkey(self, ident, label):
        element = HotkeyLayout(
            self, ident, label,
            hwctool.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 createFormGroupIntro(self):
        """Create forms for websocket connection to intro."""
        self.formGroupIntro = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Style"))
        layout = QHBoxLayout()
        styleqb = StyleComboBox(
            hwctool.settings.casting_html_dir + "/src/css/intro", "intro")
        styleqb.connect2WS(self.controller, 'intro')
        button = QPushButton(_("Show in Browser"))
        button.clicked.connect(lambda: self.openHTML(
            hwctool.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 = hwctool.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(
            hwctool.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(
            hwctool.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(
            hwctool.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(hwctool.settings.getResFile('male.png'))
        self.icons['FEMALE'] = QIcon(hwctool.settings.getResFile('female.png'))
        self.cb_tts_voice = QComboBox()

        currentIdx = 0
        idx = 0
        tts_voices = self.controller.tts.getVoices()
        tts_voice = hwctool.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(
            hwctool.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(
            hwctool.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 = hwctool.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(
            hwctool.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()
            self.__dataChanged = False
            # self.controller.refreshButtonStatus()

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

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

    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")
Пример #7
0
    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(BookmarksWidget, self).__init__(parent=parent)

        self._app_window = parent

        if self._app_window.dwarf is None:
            print('BookmarksPanel created before Dwarf exists')
            return

        self.bookmarks = {}

        self._bookmarks_list = DwarfListView()
        self._bookmarks_list.doubleClicked.connect(self._on_double_clicked)
        self._bookmarks_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._bookmarks_list.customContextMenuRequested.connect(
            self._on_contextmenu)

        self._bookmarks_model = QStandardItemModel(0, 2)
        self._bookmarks_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._bookmarks_model.setHeaderData(1, Qt.Horizontal, 'Notes')

        self._bookmarks_list.setModel(self._bookmarks_model)

        self._bookmarks_list.header().setStretchLastSection(False)
        self._bookmarks_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents | QHeaderView.Interactive)
        self._bookmarks_list.header().setSectionResizeMode(
            1, QHeaderView.Stretch | QHeaderView.Interactive)

        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        v_box.addWidget(self._bookmarks_list)
        #header = QHeaderView(Qt.Horizontal, self)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        self.btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        self.btn1.setFixedSize(20, 20)
        self.btn1.clicked.connect(lambda: self._create_bookmark(-1))
        btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')),
                           '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(self.btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)
        self.setLayout(v_box)

        self._bold_font = QFont(self._bookmarks_list.font())
        self._bold_font.setBold(True)

        shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_B),
                                       self._app_window, self._create_bookmark)
        shortcut_addnative.setAutoRepeat(False)
Пример #8
0
    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(HooksWidget, self).__init__(parent=parent)

        self._app_window = parent

        if self._app_window.dwarf is None:
            print('HooksPanel created before Dwarf exists')
            return

        # connect to dwarf
        self._app_window.dwarf.onAddJavaHook.connect(self._on_add_hook)
        self._app_window.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self._app_window.dwarf.onAddNativeOnLoadHook.connect(self._on_add_hook)
        self._app_window.dwarf.onAddJavaOnLoadHook.connect(self._on_add_hook)
        self._app_window.dwarf.onHitNativeOnLoad.connect(
            self._on_hit_native_on_load)
        self._app_window.dwarf.onHitJavaOnLoad.connect(
            self._on_hit_java_on_load)
        self._app_window.dwarf.onDeleteHook.connect(self._on_hook_deleted)

        self._hooks_list = DwarfListView()
        self._hooks_list.doubleClicked.connect(self._on_double_clicked)
        self._hooks_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._hooks_list.customContextMenuRequested.connect(
            self._on_context_menu)
        self._hooks_model = QStandardItemModel(0, 5)

        self._hooks_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._hooks_model.setHeaderData(1, Qt.Horizontal, 'T')
        self._hooks_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                        Qt.TextAlignmentRole)
        self._hooks_model.setHeaderData(2, Qt.Horizontal, 'Input')
        self._hooks_model.setHeaderData(3, Qt.Horizontal, '{}')
        self._hooks_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                        Qt.TextAlignmentRole)
        self._hooks_model.setHeaderData(4, Qt.Horizontal, '<>')
        self._hooks_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter,
                                        Qt.TextAlignmentRole)

        self._hooks_list.setModel(self._hooks_model)

        self._hooks_list.header().setStretchLastSection(False)
        self._hooks_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents | QHeaderView.Interactive)
        self._hooks_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self._hooks_list.header().setSectionResizeMode(2, QHeaderView.Stretch)
        self._hooks_list.header().setSectionResizeMode(
            3, QHeaderView.ResizeToContents)
        self._hooks_list.header().setSectionResizeMode(
            4, QHeaderView.ResizeToContents)

        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        v_box.addWidget(self._hooks_list)
        #header = QHeaderView(Qt.Horizontal, self)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        self.btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        self.btn1.setFixedSize(20, 20)
        self.btn1.clicked.connect(self._on_additem_clicked)
        btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')),
                           '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(self.btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)
        self.setLayout(v_box)

        self._bold_font = QFont(self._hooks_list.font())
        self._bold_font.setBold(True)

        shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_N),
                                       self._app_window, self._on_addnative)
        shortcut_addnative.setAutoRepeat(False)

        shortcut_addjava = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_J),
                                     self._app_window, self._on_addjava)
        shortcut_addjava.setAutoRepeat(False)

        shortcut_add_native_on_load = QShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_O), self._app_window,
            self._on_add_native_on_load)
        shortcut_add_native_on_load.setAutoRepeat(False)

        # new menu
        self.new_menu = QMenu('New')
        self.new_menu.addAction('Native', self._on_addnative)
        self.new_menu.addAction('Java', self._on_addjava)
        self.new_menu.addAction('Module loading', self._on_add_native_on_load)
Пример #9
0
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)
Пример #10
0
class SubwindowMisc(QWidget):
    """Show subwindow with miscellaneous settings."""

    current_tab = -1

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

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('settings.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(int(mainWindow.size().width() * 0.9),
                      self.sizeHint().height()))
            relativeChange = QPoint(int(mainWindow.size().width() / 2),
                                    int(mainWindow.size().height() / 3))\
                - QPoint(int(self.size().width() / 2),
                         int(self.size().height() / 3))
            self.move(mainWindow.pos() + relativeChange)

            self.setWindowTitle(_("Miscellaneous Settings"))

        except Exception:
            module_logger.exception("message")

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

        self.createMapsBox()
        self.createFavBox()
        self.createAliasBox()
        self.createOcrBox()
        self.createAlphaBox()
        self.createSC2ClientAPIBox()
        self.createAligulacTab()
        self.createCounterTab()

        # Add tabs
        self.tabs.addTab(self.mapsBox, _("Map Manager"))
        self.tabs.addTab(self.favBox, _("Favorites"))
        self.tabs.addTab(self.aliasBox, _("Alias"))
        self.tabs.addTab(self.ocrBox, _("OCR"))
        self.tabs.addTab(self.alphaBox, _("AlphaTL && Ingame Score"))
        self.tabs.addTab(self.clientapiBox, _("SC2 Client API"))
        self.tabs.addTab(self.aligulacTab, _("Aligulac"))
        self.tabs.addTab(self.counterTab, _("Countdown && Ticker"))

        table = dict()
        table['mapmanager'] = 0
        table['favorites'] = 1
        table['alias'] = 2
        table['ocr'] = 3
        table['alphatl'] = 4
        table['sc2clientapi'] = 5
        table['aligulac'] = 6
        table['counter'] = 7
        self.tabs.setCurrentIndex(table.get(tab, SubwindowMisc.current_tab))
        self.tabs.currentChanged.connect(self.tabChanged)

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

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

    def createAlphaBox(self):
        """Create Alpha QWidget."""
        self.alphaBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("AlphaTL"))
        layout = QHBoxLayout()

        self.cb_trans_banner = QCheckBox(
            " " + _("Download transparent Banner of the Match"))
        self.cb_trans_banner.setChecked(
            scctool.settings.config.parser.getboolean(
                "SCT", "transparent_match_banner"))
        self.cb_trans_banner.stateChanged.connect(self.changed)

        layout.addWidget(self.cb_trans_banner)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Set Ingame Score Task"))
        layout = QVBoxLayout()

        self.cb_ctrlx = QCheckBox(" " +
                                  _('Automatically press Ctrl+X to apply the'
                                    ' correct player order ingame'))
        self.cb_ctrlx.setToolTip(
            _("This will ensure that the player of the first team is always"
              " on the left/top in the ingame Observer UI."))
        self.cb_ctrlx.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlX"))
        self.cb_ctrlx.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlx)

        self.cb_ctrln = QCheckBox(" " + _('Automatically press Ctrl+N before'
                                          ' OCR to display player names'))
        self.cb_ctrln.setToolTip(
            _("This is recommended for Standard and Gawliq Observer UI."))
        self.cb_ctrln.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlN"))
        self.cb_ctrln.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrln)

        self.cb_ctrlshifts = QCheckBox(
            " " + _('Automatically press Ctrl+Shift+S to display'
                    ' the ingame score'))
        self.cb_ctrlshifts.setToolTip(
            _("Ctrl+Shift+S is needed for the WCS-Gameheart Oberserver"
              " Overlay, but disables the sound for other overlays."))
        self.cb_ctrlshifts.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlShiftS"))
        self.cb_ctrlshifts.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlshifts)

        self.cb_ctrlshiftc = QCheckBox(
            " " + _('Automatically press Ctrl+Shift+C to toogle the clan tag'))
        self.cb_ctrlshiftc.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlShiftC"))
        self.cb_ctrlshiftc.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlshiftc)

        container = QHBoxLayout()
        self.cb_ctrlshiftr = QComboBox()
        self.cb_ctrlshiftr.addItem("0")
        self.cb_ctrlshiftr.addItem("1")
        self.cb_ctrlshiftr.addItem("2")
        try:
            self.cb_ctrlshiftr.setCurrentIndex(
                scctool.settings.config.parser.getint("SCT", "CtrlShiftR"))
        except Exception:
            self.cb_ctrlshiftr.setCurrentIndex(0)
        self.cb_ctrlshiftr.setMaximumWidth(40)
        self.cb_ctrlshiftr.currentIndexChanged.connect(self.changed)
        container.addWidget(
            QLabel(
                _('Automatically press Ctrl+Shift+R to toogle the race icon '))
        )
        container.addWidget(self.cb_ctrlshiftr)
        container.addWidget(QLabel(_(' time(s)')))
        layout.addLayout(container)

        self.cb_blacklist = QCheckBox(" " + _('Activate Blacklist for'
                                              ' Ingame Score'))
        self.cb_blacklist.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "blacklist_on"))
        self.cb_blacklist.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_blacklist)

        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Blacklist for Ingame Score"))
        layout = QVBoxLayout()

        blacklistDesc = _("Enter your SC2 client usernames to deactivate"
                          " automatically setting the ingame score and"
                          " toogling the production tab when you are playing"
                          " yourself. Replays are exempt.")
        label = QLabel(blacklistDesc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)
        layout.addWidget(label)

        self.list_blacklist = ListTable(4,
                                        scctool.settings.config.getBlacklist())
        self.list_blacklist.dataModified.connect(self.changed)
        self.list_blacklist.setFixedHeight(50)
        layout.addWidget(self.list_blacklist)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))

        self.alphaBox.setLayout(mainLayout)

    def createFavBox(self):
        """Create favorites box."""
        self.favBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Players"))
        layout = QHBoxLayout()

        self.list_favPlayers = ListTable(
            4, scctool.settings.config.getMyPlayers())
        self.list_favPlayers.dataModified.connect(self.changed)
        self.list_favPlayers.setFixedHeight(150)
        layout.addWidget(self.list_favPlayers)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Teams"))
        layout = QVBoxLayout()

        self.list_favTeams = ListTable(3, scctool.settings.config.getMyTeams())
        self.list_favTeams.dataModified.connect(self.changed)
        self.list_favTeams.setFixedHeight(100)
        layout.addWidget(self.list_favTeams)
        self.cb_swapTeams = QCheckBox(
            _('Swap my favorite team always to the left'))
        self.cb_swapTeams.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "swap_myteam"))
        self.cb_swapTeams.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_swapTeams)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))

        self.favBox.setLayout(mainLayout)

    def createAliasBox(self):
        """Create favorites box."""
        self.aliasBox = QWidget()
        mainLayout = QGridLayout()

        aliasDesc = _(
            'Player, team, and league aliases are replaced by the actual name when'
            + ' encountered by the match grabber. Additionally, SC2 player' +
            ' names listed as aliases are replaced in the intros' +
            ' and used to identify players by the automatic' +
            ' background tasks "Auto Score Update" and "Set Ingame Score".')
        label = QLabel(aliasDesc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)

        mainLayout.addWidget(label, 1, 0, 1, 3)

        box = QGroupBox(_("Player Aliases"))
        layout = QVBoxLayout()
        self.list_aliasPlayers = AliasTreeView(self)
        self.list_aliasPlayers.aliasRemoved.connect(
            self.controller.aliasManager.removePlayerAlias)
        layout.addWidget(self.list_aliasPlayers)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasPlayers, _('Player Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 0)

        box = QGroupBox(_("Team Aliases"))
        layout = QVBoxLayout()
        self.list_aliasTeams = AliasTreeView(self)
        self.list_aliasTeams.aliasRemoved.connect(
            self.controller.aliasManager.removeTeamAlias)
        layout.addWidget(self.list_aliasTeams)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasTeams, _('Team Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 1)

        box = QGroupBox(_("League Aliases"))
        layout = QVBoxLayout()
        self.list_aliasLeagues = AliasTreeView(self)
        self.list_aliasLeagues.aliasRemoved.connect(
            self.controller.aliasManager.removeLeagueAlias)
        layout.addWidget(self.list_aliasLeagues)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasLeagues, _('League Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 2)

        alias_list = self.controller.aliasManager.playerAliasList()
        for player, aliases in alias_list.items():
            self.list_aliasPlayers.insertAliasList(player, aliases)

        alias_list = self.controller.aliasManager.teamAliasList()
        for league, aliases in alias_list.items():
            self.list_aliasTeams.insertAliasList(league, aliases)

        alias_list = self.controller.aliasManager.leagueAliasList()
        for league, aliases in alias_list.items():
            self.list_aliasLeagues.insertAliasList(league, aliases)

        self.aliasBox.setLayout(mainLayout)

    def addAlias(self, widget, scope, name=""):
        """Add an alias."""
        name, ok = QInputDialog.getText(self, scope, scope + ':', text=name)
        if not ok:
            return

        name = name.strip()
        alias, ok = QInputDialog.getText(self,
                                         _('Alias'),
                                         _('Alias of {}').format(name) + ':',
                                         text="")

        alias = alias.strip()
        if not ok:
            return

        try:
            if widget == self.list_aliasPlayers:
                self.controller.aliasManager.addPlayerAlias(name, alias)
            elif widget == self.list_aliasTeams:
                self.controller.aliasManager.addTeamAlias(name, alias)
            elif widget == self.list_aliasLeagues:
                self.controller.aliasManager.addLeagueAlias(name, alias)
            widget.insertAlias(name, alias, True)
        except Exception as e:
            module_logger.exception("message")
            QMessageBox.critical(self, _("Error"), str(e))

    def createSC2ClientAPIBox(self):
        """Create form for SC2 Client API config."""
        self.clientapiBox = QWidget()

        mainLayout = QVBoxLayout()

        box = QGroupBox(_("SC2 Client API Address"))

        layout = QGridLayout()

        self.cb_usesc2listener = QCheckBox(
            " " + _("Listen to SC2 Client API running"
                    " on a different PC in the network."))
        self.cb_usesc2listener.setChecked(
            scctool.settings.config.parser.getboolean(
                "SCT", "sc2_network_listener_enabled"))
        self.cb_usesc2listener.stateChanged.connect(self.changed)

        self.listener_address = MonitoredLineEdit()
        self.listener_address.setAlignment(Qt.AlignCenter)
        self.listener_address.setText(
            scctool.settings.config.parser.get("SCT",
                                               "sc2_network_listener_address"))
        self.listener_address.textModified.connect(self.changed)
        # self.tesseract.setAlignment(Qt.AlignCenter)
        self.listener_address.setPlaceholderText("[Your SC2 PC IP]:6119")
        self.listener_address.setToolTip(
            _('IP address and port of machine running SC2.'))
        ip_port = (
            r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)" +
            r"{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):[0-9]+$")
        self.listener_address.setValidator(QRegExpValidator(QRegExp(ip_port)))

        self.test_listener = QPushButton(" " +
                                         _("Test SC2 Client API Connection") +
                                         " ")
        self.test_listener.clicked.connect(self.testClientAPI)

        text = _("Activate this option if you are using a two computer "
                 "setup with StarCraft Casting Tool running on a different"
                 " PC than your SC2 client. Open the Battle.net launcher "
                 "on the latter PC, click 'Options', 'Game Settings', and "
                 "under SC2, check 'Additional Command Line Arguments', and "
                 "enter '-clientapi 6119'. Finally set as network"
                 " address below: '[Your SC2 PC IP]:6119'.")

        label = QLabel(text)
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addWidget(label, 1, 0, 1, 3)

        layout.addWidget(self.cb_usesc2listener, 0, 0, 1, 3)
        layout.addWidget(QLabel(_("Network Address") + ": "), 3, 0)
        layout.addWidget(self.listener_address, 3, 1)
        layout.addWidget(self.test_listener, 3, 2)

        box.setLayout(layout)
        mainLayout.addWidget(box)
        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.clientapiBox.setLayout(mainLayout)

    def testClientAPI(self):
        """Test for connection to sc2 client api."""
        QApplication.setOverrideCursor(Qt.WaitCursor)
        address = self.listener_address.text().strip()
        url = "http://{}/ui".format(address)
        try:
            r = requests.get(url, timeout=10)
            r.raise_for_status()
            successfull = True
        except Exception:
            successfull = False
            module_logger.error("message")
        finally:
            QApplication.restoreOverrideCursor()

        title = _("Connection Test")

        if successfull:
            QMessageBox.information(
                self, title, _('Connection to SC2 client API established!'))
        else:
            QMessageBox.warning(
                self, title,
                _('Unable to connect to SC2 client API.'
                  ' Please make sure that SC2 is currently'
                  ' running on that machine.'))

    def createOcrBox(self):
        """Create forms for OCR."""
        self.ocrBox = QWidget()

        mainLayout = QVBoxLayout()

        box = QGroupBox(
            _("Optical Character Recognition for"
              " Automatic Setting of Ingame Score"))

        layout = QGridLayout()

        self.cb_useocr = QCheckBox(" " +
                                   _("Activate Optical Character Recognition"))
        self.cb_useocr.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "use_ocr"))
        self.cb_useocr.stateChanged.connect(self.changed)

        self.tesseract = MonitoredLineEdit()
        self.tesseract.setText(
            scctool.settings.config.parser.get("SCT", "tesseract"))
        self.tesseract.textModified.connect(self.changed)
        # self.tesseract.setAlignment(Qt.AlignCenter)
        self.tesseract.setPlaceholderText(
            "C:\\Program Files (x86)\\Tesseract-OCR\\tesseract")
        self.tesseract.setReadOnly(True)
        self.tesseract.setToolTip(_('Tesseract-OCR Executable'))

        self.browse = QPushButton(_("Browse..."))
        self.browse.clicked.connect(self.selectTesseract)

        text = _(
            "Sometimes the order of players given by the SC2-Client-API"
            " differs from the order in the Observer-UI resulting in a"
            " swapped match score. To correct this via Optical Character"
            " Recognition you have to download {} and install and select the"
            " exectuable below, if it is not detected automatically.")
        url = 'https://github.com/UB-Mannheim/tesseract' + \
            '/wiki#tesseract-at-ub-mannheim'
        href = "<a href='{}'>" + "Tesseract-OCR" + "</a>"
        href = href.format(url)

        label = QLabel(text.format(href))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addWidget(label, 1, 0, 1, 2)

        layout.addWidget(self.cb_useocr, 0, 0, 1, 2)
        layout.addWidget(QLabel(_("Tesseract-OCR Executable") + ":"), 2, 0)
        layout.addWidget(self.tesseract, 3, 0)
        layout.addWidget(self.browse, 3, 1)

        box.setLayout(layout)
        mainLayout.addWidget(box)
        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.ocrBox.setLayout(mainLayout)

        if (not scctool.settings.windows):
            self.cb_useocr.setEnabled(False)
            self.cb_useocr.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.cb_useocr.setToolTip(
                _("This feature is only available in Windows."))
            self.tesseract.setEnabled(False)
            self.tesseract.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.tesseract.setToolTip(
                _("This feature is only available in Windows."))
            self.browse.setEnabled(False)
            self.browse.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.browse.setToolTip(
                _("This feature is only available in Windows."))

    def selectTesseract(self):
        """Create forms for tesseract."""
        old_exe = self.tesseract.text()
        default = scctool.settings.config.findTesserAct(old_exe)
        exe, ok = QFileDialog.getOpenFileName(
            self, _("Select Tesseract-OCR Executable"), default,
            _("Tesseract-OCR Executable") + " (tesseract.exe);; " +
            _("Executable") + " (*.exe);; " + _("All files") + " (*)")
        if (ok and exe != old_exe):
            self.tesseract.setText(exe)
            self.changed()

    def createAligulacTab(self):
        """Create the aligulac tab."""
        self.aligulacTab = QWidget()

        layout = QGridLayout()
        self.aligulacTreeview = AligulacTreeView(
            self, self.controller.aligulacManager)

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

        self.pb_addAligulacID = QPushButton(_("Add Aligluac ID"))
        self.pb_addAligulacID.clicked.connect(
            lambda x, self=self: self.addAligulacID())
        layout.addWidget(self.pb_addAligulacID, 1, 1)

        self.pb_removeAligulacID = QPushButton(_("Remove Aligulac ID"))
        self.pb_removeAligulacID.clicked.connect(self.removeAligulacID)
        layout.addWidget(self.pb_removeAligulacID, 2, 1)

        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), 0, 1)

        self.aligulacTab.setLayout(layout)

    def addAligulacID(self, name='', aligulac_id=1):
        """Add an aligulac ID."""
        text, ok = QInputDialog.getText(self,
                                        _('Player Name'),
                                        _('Player Name') + ':',
                                        text=name)
        text = text.strip()
        if not ok or not text:
            return
        aligulac_id, ok = QInputDialog.getInt(self,
                                              _('Aligulac ID'),
                                              _('Aligulac ID') + ':',
                                              value=aligulac_id,
                                              min=1)
        if not ok:
            return

        self.aligulacTreeview.insertItem(text, aligulac_id)

    def removeAligulacID(self):
        """Remove an selected aligulac ID."""
        self.aligulacTreeview.removeSelected()

    def createCounterTab(self):
        """Create the aligulac tab."""
        self.counterTab = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Countdown"))
        layout = QFormLayout()
        self.le_countdown_replacement = QLineEdit()
        self.le_countdown_replacement.setText(
            scctool.settings.config.parser.get("Countdown", "replacement"))
        self.le_countdown_replacement.textChanged.connect(self.changed)
        layout.addRow(QLabel(_('Replacement Text')),
                      self.le_countdown_replacement)
        self.cb_counter_matchgrabber_update = QCheckBox('')
        self.cb_counter_matchgrabber_update.setChecked(
            scctool.settings.config.parser.getboolean("Countdown",
                                                      "matchgrabber_update"))
        self.cb_counter_matchgrabber_update.stateChanged.connect(self.changed)
        layout.addRow(QLabel(_('Update Static Countdown via MatchGrabber')),
                      self.cb_counter_matchgrabber_update)
        self.counter_pretext = QPlainTextEdit()
        self.counter_pretext.setPlainText(
            scctool.settings.config.parser.get("Countdown", "pre_txt"))
        self.counter_pretext.textChanged.connect(self.changed)
        self.counter_posttext = QPlainTextEdit()
        self.counter_posttext.setPlainText(
            scctool.settings.config.parser.get("Countdown", "post_txt"))
        self.counter_posttext.textChanged.connect(self.changed)
        layout.addRow(QLabel(_('Pre-Text (in countdown.txt)')),
                      self.counter_pretext)
        layout.addRow(QLabel(_('Post-Text (in countdown.txt)')),
                      self.counter_posttext)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        box = QGroupBox(_("Ticker"))
        layout = QFormLayout()
        box.setLayout(layout)
        self.ticker_pretext = QLineEdit()
        self.ticker_pretext.setText(
            scctool.settings.config.parser.get("Ticker", "prefix"))
        self.ticker_pretext.textChanged.connect(self.changed)
        layout.addRow(QLabel(_('Prefix text (in ticker.txt)')),
                      self.ticker_pretext)
        mainLayout.addWidget(box)

        self.counterTab.setLayout(mainLayout)

    def createMapsBox(self):
        """Create box for map manager."""
        self.mapsize = 300

        self.mapsBox = QWidget()

        layout = QGridLayout()

        self.maplist = QListWidget()
        self.maplist.setSortingEnabled(True)
        for sc2map in scctool.settings.maps:
            self.maplist.addItem(QListWidgetItem(sc2map))
        self.maplist.setCurrentItem(self.maplist.item(0))
        self.maplist.currentItemChanged.connect(self.changePreview)
        # self.maplist.setFixedHeight(self.mapsize)
        self.maplist.setMinimumWidth(150)

        layout.addWidget(self.maplist, 0, 1, 2, 1)
        self.mapPreview = QLabel()
        self.mapPreview.setFixedWidth(self.mapsize)
        self.mapPreview.setFixedHeight(self.mapsize)
        self.mapPreview.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.mapPreview, 0, 0)
        self.mapInfo = QLabel()
        self.mapInfo.setIndent(10)
        layout.addWidget(self.mapInfo, 1, 0)

        self.pb_addMapLiquipedia = QPushButton(_("Add from Liquipedia"))
        self.pb_addMapLiquipedia.clicked.connect(self.addFromLquipedia)
        self.pb_addMap = QPushButton(_("Add from File"))
        self.pb_addMap.clicked.connect(self.addMap)
        self.pb_renameMap = QPushButton(_("Rename"))
        self.pb_renameMap.clicked.connect(self.renameMap)
        self.pb_changeMap = QPushButton(_("Change Image"))
        self.pb_changeMap.clicked.connect(self.changeMap)
        self.pb_removeMap = QPushButton(_("Remove"))
        self.pb_removeMap.clicked.connect(self.deleteMap)

        self.sc_removeMap = QShortcut(QKeySequence("Del"), self.maplist)
        self.sc_removeMap.setAutoRepeat(False)
        self.sc_removeMap.setContext(Qt.WidgetWithChildrenShortcut)
        self.sc_removeMap.activated.connect(self.deleteMap)

        self.cb_newMapsPrompt = QCheckBox(
            _('Prompt to download new ladders maps.'))
        self.cb_newMapsPrompt.setChecked(
            scctool.settings.config.parser.getboolean("SCT",
                                                      "new_maps_prompt"))
        self.cb_newMapsPrompt.stateChanged.connect(self.changed)

        self.pb_downloadLadderMaps = QPushButton(_("Download Ladder Maps"))
        self.pb_downloadLadderMaps.clicked.connect(self.downloadLadderMaps)

        box = QWidget()
        container = QHBoxLayout()

        container.addWidget(self.pb_addMapLiquipedia, 0)
        container.addWidget(self.pb_addMap, 0)
        container.addWidget(QLabel(), 1)
        container.addWidget(self.pb_downloadLadderMaps, 0)
        container.addWidget(QLabel(), 1)
        container.addWidget(self.pb_renameMap, 0)
        container.addWidget(self.pb_changeMap, 0)
        container.addWidget(self.pb_removeMap, 0)
        box.setLayout(container)

        layout.addWidget(box, 2, 0, 1, 2)

        layout.addWidget(self.cb_newMapsPrompt, 3, 0, 1, 1)
        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 3,
            2, 1, 2)

        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 4,
            0, 1, 2)

        self.changePreview()
        self.mapsBox.setLayout(layout)

    def renameMap(self):
        """Rename maps."""
        item = self.maplist.currentItem()
        mapname = item.text()
        text, ok = QInputDialog.getText(self,
                                        _('Map Name'),
                                        _('Map Name') + ':',
                                        text=mapname)
        if not ok:
            return
        text = text.strip()
        if (text == mapname):
            return
        if text.lower() == 'tbd':
            QMessageBox.critical(
                self, _("Error"),
                _('"{}" is not a valid map name.').format(text))
            return
        if (text in scctool.settings.maps):
            buttonReply = QMessageBox.warning(
                self, _("Duplicate Entry"),
                _("Map is already in list! Overwrite?"),
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if buttonReply == QMessageBox.No:
                return

        self.controller.addMap(self.controller.getMapImg(mapname, True), text)
        self.controller.deleteMap(mapname)
        item.setText(text)

    def changeMap(self):
        """Change a map."""
        current_map = self.maplist.currentItem().text()
        fileName, ok = QFileDialog.getOpenFileName(
            self, _("Select Map Image (> 500x500px recommended)"), "",
            _("Supported Images") + " (*.png *.jpg *.jpeg)")
        if ok:
            base = os.path.basename(fileName)
            name, __ = os.path.splitext(base)
            name = name.replace("_", " ")
            self.controller.deleteMap(current_map)
            self.controller.addMap(fileName, current_map)
            self.changePreview()

    def addMap(self):
        """Add a map."""
        fileName, ok = QFileDialog.getOpenFileName(
            self, _("Select Map Image (> 500x500px recommended)"), "",
            _("Supported Images") + " (*.png *.jpg  *.jpeg)")
        if ok:
            base = os.path.basename(fileName)
            name, __ = os.path.splitext(base)
            name = name.replace("_", " ")
            map_name, ok = QInputDialog.getText(self,
                                                _('Map Name'),
                                                _('Map Name') + ':',
                                                text=name)
            map_name = map_name.strip()
            if ok:
                if map_name.lower() == 'tbd':
                    QMessageBox.critical(
                        self, _("Error"),
                        _('"{}" is not a valid map name.').format(map_name))
                    return

                if (map_name in scctool.settings.maps):
                    buttonReply = QMessageBox.warning(
                        self, _("Duplicate Entry"),
                        _("Map is already in list! Overwrite?"),
                        QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                    if buttonReply == QMessageBox.No:
                        return
                    else:
                        self.controller.deleteMap(map_name)

                self.controller.addMap(fileName, map_name)
                items = self.maplist.findItems(map_name, Qt.MatchExactly)
                if len(items) == 0:
                    item = QListWidgetItem(map_name)
                    self.maplist.addItem(item)
                    self.maplist.setCurrentItem(item)
                else:
                    self.maplist.setCurrentItem(items[0])
                self.changePreview()

    def downloadLadderMaps(self):
        players_per_team, ok = QInputDialog.getItem(
            self,
            _('Select the type of ladder maps to download'),
            _('Please select a map type') + ':',
            ['1vs1', '2vs2', '3vs3', '4vs4'],
            editable=False)
        players_per_team = int(players_per_team[0])
        found_a_map = False
        for sc2map in LiquipediaGrabber().get_ladder_mappool(players_per_team):
            if not sc2map in scctool.settings.maps:
                found_a_map = True
                self.controller.autoDownloadMap(sc2map, self)
                scctool.settings.maps.append(sc2map)
                items = self.maplist.findItems(sc2map, Qt.MatchExactly)
                if len(items) == 0:
                    item = QListWidgetItem(sc2map)
                    self.maplist.addItem(item)
                    self.maplist.setCurrentItem(item)
                else:
                    self.maplist.setCurrentItem(items[0])
                self.changePreview()
        if not found_a_map:
            QMessageBox.information(
                self, _("No missing map"),
                _('All of the current ladder maps are already present.'))

    def addFromLquipedia(self):
        """Add a map from Liquipedia."""
        grabber = LiquipediaGrabber()
        search_str = ''
        while True:
            search_str, ok = QInputDialog.getText(self,
                                                  _('Map Name'),
                                                  _('Map Name') + ':',
                                                  text=search_str)
            search_str.strip()
            try:
                if ok and search_str:
                    if search_str.lower() == 'tbd':
                        QMessageBox.critical(
                            self, _("Error"),
                            _('"{}" is not a valid map name.').format(
                                search_str))
                        continue
                    try:
                        QApplication.setOverrideCursor(Qt.WaitCursor)
                        sc2map = grabber.get_map(search_str)
                    except MapNotFound:
                        QMessageBox.critical(
                            self, _("Map not found"),
                            _('"{}" was not found on Liquipedia.').format(
                                search_str))
                        continue
                    finally:
                        QApplication.restoreOverrideCursor()
                    map_name = sc2map.get_name()

                    if (map_name in scctool.settings.maps):
                        buttonReply = QMessageBox.warning(
                            self, _("Duplicate Entry"),
                            _("Map {} is already in list! Overwrite?".format(
                                map_name)), QMessageBox.Yes | QMessageBox.No,
                            QMessageBox.No)
                        if buttonReply == QMessageBox.No:
                            break
                        else:
                            self.controller.deleteMap(map_name)

                    try:
                        QApplication.setOverrideCursor(Qt.WaitCursor)
                        images = grabber.get_images(sc2map.get_map_images())
                        image = ""
                        for size in sorted(images):
                            if not image or size <= 2500 * 2500:
                                image = images[size]
                        url = grabber._base_url + image

                        downloader = MapDownloader(self, map_name, url)
                        downloader.download()
                        if map_name not in scctool.settings.maps:
                            scctool.settings.maps.append(map_name)
                        items = self.maplist.findItems(map_name,
                                                       Qt.MatchExactly)
                        if len(items) == 0:
                            item = QListWidgetItem(map_name)
                            self.maplist.addItem(item)
                            self.maplist.setCurrentItem(item)
                        else:
                            self.maplist.setCurrentItem(items[0])
                        self.changePreview()
                    except Exception:
                        raise
                    finally:
                        QApplication.restoreOverrideCursor()
            except Exception as e:
                module_logger.exception("message")
                QMessageBox.critical(self, _("Error"), str(e))
            break

    def deleteMap(self):
        """Delete a map."""
        item = self.maplist.currentItem()
        mapname = item.text()
        buttonReply = QMessageBox.question(
            self, _('Delete map?'),
            _("Delete '{}' permanently?").format(mapname),
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            self.controller.deleteMap(mapname)
            self.maplist.takeItem(self.maplist.currentRow())

    def changePreview(self):
        """Change the map preview."""
        if self.maplist.count() < 1:
            return

        mapname = self.maplist.currentItem().text()
        if (mapname == "TBD"):
            self.pb_renameMap.setEnabled(False)
            self.pb_removeMap.setEnabled(False)
            self.sc_removeMap.setEnabled(False)
        else:
            self.pb_removeMap.setEnabled(True)
            self.pb_renameMap.setEnabled(True)
            self.sc_removeMap.setEnabled(True)

        file = self.controller.getMapImg(mapname, True)
        pixmap = QPixmap(file)
        height = pixmap.height()
        width = pixmap.width()
        ext = os.path.splitext(file)[1].replace(".", "").upper()
        size = humanize.naturalsize(os.path.getsize(file))
        pixmap = QPixmap(file).scaled(self.mapsize, self.mapsize,
                                      Qt.KeepAspectRatio)
        self.mapPreview.setPixmap(pixmap)
        text = f"{width}x{height}px, {size}, {ext}"
        self.mapInfo.setText(text)

    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 saveData(self):
        """Save the data."""
        if (self.__dataChanged):
            scctool.settings.config.parser.set(
                "SCT", "myteams", ", ".join(self.list_favTeams.getData()))
            scctool.settings.config.parser.set(
                "SCT", "commonplayers",
                ", ".join(self.list_favPlayers.getData()))
            scctool.settings.config.parser.set("SCT", "tesseract",
                                               self.tesseract.text().strip())
            scctool.settings.config.parser.set("SCT", "use_ocr",
                                               str(self.cb_useocr.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "new_maps_prompt",
                str(self.cb_newMapsPrompt.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "transparent_match_banner",
                str(self.cb_trans_banner.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftS", str(self.cb_ctrlshifts.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftC", str(self.cb_ctrlshiftc.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "swap_myteam", str(self.cb_swapTeams.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlN",
                                               str(self.cb_ctrln.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlX",
                                               str(self.cb_ctrlx.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftR", str(self.cb_ctrlshiftr.currentText()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist_on", str(self.cb_blacklist.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist", ", ".join(self.list_blacklist.getData()))
            scctool.settings.config.parser.set(
                "SCT", "sc2_network_listener_address",
                self.listener_address.text().strip())
            scctool.settings.config.parser.set(
                "SCT", "sc2_network_listener_enabled",
                str(self.cb_usesc2listener.isChecked()))
            scctool.settings.config.parser.set(
                "Countdown", "matchgrabber_update",
                str(self.cb_counter_matchgrabber_update.isChecked()))
            scctool.settings.config.parser.set(
                "Countdown", "replacement",
                self.le_countdown_replacement.text())
            scctool.settings.config.parser.set(
                "Countdown", "pre_txt", self.counter_pretext.toPlainText())
            scctool.settings.config.parser.set(
                "Countdown", "post_txt", self.counter_posttext.toPlainText())
            scctool.settings.config.parser.set(
                "Ticker", "prefix",
                self.ticker_pretext.text().strip())
            self.controller.matchControl.tickerChanged.emit()
            self.controller.refreshButtonStatus()
            # self.controller.setCBS()
            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:
            self.mainWindow.updateAllMapCompleters()
            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")
Пример #11
0
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")
Пример #12
0
class SubwindowStyles(QWidget):
    """Show styles settings sub window."""
    def createWindow(self, mainWindow):
        """Create styles settings sub window."""
        try:
            parent = None
            super().__init__(parent)

            self.setWindowIcon(
                QIcon(hwctool.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"))

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

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

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

        try:
            container = QHBoxLayout()
            self.qb_scoreStyle = StyleComboBox(
                hwctool.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(
                hwctool.settings.casting_html_dir + "/score.html"))
            container.addWidget(self.qb_scoreStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Score:")), container)
        except Exception as e:
            module_logger.exception("message")

        try:
            container = QHBoxLayout()
            self.qb_introStyle = StyleComboBox(
                hwctool.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(
                hwctool.settings.casting_html_dir + "/intro.html"))
            container.addWidget(self.qb_introStyle)
            container.addWidget(button)
            layout.addRow(QLabel(_("Intros:")), container)
        except Exception as e:
            module_logger.exception("message")

        self.styleBox.setLayout(layout)

    def openHTML(self, file):
        """Open file in browser."""
        self.controller.openURL(hwctool.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.win_color = ColorLayout(
            self, _("Win:"),
            hwctool.settings.config.parser.get("MapIcons", "win_color"),
            "#008000")
        layout.addLayout(self.win_color)
        self.lose_color = ColorLayout(
            self, _("Lose:"),
            hwctool.settings.config.parser.get("MapIcons", "lose_color"),
            "#f22200")
        layout.addLayout(self.lose_color)
        self.undecided_color = ColorLayout(
            self, _("Undecided:"),
            hwctool.settings.config.parser.get("MapIcons", "undecided_color"),
            "#aaaaaa")
        layout.addLayout(self.undecided_color)
        self.notplayed_color = ColorLayout(
            self, _("Not played:"),
            hwctool.settings.config.parser.get("MapIcons", "notplayed_color"),
            "#aaaaaa")
        layout.addLayout(self.notplayed_color)
        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(
            hwctool.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 = hwctool.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):
        font = self.cb_font.currentText().strip()
        self.previewer.setFont(font)

    def saveData(self):
        """Save data."""
        if (self.__dataChanged):
            hwctool.settings.config.parser.set("MapIcons", "undecided_color",
                                               self.undecided_color.getColor())
            hwctool.settings.config.parser.set("MapIcons", "win_color",
                                               self.win_color.getColor())
            hwctool.settings.config.parser.set("MapIcons", "lose_color",
                                               self.lose_color.getColor())
            hwctool.settings.config.parser.set("MapIcons", "notplayed_color",
                                               self.notplayed_color.getColor())

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

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

            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 as e:
            module_logger.exception("message")
Пример #13
0
class ListTable(QTableWidget):
    """Define a custom table list."""

    dataModified = pyqtSignal()

    def __init__(self, noColumns=1, data=[]):
        """Init table list."""
        super().__init__()

        data = self.__processData(data)
        self.__noColumns = noColumns

        self.setCornerButtonEnabled(False)
        self.horizontalHeader().hide()
        self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.verticalHeader().hide()
        self.verticalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)
        self.setData(data)
        self.shortcut = QShortcut(QKeySequence("DEL"), self)
        self.shortcut.setAutoRepeat(False)
        self.shortcut.setContext(Qt.WidgetWithChildrenShortcut)
        self.shortcut.activated.connect(self.delClicked)

    def __handleDataChanged(self, item):
        self.setData(self.getData(), item)
        self.dataModified.emit()

    def __processData(self, data):
        seen = set()
        uniq = [x for x in data if x not in seen and not seen.add(x)]
        uniq.sort()
        return uniq

    def delClicked(self):
        item = self.currentItem()
        if item and item.isSelected() and item.text().strip():
            item.setText("")

    def setData(self, data, item=""):
        """Set the data."""
        try:
            self.itemChanged.disconnect()
        except Exception:
            pass

        if item:
            item = item.text()

        self.setColumnCount(self.__noColumns)
        self.setRowCount(int(len(data) / self.__noColumns) + 1)
        for idx, entry in enumerate(data):
            row, column = divmod(idx, self.__noColumns)
            self.setItem(row, column, QTableWidgetItem(entry))
            if entry == item:
                self.setCurrentCell(row, column)

        row = int(len(data) / self.__noColumns)
        for col in range(len(data) % self.__noColumns, self.__noColumns):
            self.setItem(row, col, QTableWidgetItem(""))

        row = int(len(data) / self.__noColumns) + 1
        for col in range(self.__noColumns):
            self.setItem(row, col, QTableWidgetItem(""))

        self.itemChanged.connect(self.__handleDataChanged)

    def getData(self):
        """Get the data."""
        data = []
        for row in range(self.rowCount()):
            for col in range(self.columnCount()):
                try:
                    element = self.item(row, col).text().strip()
                    if (element == ""):
                        continue
                    data.append(element)
                except Exception:
                    pass
        return self.__processData(data)
Пример #14
0
class ControlWidget(GroupBoxWidget):
    # noinspection PyArgumentList,PyUnresolvedReferences
    def __init__(self, parent: QWidget, commander: Commander):
        super(ControlWidget, self).__init__(parent, 'Controls', 'adjust')

        self._commander: Commander = commander

        self._last_seen_timestamped_general_status: typing.Optional[
            typing.Tuple[float, GeneralStatusView]] = None

        self._run_widget = RunControlWidget(self, commander)
        self._motor_identification_widget = MotorIdentificationControlWidget(
            self, commander)
        self._hardware_test_widget = HardwareTestControlWidget(self, commander)
        self._misc_widget = MiscControlWidget(self, commander)
        self._low_level_manipulation_widget = LowLevelManipulationControlWidget(
            self, commander)

        self._panel = QTabWidget(self)

        self._panel.addTab(self._run_widget, get_icon('running'), 'Run')
        self._panel.addTab(self._motor_identification_widget,
                           get_icon('caliper'), 'Motor identification')
        self._panel.addTab(self._hardware_test_widget, get_icon('pass-fail'),
                           'Self-test')
        self._panel.addTab(self._misc_widget, get_icon('ellipsis'),
                           'Miscellaneous')
        self._panel.addTab(self._low_level_manipulation_widget,
                           get_icon('hand-button'), 'Low-level manipulation')

        self._current_widget: SpecializedControlWidgetBase = self._hardware_test_widget

        # Selecting which widget to show by default - let it be Run widget, it's easy to understand and it's first
        self._panel.setCurrentWidget(self._run_widget)

        # Shared buttons
        self._stop_button =\
            make_button(self,
                        text='Stop',
                        icon_name='stop',
                        tool_tip=f'Sends a regular stop command which instructs the controller to abandon the current'
                                 f'task and activate the Idle task [{STOP_SHORTCUT}]',
                        on_clicked=self._do_regular_stop)
        self._stop_button.setSizePolicy(QSizePolicy().MinimumExpanding,
                                        QSizePolicy().MinimumExpanding)

        self._emergency_button =\
            make_button(self,
                        text='EMERGENCY\nSHUTDOWN',
                        tool_tip=f'Unconditionally  disables and locks down the VSI until restarted '
                                 f'[{EMERGENCY_SHORTCUT}]',
                        on_clicked=self._do_emergency_stop)
        self._emergency_button.setSizePolicy(QSizePolicy().MinimumExpanding,
                                             QSizePolicy().MinimumExpanding)
        small_font = QFont()
        small_font.setPointSize(round(small_font.pointSize() * 0.8))
        self._emergency_button.setFont(small_font)

        # Observe that the shortcuts are children of the window! This is needed to make them global.
        self._stop_shortcut = QShortcut(QKeySequence(STOP_SHORTCUT),
                                        self.window())
        self._stop_shortcut.setAutoRepeat(False)

        self._emergency_shortcut = QShortcut(QKeySequence(EMERGENCY_SHORTCUT),
                                             self.window())
        self._emergency_shortcut.setAutoRepeat(False)

        self.setEnabled(False)

        # Layout
        def make_tiny_label(text: str, alignment: int) -> QLabel:
            lbl = QLabel(text, self)
            lbl.setAlignment(alignment | Qt.AlignHCenter)
            font: QFont = lbl.font()
            font.setPointSize(round(font.pointSize() * 0.7))
            lbl.setFont(font)
            return lbl

        self.setLayout(
            lay_out_horizontally(
                (self._panel, 1),
                lay_out_vertically(
                    (self._stop_button, 1),
                    make_tiny_label(f'\u2191 {STOP_SHORTCUT} \u2191',
                                    Qt.AlignTop),
                    make_tiny_label(f'\u2193 {EMERGENCY_SHORTCUT} \u2193',
                                    Qt.AlignBottom),
                    (self._emergency_button, 1),
                )))

        # Configuring the event handler in the last order, because it might fire while we're configuring the widgets!
        self._panel.currentChanged.connect(self._on_current_widget_changed)
        # Invoking the handler to complete initialization
        self._on_current_widget_changed(self._panel.currentIndex())

    def on_connection_established(self):
        self._last_seen_timestamped_general_status = None
        self._enable()
        self._current_widget.start()

    def on_connection_loss(self):
        self._last_seen_timestamped_general_status = None
        self._current_widget.stop()
        self._disable()

    def on_general_status_update(self, timestamp: float, s: GeneralStatusView):
        self._last_seen_timestamped_general_status = timestamp, s
        self._current_widget.on_general_status_update(timestamp, s)

    def _on_current_widget_changed(self, new_widget_index: int):
        _logger.debug(
            f'The user has changed the active widget. '
            f'Stopping the previous widget, which was {self._current_widget!r}'
        )
        self._current_widget.stop()

        self._current_widget = self._panel.currentWidget()
        assert isinstance(self._current_widget, SpecializedControlWidgetBase)

        _logger.debug(
            f'Starting the new widget (at index {new_widget_index}), which is {self._current_widget!r}'
        )
        self._current_widget.start()

        # We also make sure to always provide the newly activated widget with the latest known general status,
        # in order to let it actualize its state faster.
        if self._last_seen_timestamped_general_status is not None:
            self._current_widget.on_general_status_update(
                *self._last_seen_timestamped_general_status)

    # noinspection PyUnresolvedReferences
    def _enable(self):
        self._stop_shortcut.activated.connect(self._do_regular_stop)
        self._emergency_shortcut.activated.connect(self._do_emergency_stop)
        self.setEnabled(True)
        self._emergency_button.setStyleSheet('''QPushButton {
             background-color: #f00;
             font-weight: 600;
             color: #300;
        }''')

    # noinspection PyUnresolvedReferences
    def _disable(self):
        self._stop_shortcut.activated.disconnect(self._do_regular_stop)
        self._emergency_shortcut.activated.disconnect(self._do_emergency_stop)
        self.setEnabled(False)
        self._emergency_button.setStyleSheet('')

    def _do_regular_stop(self):
        self._launch(self._commander.stop())
        _logger.info('Stop button clicked (or shortcut activated)')
        self.window().statusBar().showMessage(
            'Stop command has been sent. '
            'The device may choose to disregard it, depending on the current task.'
        )
        self._current_widget.stop()

    def _do_emergency_stop(self):
        for _ in range(3):
            self._launch(self._commander.emergency())

        _logger.warning('Emergency button clicked (or shortcut activated)')
        self.window().statusBar().showMessage(
            "DON'T PANIC. The hardware will remain unusable until restarted.")

        self._current_widget.stop()

    @staticmethod
    def _launch(coro: typing.Awaitable[None]):
        asyncio.get_event_loop().create_task(coro)
Пример #15
0
class SubwindowMisc(QWidget):
    """Show subwindow with miscellaneous settings."""
    def createWindow(self, mainWindow, tab=''):
        """Create subwindow with miscellaneous settings."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

            self.setWindowIcon(
                QIcon(scctool.settings.getResFile('settings.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() * .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(_("Miscellaneous Settings"))

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

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

        self.createMapsBox()
        self.createFavBox()
        self.createAliasBox()
        self.createOcrBox()
        self.createAlphaBox()

        # Add tabs
        self.tabs.addTab(self.mapsBox, _("Map Manager"))
        self.tabs.addTab(self.favBox, _("Favorites"))
        self.tabs.addTab(self.aliasBox, _("Alias"))
        self.tabs.addTab(self.ocrBox, _("OCR"))
        self.tabs.addTab(self.alphaBox, _("AlphaTL && Ingame Score"))

        table = dict()
        table['mapmanager'] = 0
        table['favorites'] = 1
        table['alias'] = 2
        table['ocr'] = 3
        table['alphatl'] = 4
        self.tabs.setCurrentIndex(table.get(tab, -1))

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

    def createAlphaBox(self):
        """Create Alpha QWidget."""
        self.alphaBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("AlphaTL"))
        layout = QHBoxLayout()

        self.cb_trans_banner = QCheckBox(
            " " + _("Download transparent Banner of the Match"))
        self.cb_trans_banner.setChecked(
            scctool.settings.config.parser.getboolean(
                "SCT", "transparent_match_banner"))
        self.cb_trans_banner.stateChanged.connect(self.changed)

        layout.addWidget(self.cb_trans_banner)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Set Ingame Score Task"))
        layout = QVBoxLayout()

        self.cb_ctrlx = QCheckBox(" " +
                                  _('Automatically press Ctrl+X to apply the'
                                    ' correct player order ingame'))
        self.cb_ctrlx.setToolTip(
            _("This will ensure that the player of the first team is always"
              " on the left/top in the ingame Observer UI."))
        self.cb_ctrlx.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlX"))
        self.cb_ctrlx.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlx)

        self.cb_ctrln = QCheckBox(" " + _('Automatically press Ctrl+N before'
                                          ' OCR to display player names'))
        self.cb_ctrln.setToolTip(
            _("This is recommended for Standard and Gawliq Observer UI."))
        self.cb_ctrln.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlN"))
        self.cb_ctrln.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrln)

        self.cb_ctrlshifts = QCheckBox(
            " " + _('Automatically press Ctrl+Shift+S to display'
                    ' the ingame score'))
        self.cb_ctrlshifts.setToolTip(
            _("Ctrl+Shift+S is needed for the WCS-Gameheart Oberserver"
              " Overlay, but disables the sound for other overlays."))
        self.cb_ctrlshifts.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlShiftS"))
        self.cb_ctrlshifts.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlshifts)

        self.cb_ctrlshiftc = QCheckBox(
            " " + _('Automatically press Ctrl+Shift+C to toogle the clan tag'))
        self.cb_ctrlshiftc.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "CtrlShiftC"))
        self.cb_ctrlshiftc.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_ctrlshiftc)

        container = QHBoxLayout()
        self.cb_ctrlshiftr = QComboBox()
        self.cb_ctrlshiftr.addItem("0")
        self.cb_ctrlshiftr.addItem("1")
        self.cb_ctrlshiftr.addItem("2")
        try:
            self.cb_ctrlshiftr.setCurrentIndex(
                scctool.settings.config.parser.getint("SCT", "CtrlShiftR"))
        except Exception:
            self.cb_ctrlshiftr.setCurrentIndex(0)
        self.cb_ctrlshiftr.setMaximumWidth(40)
        self.cb_ctrlshiftr.currentIndexChanged.connect(self.changed)
        container.addWidget(
            QLabel(
                _('Automatically press Ctrl+Shift+R to toogle the race icon '))
        )
        container.addWidget(self.cb_ctrlshiftr)
        container.addWidget(QLabel(_(' time(s)')))
        layout.addLayout(container)

        self.cb_blacklist = QCheckBox(" " + _('Activate Blacklist for'
                                              ' Ingame Score'))
        self.cb_blacklist.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "blacklist_on"))
        self.cb_blacklist.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_blacklist)

        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Blacklist for Ingame Score"))
        layout = QVBoxLayout()

        blacklistDesc = _("Enter your SC2 client usernames to deactivate"
                          " automatically setting the ingame score and"
                          " toogling the production tab when you are playing"
                          " yourself. Replays are exempt.")
        label = QLabel(blacklistDesc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)
        layout.addWidget(label)

        self.list_blacklist = ListTable(4,
                                        scctool.settings.config.getBlacklist())
        self.list_blacklist.dataModified.connect(self.changed)
        self.list_blacklist.setFixedHeight(50)
        layout.addWidget(self.list_blacklist)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))

        self.alphaBox.setLayout(mainLayout)

    def createFavBox(self):
        """Create favorites box."""
        self.favBox = QWidget()
        mainLayout = QVBoxLayout()

        box = QGroupBox(_("Players"))
        layout = QHBoxLayout()

        self.list_favPlayers = ListTable(
            4, scctool.settings.config.getMyPlayers())
        self.list_favPlayers.dataModified.connect(self.changed)
        self.list_favPlayers.setFixedHeight(150)
        layout.addWidget(self.list_favPlayers)
        box.setLayout(layout)

        mainLayout.addWidget(box)

        box = QGroupBox(_("Teams"))
        layout = QVBoxLayout()

        self.list_favTeams = ListTable(3, scctool.settings.config.getMyTeams())
        self.list_favTeams.dataModified.connect(self.changed)
        self.list_favTeams.setFixedHeight(100)
        layout.addWidget(self.list_favTeams)
        self.cb_swapTeams = QCheckBox(
            _('Swap my favorite team always to the left'))
        self.cb_swapTeams.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "swap_myteam"))
        self.cb_swapTeams.stateChanged.connect(self.changed)
        layout.addWidget(self.cb_swapTeams)
        box.setLayout(layout)
        mainLayout.addWidget(box)

        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))

        self.favBox.setLayout(mainLayout)

    def createAliasBox(self):
        """Create favorites box."""
        self.aliasBox = QWidget()
        mainLayout = QGridLayout()

        aliasDesc = _(
            'Player and team aliases are replaced by the actual name when' +
            ' encountered by the match grabber. Additionally, SC2 player' +
            ' names listed as aliases are replaced in the intros' +
            ' and used to identify players by the automatic' +
            ' background tasks "Auto Score Update" and "Set Ingame Score".')
        label = QLabel(aliasDesc)
        label.setAlignment(Qt.AlignJustify)
        label.setWordWrap(True)

        mainLayout.addWidget(label, 1, 0, 1, 2)

        box = QGroupBox(_("Player Aliases"))
        layout = QVBoxLayout()
        self.list_aliasPlayers = AliasTreeView(self)
        self.list_aliasPlayers.aliasRemoved.connect(
            self.controller.aliasManager.removePlayerAlias)
        layout.addWidget(self.list_aliasPlayers)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasPlayers, _('Player Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 0)

        box = QGroupBox(_("Team Aliases"))
        layout = QVBoxLayout()
        self.list_aliasTeams = AliasTreeView(self)
        self.list_aliasTeams.aliasRemoved.connect(
            self.controller.aliasManager.removeTeamAlias)
        layout.addWidget(self.list_aliasTeams)
        addButton = QPushButton(_("Add Alias"))
        addButton.clicked.connect(
            lambda: self.addAlias(self.list_aliasTeams, _('Team Name')))
        layout.addWidget(addButton)
        box.setLayout(layout)
        mainLayout.addWidget(box, 0, 1)

        list = self.controller.aliasManager.playerAliasList()
        for player, aliases in list.items():
            self.list_aliasPlayers.insertAliasList(player, aliases)

        list = self.controller.aliasManager.teamAliasList()
        for team, aliases in list.items():
            self.list_aliasTeams.insertAliasList(team, aliases)

        self.aliasBox.setLayout(mainLayout)

    def addAlias(self, widget, scope, name=""):

        name, ok = QInputDialog.getText(self, scope, scope + ':', text=name)
        if not ok:
            return

        name = name.strip()
        alias, ok = QInputDialog.getText(self,
                                         _('Alias'),
                                         _('Alias of {}').format(name) + ':',
                                         text="")

        alias = alias.strip()
        if not ok:
            return

        try:
            if widget == self.list_aliasPlayers:
                self.controller.aliasManager.addPlayerAlias(name, alias)
            elif widget == self.list_aliasTeams:
                self.controller.aliasManager.addTeamAlias(name, alias)
            widget.insertAlias(name, alias, True)
        except Exception as e:
            module_logger.exception("message")
            QMessageBox.critical(self, _("Error"), str(e))

    def createOcrBox(self):
        """Create forms for OCR."""
        self.ocrBox = QWidget()

        mainLayout = QVBoxLayout()

        box = QGroupBox(
            _("Optical Character Recognition for"
              " Automatic Setting of Ingame Score"))

        layout = QGridLayout()

        self.cb_useocr = QCheckBox(" " +
                                   _("Activate Optical Character Recognition"))
        self.cb_useocr.setChecked(
            scctool.settings.config.parser.getboolean("SCT", "use_ocr"))
        self.cb_useocr.stateChanged.connect(self.changed)

        self.tesseract = MonitoredLineEdit()
        self.tesseract.setText(
            scctool.settings.config.parser.get("SCT", "tesseract"))
        self.tesseract.textModified.connect(self.changed)
        # self.tesseract.setAlignment(Qt.AlignCenter)
        self.tesseract.setPlaceholderText(
            "C:\\Program Files (x86)\\Tesseract-OCR\\tesseract")
        self.tesseract.setReadOnly(True)
        self.tesseract.setToolTip(_('Tesseract-OCR Executable'))

        self.browse = QPushButton(_("Browse..."))
        self.browse.clicked.connect(self.selectTesseract)

        text = _(
            "Sometimes the order of players given by the SC2-Client-API"
            " differs from the order in the Observer-UI resulting in a"
            " swapped match score. To correct this via Optical Character"
            " Recognition you have to download {} and install and select the"
            " exectuable below, if it is not detected automatically.")
        url = 'https://github.com/UB-Mannheim/tesseract' + \
            '/wiki#tesseract-at-ub-mannheim'
        href = "<a href='{}'>" + "Tesseract-OCR" + "</a>"
        href = href.format(url)

        label = QLabel(text.format(href))
        label.setAlignment(Qt.AlignJustify)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setMargin(5)
        layout.addWidget(label, 1, 0, 1, 2)

        layout.addWidget(self.cb_useocr, 0, 0, 1, 2)
        layout.addWidget(QLabel(_("Tesseract-OCR Executable") + ":"), 2, 0)
        layout.addWidget(self.tesseract, 3, 0)
        layout.addWidget(self.browse, 3, 1)

        box.setLayout(layout)
        mainLayout.addWidget(box)
        mainLayout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.ocrBox.setLayout(mainLayout)

        if (not scctool.settings.windows):
            self.cb_useocr.setEnabled(False)
            self.cb_useocr.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.cb_useocr.setToolTip(
                _("This feature is only available in Windows."))
            self.tesseract.setEnabled(False)
            self.tesseract.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.tesseract.setToolTip(
                _("This feature is only available in Windows."))
            self.browse.setEnabled(False)
            self.browse.setAttribute(Qt.WA_AlwaysShowToolTips)
            self.browse.setToolTip(
                _("This feature is only available in Windows."))

    def selectTesseract(self):
        """Create forms for tesseract."""
        old_exe = self.tesseract.text()
        default = scctool.settings.config.findTesserAct(old_exe)
        exe, ok = QFileDialog.getOpenFileName(
            self, _("Select Tesseract-OCR Executable"), default,
            _("Tesseract-OCR Executable") + " (tesseract.exe);; " +
            _("Executable") + " (*.exe);; " + _("All files") + " (*)")
        if (ok and exe != old_exe):
            self.tesseract.setText(exe)
            self.changed()

    def createMapsBox(self):
        """Create box for map manager."""
        self.mapsize = 300

        self.mapsBox = QWidget()

        layout = QGridLayout()

        self.maplist = QListWidget()
        self.maplist.setSortingEnabled(True)
        for map in scctool.settings.maps:
            self.maplist.addItem(QListWidgetItem(map))
        self.maplist.setCurrentItem(self.maplist.item(0))
        self.maplist.currentItemChanged.connect(self.changePreview)
        # self.maplist.setFixedHeight(self.mapsize)
        self.maplist.setMinimumWidth(150)

        layout.addWidget(self.maplist, 0, 1, 2, 1)
        self.mapPreview = QLabel()
        self.mapPreview.setFixedWidth(self.mapsize)
        self.mapPreview.setFixedHeight(self.mapsize)
        self.mapPreview.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.mapPreview, 0, 0)
        self.mapInfo = QLabel()
        self.mapInfo.setIndent(10)
        layout.addWidget(self.mapInfo, 1, 0)

        self.pb_addMapLiquipedia = QPushButton(_("Add from Liquipedia"))
        self.pb_addMapLiquipedia.clicked.connect(self.addFromLquipedia)
        self.pb_addMap = QPushButton(_("Add from File"))
        self.pb_addMap.clicked.connect(self.addMap)
        self.pb_renameMap = QPushButton(_("Rename"))
        self.pb_renameMap.clicked.connect(self.renameMap)
        self.pb_changeMap = QPushButton(_("Change Image"))
        self.pb_changeMap.clicked.connect(self.changeMap)
        self.pb_removeMap = QPushButton(_("Remove"))
        self.pb_removeMap.clicked.connect(self.deleteMap)

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

        box = QWidget()
        container = QHBoxLayout()

        container.addWidget(self.pb_addMapLiquipedia, 0)
        container.addWidget(self.pb_addMap, 0)
        container.addWidget(QLabel(), 4)
        container.addWidget(self.pb_renameMap, 0)
        container.addWidget(self.pb_changeMap, 0)
        container.addWidget(self.pb_removeMap, 0)
        box.setLayout(container)

        layout.addWidget(box, 2, 0, 1, 2)

        layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 3,
            0, 1, 2)

        self.changePreview()
        self.mapsBox.setLayout(layout)

    def renameMap(self):
        """Rename maps."""
        item = self.maplist.currentItem()
        map = item.text()
        text, ok = QInputDialog.getText(self,
                                        _('Map Name'),
                                        _('Map Name') + ':',
                                        text=map)
        if not ok:
            return
        text = text.strip()
        if (text == map):
            return
        if text.lower() == 'tbd':
            QMessageBox.critical(
                self, _("Error"),
                _('"{}" is not a valid map name.').format(text))
            return
        if (text in scctool.settings.maps):
            buttonReply = QMessageBox.warning(
                self, _("Duplicate Entry"),
                _("Map is already in list! Overwrite?"),
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if buttonReply == QMessageBox.No:
                return

        self.controller.addMap(self.controller.getMapImg(map, True), text)
        self.controller.deleteMap(map)
        item.setText(text)

    def changeMap(self):
        """Change a map."""
        map = self.maplist.currentItem().text()
        fileName, ok = QFileDialog.getOpenFileName(
            self, _("Select Map Image (> 500x500px recommended)"), "",
            _("Supported Images") + " (*.png *.jpg)")
        if ok:
            base = os.path.basename(fileName)
            name, ext = os.path.splitext(base)
            name = name.replace("_", " ")
            self.controller.deleteMap(map)
            self.controller.addMap(fileName, map)
            self.changePreview()

    def addMap(self):
        """Add a map."""
        fileName, ok = QFileDialog.getOpenFileName(
            self, _("Select Map Image (> 500x500px recommended)"), "",
            _("Supported Images") + " (*.png *.jpg)")
        if ok:
            base = os.path.basename(fileName)
            name, ext = os.path.splitext(base)
            name = name.replace("_", " ")
            map_name, ok = QInputDialog.getText(self,
                                                _('Map Name'),
                                                _('Map Name') + ':',
                                                text=name)
            map_name = map_name.strip()
            if ok:
                if map_name.lower() == 'tbd':
                    QMessageBox.critical(
                        self, _("Error"),
                        _('"{}" is not a valid map name.').format(map_name))
                    return

                if (map_name in scctool.settings.maps):
                    buttonReply = QMessageBox.warning(
                        self, _("Duplicate Entry"),
                        _("Map is already in list! Overwrite?"),
                        QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                    if buttonReply == QMessageBox.No:
                        return
                    else:
                        self.controller.deleteMap(map_name)

                self.controller.addMap(fileName, map_name)
                items = self.maplist.findItems(map_name, Qt.MatchExactly)
                if len(items) == 0:
                    item = QListWidgetItem(map_name)
                    self.maplist.addItem(item)
                    self.maplist.setCurrentItem(item)
                else:
                    self.maplist.setCurrentItem(items[0])
                self.changePreview()

    def addFromLquipedia(self):
        grabber = LiquipediaGrabber()
        search_str = ''
        while True:
            search_str, ok = QInputDialog.getText(self,
                                                  _('Map Name'),
                                                  _('Map Name') + ':',
                                                  text=search_str)
            search_str.strip()
            try:
                if ok and search_str:
                    if search_str.lower() == 'tbd':
                        QMessageBox.critical(
                            self, _("Error"),
                            _('"{}" is not a valid map name.').format(
                                search_str))
                        continue
                    try:
                        map = grabber.get_map(search_str)
                    except MapNotFound:
                        QMessageBox.critical(
                            self, _("Map not found"),
                            _('"{}" was not found on Liquipedia.').format(
                                search_str))
                        continue
                    map_name = map.get_name()

                    if (map_name in scctool.settings.maps):
                        buttonReply = QMessageBox.warning(
                            self, _("Duplicate Entry"),
                            _("Map {} is already in list! Overwrite?".format(
                                map_name)), QMessageBox.Yes | QMessageBox.No,
                            QMessageBox.No)
                        if buttonReply == QMessageBox.No:
                            break
                        else:
                            self.controller.deleteMap(map_name)

                    images = grabber.get_images(map.get_map_images())
                    image = ""
                    for size in sorted(images):
                        if not image or size <= 2500 * 2500:
                            image = images[size]
                    url = grabber._base_url + image

                    downloader = MapDownloader(self, map_name, url)
                    downloader.download()
                    if map_name not in scctool.settings.maps:
                        scctool.settings.maps.append(map_name)
                    items = self.maplist.findItems(map_name, Qt.MatchExactly)
                    if len(items) == 0:
                        item = QListWidgetItem(map_name)
                        self.maplist.addItem(item)
                        self.maplist.setCurrentItem(item)
                    else:
                        self.maplist.setCurrentItem(items[0])
                    self.changePreview()
            except Exception as e:
                module_logger.exception("message")
                QMessageBox.critical(self, _("Error"), str(e))
            finally:
                break

    def deleteMap(self):
        """Delete a map."""
        item = self.maplist.currentItem()
        map = item.text()
        buttonReply = QMessageBox.question(
            self, _('Delete map?'),
            _("Delete '{}' permanently?").format(map),
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            self.controller.deleteMap(map)
            self.maplist.takeItem(self.maplist.currentRow())

    def changePreview(self):
        """Change the map preview."""
        if self.maplist.count() < 1:
            return

        map = self.maplist.currentItem().text()
        if (map == "TBD"):
            self.pb_renameMap.setEnabled(False)
            self.pb_removeMap.setEnabled(False)
            self.sc_removeMap.setEnabled(False)
        else:
            self.pb_removeMap.setEnabled(True)
            self.pb_renameMap.setEnabled(True)
            self.sc_removeMap.setEnabled(True)

        file = self.controller.getMapImg(map, True)
        map = QPixmap(file)
        width = map.height()
        height = map.width()
        ext = os.path.splitext(file)[1].replace(".", "").upper()
        size = humanize.naturalsize(os.path.getsize(file))
        map = QPixmap(file).scaled(self.mapsize, self.mapsize,
                                   Qt.KeepAspectRatio)
        self.mapPreview.setPixmap(map)
        text = "{}x{}px, {}, {}".format(width, height, str(size), ext)
        self.mapInfo.setText(text)

    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 saveData(self):
        """Save the data."""
        if (self.__dataChanged):
            scctool.settings.config.parser.set(
                "SCT", "myteams", ", ".join(self.list_favTeams.getData()))
            scctool.settings.config.parser.set(
                "SCT", "commonplayers",
                ", ".join(self.list_favPlayers.getData()))
            scctool.settings.config.parser.set("SCT", "tesseract",
                                               self.tesseract.text().strip())
            scctool.settings.config.parser.set("SCT", "use_ocr",
                                               str(self.cb_useocr.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "transparent_match_banner",
                str(self.cb_trans_banner.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftS", str(self.cb_ctrlshifts.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftC", str(self.cb_ctrlshiftc.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "swap_myteam", str(self.cb_swapTeams.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlN",
                                               str(self.cb_ctrln.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlX",
                                               str(self.cb_ctrlx.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftR", str(self.cb_ctrlshiftr.currentText()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist_on", str(self.cb_blacklist.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist", ", ".join(self.list_blacklist.getData()))

            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:
            self.mainWindow.updateMapCompleters()
            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 as e:
            module_logger.exception("message")