Пример #1
0
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)
Пример #2
0
    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()
Пример #3
0
    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)
Пример #4
0
    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)
Пример #5
0
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()
Пример #6
0
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()
Пример #7
0
class SubwindowMisc(QWidget):
    """Show subwindow with miscellaneous settings."""

    current_tab = -1

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

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

            self.createButtonGroup()
            self.createTabs(tab)

            mainLayout = QVBoxLayout()

            mainLayout.addWidget(self.tabs)
            mainLayout.addLayout(self.buttonGroup)

            self.setLayout(mainLayout)

            self.resize(
                QSize(int(mainWindow.size().width() * 0.9),
                      self.sizeHint().height()))
            relativeChange = QPoint(int(mainWindow.size().width() / 2),
                                    int(mainWindow.size().height() / 3))\
                - QPoint(int(self.size().width() / 2),
                         int(self.size().height() / 3))
            self.move(mainWindow.pos() + relativeChange)

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

        except Exception:
            module_logger.exception("message")

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

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

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

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

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

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

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

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

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

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

        mainLayout.addWidget(box)

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

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

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

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

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

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

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

        box.setLayout(layout)

        mainLayout.addWidget(box)

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

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

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

        mainLayout.addWidget(box)

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

        self.alphaBox.setLayout(mainLayout)

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

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

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

        mainLayout.addWidget(box)

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

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

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

        self.favBox.setLayout(mainLayout)

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

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

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

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

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

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

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

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

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

        self.aliasBox.setLayout(mainLayout)

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

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

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

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

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

        mainLayout = QVBoxLayout()

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

        layout = QGridLayout()

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

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

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

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

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

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

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

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

        title = _("Connection Test")

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

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

        mainLayout = QVBoxLayout()

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

        layout = QGridLayout()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.aligulacTab.setLayout(layout)

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

        self.aligulacTreeview.insertItem(text, aligulac_id)

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

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

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

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

        self.counterTab.setLayout(mainLayout)

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

        self.mapsBox = QWidget()

        layout = QGridLayout()

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

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

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

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

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

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

        box = QWidget()
        container = QHBoxLayout()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def createButtonGroup(self):
        """Create buttons."""
        try:
            layout = QHBoxLayout()

            layout.addWidget(QLabel(""))

            buttonCancel = QPushButton(_('Cancel'))
            buttonCancel.clicked.connect(self.closeWindow)
            layout.addWidget(buttonCancel)

            buttonSave = QPushButton(_('&Save && Close'))
            buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S"))
            self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self)
            self.shortcut.setAutoRepeat(False)
            self.shortcut.activated.connect(self.saveCloseWindow)
            buttonSave.clicked.connect(self.saveCloseWindow)
            layout.addWidget(buttonSave)

            self.buttonGroup = layout
        except Exception:
            module_logger.exception("message")

    def saveData(self):
        """Save the data."""
        if (self.__dataChanged):
            scctool.settings.config.parser.set(
                "SCT", "myteams", ", ".join(self.list_favTeams.getData()))
            scctool.settings.config.parser.set(
                "SCT", "commonplayers",
                ", ".join(self.list_favPlayers.getData()))
            scctool.settings.config.parser.set("SCT", "tesseract",
                                               self.tesseract.text().strip())
            scctool.settings.config.parser.set("SCT", "use_ocr",
                                               str(self.cb_useocr.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "new_maps_prompt",
                str(self.cb_newMapsPrompt.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "transparent_match_banner",
                str(self.cb_trans_banner.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftS", str(self.cb_ctrlshifts.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftC", str(self.cb_ctrlshiftc.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "swap_myteam", str(self.cb_swapTeams.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlN",
                                               str(self.cb_ctrln.isChecked()))
            scctool.settings.config.parser.set("SCT", "CtrlX",
                                               str(self.cb_ctrlx.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "CtrlShiftR", str(self.cb_ctrlshiftr.currentText()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist_on", str(self.cb_blacklist.isChecked()))
            scctool.settings.config.parser.set(
                "SCT", "blacklist", ", ".join(self.list_blacklist.getData()))
            scctool.settings.config.parser.set(
                "SCT", "sc2_network_listener_address",
                self.listener_address.text().strip())
            scctool.settings.config.parser.set(
                "SCT", "sc2_network_listener_enabled",
                str(self.cb_usesc2listener.isChecked()))
            scctool.settings.config.parser.set(
                "Countdown", "matchgrabber_update",
                str(self.cb_counter_matchgrabber_update.isChecked()))
            scctool.settings.config.parser.set(
                "Countdown", "replacement",
                self.le_countdown_replacement.text())
            scctool.settings.config.parser.set(
                "Countdown", "pre_txt", self.counter_pretext.toPlainText())
            scctool.settings.config.parser.set(
                "Countdown", "post_txt", self.counter_posttext.toPlainText())
            scctool.settings.config.parser.set(
                "Ticker", "prefix",
                self.ticker_pretext.text().strip())
            self.controller.matchControl.tickerChanged.emit()
            self.controller.refreshButtonStatus()
            # self.controller.setCBS()
            self.__dataChanged = False

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

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

    def closeEvent(self, event):
        """Handle close event."""
        try:
            self.mainWindow.updateAllMapCompleters()
            if (not self.__dataChanged):
                event.accept()
                return
            if (not self.passEvent):
                if (self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Save data?"),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            event.accept()
        except Exception:
            module_logger.exception("message")
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'
Пример #9
0
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)
Пример #10
0
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)
Пример #11
0
class SubwindowMisc(QWidget):
    """Show subwindow with miscellaneous settings."""
    def createWindow(self, mainWindow, tab=''):
        """Create subwindow with miscellaneous settings."""
        try:
            parent = None
            super().__init__(parent)
            # self.setWindowFlags(Qt.WindowStaysOnTopHint)

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

            self.createButtonGroup()
            self.createTabs(tab)

            mainLayout = QVBoxLayout()

            mainLayout.addWidget(self.tabs)
            mainLayout.addLayout(self.buttonGroup)

            self.setLayout(mainLayout)

            self.resize(
                QSize(mainWindow.size().width() * .80,
                      self.sizeHint().height()))
            relativeChange = QPoint(mainWindow.size().width() / 2,
                                    mainWindow.size().height() / 3)\
                - QPoint(self.size().width() / 2,
                         self.size().height() / 3)
            self.move(mainWindow.pos() + relativeChange)

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

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

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

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

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

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

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

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

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

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

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

        mainLayout.addWidget(box)

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

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

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

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

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

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

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

        box.setLayout(layout)

        mainLayout.addWidget(box)

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

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

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

        mainLayout.addWidget(box)

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

        self.alphaBox.setLayout(mainLayout)

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

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

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

        mainLayout.addWidget(box)

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

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

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

        self.favBox.setLayout(mainLayout)

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

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

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

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

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

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

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

        self.aliasBox.setLayout(mainLayout)

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

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

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

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

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

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

        mainLayout = QVBoxLayout()

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

        layout = QGridLayout()

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

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

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

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

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

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

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

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

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

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

        self.mapsBox = QWidget()

        layout = QGridLayout()

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

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

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

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

        box = QWidget()
        container = QHBoxLayout()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def createButtonGroup(self):
        """Create buttons."""
        try:
            layout = QHBoxLayout()

            layout.addWidget(QLabel(""))

            buttonCancel = QPushButton(_('Cancel'))
            buttonCancel.clicked.connect(self.closeWindow)
            layout.addWidget(buttonCancel)

            buttonSave = QPushButton(_('&Save && Close'))
            buttonSave.setToolTip(_("Shortcut: {}").format("Ctrl+S"))
            self.shortcut = QShortcut(QKeySequence("Ctrl+S"), self)
            self.shortcut.setAutoRepeat(False)
            self.shortcut.activated.connect(self.saveCloseWindow)
            buttonSave.clicked.connect(self.saveCloseWindow)
            layout.addWidget(buttonSave)

            self.buttonGroup = layout
        except Exception as e:
            module_logger.exception("message")

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

            self.__dataChanged = False

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

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

    def closeEvent(self, event):
        """Handle close event."""
        try:
            self.mainWindow.updateMapCompleters()
            if (not self.__dataChanged):
                event.accept()
                return
            if (not self.passEvent):
                if (self.isMinimized()):
                    self.showNormal()
                buttonReply = QMessageBox.question(
                    self, _('Save data?'), _("Save data?"),
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if buttonReply == QMessageBox.Yes:
                    self.saveData()
            event.accept()
        except Exception as e:
            module_logger.exception("message")
Пример #12
0
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"))
Пример #13
0
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
Пример #14
0
    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)