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)
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)
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')
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)
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")
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")
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)
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)
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)
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")
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")
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")
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)
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)
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")