class Window(QMainWindow): def __init__(self): super().__init__() self.shortcutQuit = QShortcut(QKeySequence("q"), self) self.shortcutQuit.activated.connect(self.close) self.shortcutQuit.setEnabled(False) self.InitWindow() def InitWindow(self): self.mainMenu = self.menuBar() fileMenu = self.mainMenu.addMenu("&File") hideItem = QAction("Hide Menu Bar", self) hideItem.setShortcut("h") # print(hideItem.shortcut().keyBindings()) # print(hideItem.shortcut().listToString()) hideItem.triggered.connect(self.my_hide) quitItem = QAction("Quit", self) quitItem.setShortcut("Q") quitItem.triggered.connect(self.close) fileMenu.addAction(hideItem) fileMenu.addAction(quitItem) def my_hide(self): self.mainMenu.hide() self.shortcutQuit.setEnabled(True)
def __init__(self): super(Ui, self).__init__() self.appName = APP_NAME appPath = (os.path.dirname(os.path.realpath(__file__))) uic.loadUi(f'{appPath}/ui/qt.ui', self) # create a thread # self.thread = Worker() # set window icon self.setWindowIcon(QtGui.QIcon(f'{appPath}/ui/img/title-bar-icon.png')) # connect buttons and functions - youtube single video self.btnOK.clicked.connect(self.enterURL) self.lineEditURL.returnPressed.connect(self.enterURL) self.btnDownload.clicked.connect(self.download_button) self.btnDownloadLocation.clicked.connect(self.getSaveLocation) self.lineEditDownloadLocation.returnPressed.connect( self.onSaveLocationChange) self.checkBoxVideo.stateChanged.connect(self.populateComboBox) self.checkBoxAudio.stateChanged.connect(self.populateComboBox) # connect buttons and functions - youtube playlist self.btnOK_playlist.clicked.connect(self.enterPlaylistURL) self.btnPlaylistDownload.clicked.connect(self.playlistDownloadSelected) self.checkBoxPlaylistSelectAll.stateChanged.connect( self.playlistSelectAllChanged) # initialize controls self.progressBar.setValue(0) # progress bar value to 0 self.checkBoxVideo.setChecked(True) self.btnDownload.setEnabled(False) # test URL self.lineEditPlaylistURL.setText( "https://www.youtube.com/playlist?list=PLE-H3cfY2nKOgX-bjk_5ObFYtgHw9BI8j" ) shortcut = QShortcut(QKeySequence("Ctrl+Shift+U"), self.lineEditURL) shortcut.activated.connect( lambda: self.lineEditURL.setText(DEFAULT_URL)) shortcut.setEnabled(True) # user directory (chosen for the download) self.user_directory = DEFAULT_DIRECTORY self.show()
def init_ui(self): layout = QHBoxLayout() self.message_input.setStyleSheet( ThemeLoader.loaded_theme.get_default_for_widget( self.message_input)) self.message_input.setAutoFormatting(QTextEdit.AutoAll) self.send_button.clicked.connect(lambda: self.send_button_clicked()) shortcut = QShortcut(QKeySequence("Ctrl+Return"), self.send_button) shortcut.activated.connect(lambda: self.send_button_clicked(True)) shortcut.setEnabled(True) self.send_button.clicked.connect( lambda: self.send_button_clicked(True)) self.send_button.setStyleSheet( ThemeLoader.loaded_theme.apply_to_stylesheet(button_style)) layout.addWidget(self.message_input) layout.addWidget(self.send_button) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout)
def __init__(self, parent: QWidget = None): super(MainWindow, self).__init__(parent) self.setupUi(self) # configure canvas graph view self.mtplt_figure = Figure() self.qt_figure = FigureCanvas(self.mtplt_figure) self.graph_ax = self.mtplt_figure.subplots() self.configureAx() self.qt_figure.setMinimumSize(100, 100) self.horizontalLayout_2.insertWidget(1, self.qt_figure) # configure connections self.startButton.clicked.connect(self.algo) #self.MaxCliqueButton.clicked.connect self.clearButton.clicked.connect(self.clearGraph) self.addButton.clicked.connect(self.addNode) self.deleteButton.clicked.connect(self.deleteNode) # configure table graph view self.matrix_graph = GraphMatrixModel(self) self.tableView.setModel(self.matrix_graph) self.tableView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) # title self.setWindowTitle('Курсовая работа №8') # shortcuts start_shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_R), self) start_shortcut.activated.connect(self.algo) start_shortcut.setEnabled(True) clear_shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self) clear_shortcut.activated.connect(self.clearGraph) clear_shortcut.setEnabled(True)
class SearchWindow(QWidget): def __init__(self, go_to_image, search_image): super().__init__() self.setWindowModality(Qt.ApplicationModal) self.setFixedSize(400, 200) self.setWindowTitle("Go to image") layout = QVBoxLayout(self) def text_changed(): enabled = search_image(self.search_input.text()) self.go_button.setEnabled(enabled) self.go_shortcut.setEnabled(enabled) self.search_input = QLineEdit() self.search_input.textChanged.connect(text_changed) layout.addWidget(self.search_input) def cb(): go_to_image(self.search_input.text()) self.close() self.go_to_image = cb self.go_button = QPushButton("Go!") self.go_button.clicked.connect(self.go_to_image) self.go_button.setEnabled(False) self.go_shortcut = QShortcut(Qt.Key_Return, self) self.go_shortcut.activated.connect(self.go_to_image) self.go_shortcut.setEnabled(False) layout.addWidget(self.go_button) def closeEvent(self, evt): evt.accept() self.deleteLater()
class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(1200, 720) Form.setMinimumSize(QtCore.QSize(200, 199)) Form.setStyleSheet("background-color:black;") self.isOnline = False self.isMini = False self.isOnTop = True self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName("verticalLayout_2") self.mainFrame = QtWidgets.QFrame(Form) self.mainFrame.setMinimumSize(QtCore.QSize(200, 82)) self.mainFrame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.mainFrame.setFrameShadow(QtWidgets.QFrame.Raised) self.mainFrame.setObjectName("mainFrame") self.verticalLayout = QtWidgets.QVBoxLayout(self.mainFrame) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName("verticalLayout") self.frame_3 = QtWidgets.QFrame(self.mainFrame) sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.frame_3.sizePolicy().hasHeightForWidth()) self.frame_3.setSizePolicy(sizePolicy) self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_3.setObjectName("frame_3") self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.frame_3) self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.Player_name = QtWidgets.QLabel(self.frame_3) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.Player_name.sizePolicy().hasHeightForWidth()) self.Player_name.setSizePolicy(sizePolicy) self.Player_name.setStyleSheet("QLabel\n" " {\n" " font: 12pt \"Helvetica\";\n" " color: white;\n" " border: 0px solid #076100;\n" " }") self.Player_name.setObjectName("status_label") self.horizontalLayout_6.addWidget(self.Player_name) spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_6.addItem(spacerItem) self.minimize_button = QtWidgets.QPushButton(self.frame_3) self.minimize_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.minimize_button.setText("") icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("icon_sets/minimize.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.minimize_button.setIconSize(QSize(25, 17)) self.minimize_button.setMaximumSize(QSize(25, 17)) self.minimize_button.setIcon(icon) self.minimize_button.setIconSize(QtCore.QSize(27, 20)) # self.minimize_button.setFlat(True) self.minimize_button.setObjectName("minimize_button") self.horizontalLayout_6.addWidget(self.minimize_button) self.maximize_button = QtWidgets.QPushButton(self.frame_3) self.maximize_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.maximize_button.setText("") icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap("icon_sets/maximize.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.maximize_button.setIcon(icon1) self.maximize_button.setIconSize(QSize(25, 17)) self.maximize_button.setMaximumSize(QSize(25, 17)) self.maximize_button.setIconSize(QtCore.QSize(27, 20)) # self.maximize_button.setFlat(True) self.maximize_button.setObjectName("maximize_button") self.horizontalLayout_6.addWidget(self.maximize_button) self.cross_button = QtWidgets.QPushButton(self.frame_3) self.cross_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.cross_button.setText("") self.cross_button.setIconSize(QSize(25, 17)) self.cross_button.setMaximumSize(QSize(25, 17)) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap("icon_sets/cross.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cross_button.setIcon(icon2) self.cross_button.setIconSize(QtCore.QSize(27, 20)) # self.cross_button.setFlat(True) self.cross_button.setObjectName("cross_button") self.horizontalLayout_6.addWidget(self.cross_button) self.verticalLayout.addWidget(self.frame_3) self.video_playback = Videowidget(self.frame_3) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(200) sizePolicy.setHeightForWidth( self.video_playback.sizePolicy().hasHeightForWidth()) self.video_playback.setSizePolicy(sizePolicy) self.video_playback.setMinimumSize(QtCore.QSize(200, 100)) self.video_playback.setMouseTracking(False) self.video_playback.setTabletTracking(False) self.video_playback.setAcceptDrops(False) self.video_playback.setAutoFillBackground(False) self.video_playback.setObjectName("video_playback") self.video_playback.setStyleSheet("background-color:grey") self.verticalLayout.addWidget(self.video_playback) self.pos_frame = QtWidgets.QFrame(self.mainFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth( self.pos_frame.sizePolicy().hasHeightForWidth()) self.pos_frame.setSizePolicy(sizePolicy) self.pos_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.pos_frame.setFrameShadow(QtWidgets.QFrame.Raised) self.pos_frame.setObjectName("pos_frame") self.horizontalLayout = QtWidgets.QHBoxLayout(self.pos_frame) self.horizontalLayout.setObjectName("horizontalLayout") self.position_slider = PosSlider(self.pos_frame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(100) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.position_slider.sizePolicy().hasHeightForWidth()) self.position_slider.setSizePolicy(sizePolicy) self.position_slider.setMouseTracking(True) self.position_slider.setAutoFillBackground(False) self.position_slider.setStyleSheet( "QSlider::handle:horizontal \n" " {\n" " background: transparent;\n" " width: 8px;\n" " }\n" "QSlider::groove:horizontal {\n" " border: 1px solid white;\n" " height: 8px;\n" " background: qlineargradient(y1: 0, y2: 1,stop: 0 #2e3436, stop: 1.0 #000000);\n" "}\n" " QSlider::sub-page:horizontal {\n" " background:qlineargradient( y1: 0, y2: 1,\n" " stop: 0 #42a6db, stop: 1 #0074e0); \n" " border: 1px solid white;\n" " height: 8px;\n" "}\n" "QSlider::handle:horizontal:hover {\n" " background: black;\n" " height: 8px;\n" " width: 8px;\n" " border: 1px solid white;\n" " }\n" "") self.position_slider.setOrientation(QtCore.Qt.Horizontal) self.position_slider.setInvertedAppearance(False) self.position_slider.setInvertedControls(False) self.position_slider.setObjectName("position_slider") self.horizontalLayout.addWidget(self.position_slider) self.time_status = QtWidgets.QLabel(self.pos_frame) font = QtGui.QFont() font.setFamily("Arial Rounded MT Bold") font.setPointSize(8) font.setBold(False) font.setItalic(False) font.setWeight(50) self.time_status.setFont(font) self.time_status.setStyleSheet( "QLabel\n" " {\n" " \n" " font: 8pt \"Arial Rounded MT Bold\";\n" " color: white;\n" " border: 0px solid #076100;\n" "\n" " }") self.time_status.setObjectName("time_status") self.horizontalLayout.addWidget(self.time_status) self.backslash = QtWidgets.QLabel(self.pos_frame) font = QtGui.QFont() font.setFamily("Arial Rounded MT Bold") font.setPointSize(8) font.setBold(False) font.setItalic(False) font.setWeight(50) self.backslash.setFont(font) self.backslash.setStyleSheet( "QLabel\n" " {\n" " \n" " font: 8pt \"Arial Rounded MT Bold\";\n" " color: white;\n" " border: 0px solid #076100;\n" "\n" " }") self.backslash.setObjectName("backslash") self.horizontalLayout.addWidget(self.backslash) self.duration_status = QtWidgets.QLabel(self.pos_frame) font = QtGui.QFont() font.setFamily("Arial Rounded MT Bold") font.setPointSize(8) font.setBold(False) font.setItalic(False) font.setWeight(50) self.duration_status.setFont(font) self.duration_status.setStyleSheet( "QLabel\n" " {\n" " \n" " font: 8pt \"Arial Rounded MT Bold\";\n" " color: white;\n" " border: 0px solid #076100;\n" "\n" " }") self.duration_status.setObjectName("duration_status") self.horizontalLayout.addWidget(self.duration_status) self.verticalLayout.addWidget(self.pos_frame) self.frame_2 = QtWidgets.QFrame(self.mainFrame) sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(22) # sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) self.frame_2.setSizePolicy(sizePolicy) self.frame_2.setContentsMargins(0, 0, 0, 0) self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_2.setObjectName("frame_2") self.frame_2.setStyleSheet("") self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame_2) self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.play_button = QtWidgets.QPushButton(self.frame_2) self.play_button.setEnabled(False) self.play_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.play_button.setStyleSheet( "QPushButton[play=true]{image:url(icon_sets/play/play.png);width:22px;height:22px}\n" "QPushButton[play=false]{image:url(icon_sets/pause/pause.png);width:22px;height:22px }\n" ) self.play_button.setProperty("play", True) self.play_button.setText("") self.play_button.setObjectName("play_button") self.horizontalLayout_4.addWidget(self.play_button) self.playback_button = QtWidgets.QPushButton(self.frame_2) self.playback_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.playback_button.setStyleSheet( "QPushButton{image:url(icon_sets/playback/playback.png);width:22px;height:22px }\n" ) self.playback_button.setText("") self.playback_button.setObjectName("playback_button") self.horizontalLayout_4.addWidget(self.playback_button) self.always_on_top_button = QtWidgets.QPushButton(self.frame_2) self.always_on_top_button.setCursor( QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.always_on_top_button.setStyleSheet( "QPushButton[top=false]{image : url(icon_sets/always_on_top/top_off.png) }\n" "QPushButton[top=true]{image : url(icon_sets/always_on_top/top_on.png) }\n" ) self.always_on_top_button.setProperty("top", True) self.always_on_top_button.setText("") self.always_on_top_button.setObjectName("always_on_top_button") self.horizontalLayout_4.addWidget(self.always_on_top_button) self.miniplayer_button = QtWidgets.QPushButton(self.frame_2) self.miniplayer_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.miniplayer_button.setStyleSheet( "QPushButton[mini=false]{image : url(icon_sets/standard_player/standard.png) }\n" # "QPushButton[mini=false]:hover{ image:url(icon_sets/standard_player/.png) }\n" "QPushButton[mini=true]{image : url(icon_sets/mini_player/mini.png) }\n" # "QPushButton[mini=true]:hover{ image:url(icon_sets/mini_player/.png) }\n" ) self.miniplayer_button.setProperty("mini", False) self.miniplayer_button.setContentsMargins(0, 0, 0, 0) self.miniplayer_button.setText("") self.miniplayer_button.setObjectName("miniplayer_button") self.horizontalLayout_4.addWidget(self.miniplayer_button) self.open_File_button = QtWidgets.QPushButton(self.frame_2) self.open_File_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.open_File_button.setStyleSheet( "QPushButton{image:url(icon_sets/new_file/new_file.png);width:22px;height:22px }\n" ) self.open_File_button.setText("") self.open_File_button.setObjectName("playback_button") self.horizontalLayout_4.addWidget(self.open_File_button) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem1) self.screenshot_button = QtWidgets.QPushButton(self.frame_2) self.screenshot_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.screenshot_button.setStyleSheet( "QPushButton{image : url(icon_sets/snapshot/snapshot.png);width:22px;height:22px} \n" ) self.screenshot_button.setText("") self.screenshot_button.setObjectName("screenshot_button") self.horizontalLayout_4.addWidget(self.screenshot_button) ''' Video Setting button for Video subs and dubs''' # self.video_setting_button = QtWidgets.QPushButton(self.frame_2) # self.video_setting_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) # self.video_setting_button.setText("") # self.video_setting_button.setIconSize(QtCore.QSize(22, 22)) # self.video_setting_button.setStyleSheet("QPushButton{image : url(icon_sets/video_setting/video_settings.png) }\n" # ) # self.video_setting_button.setObjectName("video_setting_button") # self.horizontalLayout_4.addWidget(self.video_setting_button) self.setting_button = QtWidgets.QPushButton(self.frame_2) self.setting_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.setting_button.setStyleSheet( "QPushButton{image : url(icon_sets/settings/settings.png) }\n") self.setting_button.setText("") self.setting_button.setObjectName("setting_button") self.horizontalLayout_4.addWidget(self.setting_button) self.Quality_box = QtWidgets.QComboBox(self.frame_2) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.Quality_box.sizePolicy().hasHeightForWidth()) self.Quality_box.setSizePolicy(sizePolicy) self.Quality_box.setStyleSheet( "QComboBox\n" " {\n" " border-image :url(icon_sets/quality/border.png);\n" " color: #fcffff;\n" " font-size: 8pt;\n" " font-weight: bold;\n" # " width: 41px;\n" " background-color: #000000;\n" " }\n" " QComboBox QAbstractItemView \n" " {\n" " background: #fcffff;\n" " border: 2px solid darkgray;\n" " selection-background-color: #000000;\n" " }\n" "QComboBox::drop-down {\n" " border: 0px;\n" " subcontrol-origin: padding;\n" " subcontrol-position: top right;\n" "\n" " border-top-right-radius: 3px;\n" " border-bottom-right-radius: 3px;\n" "}\n") self.Quality_box.setIconSize(QtCore.QSize(0, 0)) self.Quality_box.setDuplicatesEnabled(False) self.Quality_box.setObjectName("comboBox") self.Quality_box.addItem(" -") self.horizontalLayout_4.addWidget(self.Quality_box) self.volume_button = QtWidgets.QPushButton(self.frame_2) self.volume_button.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.volume_button.setText("") icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap("icon_sets/volume/volume1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.volume_button.setIcon(icon9) self.volume_button.setIconSize(QtCore.QSize(22, 22)) self.volume_button.setCheckable(True) # self.volume_button.setFlat(True) self.volume_button.setObjectName("volume_button") self.horizontalLayout_4.addWidget(self.volume_button) self.volumeslider = VolSlider(self.frame_2) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.volumeslider.sizePolicy().hasHeightForWidth()) self.volumeslider.setSizePolicy(sizePolicy) self.volumeslider.setAutoFillBackground(False) self.volumeslider.setStyleSheet( "QSlider::handle:horizontal \n" " {\n" " background: transparent;\n" " width: 5px;\n" " }\n" "QSlider::groove:horizontal {\n" " border: 1px solid #444444;\n" " height: 5px;\n" " background: qlineargradient(y1: 0, y2: 1,stop: 0 grey, stop: 1.0 grey);\n" "}\n" " QSlider::sub-page:horizontal {\n" " background:qlineargradient( y1: 0, y2: 1,\n" " stop: 0 #42a6db, stop: 1 #0074e0); \n" " border: 1px solid #777;\n" " height: 5px;\n" "}\n" "QSlider::handle:horizontal:hover {\n" " background: black;\n" " height: 5px;\n" " width: 5px;\n" " border: 1px solid #0074e0;\n" " }\n" "QSlider::sub-page:horizontal:disabled{background:qlineargradient( y1: 0, y2: 1,\n" " stop: 0 #909090, stop: 1 #A8A8A8 );}\n" "") self.volumeslider.setOrientation(QtCore.Qt.Horizontal) self.volumeslider.setObjectName("volume_slider") self.horizontalLayout_4.addWidget(self.volumeslider) self.volume_percentage = QtWidgets.QLabel(self.frame_2) self.volume_percentage.setStyleSheet( "QLabel\n" " {\n" " font: 7pt \"Arial Rounded MT Bold\";\n" " color: white;\n" " border: 0px solid #076100;\n" " }") self.volume_percentage.setObjectName("volume_status") self.volume_percentage.setMinimumWidth(35) self.volume_percentage.setText(" 75 %") self.horizontalLayout_4.addWidget(self.volume_percentage) self.verticalLayout.addWidget(self.frame_2) self.frame = QtWidgets.QFrame(self.mainFrame) font = QtGui.QFont() font.setFamily("Lucida Console") self.frame.setFont(font) self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Raised) self.frame.setObjectName("frame") self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.frame) self.horizontalLayout_5.setObjectName("horizontalLayout_5") sizegrip_2 = QtWidgets.QSizeGrip(Form) sizegrip_2.setStyleSheet("image:url(icon_sets/.png)") self.horizontalLayout_5.addWidget( sizegrip_2, 0, QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft) # self.enter_url_label = QtWidgets.QLabel(self.frame) # font = QtGui.QFont() # font.setFamily("Arial Rounded MT Bold") # font.setPointSize(8) # font.setBold(False) # font.setItalic(False) # font.setWeight(50) # self.enter_url_label.setFont(font) # self.enter_url_label.setStyleSheet("QLabel\n" # " {\n" # " \n" # " font: 8pt \"Arial Rounded MT Bold\";\n" # " color: white;\n" # " border: 0px solid #076100;\n" # " }") # self.enter_url_label.setObjectName("enter_url_label") # self.horizontalLayout_5.addWidget(self.enter_url_label) self.url_box = QtWidgets.QComboBox(self.frame) sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(200) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.url_box.sizePolicy().hasHeightForWidth()) self.url_box.setSizePolicy(sizePolicy) self.url_box.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) self.url_box.setMouseTracking(False) self.url_box.setAcceptDrops(False) self.url_box.setWhatsThis("") self.url_box.setAccessibleDescription("") self.url_box.setAutoFillBackground(True) self.url_box.setStyleSheet("QComboBox\n" " {\n" " border: 2px solid #0074e0;\n" " color: #fcffff;\n" " font-size: 8pt;\n" " font-weight: bold;\n" " background-color: #000000;\n" " border-radius: 6px;\n" " }\n" " QComboBox QAbstractItemView \n" " {\n" " background: #fcffff;\n" " border: 2px solid darkgray;\n" " selection-background-color: #000000;\n" " }\n" "QComboBox::down-arrow {\n" " image: url(icon_sets/url/url4.png)\n" "}\n" "QComboBox::drop-down {\n" " subcontrol-origin: padding;\n" " subcontrol-position: top right;\n" " width: 20px;\n" " \n" " border-top-right-radius: 3px;\n" " border-bottom-right-radius: 3px;\n" "}") self.url_box.setInputMethodHints(QtCore.Qt.ImhUrlCharactersOnly) self.url_box.setEditable(True) self.url_box.setCurrentText("") self.url_box.setMaxVisibleItems(100) self.url_box.setMaxCount(100) self.url_box.setInsertPolicy(QtWidgets.QComboBox.InsertAtCurrent) self.url_box.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToContentsOnFirstShow) self.url_box.setMinimumContentsLength(2) self.url_box.setIconSize(QtCore.QSize(20, 20)) self.url_box.setDuplicatesEnabled(False) self.url_box.setFrame(True) self.url_box.setObjectName("url_box") self.horizontalLayout_5.addWidget(self.url_box) self.verticalLayout.addWidget(self.frame) sizegrip_1 = QtWidgets.QSizeGrip(Form) sizegrip_1.setStyleSheet( "image:url(icon_sets/size.png);width:15; height:18;") self.horizontalLayout_5.addWidget( sizegrip_1, 0, QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight) self.verticalLayout_2.addWidget(self.mainFrame) self.retranslateUi(Form) self.url_box.setCurrentIndex(-1) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Q-Stream Player")) self.Player_name.setText(_translate("Form", "Q-Stream Player")) self.time_status.setText(_translate("Form", "00:00:00")) self.backslash.setText(_translate("Form", "/")) self.duration_status.setText(_translate("Form", "00:00:00")) # self.enter_url_label.setText(_translate("Form", statusList[4])) # Set intial Volume self.volumeslider.setRange(0, 100) self.volumeslider.setValue(75) self.mediaPlayer.setVolume(75) self.position_slider.setRange(0, 100) # connecting buttons self.miniplayer_button.clicked.connect(self.setupMiniPlayer) self.position_slider.sliderMoved.connect(self.setPosition) self.position_slider.sliderMoved.connect(self.handleLabel) self.volume_button.clicked.connect(self.mute) self.volumeslider.valueChanged.connect(self.setVolume) self.screenshot_button.clicked.connect(self.screenshot) self.playback_button.clicked.connect(self.stopplayback) self.always_on_top_button.clicked.connect(self.checkOnTop) self.play_button.clicked.connect(self.play) self.open_File_button.clicked.connect(self.openFile) # self.video_setting_button.clicked.connect(self.handleQuality) self.setting_button.clicked.connect(self.handleSetting) self.Quality_box.currentTextChanged.connect(self.handleQuality) self.cross_button.clicked.connect(self.exit) self.maximize_button.clicked.connect(self.max) self.minimize_button.clicked.connect(self.min) # Shortcuts shortcut = QShortcut(QKeySequence('Esc'), self.video_playback) shortcut.activated.connect(self.EscFun) shortcut = QShortcut(QKeySequence('Space'), self.video_playback) shortcut.activated.connect(self.play) shortcut = QShortcut(QKeySequence('f'), self.video_playback) shortcut.activated.connect(self.fullscreen_video) shortcut = QShortcut(QKeySequence('c'), self.video_playback) shortcut.activated.connect(self.setupMiniPlayer) shortcut = QShortcut(QKeySequence('o'), self.video_playback) shortcut.activated.connect(self.openFile) shortcut = QShortcut(QKeySequence('a'), self.video_playback) shortcut.activated.connect(self.checkOnTop) shortcut = QShortcut(QKeySequence("Return"), self.video_playback) shortcut.activated.connect(self.playOnline) shortcut = QShortcut(QKeySequence('m'), self.video_playback) shortcut.activated.connect(self.mute) shortcut = QShortcut(QKeySequence(Qt.Key_Right), self.video_playback) shortcut.activated.connect(self.forwardSlider) shortcut = QShortcut(QKeySequence(Qt.Key_Left), self.video_playback) shortcut.activated.connect(self.backSlider) self.volupshortcut = QShortcut(QKeySequence(Qt.Key_Up), self.video_playback) self.volupshortcut.activated.connect(self.volumeUp) self.voldownshortcut = QShortcut(QKeySequence(Qt.Key_Down), self.video_playback) self.voldownshortcut.activated.connect(self.volumeDown) shortcut = QShortcut(QKeySequence(Qt.ControlModifier + Qt.Key_Right), self.video_playback) shortcut.activated.connect(self.forwardSlider10) shortcut = QShortcut(QKeySequence(Qt.ControlModifier + Qt.Key_Left), self.video_playback) shortcut.activated.connect(self.backSlider10) shortcut = QShortcut(QKeySequence(Qt.AltModifier + Qt.Key_Left), self.video_playback) shortcut.activated.connect(self.backSlider5) shortcut = QShortcut(QKeySequence(Qt.AltModifier + Qt.Key_Right), self.video_playback) shortcut.activated.connect(self.forwardSlider5) # loading history to Url Box items = self.load() self.url_box.addItems(items) self.url_box.setCurrentIndex(-1) # icon size # iconSize = QSize(5,5) # self.play_button.setIconSize(iconSize) # self.playback_button.setIconSize(iconSize) # self.screenshot_button.setIconSize(iconSize) # self.always_on_top_button.setIconSize(iconSize) # self.miniplayer_button.setIconSize(iconSize) # self.setting_button.setIconSize(iconSize) # self.volume_button.setIconSize(iconSize) # button size btnSize = QSize(22, 22) self.play_button.setMaximumSize(btnSize) self.playback_button.setMaximumSize(btnSize) self.screenshot_button.setMaximumSize(btnSize) self.always_on_top_button.setMaximumSize(btnSize) self.miniplayer_button.setMaximumSize(btnSize) # self.video_setting_button.setMaximumSize(btnSize) self.setting_button.setMaximumSize(btnSize) self.volume_button.setMaximumSize(btnSize) # set margin self.horizontalLayout.setContentsMargins(10, 5, 9, 0) self.horizontalLayout_4.setContentsMargins(9, 0, 9, 0) self.horizontalLayout_4.setSpacing(4) self.horizontalLayout_5.setContentsMargins(0, 5, 5, 5) self.horizontalLayout_6.setContentsMargins(9, 0, 9, 0) # Media player settings self.mediaPlayer.setVideoOutput(self.video_playback) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.positionChanged.connect(self.handleLabel) self.mediaPlayer.durationChanged.connect(self.durationChanged) def handleLabel(self): self.time_status.clear() mtime = QtCore.QTime(0, 0, 0, 0) self.time = mtime.addMSecs(self.mediaPlayer.position()) self.time_status.setText(self.time.toString()) def hide_all(self): self.frame_3.close() # self.frame.close() self.url_box.close() self.playback_button.close() self.screenshot_button.close() self.Quality_box.close() self.volume_button.close() # self.video_setting_button.close() # self.setting_button.close() def show_all(self): self.frame_3.show() # self.frame.show() self.url_box.show() self.playback_button.show() self.screenshot_button.show() self.Quality_box.show() self.volume_button.show() # self.video_setting_button.show() self.setting_button.show() def checkOnTop(self): self.isOnTop = not self.isOnTop if self.isOnTop: self.always_on_top_button.setProperty("top", True) self.always_on_top_button.setStyle( self.always_on_top_button.style()) Form.setWindowFlags(Form.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) else: self.always_on_top_button.setProperty("top", False) self.always_on_top_button.setStyle( self.always_on_top_button.style()) Form.setWindowFlags(Form.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint) Form.show() def miniProperty(self): self.video_playback.setMinimumSize(QSize(200, 100)) self.video_playback.resize(QSize(550, 270)) Form.resize(QSize(550, 270)) Form.setMinimumSize(QSize(200, 175)) self.mainFrame.setMinimumSize(QSize(200, 60)) self.mainFrame.resize(QSize(200, 53)) def standardProperty(self): self.video_playback.setMinimumSize(QSize(200, 100)) self.mainFrame.setMinimumSize(QSize(200, 82)) Form.setMinimumSize(QSize(200, 202)) Form.resize(550, 369) def setupMiniPlayer(self): self.isMini = not self.isMini if self.isMini: self.miniplayer_button.setProperty("mini", True) self.miniplayer_button.setStyle(self.miniplayer_button.style()) self.hide_all() self.miniProperty() else: self.miniplayer_button.setProperty("mini", False) self.miniplayer_button.setStyle(self.miniplayer_button.style()) self.standardProperty() self.show_all() def load(self): scorefile = "db.bat" if os.path.exists(scorefile): with open(scorefile, 'rb') as sf: scores = pickle.load(sf) else: scores = [] with open(scorefile, "wb") as sf: pickle.dump(scores, sf) return scores def scor_func(self, url): scorefile = "db.bat" if os.path.exists(scorefile): with open(scorefile, 'rb') as sf: scores = pickle.load(sf) else: scores = [] scores.append(url) with open(scorefile, "wb") as sf: if len(scores) > 100: print("here", scores) scores = scores[1:] pickle.dump(scores, sf) return scores def mute(self): if self.mediaPlayer.isMuted(): print('[ ! Full Volume]') self.mediaPlayer.setMuted(False) icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap("icon_sets/volume/volume1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.volume_button.setIcon(icon9) self.volume_button.setIconSize(QSize(22, 22)) self.volumeslider.setEnabled(True) # shortcut Enable self.volupshortcut.setEnabled(True) self.voldownshortcut.setEnabled(True) else: print('[ ! Mute Volume]') self.mediaPlayer.setMuted(True) icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap("icon_sets/volume/volume2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.volume_button.setIcon(icon9) self.volume_button.setIconSize(QSize(22, 22)) self.volumeslider.setEnabled(False) # shrotcut disable self.volupshortcut.setEnabled(False) self.voldownshortcut.setEnabled(False) def play(self): if self.mediaPlayer.isVideoAvailable(): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: print("[ ! PAUSE PRESSED ]") self.mediaPlayer.pause() else: print("[ ! PLAY PRESSED ]") self.mediaPlayer.play() def playOnline(self): if self.url_box.currentText() != '': print('[ ! GETTING VIDEO ONLINE ]') fileName = self.url_box.currentText() res = requests.get('https://mediaplayerserver.herokuapp.com/', params={"key": fileName}) try: self.streams = json.loads(res.text) try: self.mediaPlayer.setMedia( QMediaContent(QUrl(self.streams['best']))) self.play_video() self.isOnline = True self.addQuality() if self.url_box.findText(fileName, Qt.MatchExactly) < 0: self.url_box.addItem(fileName) self.scor_func(fileName) except KeyError: print("[ ! Error Video Not Supported By platform]") except json.JSONDecodeError: print("[ ! Error NoPluginError]") finally: self.url_box.setCurrentText("") def openFile(self): print('[ ! OPEN FILE ]') username = getpass.getuser() if sys.platform == 'win32': path = 'C:/Users/' + username + '/Videos/' elif sys.platform == 'linux' or sys.platform == 'Darwin': path = '/home/' + username + '/Videos/' formats = str.join(' ', [ '*.%s' % str(fmt).strip('b').strip("'") for fmt in QtGui.QMovie.supportedFormats() ]) fileName, _ = QFileDialog.getOpenFileName( self.video_playback, "Select media file", path, "Video Files (*.mp4 *.flv *.ts *.mts *.avi *.mkv)") if fileName != '': self.mediaPlayer.setMedia( QMediaContent(QUrl.fromLocalFile(fileName))) self.play_video() # def decodeLink(self,url): # try: # streams = streamlink.streams(url) # keys = [k for k in streams.keys()] # data = dict() # for k in keys: # data[k] = streams[k].url # return data # except streamlink.NoPluginError: # return None # def playOnline(self): # if self.url_box.currentText() != '': # print('[ ! GETTING VIDEO ONLINE ]') # fileName = self.url_box.currentText() # self.streams = self.decodeLink(fileName) # try: # self.mediaPlayer.setMedia(QMediaContent(QUrl(self.streams['best']))) # self.play_video() # self.isOnline = True # self.addQuality() # if self.url_box.findText(fileName, Qt.MatchExactly) < 0: # self.url_box.addItem(fileName) # self.scor_func(fileName) # except KeyError: # print("[ ! Error Video Not Supported By platform]") # finally: # self.url_box.setCurrentText("") def play_video(self): print('[ ! PLAYING VIDEO ]') self.play_button.setEnabled(True) if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self, state): print('[ ! CHANGING MEDIA STATE ]') if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.play_button.setProperty('play', False) self.play_button.setStyle(self.play_button.style()) else: self.play_button.setProperty('play', True) self.play_button.setStyle(self.play_button.style()) def stopplayback(self): print('[ ! STOP PLAYBACK VIDEO ]') self.mediaPlayer.stop() self.play_video() def positionChanged(self, position): print('[ ! POSITION CHANGED ]') self.position_slider.setValue(position) def durationChanged(self, duration): print('[ ! DURATION CHANGED ]') self.position_slider.setRange(0, duration) self.duration_status.clear() mtime = QtCore.QTime(0, 0, 0, 0) time = mtime.addMSecs(self.mediaPlayer.duration()) self.duration_status.setText(time.toString()) def setPosition(self, position): print('[ ! POSITION SET ]') self.mediaPlayer.setPosition(position) def setVolumePos(self, remain): print('[ ! REMANING VOLUME ]') print(remain) self.volumeslider.setRange(remain, 100) def setVolume(self, vol): print('[ ! SET VOLUME ]') print("set volume = " + str(vol)) if vol >= 0 and vol <= 100: self.volume_percentage.setText(" " + str(vol) + " %") if vol <= 0: icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap("icon_sets/volume/volume2.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.volume_button.setIcon(icon9) else: icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap("icon_sets/volume/volume1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.volume_button.setIcon(icon9) self.volumeslider.setValue(vol) self.mediaPlayer.setVolume(vol) def screenshot(self): print('[ ! SCREENSHOT ]') wincen = Form.geometry() topX = wincen.topLeft().x() topY = wincen.topLeft().y() geo = self.video_playback.geometry() image = pyautogui.screenshot(region=(topX, topY + 38, geo.width(), geo.height() - 35)) filename = "screenshot" + str(uuid.uuid4()) + ".png" username = getpass.getuser() if sys.platform == 'win32': path = 'C:/Users/' + username + '/Pictures/' + filename elif sys.platform == 'linux' or sys.platform == 'Darwin': path = '/home/' + username + '/Pictures/' + filename image.save(path) def EscFun(self): if self.video_playback.isFullScreen(): Form.show() self.video_playback.setFullScreen(False) def fullscreen_video(self): if self.mediaPlayer.isVideoAvailable(): if self.video_playback.isFullScreen(): self.video_playback.setFullScreen(False) print('[ ! Normal Screen ]') Form.show() else: print('[ ! Full Screen ]') self.video_playback.setFullScreen(True) Form.hide() def getFormat(self): li = [k for k in self.streams.keys()] for q in li: if q.startswith('audio'): li.remove(q) try: li.remove('audio_opus') except ValueError: pass return li def changeQuality(self, quality): pos = self.mediaPlayer.position() try: self.mediaPlayer.setMedia( QMediaContent(QUrl(self.streams[quality]))) except KeyError: pass self.setPosition(pos) self.mediaPlayer.play() def handleSetting(self): from setting import SettingDialog dlg = SettingDialog() dlg.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) dlg.exec_() def addQuality(self): self.Quality_box.clear() self.Quality_box.addItems(self.getFormat()) def handleQuality(self): if self.isOnline: self.changeQuality(self.Quality_box.currentText()) def forwardSlider(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() + 1000) def forwardSlider10(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() + 10000) def forwardSlider5(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() + 5000) def backSlider(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() - 1000) def backSlider10(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() - 10000) def backSlider5(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() - 5000) def volumeUp(self): self.setVolume(self.mediaPlayer.volume() + 10) print("Volume: " + str(self.mediaPlayer.volume())) def volumeDown(self): self.setVolume(self.mediaPlayer.volume() - 10) print("Volume: " + str(self.mediaPlayer.volume())) def max(self): if not Form.isMaximized(): print("[! Window is Maximized]") Form.showMaximized() else: print("[! Window is Normal]") Form.showNormal() def min(self): if not Form.isMinimized(): print("[! Window is Minimized]") Form.showMinimized() else: print("[! Window is Normal]") Form.showNormal() def exit(self): self.mediaPlayer.stop() sys.exit()
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 RecordTab(QWidget): def __init__(self): super().__init__() self.processed_fields = {} self.state = 'Waiting' # Setup Layouts self.mainLayout = QVBoxLayout() self.hBox = QHBoxLayout() self.outputGrid = QGridLayout() self.lGrpBox = QGroupBox('Output Options') self.lGrpBox.setLayout(self.outputGrid) self.rGrpBox = QGroupBox('Input Device') self.rVBox = QVBoxLayout() self.rGrpBox.setLayout(self.rVBox) # Center self.recordBtn = QPushButton() # Microphone Stream and live microphone canvas self.tempFile = None self.stream = MicrophoneStream() self.stream.recordingDone.connect(self.on_recording_done) self.canvasStack = QStackedWidget() self.microphoneCanvas = MicrophoneCanvas(self.stream) self.recordBtn.clicked.connect(self.stream.toggle_recording) self.recordBtn.clicked.connect(self.on_recording_toggle) self.plotCanvas = PlotCanvas(self) self.canvasStack.addWidget(self.microphoneCanvas) self.canvasStack.addWidget(self.plotCanvas) self.canvasStack.setCurrentWidget(self.microphoneCanvas) self.mainLayout.addWidget(self.canvasStack, 5) # Output Options (LEFT) self.dbLabel = QLabel('Database:') self.dbPath = QLineEdit(os.path.dirname(__file__)) self.dbBrowse = QPushButton('Browse') self.dbBrowse.clicked.connect(self.on_browse_db) self.dbChk = QCheckBox() self.dbChk.clicked.connect(self.handle_chk_state) Utils.addWidgets_to_grid(self.outputGrid, [(self.dbLabel, 1, 1, 1, 1), (self.dbPath, 1, 2, 1, 1), (self.dbBrowse, 1, 3, 1, 1), (self.dbChk, 1, 4, 1, 1)]) self.savePathLabel = QLabel('Directory:') self.savePath = QLineEdit(os.path.dirname(__file__)) self.savePathBrowse = QPushButton('Browse') self.savePathBrowse.clicked.connect(self.on_browse_local_dir) self.savePathChk = QCheckBox() self.savePathChk.setChecked(True) self.savePathChk.clicked.connect(self.handle_chk_state) Utils.addWidgets_to_grid(self.outputGrid, [(self.savePathLabel, 2, 1, 1, 1), (self.savePath, 2, 2, 1, 1), (self.savePathBrowse, 2, 3, 1, 1), (self.savePathChk, 2, 4, 1, 1)]) self.saveNameLabel = QLabel('Name:') self.saveName = QLineEdit() Utils.addWidgets_to_grid(self.outputGrid, [(self.saveNameLabel, 3, 1, 1, 1), (self.saveName, 3, 2, 1, 2)]) self.deleteBtn = QPushButton('Cancel') self.deleteBtn.setEnabled(False) self.deleteBtn.clicked.connect(self.on_press_delete) self.saveBtn = QPushButton('Save') self.saveBtn.setEnabled(False) self.saveBtn.clicked.connect(self.on_press_save) Utils.addWidgets_to_grid(self.outputGrid, [(self.saveBtn, 4, 1, 1, 2), (self.deleteBtn, 4, 3, 1, 2)]) self.outputGrid.setColumnMinimumWidth(4, 20) # Sound Device Controls (RIGHT) self.inputDropDown = QComboBox() self.inputDevices = [ device['name'] for device in sounddevice.query_devices() if device['max_input_channels'] > 0 ] self.inputDropDown.addItems(self.inputDevices) activeDevice = sounddevice.query_devices( device=self.stream.inputStream.device) self.inputDropDown.setCurrentIndex( self.inputDevices.index(activeDevice['name'])) self.inputDropDown.currentIndexChanged.connect(self.on_input_changed) # Add widgets to layouts self.mainLayout.addLayout(self.hBox, 1) self.hBox.addWidget(self.lGrpBox, 5) self.hBox.addStretch(2) self.hBox.addWidget(self.recordBtn, 3) self.hBox.addStretch(1) self.hBox.addWidget(self.rGrpBox, 1) Utils.setAlignments(self.hBox, [(self.recordBtn, Qt.AlignCenter), (self.rGrpBox, Qt.AlignRight)]) self.rVBox.addWidget(self.inputDropDown) self.setLayout(self.mainLayout) # Style self.recordBtn.setIcon(QIcon(r'.\assets\record.png')) self.recordBtn.setIconSize(QSize(100, 100)) self.recordBtn.setStyleSheet('QPushButton{border: 1px solid;}') # Shortcuts self.recordShortCut = QShortcut(QKeySequence('Space'), self) self.recordShortCut.activated.connect(self.on_recording_toggle) self.recordShortCut.activated.connect(self.stream.toggle_recording) self.handle_chk_state() def handle_chk_state(self): self.dbBrowse.setEnabled(self.dbChk.isChecked()) self.dbPath.setEnabled(self.dbChk.isChecked()) self.savePathBrowse.setEnabled(self.savePathChk.isChecked()) self.savePath.setEnabled(self.savePathChk.isChecked()) self.saveName.setEnabled(self.savePathChk.isChecked()) if self.state == 'saving': if not self.dbChk.isChecked() and not self.savePathChk.isChecked(): self.saveBtn.setEnabled(False) else: self.saveBtn.setEnabled(True) def on_browse_local_dir(self): options = QFileDialog.Options() path = QFileDialog.getExistingDirectory( self, caption="Choose save directory", options=options) self.savePath.setText(path) def on_browse_db(self): options = QFileDialog.Options() path, _ = QFileDialog.getOpenFileName(self, "Open .db File", "", "Sqlite3 DB File (*.db)", options=options) self.dbPath.setText(path) @pyqtSlot(int) def on_input_changed(self, index): deviceName = self.inputDevices[index] for i, dev in enumerate(sounddevice.query_devices()): if dev['name'] == deviceName: device = i break else: device = sounddevice.default.device[0] print('Failed to set input device') self.stream.inputStream.stop() self.stream.inputStream = sounddevice.InputStream( samplerate=self.stream.inputStream.samplerate, device=device, channels=1, callback=self.stream.audio_callback) self.stream.inputStream.start() @pyqtSlot() def on_recording_toggle(self): if self.state == 'Waiting': self.on_recording_start() @pyqtSlot() def on_recording_start(self): self.inputDropDown.setEnabled(False) self.saveBtn.setEnabled(False) self.deleteBtn.setEnabled(False) self.recordBtn.setIcon(QIcon(r'.\assets\stopWithSquare.png')) self.state = 'recording' @pyqtSlot(tuple) def on_recording_done(self, tempFile): """ Triggered when the microphone stream has finished recording. Enables / Disables appropriate buttons and receives tempFile from the microphone stream. Also processes an FFT for display :param tempFile: (file handle: int, file path: str) temporary audio file generated by the microphone stream to store the audio while the user decides whether or not to save it""" self.state = 'saving' self.handle_chk_state() self.deleteBtn.setEnabled(True) self.recordBtn.setEnabled(False) self.recordShortCut.setEnabled(False) self.recordBtn.setIcon(QIcon(r'.\assets\record.png')) self.inputDropDown.setEnabled(True) self.tempFile = tempFile # Process FFT tChopped, vChopped, fVals, \ powerFFT, peakFreqs, peakAmps = Utils.AnalyzeFFT(tempFile[1], tChop=None) # Get these fields ready for a possible insertion into DB self.processed_fields['PCM'] = str(list(vChopped)) self.processed_fields['Date'] = str(datetime.datetime.now()) self.processed_fields['FFT_Processed'] = str(list(powerFFT)) self.processed_fields['Sample_Rate'] = str(self.stream.sampleRate) self.processed_fields['Hash'] = hashlib.sha256( str(list(powerFFT)).encode('utf-8')).hexdigest() self.processed_fields['Peaks_Processed'] = [] for freq, amp in zip(peakFreqs, peakAmps): self.processed_fields['Peaks_Processed'].append({ "Frequency": freq, "Amplitude": amp }) self.processed_fields['Peaks_Processed'].sort( reverse=True, key=lambda peak: peak['Amplitude']) self.processed_fields['Peaks_Processed'] = str( self.processed_fields['Peaks_Processed']) os.close(self.tempFile[0]) os.remove(self.tempFile[1]) # Make a new .wav file from the processed data self.tempFile = tempfile.mkstemp(prefix='temp_processed_', suffix='.wav', dir='') fileStream = soundfile.SoundFile(self.tempFile[1], mode='w', samplerate=self.stream.sampleRate, channels=max(self.stream.channels), subtype=self.stream.subtype) fileStream.write(vChopped) fileStream.close() self.plotCanvas.plot(tChopped, vChopped, fVals, powerFFT, peakFreqs, peakAmps, 'Impulse Recording') self.canvasStack.setCurrentWidget(self.plotCanvas) @pyqtSlot() def on_press_save(self): """ Triggered when the save button is pressed. Opens a save file dialog to permanently save the temporary audio file. Removes temporary file after.""" if self.dbChk.isChecked(): self.processed_fields['db'] = self.dbPath.text() self.dbForm = DBFormWindow(self.processed_fields, self) self.dbForm.show() if self.savePathChk.isChecked(): if self.savePath.text(): shutil.copy( self.tempFile[1], os.path.join(self.savePath.text(), self.saveName.text() + '.wav')) os.close(self.tempFile[0]) os.remove(self.tempFile[1]) QMessageBox.information( self, 'Saved', f'Saved to: {os.path.join(self.savePath.text(), self.saveName.text()+".wav")}' ) self.saveBtn.setEnabled(False) self.deleteBtn.setEnabled(False) self.recordBtn.setEnabled(True) self.recordBtn.setIcon(QIcon(r'.\assets\record.png')) self.recordShortCut.setEnabled(True) self.inputDropDown.setEnabled(True) self.canvasStack.setCurrentWidget(self.microphoneCanvas) self.state = 'Waiting' @pyqtSlot() def on_press_delete(self): """ Triggered when the delete button is pressed. Removes temporary audio file""" os.close(self.tempFile[0]) os.remove(self.tempFile[1]) self.recordBtn.setEnabled(True) self.recordBtn.setIcon(QIcon(r'.\assets\record.png')) self.deleteBtn.setEnabled(False) self.saveBtn.setEnabled(False) self.inputDropDown.setEnabled(True) self.canvasStack.setCurrentWidget(self.microphoneCanvas) self.state = 'Waiting'
class EditTerrainDialog(QDialog): def __init__(self, mapDocument, tileset, parent = None): super().__init__(parent) self.mUi = Ui_EditTerrainDialog() self.mMapDocument = mapDocument self.mInitialUndoStackIndex = self.mMapDocument.undoStack().index() self.mTileset = tileset self.mUi.setupUi(self) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) Utils.setThemeIcon(self.mUi.redo, "edit-redo") Utils.setThemeIcon(self.mUi.undo, "edit-undo") zoomable = Zoomable(self) zoomable.connectToComboBox(self.mUi.zoomComboBox) tilesetModel = TilesetModel(self.mTileset, self.mUi.tilesetView) mapDocument.tileTerrainChanged.connect(tilesetModel.tilesChanged) self.mUi.tilesetView.setEditTerrain(True) self.mUi.tilesetView.setMapDocument(mapDocument) self.mUi.tilesetView.setZoomable(zoomable) self.mUi.tilesetView.setModel(tilesetModel) self.mTerrainModel = mapDocument.terrainModel() rootIndex = self.mTerrainModel.index(tileset) self.mUi.terrainList.setMapDocument(mapDocument) self.mUi.terrainList.setModel(self.mTerrainModel) self.mUi.terrainList.setRootIndex(rootIndex) terrainListHeader = self.mUi.terrainList.header() terrainListHeader.setSectionResizeMode(0, QHeaderView.ResizeToContents) selectionModel = self.mUi.terrainList.selectionModel() selectionModel.currentRowChanged.connect(self.selectedTerrainChanged) if (self.mTerrainModel.rowCount(rootIndex) > 0): selectionModel.setCurrentIndex(self.mTerrainModel.index(0, 0, rootIndex), QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) self.mUi.terrainList.setFocus() self.mUi.eraseTerrain.toggled.connect(self.eraseTerrainToggled) self.mUi.addTerrainTypeButton.clicked.connect(self.addTerrainType) self.mUi.removeTerrainTypeButton.clicked.connect(self.removeTerrainType) self.mUi.tilesetView.createNewTerrainSignal.connect(self.addTerrainType) self.mUi.tilesetView.terrainImageSelected.connect(self.setTerrainImage) undoStack = mapDocument.undoStack() undoStack.indexChanged.connect(self.updateUndoButton) undoStack.canRedoChanged.connect(self.mUi.redo.setEnabled) self.mUi.undo.clicked.connect(undoStack.undo) self.mUi.redo.clicked.connect(undoStack.redo) self.mUndoShortcut = QShortcut(QKeySequence.Undo, self) self.mRedoShortcut = QShortcut(QKeySequence.Redo, self) self.mUndoShortcut.activated.connect(undoStack.undo) self.mRedoShortcut.activated.connect(undoStack.redo) eraseShortcut = QShortcut(QKeySequence(self.tr("E")), self) eraseShortcut.activated.connect(self.mUi.eraseTerrain.toggle) self.updateUndoButton() Utils.restoreGeometry(self) def __del__(self): Utils.saveGeometry(self) del self.mUi def selectedTerrainChanged(self, index): terrainId = -1 terrain = self.mTerrainModel.terrainAt(index) if terrain: terrainId = terrain.id() self.mUi.tilesetView.setTerrainId(terrainId) self.mUi.removeTerrainTypeButton.setEnabled(terrainId != -1) def eraseTerrainToggled(self, checked): self.mUi.tilesetView.setEraseTerrain(checked) def addTerrainType(self, tile = None): if tile: x = tile.id() else: x = -1 terrain = Terrain(self.mTileset.terrainCount(), self.mTileset, QString(), x) terrain.setName(self.tr("New Terrain")) self.mMapDocument.undoStack().push(AddTerrain(self.mMapDocument, terrain)) # Select the newly added terrain and edit its name index = self.mTerrainModel.index(terrain) selectionModel = self.mUi.terrainList.selectionModel() selectionModel.setCurrentIndex(index, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) self.mUi.terrainList.edit(index) def removeTerrainType(self): currentIndex = self.mUi.terrainList.currentIndex() if (not currentIndex.isValid()): return terrain = self.mTerrainModel.terrainAt(currentIndex) removeTerrain = RemoveTerrain(self.mMapDocument, terrain) ## # Clear any references to the terrain that is about to be removed with # an undo command, as a way of preserving them when undoing the removal # of the terrain. ## changes = ChangeTileTerrain.Changes() for tile in terrain.tileset().tiles(): tileTerrain = tile.terrain() for corner in range(4): if (tile.cornerTerrainId(corner) == terrain.id()): tileTerrain = setTerrainCorner(tileTerrain, corner, 0xFF) if (tileTerrain != tile.terrain()): changes.insert(tile, ChangeTileTerrain.Change(tile.terrain(), tileTerrain)) undoStack = self.mMapDocument.undoStack() if (not changes.isEmpty()): undoStack.beginMacro(removeTerrain.text()) undoStack.push(ChangeTileTerrain(self.mMapDocument, changes)) self.mMapDocument.undoStack().push(removeTerrain) if (not changes.isEmpty()): undoStack.endMacro() ## # Removing a terrain usually changes the selected terrain without the # selection changing rows, so we can't rely on the currentRowChanged # signal. ## self.selectedTerrainChanged(self.mUi.terrainList.currentIndex()) def setTerrainImage(self, tile): currentIndex = self.mUi.terrainList.currentIndex() if (not currentIndex.isValid()): return terrain = self.mTerrainModel.terrainAt(currentIndex) self.mMapDocument.undoStack().push(SetTerrainImage(self.mMapDocument, terrain.tileset(), terrain.id(), tile.id())) def updateUndoButton(self): undoStack = self.mMapDocument.undoStack() canUndo = undoStack.index() > self.mInitialUndoStackIndex canRedo = undoStack.canRedo() self.mUi.undo.setEnabled(canUndo) self.mUi.redo.setEnabled(canRedo) self.mUndoShortcut.setEnabled(canUndo) self.mRedoShortcut.setEnabled(canRedo)
class EditTerrainDialog(QDialog): def __init__(self, mapDocument, tileset, parent=None): super().__init__(parent) self.mUi = Ui_EditTerrainDialog() self.mMapDocument = mapDocument self.mInitialUndoStackIndex = self.mMapDocument.undoStack().index() self.mTileset = tileset self.mUi.setupUi(self) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) Utils.setThemeIcon(self.mUi.redo, "edit-redo") Utils.setThemeIcon(self.mUi.undo, "edit-undo") zoomable = Zoomable(self) zoomable.connectToComboBox(self.mUi.zoomComboBox) tilesetModel = TilesetModel(self.mTileset, self.mUi.tilesetView) mapDocument.tileTerrainChanged.connect(tilesetModel.tilesChanged) self.mUi.tilesetView.setEditTerrain(True) self.mUi.tilesetView.setMapDocument(mapDocument) self.mUi.tilesetView.setZoomable(zoomable) self.mUi.tilesetView.setModel(tilesetModel) self.mTerrainModel = mapDocument.terrainModel() rootIndex = self.mTerrainModel.index(tileset) self.mUi.terrainList.setMapDocument(mapDocument) self.mUi.terrainList.setModel(self.mTerrainModel) self.mUi.terrainList.setRootIndex(rootIndex) terrainListHeader = self.mUi.terrainList.header() terrainListHeader.setSectionResizeMode(0, QHeaderView.ResizeToContents) selectionModel = self.mUi.terrainList.selectionModel() selectionModel.currentRowChanged.connect(self.selectedTerrainChanged) if (self.mTerrainModel.rowCount(rootIndex) > 0): selectionModel.setCurrentIndex( self.mTerrainModel.index(0, 0, rootIndex), QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) self.mUi.terrainList.setFocus() self.mUi.eraseTerrain.toggled.connect(self.eraseTerrainToggled) self.mUi.addTerrainTypeButton.clicked.connect(self.addTerrainType) self.mUi.removeTerrainTypeButton.clicked.connect( self.removeTerrainType) self.mUi.tilesetView.createNewTerrainSignal.connect( self.addTerrainType) self.mUi.tilesetView.terrainImageSelected.connect(self.setTerrainImage) undoStack = mapDocument.undoStack() undoStack.indexChanged.connect(self.updateUndoButton) undoStack.canRedoChanged.connect(self.mUi.redo.setEnabled) self.mUi.undo.clicked.connect(undoStack.undo) self.mUi.redo.clicked.connect(undoStack.redo) self.mUndoShortcut = QShortcut(QKeySequence.Undo, self) self.mRedoShortcut = QShortcut(QKeySequence.Redo, self) self.mUndoShortcut.activated.connect(undoStack.undo) self.mRedoShortcut.activated.connect(undoStack.redo) eraseShortcut = QShortcut(QKeySequence(self.tr("E")), self) eraseShortcut.activated.connect(self.mUi.eraseTerrain.toggle) self.updateUndoButton() Utils.restoreGeometry(self) def __del__(self): Utils.saveGeometry(self) del self.mUi def selectedTerrainChanged(self, index): terrainId = -1 terrain = self.mTerrainModel.terrainAt(index) if terrain: terrainId = terrain.id() self.mUi.tilesetView.setTerrainId(terrainId) self.mUi.removeTerrainTypeButton.setEnabled(terrainId != -1) def eraseTerrainToggled(self, checked): self.mUi.tilesetView.setEraseTerrain(checked) def addTerrainType(self, tile=None): if tile: x = tile.id() else: x = -1 terrain = Terrain(self.mTileset.terrainCount(), self.mTileset, QString(), x) terrain.setName(self.tr("New Terrain")) self.mMapDocument.undoStack().push( AddTerrain(self.mMapDocument, terrain)) # Select the newly added terrain and edit its name index = self.mTerrainModel.index(terrain) selectionModel = self.mUi.terrainList.selectionModel() selectionModel.setCurrentIndex( index, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) self.mUi.terrainList.edit(index) def removeTerrainType(self): currentIndex = self.mUi.terrainList.currentIndex() if (not currentIndex.isValid()): return terrain = self.mTerrainModel.terrainAt(currentIndex) removeTerrain = RemoveTerrain(self.mMapDocument, terrain) ## # Clear any references to the terrain that is about to be removed with # an undo command, as a way of preserving them when undoing the removal # of the terrain. ## changes = ChangeTileTerrain.Changes() for tile in terrain.tileset().tiles(): tileTerrain = tile.terrain() for corner in range(4): if (tile.cornerTerrainId(corner) == terrain.id()): tileTerrain = setTerrainCorner(tileTerrain, corner, 0xFF) if (tileTerrain != tile.terrain()): changes.insert( tile, ChangeTileTerrain.Change(tile.terrain(), tileTerrain)) undoStack = self.mMapDocument.undoStack() if (not changes.isEmpty()): undoStack.beginMacro(removeTerrain.text()) undoStack.push(ChangeTileTerrain(self.mMapDocument, changes)) self.mMapDocument.undoStack().push(removeTerrain) if (not changes.isEmpty()): undoStack.endMacro() ## # Removing a terrain usually changes the selected terrain without the # selection changing rows, so we can't rely on the currentRowChanged # signal. ## self.selectedTerrainChanged(self.mUi.terrainList.currentIndex()) def setTerrainImage(self, tile): currentIndex = self.mUi.terrainList.currentIndex() if (not currentIndex.isValid()): return terrain = self.mTerrainModel.terrainAt(currentIndex) self.mMapDocument.undoStack().push( SetTerrainImage(self.mMapDocument, terrain.tileset(), terrain.id(), tile.id())) def updateUndoButton(self): undoStack = self.mMapDocument.undoStack() canUndo = undoStack.index() > self.mInitialUndoStackIndex canRedo = undoStack.canRedo() self.mUi.undo.setEnabled(canUndo) self.mUi.redo.setEnabled(canRedo) self.mUndoShortcut.setEnabled(canUndo) self.mRedoShortcut.setEnabled(canRedo)
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")
class UPythonShell(QsciScintillaCompat): """ Class implementing a graphical Python shell. A user can enter commands that are executed in the remote Python interpreter. @signal searchStringFound(found) emitted to indicate the search result (boolean) """ searchStringFound = pyqtSignal(bool) focusChanged = pyqtSignal(bool) def __init__(self, dbs, vm, parent=None): """ Constructor @param dbs reference to the debug server object @param vm reference to the viewmanager object @param parent parent widget (QWidget) """ super(UPythonShell, self).__init__(parent) self.__rxBuffer = None self.patch() self.setUtf8(True) self.vm = vm self.__mainWindow = parent self.__lastSearch = () self.__viewManager = e5App().getObject("ViewManager") self.linesepRegExp = r"\r\n|\n|\r" self.setWindowTitle(self.tr('Pycom Device Shell')) # #todo improve this text (original was more complete) self.setWhatsThis( self.tr( """<b>The Pycom Shell Window</b>""" """<p>This is a direct connection into a Pycom Device</p>""")) self.linesChanged.connect(self.__resizeLinenoMargin) dbs.dataReceptionEvent.connect(self.__write) self.dbs = dbs # Initialize instance variables. self.__initialize() self.prline = 0 self.prcol = 0 self.inDragDrop = False self.lexer_ = None self.clientType = 'Python3' # clear QScintilla defined keyboard commands # we do our own handling through the view manager self.__actionsAdded = False # Create a little context menu self.menu = QMenu(self) self.menu.addAction(self.tr('Cut'), self.cut) self.menu.addAction(self.tr('Copy'), self.copy) self.menu.addAction(self.tr('Paste'), self.__paste) self.menu.addSeparator() self.menu.addAction(self.tr('Find'), self.__find) self.menu.addSeparator() self.menu.addAction(self.tr('Clear'), self.clear) self.menu.addAction(self.tr('Reset'), self.__reset) self.menu.addSeparator() self.menu.addAction(self.tr("Configure..."), self.__configure) self.__bindLexer() self.__setTextDisplay() self.__setMargin0() self.incrementalSearchString = "" self.incrementalSearchActive = False self.grabGesture(Qt.PinchGesture) self.focusChanged.connect(self.__focusChanged) self.dbs.statusChanged.connect(self.notifyStatus) self.dbs.pyboardError.connect(self.pyboardError) self.__printWelcome() # advanced key features self.ctrl_active = False self.cmd_active = False def __focusChanged(self, result): self.dbs.tryConnecting(result) self.ctrl_active = False self.cmd_active = False def __toVT100(self, key): key = key.key() if key == Qt.Key_Up: return b'\x1b[A' elif key == Qt.Key_Down: return b'\x1b[B' elif key == Qt.Key_Right: return b'\x1b[C' elif key == Qt.Key_Left: return b'\x1b[D' elif key == Qt.Key_Home: return b'\x01' elif key == Qt.Key_End: return b'\x05' return '' def patch(self): self.passive = False def keyReleaseEvent(self, ev): if ev.key() == Qt.Key_Control: self.ctrl_active = False elif ev.key() == Qt.Key_Meta: self.cmd_active = False def keyPressEvent(self, ev): """ Protected method to handle the user input a key at a time. @param ev key event (QKeyEvent) """ if ev.key( ) == Qt.Key_Enter: # make mac right "enter" key behave as "return" txt = '\r' else: txt = ev.text() if not txt: txt = self.__toVT100(ev) if txt: self.dbs.send(txt) ev.accept() else: ev.ignore osFamily = sys.platform.rstrip('1234567890') if osFamily == 'win32' or osFamily == 'win64': osFamily == 'win' if ev.key() == Qt.Key_Control: self.ctrl_active = True elif ev.key() == Qt.Key_Meta: self.cmd_active = True # ctrl-c ctrl-v logic. if (self.ctrl_active or self.cmd_active) and ev.key() == Qt.Key_V: self.__paste() elif (self.ctrl_active or self.cmd_active) and ev.key() == Qt.Key_C: if self.cmd_active: self.__stopRunningPrograms() elif self.ctrl_active: if osFamily == 'win': # for windows, we want the ctrl to do both copy as well as reset if self.hasSelectedText(): self.copy() # else: # self.__stopRunningPrograms() # turns out, this is not needed, because ctrl-c already works in windows! somehow.... else: self.copy() def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton and self.hasFocus(): self.focusChanged.emit(True) super(UPythonShell, self).mouseReleaseEvent(event) def focusInEvent(self, event): """ Protected method called when the shell receives focus. @param event the event object (QFocusEvent) """ self.setFocus() if not self.__actionsAdded: self.addActions(self.vm.copyActGrp.actions()) self.addActions(self.vm.viewActGrp.actions()) self.__searchShortcut = QShortcut(self.vm.searchAct.shortcut(), self, self.__find, self.__find) self.__searchNextShortcut = QShortcut( self.vm.searchNextAct.shortcut(), self, self.__searchNext, self.__searchNext) self.__searchPrevShortcut = QShortcut( self.vm.searchPrevAct.shortcut(), self, self.__searchPrev, self.__searchPrev) try: self.vm.editorActGrp.setEnabled(False) self.vm.copyActGrp.setEnabled(True) self.vm.viewActGrp.setEnabled(True) self.vm.searchActGrp.setEnabled(False) except AttributeError: pass self.__searchShortcut.setEnabled(True) self.__searchNextShortcut.setEnabled(True) self.__searchPrevShortcut.setEnabled(True) self.setCaretWidth(self.caretWidth) self.setCursorFlashTime(QApplication.cursorFlashTime()) # moved the next line to the mouseRelease event # self.focusChanged.emit(True) super(UPythonShell, self).focusInEvent(event) def focusOutEvent(self, event): """ Protected method called when the shell loses focus. @param event the event object (QFocusEvent) """ self.__searchShortcut.setEnabled(False) self.__searchNextShortcut.setEnabled(False) self.__searchPrevShortcut.setEnabled(False) self.setCaretWidth(0) self.focusChanged.emit(False) super(UPythonShell, self).focusOutEvent(event) def refreshLexer(self): self.__bindLexer() self.__setTextDisplay() self.__setMargin0() def __bindLexer(self, language='Python3'): """ Private slot to set the lexer. @param language lexer language to set (string) """ self.language = language if Preferences.getShell("SyntaxHighlightingEnabled"): from QScintilla import Lexers self.lexer_ = Lexers.getLexer(self.language, self) else: self.lexer_ = None if self.lexer_ is None: self.setLexer(None) font = Preferences.getShell("MonospacedFont") self.monospacedStyles(font) return # get the font for style 0 and set it as the default font key = 'Scintilla/{0}/style0/font'.format(self.lexer_.language()) fdesc = Preferences.Prefs.settings.value(key) if fdesc is not None: font = QFont(fdesc[0], int(fdesc[1])) self.lexer_.setDefaultFont(font) self.setLexer(self.lexer_) self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") # initialize the lexer APIs settings api = self.vm.getAPIsManager().getAPIs(self.language) if api is not None: api = api.getQsciAPIs() if api is not None: self.lexer_.setAPIs(api) self.lexer_.setDefaultColor(self.lexer_.color(0)) self.lexer_.setDefaultPaper(self.lexer_.paper(0)) def __setMargin0(self): """ Private method to configure margin 0. """ # set the settings for all margins self.setMarginsFont(Preferences.getShell("MarginsFont")) self.setMarginsForegroundColor( Preferences.getEditorColour("MarginsForeground")) self.setMarginsBackgroundColor( Preferences.getEditorColour("MarginsBackground")) # set margin 0 settings linenoMargin = Preferences.getShell("LinenoMargin") self.setMarginLineNumbers(0, linenoMargin) if linenoMargin: self.__resizeLinenoMargin() else: self.setMarginWidth(0, 0) # disable margins 1 and 2 self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) def __setTextDisplay(self): """ Private method to configure the text display. """ self.setTabWidth(Preferences.getEditor("TabWidth")) if Preferences.getEditor("ShowWhitespace"): self.setWhitespaceVisibility(QsciScintilla.WsVisible) try: self.setWhitespaceForegroundColor( Preferences.getEditorColour("WhitespaceForeground")) self.setWhitespaceBackgroundColor( Preferences.getEditorColour("WhitespaceBackground")) self.setWhitespaceSize(Preferences.getEditor("WhitespaceSize")) except AttributeError: # QScintilla before 2.5 doesn't support this pass else: self.setWhitespaceVisibility(QsciScintilla.WsInvisible) self.setEolVisibility(Preferences.getEditor("ShowEOL")) if Preferences.getEditor("BraceHighlighting"): self.setBraceMatching(QsciScintilla.SloppyBraceMatch) else: self.setBraceMatching(QsciScintilla.NoBraceMatch) self.setMatchedBraceForegroundColor( Preferences.getEditorColour("MatchingBrace")) self.setMatchedBraceBackgroundColor( Preferences.getEditorColour("MatchingBraceBack")) self.setUnmatchedBraceForegroundColor( Preferences.getEditorColour("NonmatchingBrace")) self.setUnmatchedBraceBackgroundColor( Preferences.getEditorColour("NonmatchingBraceBack")) if Preferences.getEditor("CustomSelectionColours"): self.setSelectionBackgroundColor( Preferences.getEditorColour("SelectionBackground")) else: self.setSelectionBackgroundColor(QApplication.palette().color( QPalette.Highlight)) if Preferences.getEditor("ColourizeSelText"): self.resetSelectionForegroundColor() elif Preferences.getEditor("CustomSelectionColours"): self.setSelectionForegroundColor( Preferences.getEditorColour("SelectionForeground")) else: self.setSelectionForegroundColor(QApplication.palette().color( QPalette.HighlightedText)) self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol")) self.setCaretForegroundColor( Preferences.getEditorColour("CaretForeground")) self.setCaretLineVisible(False) self.caretWidth = Preferences.getEditor("CaretWidth") self.setCaretWidth(self.caretWidth) if Preferences.getShell("WrapEnabled"): self.setWrapMode(QsciScintilla.WrapWord) else: self.setWrapMode(QsciScintilla.WrapNone) self.useMonospaced = Preferences.getShell("UseMonospacedFont") self.__setMonospaced(self.useMonospaced) self.setCursorFlashTime(QApplication.cursorFlashTime()) if Preferences.getEditor("OverrideEditAreaColours"): self.setColor(Preferences.getEditorColour("EditAreaForeground")) self.setPaper(Preferences.getEditorColour("EditAreaBackground")) def __setMonospaced(self, on): """ Private method to set/reset a monospaced font. @param on flag to indicate usage of a monospace font (boolean) """ if on: if not self.lexer_: f = Preferences.getShell("MonospacedFont") self.monospacedStyles(f) else: if not self.lexer_: self.clearStyles() self.__setMargin0() self.setFont(Preferences.getShell("MonospacedFont")) self.useMonospaced = on def __resizeLinenoMargin(self): """ Private slot to resize the line numbers margin. """ linenoMargin = Preferences.getShell("LinenoMargin") if linenoMargin: self.setMarginWidth(0, '8' * (len(str(self.lines())) + 1)) def __isCursorOnLastLine(self): """ Private method to check, if the cursor is on the last line. @return flag indicating that the cursor is on the last line (boolean) """ cline, ccol = self.getCursorPosition() return cline == self.lines() - 1 def __getEndPos(self): """ Private method to return the line and column of the last character. @return tuple of two values (int, int) giving the line and column """ line = self.lines() - 1 return (line, len(self.text(line))) def __find(self): """ Private slot to show the find widget. """ txt = self.selectedText() self.__mainWindow.showFind(txt) def __searchNext(self): """ Private method to search for the next occurrence. """ if self.__lastSearch: self.searchNext(*self.__lastSearch) def searchNext(self, txt, caseSensitive, wholeWord): """ Public method to search the next occurrence of the given text. @param txt text to search for (string) @param caseSensitive flag indicating to perform a case sensitive search (boolean) @param wholeWord flag indicating to search for whole words only (boolean) """ self.__lastSearch = (txt, caseSensitive, wholeWord) ok = self.findFirst(txt, False, caseSensitive, wholeWord, False, forward=True) self.searchStringFound.emit(ok) def __searchPrev(self): """ Private method to search for the next occurrence. """ if self.__lastSearch: self.searchPrev(*self.__lastSearch) def searchPrev(self, txt, caseSensitive, wholeWord): """ Public method to search the previous occurrence of the given text. @param txt text to search for (string) @param caseSensitive flag indicating to perform a case sensitive search (boolean) @param wholeWord flag indicating to search for whole words only (boolean) """ self.__lastSearch = (txt, caseSensitive, wholeWord) if self.hasSelectedText(): line, index = self.getSelection()[:2] else: line, index = -1, -1 ok = self.findFirst(txt, False, caseSensitive, wholeWord, False, forward=False, line=line, index=index) self.searchStringFound.emit(ok) def contextMenuEvent(self, ev): """ Protected method to show our own context menu. @param ev context menu event (QContextMenuEvent) """ self.menu.popup(ev.globalPos()) ev.accept() def __reset(self): """ Private slot to handle the 'reset' context menu entry. """ self.dbs.restart() def __stopRunningPrograms(self): """ Private slot to handle a ctrl c in the console """ self.dbs.send(b'\x03') # self.dbs.stopRunningPrograms() def __initialize(self): """ Private method to get ready for a new remote interpreter. """ self.buff = "" self.inContinue = False self.echoInput = True self.clientCapabilities = 0 self.inCommandExecution = False self.interruptCommandExecution = False def __moveCursorRel(self, relPos): line, col = self.getCursorPosition() col += relPos self.setCursorPosition(line, col) def __overwrite(self, text): line, col = self.getCursorPosition() self.setSelection(line, col, line, col + len(text)) text = text.replace('\x04', '') self.replaceSelectedText(text) def __insertNewLine(self): line, col = self.__getEndPos() self.setCursorPosition(line, col) self.__overwrite("\n") def __simulateVT100(self, s): if s[0] == b'\x08': self.moveCursorLeft() self.__movement = True return 1 elif s[0] == b'\r': return 1 elif s[0] == b'\n': self.__insertNewLine() self._movement = True return 1 elif s[0:2] == b'\x1b[': s = s[2:] # when the command itself is not in this message, add back to the buffer if len(s) == 0: return 0 # erase from cursor to end of line if s[0] == 'K': self.deleteLineRight() return 3 # Cursor Backward if s.find('D') != -1: pos = s.find('D') numChars = int(s[:pos]) self.__moveCursorRel(-numChars) self.__movement = True return 3 + pos # when there is data but no D or K, add it back to the buffer return 0 elif s[0] == b'\x1b': return 0 return -1 def __write(self, s): """ Private method to display some text. @param s text to be displayed (string) """ try: self.setCursorPosition(self.keyLine, self.keyCol) except: pass i = 0 if self.__rxBuffer: s = self.__rxBuffer + s self.__rxBuffer = None while i < len(s): self.__movement = False advance = self.__simulateVT100(s[i:]) if advance == -1: self.__overwrite(s[i]) i += 1 elif advance == 0: self.__rxBuffer = s[i:] break else: i += advance self.keyLine, self.keyCol = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(self.keyLine) def __insertTextNoEcho(self, s): """ Private method to insert some text at the end of the buffer without echoing it. @param s text to be inserted (string) """ self.buff += s self.prline, self.prcol = self.getCursorPosition() def __paste(self): text = QApplication.clipboard().text() text = re.sub(re.compile('\r|\n|\r\n', re.MULTILINE), '\n', text) text = re.sub(re.compile('^\s*', re.MULTILINE), '', text) text = text.replace('\n', '\r') self.dbs.send(text) def __clientStatement(self, more): """ Private method to handle the response from the debugger client. @param more flag indicating that more user input is required (boolean) """ self.inContinue = more self.__writePrompt() self.inCommandExecution = False def __writePrompt(self): """ Private method to write the prompt. """ self.__write('') def __configure(self): """ Private method to open the configuration dialog. """ e5App().getObject("UserInterface").showPreferences("MicroPython") def __printWelcome(self): if not self.dbs.isConfigured(): if not hasattr(QtGui, "qt_mac_set_native_menubar"): notConfiguredMsg = "Settings" else: notConfiguredMsg = "Pymakr" notConfiguredMsg = "Proceed to configure a Pycom device in: {0} > Preferences > Pycom Device\n".format( notConfiguredMsg) self.__write(self.tr(notConfiguredMsg)) else: self.notifyStatus("connecting") @pyqtSlot(str) def pyboardError(self, error): if self.dbs.uname: dev_str = self.dbs.uname[0] else: dev_str = "Pycom device" self.__write( self.tr("> {0}. (click to attempt to reconnect)\n".format(error))) @pyqtSlot(str) def notifyStatus(self, status): if self.dbs.uname: dev_str = self.dbs.uname[0] else: dev_str = "Pycom device" if status == "connecting": self.__write(self.tr("Connecting to a {0}...\n".format(dev_str))) if status == "connected": self.__write(self.tr("Connected\n")) elif status == "disconnected": self.__write(self.tr("Connection closed\n")) elif status == "lostconnection": self.__write( self. tr("Lost connection with the {0}! (click to attempt to reconnect)\n" .format(dev_str))) elif status == "softreset": self.__write(self.tr("Soft resetting the {0}\n".format(dev_str))) elif status == "error": self.__write( self. tr("Error while communicating with the {0}! (click to attempt to reconnect)\n" .format(dev_str))) elif status == "reattempt": self.__write(self.tr("Reattempting in a few seconds...\n")) elif status == "invcredentials": self.__write( self. tr("Invalid credentials, please check device username and password\n" )) elif status == "invaddress": self.__write( self.tr("Invalid device address, please check the settings\n")) elif status == "runinit": editor = self.__viewManager.activeWindow() code = editor.text() fn = editor.getFileName() txt = os.path.basename(fn) self.__write(self.tr("\n\nRunning {0}\n".format(txt))) elif status == "syncinit": self.__write( self.tr( "\n\nSyncing the project with the {0}. Please wait...\n". format(dev_str))) elif status == "syncend": self.__write(self.tr("Successfully synced!\n\n")) elif status == "syncfailed": self.__write(self.tr("Syncing failed!\n\n")) elif status == "syncfailed_connection": self.__write( self.tr( "Sync failed, connection to the {0} was lost\n\n".format( dev_str))) elif status == "syncfailed_busy": self.__write(self.tr("Sync is already in progress\n\n")) elif status == "syncfailed_project": self.__write( self.tr("Syncing failed, there is no project selected\n\n"))
class BetterEditor: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value("locale/userLocale")[0:2] locale_path = os.path.join(self.plugin_dir, "i18n", "BetterEditor_{}.qm".format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Init settings self.settings = QSettings() self.settings.beginGroup("plugins/bettereditor") # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate("BetterEditor", message) def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" # Create settings dialog self.settings_dialog = SettingsDialog(self.settings, self.iface.mainWindow()) self.settings_dialog.settingsChanged.connect(self.on_settings_changed) # Show Python console to trigger its creation self.python_console = self.iface.mainWindow().findChild(PythonConsole) if not self.python_console: self.iface.actionShowPythonDialog().trigger() self.python_console = self.iface.mainWindow().findChild( PythonConsole) self.python_console.hide() self.iface.actionShowPythonDialog().setChecked(False) self.about_action = QAction( QIcon(":/plugins/bettereditor/icons/about.svg"), self.tr("About"), parent=self.iface.mainWindow(), ) self.about_action.triggered.connect(self.show_about) self.settings_action = QAction( QIcon(":/images/themes/default/console/iconSettingsConsole.svg"), self.tr("Settings"), parent=self.iface.mainWindow(), ) self.settings_action.triggered.connect(self.show_settings) self.plugin_menu = self.iface.pluginMenu().addMenu( QIcon(":/plugins/bettereditor/icons/icon.svg"), "Better Editor") self.plugin_menu.addAction(self.about_action) self.plugin_menu.addAction(self.settings_action) self.toolbar = self.python_console.findChild(QToolBar) self.tab_widget = self.python_console.findChild(EditorTabWidget) # Connect current self.tab_widget.currentChanged.connect(self.customize_current_editor) # Tweak zoom shortcuts to prevent them from interfering # when typing '|' and '}' with an AZERTY (French) keyboard layout action_zoom_in = self.iface.mainWindow().findChild( QAction, "mActionZoomIn") action_zoom_out = self.iface.mainWindow().findChild( QAction, "mActionZoomOut") action_zoom_in.setShortcut("") action_zoom_out.setShortcut("") self.iface.initializationCompleted.connect( self.on_initialization_completed) for shortcut_name in ( "ZoomInToCanvas", "ZoomInToCanvas2", "ZoomIn2", "ZoomOutOfCanvas", ): shortcut = self.iface.mainWindow().findChild( QShortcut, shortcut_name) if shortcut: shortcut.setParent(self.iface.mapCanvas()) shortcut.setContext(Qt.WidgetWithChildrenShortcut) self.zoom_out_shortcut = QShortcut("Ctrl+Alt+-", self.iface.mapCanvas()) self.zoom_out_shortcut.setObjectName("MyZoomOut") self.zoom_out_shortcut.setContext(Qt.WidgetWithChildrenShortcut) self.zoom_out_shortcut.activated.connect(action_zoom_out.trigger) # Customize buttons tooltips save_button = self.python_console.widget().saveFileButton saveas_button = self.python_console.widget().saveAsFileButton run_button = self.python_console.widget().runScriptEditorButton save_button.setToolTip(f"<b>{save_button.text()}</b> (Ctrl+S)") saveas_button.setToolTip( f"<b>{saveas_button.text()}</b> (Ctrl+Shift+S)") run_button.setToolTip(f"<b>{run_button.text()}</b> (Ctrl+R)") # Create our own toggle comment action separator = self.toolbar.addSeparator() separator = separator.setObjectName("separator") self.toggle_comment_action = self.toolbar.addAction( QIcon( ":/images/themes/default/console/iconCommentEditorConsole.svg" ), self.tr("Toggle Comment"), ) self.toggle_comment_action.setObjectName("toggleComment") self.toggle_comment_action.triggered.connect(self.toggle_comment) self.toggle_comment_action.setShortcut("Ctrl+:") self.toggle_comment_action.setToolTip( f"<b>{self.toggle_comment_action.text()}</b> ({self.toggle_comment_action.shortcut().toString()})" ) # Check that submodules are installed self.black = None self.jedi = None self.check_dependencies() # Add format action self.format_action = self.toolbar.addAction( QIcon(r":/plugins/bettereditor/icons/wizard.svg"), self.tr("Format file")) self.format_action.setObjectName("format") self.format_action.setShortcut("Ctrl+Alt+F") self.format_action.triggered.connect(self.format_file) self.format_action.setToolTip( f"<b>{self.format_action.text()}</b> ({self.format_action.shortcut().toString()})" ) if not self.black: self.format_action.setEnabled(False) self.project = None if self.jedi: self.project = self.jedi.Project("", sys_path=sys.path, load_unsafe_extensions=True, smart_sys_path=False) patch(Editor, MonkeyEditor) patch(EditorTab, MonkeyEditorTab) patch(ScriptEdit, MonkeyScriptEditor) patch(ScriptEditorDialog, MonkeyScriptEditorDialog) ScriptEdit.project = self.project ScriptEdit.settings = self.settings self.oldAutoCloseBracketEditor = QSettings().value( "pythonConsole/autoCloseBracketEditor", False, bool) QSettings().setValue("pythonConsole/autoCloseBracketEditor", False) # Add insert icon from ressource action self.insert_resource_action = self.toolbar.addAction( QIcon(":/images/themes/default/propertyicons/diagram.svg"), self.tr("Insert resource path"), ) self.insert_resource_action.setObjectName("insertResource") self.insert_resource_action.triggered.connect(self.insert_resource) self.insert_resource_action.setToolTip( f"<b>{self.insert_resource_action.text()}</b>") # Add next / previous tab shortcuts self.next_tab_shortcut = QShortcut("Ctrl+PgDown", self.python_console) self.next_tab_shortcut.setObjectName("NextTab") self.next_tab_shortcut.activated.connect(self.go_to_next_tab) self.previous_tab_shortcut = QShortcut("Ctrl+PgUp", self.python_console) self.previous_tab_shortcut.setObjectName("PreviousTab") self.previous_tab_shortcut.activated.connect(self.go_to_previous_tab) self.on_settings_changed() if not self.black or not self.jedi: if not check_pip(): QMessageBox.warning( self.iface.mainWindow(), self.tr("Error"), self. tr("Pip is not installed. Try to get it, then restart QGIS, or manually install <b>black</b> and <b>jedi</b>" ), ) else: if not self.black: QMessageBox.warning( self.iface.mainWindow(), self.tr("Error"), self. tr("Unable to load <b>black</b>. Formatting will be disabled. You could try to manually install <b>black</b> with pip" ), ) if not self.jedi: # If check_module return true, an obsolete version was loaded, ad user was already informed if not check_module("jedi"): QMessageBox.warning( self.iface.mainWindow(), self.tr("Error"), self. tr("Unable to load <b>jedi</b>. Multi syntax error check will be disabled. You could try to manually install <b>jedi</b> with pip" ), ) def check_dependencies(self): install("packaging") self.black, _ = import_or_install("black") self.jedi, jedi_version = import_or_install("jedi") # JEDI 0.17 is required if not check_minimum_version(jedi_version, "0.17"): res = QMessageBox.question( self.iface.mainWindow(), self.tr("Information"), self. tr("<b>jedi</b> version is {0} and BetterEditor needs {1}. Do you want to upgrade <b>jedi</b>?<br><b>Warning:</b> it could cause old code relying on the obsolete <b>jedi</b> to stop working correctly." ).format(jedi_version, "0.17"), QMessageBox.Yes | QMessageBox.No, ) if res == QMessageBox.Yes: install("jedi", True) QMessageBox.information( self.iface.mainWindow(), self.tr("Information"), self. tr("Jedi was upgraded. You need to restart QGIS to fully use BetterEditor" ), ) self.jedi = None def on_initialization_completed(self): """ Called after QGIS has completed its initialization """ # Shortcuts are created after plugins for shortcut_name in ( "ZoomInToCanvas", "ZoomInToCanvas2", "ZoomIn2", "ZoomOutOfCanvas", ): shortcut = self.iface.mainWindow().findChild( QShortcut, shortcut_name) if shortcut: shortcut.setParent(self.iface.mapCanvas()) shortcut.setContext(Qt.WidgetWithChildrenShortcut) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" # Delete Settings dialog self.settings_dialog.deleteLater() # Remove buttons tooltips save_button = self.python_console.widget().saveFileButton saveas_button = self.python_console.widget().saveAsFileButton run_button = self.python_console.widget().runScriptEditorButton save_button.setToolTip(save_button.text()) saveas_button.setToolTip(saveas_button.text()) run_button.setToolTip(run_button.text()) # Show comment actions self.set_old_comments_action_visible(True) # Remove custom actions for action_name in ("separator", "toggleComment", "format", "insertResource"): action = self.toolbar.findChild(QAction, action_name) if action: action.deleteLater() # Remove Next tab / previous tab shortcuts for shortcut in (self.next_tab_shortcut, self.previous_tab_shortcut): shortcut.activated.disconnect() shortcut.setEnabled(False) shortcut.deleteLater() # Reenable zoom shortcuts & actions for shortcut_name in ( "ZoomInToCanvas", "ZoomInToCanvas2", "ZoomIn2", "ZoomOutOfCanvas", ): shortcut = self.iface.mainWindow().findChild( QShortcut, shortcut_name) if shortcut: shortcut.setParent(self.iface.mainWindow()) shortcut.setContext(Qt.ApplicationShortcut) self.iface.mainWindow().findChild( QAction, "mActionZoomIn").setShortcut("Ctrl+Alt++") self.iface.mainWindow().findChild( QAction, "mActionZoomOut").setShortcut("Ctrl+Alt+-") self.zoom_out_shortcut.activated.disconnect() self.zoom_out_shortcut.setEnabled(False) self.zoom_out_shortcut.deleteLater() self.tab_widget.currentChanged.disconnect( self.customize_current_editor) for editor in self.python_console.findChildren(Editor): clear_all_indicators(editor) self.restore_editor(editor) # Revert MonkeyPatch unpatch(Editor) unpatch(EditorTab) unpatch(ScriptEdit) unpatch(ScriptEditorDialog) del ScriptEdit.project del ScriptEdit.settings # Remove menu from plugins menu self.iface.pluginMenu().removeAction(self.plugin_menu.menuAction()) # Reactivate old autoCloseBracketEditor QSettings().setValue("pythonConsole/autoCloseBracketEditor", self.oldAutoCloseBracketEditor) def current_editor(self) -> Editor: if not self.tab_widget.currentWidget(): return None return self.tab_widget.currentWidget().findChild(Editor) def toggle_comment(self): self.current_editor().toggle_comment() def format_file(self): self.current_editor().format_file() def insert_resource(self): self.current_editor().insert_resource() def go_to_next_tab(self): self.tab_widget.setCurrentIndex( (self.tab_widget.currentIndex() + 1) % self.tab_widget.count()) def go_to_previous_tab(self): self.tab_widget.setCurrentIndex( (self.tab_widget.currentIndex() - 1) % self.tab_widget.count()) def show_about(self): # Used to display plugin icon in the about message box bogus = QWidget(self.iface.mainWindow()) bogus.setWindowIcon(QIcon(":/plugins/bettereditor/icons/icon.svg")) cfg = configparser.ConfigParser() cfg.read(os.path.join(os.path.dirname(__file__), "metadata.txt")) version = cfg.get("general", "version") QMessageBox.about( bogus, self.tr("About Better Editor"), "<b>Version</b> {0}<br><br>" "<b>{1}</b> : <a href=https://github.com/YoannQDQ/qgis-better-editor>GitHub</a><br>" "<b>{2}</b> : <a href=https://github.com/YoannQDQ/qgis-better-editor/issues>GitHub</a><br>" "<b>{3}</b> : <a href=https://github.com/YoannQDQ/qgis-better-editor#better-editor-qgis-plugin>GitHub</a>" .format( version, self.tr("Source code"), self.tr("Report issues"), self.tr("Documentation"), ), ) bogus.deleteLater() def set_old_comments_action_visible(self, value): self.toolbar.actions()[13].setVisible(value) self.toolbar.actions()[14].setVisible(value) self.toolbar.actions()[15].setVisible(value) def show_settings(self): self.settings_dialog.show() self.settings_dialog.raise_() def on_settings_changed(self): # Hide / Show old comment actions self.set_old_comments_action_visible( not self.settings.value("hide_old_comment_actions", True, bool)) for editor in self.python_console.findChildren(Editor): self.customize_editor(editor) def customize_current_editor(self): return self.customize_editor() def customize_editor(self, editor: Editor = None): if editor is None: editor = self.current_editor() if editor is None: return editor.project = self.project # Disable shortcuts for shortcut in editor.findChildren(QShortcut): shortcut.setEnabled(False) editor.set_completer(QCompleter(editor)) editor.completer.setModel(CompletionModel([], editor)) editor.callTips = CallTips(editor) editor.callTipsTimer = QTimer(editor) editor.callTipsTimer.setSingleShot(True) editor.callTipsTimer.setInterval(500) editor.callTipsTimer.timeout.connect(editor.update_calltips) editor.setCallTipsStyle(QsciScintilla.CallTipsNone) editor.setAutoCompletionSource(QsciScintilla.AcsNone) editor.setFolding( self.settings.value("folding_style", QsciScintilla.BoxedTreeFoldStyle, int)) # Add a small margin after the indicator (if folding is not Plain or None) if editor.folding() > 1: editor.setMarginWidth(3, "0") else: editor.setMarginWidth(3, "") if self.settings.value("ruler_visible", True, bool): editor.setEdgeMode(QsciScintilla.EdgeLine) editor.setEdgeColumn( self.settings.value("max_line_length", 88, int)) editor.setEdgeColor( self.settings.value("ruler_color", QColor("#00aaff"), QColor)) else: editor.setEdgeMode(QsciScintilla.EdgeNone) # Change syntax error marker define_indicators(editor) editor.cursorPositionChanged.connect(editor.on_position_changed) def restore_editor(self, editor: Editor): editor.cursorPositionChanged.disconnect(editor.on_position_changed) editor.setFolding(QsciScintilla.PlainFoldStyle) editor.setEdgeMode(QsciScintilla.EdgeLine) editor.setEdgeColumn(80) editor.setMarginWidth(3, "") editor.setEdgeColor(QSettings().value("pythonConsole/edgeColorEditor", QColor("#efefef"), QColor)) # Disable shortcuts for shortcut in editor.findChildren(QShortcut): shortcut.setEnabled(True) editor.markerDefine( QgsApplication.getThemePixmap( "console/iconSyntaxErrorConsole.svg"), editor.MARKER_NUM, ) editor.setAnnotationDisplay(QsciScintilla.AnnotationBoxed) editor.setCallTipsStyle(QsciScintilla.CallTipsNoContext) editor.setAutoCompletionSource(QsciScintilla.AcsAll) editor.callTips.deleteLater() del editor.callTips editor.callTipsTimer.deleteLater() del editor.callTipsTimer editor.completer.deleteLater() del editor.completer del editor.project
def init_ui(self): layout = QVBoxLayout() theme = ThemeLoader.loaded_theme # shortcuts setup close_dialog_shortcut = QShortcut(QKeySequence("Esc"), self) close_dialog_shortcut.activated.connect( lambda: self.close_dialog_event()) close_dialog_shortcut.setEnabled(True) dialog_up_shortcut = QShortcut(QKeySequence("Alt+Up"), self) dialog_up_shortcut.activated.connect(lambda: self.open_dialog_above()) dialog_up_shortcut.setEnabled(True) dialog_down_shortcut = QShortcut(QKeySequence("Alt+Down"), self) dialog_down_shortcut.activated.connect( lambda: self.open_dialog_below()) dialog_down_shortcut.setEnabled(True) open_dialogs_shortcut = QShortcut(QKeySequence("Ctrl+Alt+D"), self) open_dialogs_shortcut.activated.connect( lambda: self.tabs.setCurrentIndex(0)) open_dialogs_shortcut.setEnabled(True) open_incoming_shortcut = QShortcut(QKeySequence("Ctrl+Alt+I"), self) open_incoming_shortcut.activated.connect( lambda: self.tabs.setCurrentIndex(1)) open_incoming_shortcut.setEnabled(True) accept_connection_shortcut = QShortcut(QKeySequence("Ctrl+Shift+A"), self) accept_connection_shortcut.activated.connect( lambda: self.accept_connection_event(self.incoming_list. currentItem())) accept_connection_shortcut.setEnabled(True) decline_connection_shortcut = QShortcut(QKeySequence("Ctrl+Shift+D"), self) decline_connection_shortcut.activated.connect( lambda: self.decline_connection_event(self.incoming_list. currentItem())) decline_connection_shortcut.setEnabled(True) # dialogs list setup self.dialogs_list.setStyleSheet( theme.apply_to_stylesheet(styles.side_list_style)) self.dialogs_list.setIconSize(QSize(40, 40)) self.dialogs_list.setSelectionMode(QAbstractItemView.SingleSelection) self.dialogs_list.currentItemChanged.connect( lambda current, previous: dialog_item_changed_callback( current, previous, main_window)) self.dialogs_list.setSpacing(5) self.dialogs_list.setContextMenuPolicy(Qt.CustomContextMenu) self.dialogs_list.customContextMenuRequested.connect( self.dialog_context_menu_event) dialogs = SQLManager.get_instance(DB_MESSAGING).select_all("dialogs") for item in dialogs: self.dialogs_list.addItem( DialogItemWidget(item[4], item[0], item[1], item[2], peer_id=item[3])) # incoming list setup self.incoming_list.setStyleSheet( theme.apply_to_stylesheet(styles.side_list_style)) self.incoming_list.setContextMenuPolicy(Qt.CustomContextMenu) self.incoming_list.customContextMenuRequested.connect( self.incoming_context_menu_event) # tabs setup self.tabs.setStyleSheet(theme.get_default_for_widget(self.tabs)) self.tabs.addTab(self.dialogs_list, "Dialogs") self.tabs.addTab(self.incoming_list, "Incoming Connections") layout.addWidget(self.head) layout.addWidget(self.tabs) layout.setContentsMargins(0, 5, 0, 0) client_base.incoming_connection_callback = lambda address, connection: handle_incoming_connection_callback( self.incoming_list, address) self.setLayout(layout)