コード例 #1
0
class ProgressDialog(ProgressDialogUI, ProgressDialogBase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setupUi(self)
        self.setWindowFlags(Qt.Tool | Qt.CustomizeWindowHint
                            | Qt.WindowCloseButtonHint)
        if HAS_WINEXTRAS:
            self._button = QWinTaskbarButton(self)
            self._button.setWindow(self.windowHandle())
            self._button.setOverlayIcon(self.icon())

    def value(self):
        return self.progressBar.value()

    def setValue(self, value):
        self.progressBar.setValue(value)
        if HAS_WINEXTRAS:
            self._button.progress().setValue(value)

    def format(self):
        return self.windowTitle()

    def setFormat(self, value):
        self.setWindowTitle(value)

    def minimum(self):
        return self.progressBar.minimum()

    def setMinimum(self, minimum):
        self.progressBar.setMinimum(minimum)
        if HAS_WINEXTRAS:
            self._button.progress().setMinimum(minimum)

    def maximum(self):
        return self.progressBar.maximum()

    def setMaximum(self, maximum):
        self.progressBar.setMaximum(maximum)
        if HAS_WINEXTRAS:
            self._button.progress().setMaximum(maximum)

    def setRange(self, minimum, maximum):
        self.progresBar.setRange(minimum, maximum)
        if HAS_WINEXTRAS:
            self._button.progress().setRange(minimum, maximum)

    def reject(self):
        return super().reject()

    def done(self, r: int):
        return super().done(r)

    def show(self):
        super().show()
        if HAS_WINEXTRAS:
            self._button.progress().show()
コード例 #2
0
        def b():
            button = QWinTaskbarButton()
            button.setWindow(self.windowHandle())

            progress = button.progress()

            self.taskbarButton = button
            self.taskbarProgress = progress

            while len(self.tasksDependOnWindow) > 0:
                task = self.tasksDependOnWindow.pop(0)
                task()
コード例 #3
0
        def b():
            # self.setWindowFlag(Qt.WindowStaysOnTopHint, True)

            button = QWinTaskbarButton()
            button.setWindow(self.windowHandle())

            progress = button.progress()

            self.taskbarButton = button
            self.taskbarProgress = progress

            while len(self.tasks) > 0:
                self.tasks.pop(0)()
コード例 #4
0
class DownloadManager(QWidget):

    # DownloadOption
    OpenFile = 0
    SaveFile = 1
    ExternalManager = 2
    NoOption = 3

    class DownloadInfo:
        def __init__(self, page):
            '''
            @param: page WebPage
            '''
            self.page = page  # WebPage
            self.suggestedFileName = ''

            self.askWhatToDo = True
            self.forceChoosingPath = False

    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = None  # Ui::DownloadManager
        self._timer = QBasicTimer()

        self._lastDownloadPath = ''
        self._downloadPath = ''
        self._useNativeDialog = False
        self._isClosing = False
        self._closeOnFinish = False
        self._activeDownloadsCount = 0

        self._useExternalManager = False
        self._externalExecutable = ''
        self._externalArguments = ''

        self._lastDownloadOption = self.NoOption  # DownloadOption

        self._taskbarButton = None  # QPointer<QWinTaskbarButton>

        self._ui = uic.loadUi('mc/downloads/DownloadManager.ui', self)
        self.setWindowFlags(self.windowFlags() ^ Qt.WindowMaximizeButtonHint)
        if const.OS_WIN:
            if QtWin.isCompositionEnabled():  # TODO: ?
                QtWin.extendFrameIntoClientArea(self, -1, -1, -1, -1)
        self._ui.clearButton.setIcon(QIcon.fromTheme('edit-clear'))
        gVar.appTools.centerWidgetOnScreen(self)

        self._ui.clearButton.clicked.connect(self._clearList)

        clearShortcut = QShortcut(QKeySequence('CTRL+L'), self)
        clearShortcut.activated.connect(self._clearList)

        self.loadSettings()

        gVar.appTools.setWmClass('Download Manager', self)

    def loadSettings(self):
        settings = Settings()
        settings.beginGroup("DownloadManager")
        self._downloadPath = settings.value("defaultDownloadPath", '')
        self._lastDownloadPath = settings.value(
            "lastDownloadPath",
            QStandardPaths.writableLocation(QStandardPaths.DownloadLocation))
        self._closeOnFinish = settings.value("CloseManagerOnFinish", False)
        self._useNativeDialog = settings.value(
            "useNativeDialog", const.DEFAULT_DOWNLOAD_USE_NATIVE_DIALOG)

        self._useExternalManager = settings.value("UseExternalManager", False)
        self._externalExecutable = settings.value("ExternalManagerExecutable",
                                                  '')
        self._externalArguments = settings.value("ExternalManagerArguments",
                                                 '')
        settings.endGroup()

        if "%d" not in self._externalArguments:
            self._externalArguments += " %d"

    def download(self, downloadItem):  # noqa C901
        '''
        @param: downloadItem QWebEngineDownloadItem
        '''
        downloadTimer = QTime()
        downloadTimer.start()

        self.closeDownloadTab(downloadItem)

        downloadPath = ''
        openFile = False

        fileName = basename(downloadItem.path())

        forceAsk = downloadItem.savePageFormat() != QWebEngineDownloadItem.UnknownSaveFormat \
            or downloadItem.type() == QWebEngineDownloadItem.UserRequested

        if self._useExternalManager:
            self.startExternalManager(downloadItem.url())
        elif forceAsk or not self._downloadPath:
            (Unknown, Open, Save, ExternalManager, SavePage) = range(5)
            result = Unknown

            if downloadItem.savePageFormat(
            ) != QWebEngineDownloadItem.UnknownSaveFormat:
                # Save Page Requested
                result = SavePage
            elif downloadItem.type() == QWebEngineDownloadItem.UserRequested:
                # Save x as... requested
                result = Save
            else:
                # Ask what to do
                optionsDialog = DownloadOptionsDialog(fileName, downloadItem,
                                                      gVar.app.activeWindow())
                optionsDialog.showExternalManagerOption(
                    self._useExternalManager)
                optionsDialog.setLastDownloadOption(self._lastDownloadOption)
                result = optionsDialog.exec_()

            if result == Open:
                openFile = True
                downloadPath = gVar.appTools.ensureUniqueFilename(
                    pathjoin(DataPaths.path(DataPaths.Temp), fileName))
                self._lastDownloadOption = self.OpenFile
            elif result == Save:
                downloadPath, selectedFitler = QFileDialog.getSaveFileName(
                    gVar.app.activeWindow(), _('Save file as...'),
                    pathjoin(self._lastDownloadPath, fileName))

                if downloadPath:
                    self._lastDownloadPath = QFileInfo(
                        downloadPath).absolutePath()
                    Settings().setValue('DownloadManager/lastDownloadPath',
                                        self._lastDownloadPath)
                    self._lastDownloadOption = self.SaveFile

            elif result == SavePage:
                mhtml = _('MIME HTML Archive (*.mhtml)')
                htmlSingle = _('HTML Page, single (*.html)')
                htmlComplete = _('HTML Page, complete (*.html)')
                filter_ = '%s;;%s;;%s' % (mhtml, htmlSingle, htmlComplete)

                selectedFilter = ''
                downloadPath, selectedFilter = QFileDialog.getSaveFileName(
                    gVar.app.activeWindow(), _('Save page as...'),
                    pathjoin(self._lastDownloadPath, fileName), filter_,
                    selectedFilter)

                if downloadPath:
                    self._lastDownloadPath = QFileInfo(
                        downloadPath).absolutePath()
                    Settings().setValue('DownloadManager/lastDownloadPath',
                                        self._lastDownloadPath)
                    self._lastDownloadOption = self.SaveFile

                    format_ = QWebEngineDownloadItem.UnknownSaveFormat

                    if selectedFilter == mhtml:
                        format_ = QWebEngineDownloadItem.MimeHtmlSaveFormat
                    elif selectedFilter == htmlSingle:
                        format_ = QWebEngineDownloadItem.SingleHtmlSaveFormat
                    elif selectedFilter == htmlComplete:
                        format_ = QWebEngineDownloadItem.CompleteHtmlSaveFormat

                    if format_ == QWebEngineDownloadItem.UnknownSaveFormat:
                        downloadItem.setSavePageFormat(format_)

            elif result == ExternalManager:
                self.startExternalManager(downloadItem.url())
                downloadItem.cancel()
            else:
                downloadItem.cancel()
        else:
            downloadPath = gVar.appTools.ensureUniqueFilename(
                pathjoin(self._downloadPath, fileName))

        if not downloadPath:
            downloadItem.cancel()
            return

        # Set download path ad accept
        downloadItem.setPath(downloadPath)
        downloadItem.accept()

        # Create download item
        listItem = QListWidgetItem(self._ui.list)
        downItem = DownloadItem(listItem, downloadItem,
                                QFileInfo(downloadPath).absolutePath(),
                                basename(downloadPath), openFile, self)
        downItem.setDownTimer(downloadTimer)
        downItem.startDownloading()
        downItem.deleteItem.connect(self._deleteItem)
        downItem.downloadFinished.connect(self._downloadFinished)
        self._ui.list.setItemWidget(listItem, downItem)
        listItem.setSizeHint(downItem.sizeHint())
        downItem.show()

        self._activeDownloadsCount += 1
        self.downloadsCountChanged.emit()

    def downloadsCount(self):
        return self._ui.list.count()

    def activeDownloadsCount(self):
        return self._activeDownloadsCount

    def canClose(self):
        '''
        @return: bool
        '''
        if self._isClosing:
            return True

        isDownloading = False
        for idx in range(self._ui.list.count()):
            downItem = self._ui.list.itemWidget(self._ui.list.item(idx))
            if not downItem:
                continue
            if downItem.isDownloading():
                isDownloading = True
                break

        return not isDownloading

    def useExternalManager(self):
        '''
        @return: bool
        '''
        return self._useExternalManager

    def startExternalManager(self, url):
        '''
        param: url QUrl
        '''
        arguments = self._externalArguments
        arguments.replace('%d', url.toEncoded().data().decode())

        gVar.appTools.startExternalProcess(self._externalExecutable, arguments)
        self._lastDownloadOption = self.ExternalManager

    def setLastDownloadPath(self, lastPath):
        self._lastDownloadPath = lastPath

    def setLastDownloadOption(self, option):
        self._lastDownloadOption = option

    # public Q_SLOTS:
    def show(self):
        self._timer.start(500, self)

        super().show()
        self.raise_()
        self.activateWindow()

    # private Q_SLOTS:
    def _clearList(self):
        items = []  # QList<DownloadItem>
        listItems = []
        for idx in range(self._ui.list.count()):
            listItem = self._ui.list.item(idx)
            downItem = self._ui.list.itemWidget(listItem)
            if not downItem:
                continue
            if downItem.isDownloading():
                continue
            items.append(downItem)
            listItems.append(listItem)
        for listItem in listItems:
            row = self._ui.list.row(listItem)
            self._ui.list.takeItem(row)
        qtUtil.qDeleteAll(items)
        self.downloadsCountChanged.emit()

    def _deleteItem(self, item):
        '''
        @parma: item DownloadItem
        '''
        if item and not item.isDownloading():
            row = self._ui.list.row(item.item())
            self._ui.list.takeItem(row)
            item.deleteLater()

    def _downloadFinished(self, success):
        '''
        @param: success bool
        '''
        self._activeDownloadsCount = 0
        downloadingAllFilesFinished = True
        for idx in range(self._ui.list.count()):
            downItem = self._ui.list.itemWidget(self._ui.list.item(idx))
            if not isinstance(downItem, DownloadItem):
                continue
            if downItem.isDownloading():
                self._activeDownloadsCount += 1
            if downItem.isCancelled() or not downItem.isDownloading():
                continue
            downloadingAllFilesFinished = False

        self.downloadsCountChanged.emit()

        if downloadingAllFilesFinished:
            if success and gVar.app.activeWindow() != self:
                icon = QIcon.fromTheme('download',
                                       QIcon(':/icons/other/download.svg'))
                gVar.app.desktopNotifications().showNotification(
                    icon.pixmap(48), _('App: Download Finished'),
                    _('All files have been successfully downloaded.'))
                if not self._closeOnFinish:
                    self.raise_()
                    self.activateWindow()
            self._ui.speedLabel.clear()
            self.setWindowTitle(_('Download Manager'))
            if const.OS_WIN:
                self.taskbarButton().progress().hide()
            if self._closeOnFinish:
                self.close()

    # Q_SIGNALS
    resized = pyqtSignal(QSize)
    downloadsCountChanged = pyqtSignal()

    # private:
    # override
    def timerEvent(self, event):
        '''
        @param: event QTimerEvent
        '''
        remTimes = []  # QVector<QTime>
        progresses = []  # QVector<int>
        speeds = []  # QVector<double>

        if event.timerId() == self._timer.timerId():
            if not self._ui.list.count():
                self._ui.speedLabel.clear()
                self.setWindowTitle(_('Download Manager'))
                if const.OS_WIN:
                    self.taskbarButton().progress().hide()
                return
            for idx in range(self._ui.list.count()):
                downItem = self._ui.list.itemWidget(self._ui.list.item(idx))
                if not isinstance(downItem, DownloadItem) or downItem.isCancelled() \
                        or not downItem.isDownloading():
                    continue
                progresses.append(downItem.progress())
                remTimes.append(downItem.remainingTime())
                speeds.append(downItem.currentSpeed())
            if not remTimes:
                return

            remaining = QTime()
            for time in remTimes:
                if time > remaining:
                    remaining = time

            progress = 0
            for prog in progresses:
                progress += prog

            progress = int(progress / len(progresses) * 100) / 100

            speed = sum(speeds)

            if not const.OS_WIN:
                self._ui.speedLabel.setText(
                    _('%s%% of %s files (%s) %s remaining') %
                    (progress, len(progresses),
                     DownloadItem.currentSpeedToString(speed),
                     DownloadItem.remainingTimeToString(remaining)))
            else:
                self.setWindowTitle(_('%s%% - Download Manager') % progress)
            if const.OS_WIN:
                self.taskbarButton().progress().show()
                self.taskbarButton().progress().setValue(progress)

        super().timerEvent(event)

    # override
    def closeEvent(self, event):
        '''
        @param: event QCloseEvent
        '''
        if gVar.app.windowCount(
        ) == 0:  # No main window -> we are going to quit
            if not self.canClose():
                # QMessageBox.StandardButton
                button = QMessageBox.warning(
                    self, _('Warning'),
                    _('Are you sure you want to quit? All uncompleted downloads will be cancelled!'
                      ), QMessageBox.Yes | QMessageBox.No)
                if button != QMessageBox.Yes:
                    event.ignore()
                    return
                self._isClosing = True
            gVar.app.quitApplication()
        event.accept()

    # override
    def resizeEvent(self, event):
        '''
        @param: event QResizeEvent
        '''
        super().resizeEvent(event)
        self.resized.emit(self.size())

    # override
    def keyPressEvent(self, event):
        '''
        @param: event QKeyEvent
        '''
        if event.key() == Qt.Key_Escape or (event.key() == Qt.Key_W
                                            and event.modifiers()
                                            == Qt.ControlModifier):
            self.close()

        super().keyPressEvent(event)

    def closeDownloadTab(self, item):  # noqa C901
        '''
        @param: item QWebEngineDownloadItem
        '''

        # Attemp to close empty tab that was opened only for loading the
        # download url
        def testWebView(view, url):
            '''
            @param: view TabbedWebView
            @param: url QUrl
            '''
            if not view.webTab().isRestored():
                return False
            if view.browserWindow().tabWidget().tabBar().normalTabsCount() < 2:
                return False
            page = view.page()
            if page.history().count() != 0:
                return False
            if const.QTWEBENGINEWIDGETS_VERSION >= const.QT_VERSION_CHECK(
                    5, 12, 0):
                return True
            else:
                if page.url() != QUrl():
                    return False
                tabUrl = page.requestedUrl()
                if tabUrl.isEmpty():
                    tabUrl = QUrl(view.webTab().locationBar().text())
                return tabUrl.host() == url.host()

        if const.QTWEBENGINEWIDGETS_VERSION >= const.QT_VERSION_CHECK(
                5, 12, 0):
            from mc.webengine.WebPage import WebPage
            from mc.webtab.TabbedWebView import TabbedWebView
            page = item.page()
            if not page:
                return
            if not isinstance(page, WebPage):
                return
            view = page.view()
            if not isinstance(view, TabbedWebView):
                return
            if testWebView(view, QUrl()):
                view.closeView()
        else:
            mainWindow = gVar.app.getWindow()
            # If the main window was closed, threre is no need to go further
            if mainWindow == None:
                return

            if testWebView(mainWindow.weView(), item.url()):
                mainWindow.weView().closeView()
                return

            windows = gVar.app.windows()
            for window in windows:
                tabs = window.tabWidget().allTabs()
                for tab in tabs:
                    view = tab.webView()
                    if testWebView(view, item.url()):
                        view.closeView()
                        return

    def taskbarButton(self):
        '''
        @return: QWinTaskbarButton
        '''
        if const.OS_WIN:
            if not self._taskbarButton:
                window = gVar.app.getWindow()
                self._taskbarButton = QWinTaskbarButton(
                    window and window.windowHandle() or self.windowHandle())
                self._taskbarButton.progress().setRange(0, 100)
            return self._taskbarButton
        else:
            return None
コード例 #5
0
ファイル: timer.py プロジェクト: char101/timer
class Window(QMainWindow):
    INTERVALS = (
        (0, 'None', ''),
        (5, '5 secs.', '5s'),
        (15 * 60, '15 mins.', '15m'),
        (30 * 60, '30 mins.', '30m'),
        (60 * 60, '60 mins.', '60m')
    )

    CONTEXTS = (None, 'work', 'play')

    def __init__(self):
        super().__init__()
        self.setWindowIcon(QIcon('clock.ico'))
        self.resize(250, 300)
        self.interval = None
        self.context = None

        self.timer = Timer()
        self.timer.textChanged.connect(self.updateTitle)
        self.timer.started.connect(self.updateTitle)
        self.timer.stopped.connect(self.updateTitle)
        self.timer.reset_.connect(self.updateTitle)

        self.progressButton = QWinTaskbarButton()
        self.progress = self.progressButton.progress()

        self.updateTitle()
        self.setupWidgets()

    def showEvent(self, event):
        super().showEvent(event)
        self.progressButton.setWindow(self.windowHandle())

    def updateTitle(self, text=None):
        title = ''
        if self.context:
            title += self.context.title()
            if self.interval:
                title += ' ({})'.format(self.INTERVALS[self.interval][2])
        elif self.interval:
            title = '{}'.format(self.INTERVALS[self.interval][2])
        if self.context or self.interval:
            title += ' | '
        title += text or self.timer.text()
        if self.interval:
            secs = self.INTERVALS[self.interval][0]
            title += ' ({}%)'.format(int(100 * self.timer.seconds() / secs))
        self.setWindowTitle(title)

        if self.interval:
            seconds = self.timer.seconds()
            if seconds <= self.progress.maximum():
                self.progress.setValue(seconds)
            elif not self.progress.isStopped():
                self.progress.stop()

    def setupWidgets(self):
        central = QWidget()
        layout = QVBoxLayout()
        central.setLayout(layout)
        layout.addWidget(self.controlButtons())
        layout.addWidget(self.timer, 1)
        layout.addWidget(self.intervalButtons())
        layout.addWidget(self.contextButtons())
        self.setCentralWidget(central)

    def controlButtons(self):
        widget = QWidget()
        group = QButtonGroup(widget)
        layout = QHBoxLayout()
        widget.setLayout(layout)

        startButton = QPushButton('Start')
        startButton.setCheckable(True)

        def onStart():
            self.timer.start()
            if self.progress.isPaused():
                self.progress.resume()

        startButton.clicked.connect(lambda: self.timer.start())
        group.addButton(startButton)
        layout.addWidget(startButton)

        stopButton = QPushButton('Stop')
        stopButton.setCheckable(True)

        def onStop():
            self.timer.stop()
            self.progress.setPaused(True)

        stopButton.clicked.connect(onStop)
        group.addButton(stopButton)
        layout.addWidget(stopButton)

        resetButton = QPushButton('Reset')

        def onReset():
            self.timer.reset()
            self.progress.resume()

        resetButton.clicked.connect(onReset)
        layout.addWidget(resetButton)

        return widget

    def intervalButtons(self):
        widget = QGroupBox('Interval')
        group = QButtonGroup(widget)
        layout = QHBoxLayout()
        widget.setLayout(layout)

        def setInterval():
            self.interval = group.checkedId()
            interval = self.INTERVALS[self.interval][0]
            self.updateTitle()
            if interval:
                self.progress.show()
                self.progress.setMaximum(interval)
                value = self.timer.seconds()
                if value < interval:
                    self.progress.resume()
                else:
                    self.progress.stop()
                self.progress.setValue(min(interval, value))
            else:
                self.progress.hide()

        for i, interval in enumerate(self.INTERVALS):
            button = QPushButton(interval[1])
            button.setCheckable(True)
            button.clicked.connect(setInterval)
            group.addButton(button, i)
            layout.addWidget(button, 1 if i > 0 else 0)

        return widget

    def contextButtons(self):
        widget = QGroupBox('Context')
        group = QButtonGroup(widget)
        layout = QHBoxLayout()
        widget.setLayout(layout)

        def setContext():
            self.context = self.CONTEXTS[group.checkedId()]
            self.updateTitle()

        for i, context in enumerate(self.CONTEXTS):
            button = QPushButton(context.title() if context else 'None')
            button.setCheckable(True)
            button.clicked.connect(setContext)
            group.addButton(button, i)
            layout.addWidget(button, 1 if i > 0 else 0)

        return widget
コード例 #6
0
class Updater(QDialog):
    def __init__(self, server, parent=None):
        super(Updater, self).__init__(parent)
        self.server = server
        self.reporter_thread = None
        self.update_thread = None
        self.step_unit = None
        self.progress = 0

        if getattr(sys, 'frozen', False):
            self.app_path = os.path.abspath(sys.executable)
        else:
            self.app_path = os.path.abspath(__file__)

        self.root_path = QLineEdit()
        self.root_path.textChanged[str].connect(self.on_path_changed)
        self.browse_button = QPushButton('Browse...')
        self.browse_button.clicked.connect(self.on_browse_button)
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        self.from_combo_box = QComboBox()
        self.from_combo_box.addItems(self.server.available_indexes())
        self.from_combo_box.setEnabled(False)
        self.autodetect_checkbox = QCheckBox('Autodetect')
        self.autodetect_checkbox.setChecked(True)
        self.autodetect_checkbox.toggled.connect(self.on_autodetect_checked)
        self.to_combo_box = QComboBox()
        self.to_combo_box.addItems(self.server.available_updates())
        self.update_button = QPushButton('Go!')
        self.update_button.setEnabled(False)
        self.update_button.clicked.connect(self.on_update_button)
        self.status_label = QLabel('Idle.')
        self.step_label = QLabel()

        # Update to the latest version by default
        pos = self.to_combo_box.findText(self.server.get_latest())
        if pos != -1:
            self.to_combo_box.setCurrentIndex(pos)

        self.win_taskbar = None
        if os.name == 'nt':
            from PyQt5.QtWinExtras import QWinTaskbarButton
            self.win_taskbar = QWinTaskbarButton(self)
            self.win_taskbar.progress().setVisible(True)

        bottom = QVBoxLayout()
        bottom.addWidget(self.status_label)
        bottom.addWidget(self.progress_bar)
        bottom.addWidget(self.step_label)

        layout = QGridLayout()
        layout.addWidget(QLabel('Root Path'), 0, 0)
        layout.addWidget(self.root_path, 0, 1)
        layout.addWidget(self.browse_button, 0, 2)
        layout.addWidget(QLabel('From'), 1, 0)
        layout.addWidget(self.from_combo_box, 1, 1)
        layout.addWidget(self.autodetect_checkbox, 1, 2)
        layout.addWidget(QLabel('To'), 2, 0)
        layout.addWidget(self.to_combo_box, 2, 1)
        layout.addWidget(self.update_button, 2, 2)
        layout.addLayout(bottom, 3, 0, 2, 0)

        self.setLayout(layout)
        self.setGeometry(100, 100, 350, 100)
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)

    def set_task(self, task, unit, length):
        self.status_label.setText(task)
        self.progress_bar.setRange(0, length)
        self.progress_bar.setValue(0)
        if self.win_taskbar:
            self.win_taskbar.progress().setRange(0, length)
            self.win_taskbar.progress().setValue(0)
        self.step_unit = unit
        if not self.step_unit:
            self.step_label.setText('')

    def step(self, payload):
        self.progress_bar.setValue(self.progress_bar.value() + 1)
        if self.win_taskbar:
            self.win_taskbar.progress().setValue(self.progress_bar.value())
        if self.step_unit:
            self.step_label.setText('Current %s: %s' %
                                    (self.step_unit, payload))

    def set_done(self, elapsed):
        self.update_button.setEnabled(True)
        self.status_label.setText('Finished in %s' % elapsed)
        self.step_label.setText('')

    def update_failed(self, exception):
        QMessageBox.critical(self, 'Update failed', str(exception))

    def perform_autodetect(self):
        path = str(self.root_path.text())
        if self.autodetect_checkbox.isChecked() and self.server.get_anchor():
            try:
                hash = index.hash(
                    os.path.join(path,
                                 self.server.get_anchor()['file']), 'sha1')
                pos = self.from_combo_box.findText(
                    self.server.autodetect_anchor(hash))
                if pos != -1:
                    self.from_combo_box.setCurrentIndex(pos)
            except FileNotFoundError:
                pass

    def on_autodetect_checked(self):
        self.from_combo_box.setEnabled(
            not self.autodetect_checkbox.isChecked())
        self.perform_autodetect()

    def on_path_changed(self, path):
        self.update_button.setEnabled(bool(path))

    def on_browse_button(self):
        dialog = QFileDialog(self, 'Select root path...')
        dialog.setFileMode(QFileDialog.DirectoryOnly)
        if dialog.exec_() == QDialog.Accepted:
            folder = dialog.selectedFiles()[0]
            if self.server.get_anchor():
                for r, d, f in os.walk(folder, topdown=True):
                    if r.count(os.sep) - folder.count(os.sep) == 1:
                        del d[:]
                    if self.server.get_anchor()['file'] in f:
                        folder = os.path.normpath(r)
                        break
            self.root_path.setText(folder)
            self.perform_autodetect()

    def on_update_button(self):
        root_path = index.win_path(str(self.root_path.text()))
        if not os.path.isdir(root_path):
            QMessageBox.critical(
                self, 'Cannot proceed',
                'Please make sure that the root path exists.')
            return
        if index.win_path(self.app_path).lower().startswith(root_path.lower()):
            QMessageBox.critical(
                self, 'Cannot proceed',
                'Flashpoint Updater is found under the root path.\nPlease move it to a different location to proceed.'
            )
            return
        self.update_button.setEnabled(False)
        current = str(self.from_combo_box.currentText())
        target = str(self.to_combo_box.currentText())
        logger.info('Starting update from %s to %s' % (current, target))
        reporter = ProgressReporter()
        self.reporter_thread = ReporterThread(reporter)
        self.reporter_thread.sig_task.connect(self.set_task)
        self.reporter_thread.sig_step.connect(self.step)
        self.reporter_thread.sig_done.connect(self.set_done)
        self.reporter_thread.start()
        self.update_thread = UpdateThread(reporter, root_path, self.server,
                                          current, target)
        self.update_thread.sig_exc.connect(self.update_failed)
        self.update_thread.start()

    def showEvent(self, event):
        self.setFixedSize(self.size())  # Make non-resizable
        if self.win_taskbar:
            self.win_taskbar.setWindow(updater.windowHandle())
        event.accept()

    def closeEvent(self, event):
        if self.update_thread and self.update_thread.isRunning():
            self.update_thread.reporter.stop()
            self.update_thread.wait()
            self.reporter_thread.wait()
        event.accept()
コード例 #7
0
ファイル: ProgressBar.py プロジェクト: uyitroa/osr2mp4-app
class ProgressBar(QProgressBar):
    def __init__(self, parent):
        super(ProgressBar, self).__init__(parent)
        self.main_window = parent

        # taskbar progress
        if using_windows:
            self.taskbar_btn = QWinTaskbarButton(parent)
            self.taskbar_prog = self.taskbar_btn.progress()
            self.taskbar_prog.setRange(0, 100)

        self.default_x = 0
        self.default_y = 420
        self.default_width = 830
        self.default_height = 40
        self.default_fontsize = 250
        self.notification = {
            "done": QSound(os.path.join(abspath, "res/success.wav")),
            "error": QSound(os.path.join(abspath, "res/fail.wav")),
            ".": QSound("blank")
        }

        self.setGeometry(self.default_x, self.default_y, self.default_width,
                         self.default_height)

        self.setAlignment(QtCore.Qt.AlignCenter)
        self.text = QLabel(self)
        self.text.setStyleSheet(
            "QLabel{font-size: %ipt; font-weight: bold; color: white; background-color: transparent;}QToolTip { background-color:white;color: black; }"
            % self.default_fontsize)

        self.setStyleSheet("""
QProgressBar {
	border: 2px solid white;
	border-radius: 5px;
	color:white;	
}

QProgressBar::chunk {
	background-color: rgba(226, 107, 167, 255);
}""")

    def directory_changed(self, path):
        print('Directory Changed:', path)

    def file_changed(self, path):
        f = open(path, "r")
        content = f.read()
        if content in self.notification:
            self.notification[content].play()
            self.hide()
            self.setValue(0)
            return

        val = max(self.value(), float("0" + content))
        self.setValue(val)

        if using_windows:
            self.taskbar_prog.setValue(val)
        f.close()
        # if self.value() >= 100:
        # 	self.hide()
        # 	self.setValue(0)

    def hide(self):
        self.main_window.startbutton.default_y = 370
        self.main_window.startbutton.show()
        self.main_window.cancelbutton.hide()
        self.main_window.options.default_y = 430
        self.main_window.updatebutton.default_y = 400
        self.main_window.resizeEvent(True)

        if using_windows:
            self.taskbar_prog.hide()
        super().hide()

    def show(self):
        mapendtime = "Max" if current_config[
            'End time'] == -1 else current_config['End time']
        self.text.setToolTip(
            f"Map start time: {current_config['Start time']}, Map end time: {mapendtime}"
        )
        self.main_window.startbutton.default_y = 330
        self.main_window.options.default_y = 390
        self.main_window.updatebutton.default_y = 360
        self.main_window.resizeEvent(True)
        self.main_window.startbutton.hide()
        self.main_window.cancelbutton.show()

        if using_windows:
            self.taskbar_prog.show()
        super().show()

    def changesize(self):
        scale = self.main_window.height() / self.main_window.default_height
        width = self.default_width * scale
        height = self.default_height * scale
        x = self.default_x * scale
        y = self.default_y * scale

        self.setGeometry(x, y, width, height)
        self.text.setGeometry(0, 0, width, height)
コード例 #8
0
ファイル: TaskbarProgress.py プロジェクト: xiliangjianke/PyQt
class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        # 获取任务栏按钮
        self.taskButton = QWinTaskbarButton(self)
        # 获取任务栏进度条
        self.taskProgress = self.taskButton.progress()
        # 定时器模拟进度
        self.timerProgress = QTimer(self)
        self.timerProgress.timeout.connect(self.update_progress)

        self.setup_ui()

    def showEvent(self, event):
        super(Window, self).showEvent(event)
        if not self.taskButton.window():
            # 必须等窗口显示后设置才有效,或者通过软件流程在适当的时候设置也可以
            self.taskButton.setWindow(self.windowHandle())
            self.taskProgress.show()

    def closeEvent(self, event):
        self.timerProgress.stop()
        super(Window, self).closeEvent(event)

    def setup_ui(self):
        layout = QGridLayout(self)

        # 设置最新小值和最大值
        self.spinBoxMin = QSpinBox(self)
        self.spinBoxMax = QSpinBox(self)
        self.spinBoxMax.setMaximum(100)
        self.spinBoxMax.setValue(100)
        layout.addWidget(self.spinBoxMin, 0, 0)
        layout.addWidget(self.spinBoxMax, 0, 1)
        layout.addWidget(QPushButton('设置范围值', self, clicked=self.set_range), 0,
                         2)

        # 设置当前值
        self.spinBoxCur = QSpinBox(self)
        self.spinBoxCur.setMaximum(100)
        self.spinBoxCur.setValue(50)
        layout.addWidget(self.spinBoxCur, 0, 3)
        layout.addWidget(
            QPushButton('设置当前值', self, clicked=self.set_current_value), 0, 4)

        # 功能按钮
        layout.addWidget(QPushButton('隐藏', self, clicked=self.set_show_hide),
                         1, 0)
        layout.addWidget(
            QPushButton('暂停', self, clicked=self.set_pause_resume), 1, 1)
        layout.addWidget(QPushButton('重置', self, clicked=self.set_reset), 1, 2)
        layout.addWidget(QPushButton('停止', self, clicked=self.set_stop), 1, 3)
        layout.addWidget(QPushButton('不可见', self, clicked=self.set_visible), 1,
                         4)

        # 模拟进度
        layout.addWidget(
            QPushButton('模拟进度动画', self, clicked=self.start_progress), 2, 0, 1,
            5)

        # 状态
        layout.addWidget(QLabel('暂停信号 :', self), 3, 0)
        self.labelPause = QLabel(self)
        layout.addWidget(self.labelPause, 3, 1)
        self.taskProgress.pausedChanged.connect(
            lambda v: self.labelPause.setText(str(v)))

        layout.addWidget(QLabel('停止信号 :', self), 4, 0)
        self.labelStop = QLabel(self)
        layout.addWidget(self.labelStop, 4, 1)
        self.taskProgress.stoppedChanged.connect(
            lambda v: self.labelStop.setText(str(v)))

        layout.addWidget(QLabel('值改变信号:', self), 5, 0)
        self.labelValue = QLabel(self)
        layout.addWidget(self.labelValue, 5, 1)
        self.taskProgress.valueChanged.connect(
            lambda v: self.labelValue.setText(str(v)))

        layout.addWidget(QLabel('可见度信号:', self), 6, 0)
        self.labelVisible = QLabel(self)
        layout.addWidget(self.labelVisible, 6, 1)
        self.taskProgress.visibilityChanged.connect(
            lambda v: self.labelVisible.setText(str(v)))

    def set_range(self):
        # 设置进度条范围值
        vmin = min(self.spinBoxMin.value(), self.spinBoxMax.value())
        vmax = max(self.spinBoxMin.value(), self.spinBoxMax.value())
        self.taskProgress.setRange(vmin, vmax)

    def set_current_value(self):
        # 设置进度条当前值
        self.taskProgress.setValue(self.spinBoxCur.value())

    def set_show_hide(self):
        # 显示/隐藏
        visible = self.taskProgress.isVisible()
        # 也可以使用self.taskProgress.setVisible
        if visible:
            self.taskProgress.hide()
            self.sender().setText('显示')
        else:
            self.taskProgress.show()
            self.sender().setText('隐藏')

    def set_pause_resume(self):
        # 暂停/恢复
        paused = self.taskProgress.isPaused()
        # 也可以使用self.taskProgress.setPaused
        if paused:
            self.taskProgress.resume()
            self.timerProgress.start(100)
            self.sender().setText('暂停')
        else:
            self.taskProgress.pause()
            self.timerProgress.stop()
            self.sender().setText('恢复')

    def set_reset(self):
        # 重置
        self.taskProgress.reset()
        paused = self.taskProgress.isPaused()
        if not paused:
            self.timerProgress.stop()
            self.timerProgress.start(100)

    def set_stop(self):
        # 停止
        self.timerProgress.stop()
        self.taskProgress.stop()
        self.setEnabled(False)

    def set_visible(self):
        # 可见/不可见
        visible = self.taskProgress.isVisible()
        self.taskProgress.setVisible(not visible)
        self.sender().setText('可见' if visible else '不可见')

    def start_progress(self):
        # 模拟进度
        self.timerProgress.start(100)
        self.sender().setEnabled(False)

    def update_progress(self):
        value = self.taskProgress.value()
        value += 1
        if value > self.taskProgress.maximum():
            value = 0
        self.taskProgress.setValue(value)
コード例 #9
0
 def __init__(self, window):
     super().__init__()
     taskbar_button = QWinTaskbarButton(window)
     taskbar_button.setWindow(window)
     self._progress = taskbar_button.progress()
コード例 #10
0
ファイル: __main__.py プロジェクト: OptimusGREEN/tvlinker
class TVLinker(QWidget):
    def __init__(self, settings: QSettings, parent=None):
        super(TVLinker, self).__init__(parent)
        self.firstrun = True
        self.rows, self.cols = 0, 0
        self.parent = parent
        self.settings = settings
        self.taskbar = TaskbarProgress(self)
        self.init_styles()
        self.init_settings()
        self.init_icons()
        if sys.platform.startswith('linux'):
            notify.init(qApp.applicationName())
        layout = QVBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(15, 15, 15, 0)
        form_groupbox = QGroupBox(self, objectName='mainForm')
        form_groupbox.setLayout(self.init_form())
        self.table = TVLinkerTable(0, 4, self)
        self.table.doubleClicked.connect(self.show_hosters)
        layout.addWidget(form_groupbox)
        layout.addWidget(self.table)
        layout.addLayout(self.init_metabar())
        self.setLayout(layout)
        qApp.setWindowIcon(self.icon_app)
        self.resize(FixedSettings.windowSize)
        self.show()
        self.start_scraping()
        self.firstrun = False

    class ProcError(Enum):
        FAILED_TO_START = 0
        CRASHED = 1
        TIMED_OUT = 2
        READ_ERROR = 3
        WRITE_ERROR = 4
        UNKNOWN_ERROR = 5

    class NotifyIcon(Enum):
        SUCCESS = ':assets/images/tvlinker.png'
        DEFAULT = ':assets/images/tvlinker.png'

    def init_threads(self, threadtype: str = 'scrape') -> None:
        if threadtype == 'scrape':
            if hasattr(self, 'scrapeThread'):
                if not sip.isdeleted(
                        self.scrapeThread) and self.scrapeThread.isRunning():
                    self.scrapeThread.terminate()
                    del self.scrapeWorker
                    del self.scrapeThread
            self.scrapeThread = QThread(self)
            self.scrapeWorker = ScrapeWorker(self.source_url, self.user_agent,
                                             self.dl_pagecount)
            self.scrapeThread.started.connect(self.show_progress)
            self.scrapeThread.started.connect(self.scrapeWorker.begin)
            self.scrapeWorker.moveToThread(self.scrapeThread)
            self.scrapeWorker.addRow.connect(self.add_row)
            self.scrapeWorker.workFinished.connect(self.scrape_finished)
            self.scrapeWorker.workFinished.connect(
                self.scrapeWorker.deleteLater, Qt.DirectConnection)
            self.scrapeWorker.workFinished.connect(self.scrapeThread.quit,
                                                   Qt.DirectConnection)
            self.scrapeThread.finished.connect(self.scrapeThread.deleteLater,
                                               Qt.DirectConnection)
        elif threadtype == 'unrestrict':
            pass

    @staticmethod
    def load_stylesheet(qssfile: str) -> None:
        if QFileInfo(qssfile).exists():
            qss = QFile(qssfile)
            qss.open(QFile.ReadOnly | QFile.Text)
            qApp.setStyleSheet(QTextStream(qss).readAll())

    def init_styles(self) -> None:
        if sys.platform == 'darwin':
            qss_stylesheet = self.get_path('%s_osx.qss' %
                                           qApp.applicationName().lower())
        else:
            qss_stylesheet = self.get_path('%s.qss' %
                                           qApp.applicationName().lower())
        TVLinker.load_stylesheet(qss_stylesheet)
        QFontDatabase.addApplicationFont(':assets/fonts/opensans.ttf')
        QFontDatabase.addApplicationFont(':assets/fonts/opensans-bold.ttf')
        QFontDatabase.addApplicationFont(':assets/fonts/opensans-semibold.ttf')
        qApp.setFont(QFont('Open Sans',
                           12 if sys.platform == 'darwin' else 10))

    def init_icons(self) -> None:
        self.icon_app = QIcon(
            self.get_path('images/%s.png' % qApp.applicationName().lower()))
        self.icon_faves_off = QIcon(':assets/images/star_off.png')
        self.icon_faves_on = QIcon(':assets/images/star_on.png')
        self.icon_refresh = QIcon(':assets/images/refresh.png')
        self.icon_menu = QIcon(':assets/images/menu.png')
        self.icon_settings = QIcon(':assets/images/cog.png')
        self.icon_updates = QIcon(':assets/images/cloud.png')

    def init_settings(self) -> None:
        self.provider = 'Scene-RLS'
        self.select_provider(0)
        self.user_agent = self.settings.value('user_agent')
        self.dl_pagecount = self.settings.value('dl_pagecount', 20, int)
        self.dl_pagelinks = FixedSettings.linksPerPage
        self.realdebrid_api_token = self.settings.value('realdebrid_apitoken')
        self.realdebrid_api_proxy = self.settings.value('realdebrid_apiproxy')
        self.download_manager = self.settings.value('download_manager')
        self.persepolis_cmd = self.settings.value('persepolis_cmd')
        self.pyload_host = self.settings.value('pyload_host')
        self.pyload_username = self.settings.value('pyload_username')
        self.pyload_password = self.settings.value('pyload_password')
        self.idm_exe_path = self.settings.value('idm_exe_path')
        self.kget_cmd = self.settings.value('kget_cmd')
        self.favorites = self.settings.value('favorites')

    def init_form(self) -> QHBoxLayout:
        self.search_field = QLineEdit(self,
                                      clearButtonEnabled=True,
                                      placeholderText='Enter search criteria')
        self.search_field.setObjectName('searchInput')
        self.search_field.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Fixed)
        self.search_field.setFocus()
        self.search_field.textChanged.connect(self.clear_filters)
        self.search_field.returnPressed.connect(
            lambda: self.filter_table(self.search_field.text()))
        self.favorites_button = QPushButton(parent=self,
                                            flat=True,
                                            cursor=Qt.PointingHandCursor,
                                            objectName='favesButton',
                                            toolTip='Favorites',
                                            checkable=True,
                                            toggled=self.filter_faves,
                                            checked=self.settings.value(
                                                'faves_filter', False, bool))
        self.refresh_button = QPushButton(parent=self,
                                          flat=True,
                                          cursor=Qt.PointingHandCursor,
                                          objectName='refreshButton',
                                          toolTip='Refresh',
                                          clicked=self.start_scraping)
        self.dlpages_field = QComboBox(self,
                                       toolTip='Pages',
                                       editable=False,
                                       cursor=Qt.PointingHandCursor)
        self.dlpages_field.addItems(
            ('10', '20', '30', '40', '50', '60', '70', '80'))
        self.dlpages_field.setCurrentIndex(
            self.dlpages_field.findText(str(self.dl_pagecount),
                                        Qt.MatchFixedString))
        self.dlpages_field.currentIndexChanged.connect(self.update_pagecount)
        self.settings_button = QPushButton(parent=self,
                                           flat=True,
                                           toolTip='Menu',
                                           objectName='menuButton',
                                           cursor=Qt.PointingHandCursor)
        self.settings_button.setMenu(self.settings_menu())
        layout = QHBoxLayout(spacing=10)
        # providerCombo = QComboBox(self, toolTip='Provider', editable=False, cursor=Qt.PointingHandCursor)
        # providerCombo.setObjectName('providercombo')
        # providerCombo.addItem(QIcon(':assets/images/provider-scenerls.png'), '')
        # providerCombo.addItem(QIcon(':assets/images/provider-tvrelease.png'), '')
        # providerCombo.setIconSize(QSize(146, 36))
        # providerCombo.setMinimumSize(QSize(160, 40))
        # providerCombo.setStyleSheet('''
        #     QComboBox, QComboBox::drop-down { background-color: transparent; border: none; margin: 5px; }
        #     QComboBox::down-arrow { image: url(:assets/images/down_arrow.png); }
        #     QComboBox QAbstractItemView { selection-background-color: #DDDDE4; }
        # ''')
        # providerCombo.currentIndexChanged.connect(self.select_provider)
        layout.addWidget(
            QLabel(pixmap=QPixmap(':assets/images/provider-scenerls.png')))
        layout.addWidget(self.search_field)
        layout.addWidget(self.favorites_button)
        layout.addWidget(self.refresh_button)
        layout.addWidget(QLabel('Pages:'))
        layout.addWidget(self.dlpages_field)
        layout.addWidget(self.settings_button)
        return layout

    @pyqtSlot(int)
    def select_provider(self, index: int):
        if index == 0:
            self.provider = 'Scene-RLS'
            self.source_url = 'http://scene-rls.net/releases/index.php?p={0}&cat=TV%20Shows'
        elif index == 1:
            self.provider = 'TV-Release'
            self.source_url = 'http://tv-release.pw/?cat=TV'
        self.setWindowTitle('%s :: %s' %
                            (qApp.applicationName(), self.provider))

    def settings_menu(self) -> QMenu:
        settings_action = QAction(self.icon_settings,
                                  'Settings',
                                  self,
                                  triggered=self.show_settings)
        updates_action = QAction(self.icon_updates,
                                 'Check for updates',
                                 self,
                                 triggered=self.check_update)
        aboutqt_action = QAction('About Qt', self, triggered=qApp.aboutQt)
        about_action = QAction('About %s' % qApp.applicationName(),
                               self,
                               triggered=self.about_app)
        menu = QMenu()
        menu.addAction(settings_action)
        menu.addAction(updates_action)
        menu.addSeparator()
        menu.addAction(aboutqt_action)
        menu.addAction(about_action)
        return menu

    def init_metabar(self) -> QHBoxLayout:
        self.meta_template = 'Total number of links retrieved: <b>%i</b> / <b>%i</b>'
        self.progress = QProgressBar(parent=self,
                                     minimum=0,
                                     maximum=(self.dl_pagecount *
                                              self.dl_pagelinks),
                                     visible=False)
        self.taskbar.setProgress(0.0, True)
        if sys.platform == 'win32':
            self.win_taskbar_button = QWinTaskbarButton(self)

        self.meta_label = QLabel(textFormat=Qt.RichText,
                                 alignment=Qt.AlignRight,
                                 objectName='totals')
        self.update_metabar()
        layout = QHBoxLayout()
        layout.setContentsMargins(10, 5, 10, 10)
        layout.addWidget(self.progress, Qt.AlignLeft)
        layout.addWidget(self.meta_label, Qt.AlignRight)
        return layout

    @pyqtSlot()
    def check_update(self) -> None:
        QDesktopServices.openUrl(QUrl(FixedSettings.latest_release_url))

    @pyqtSlot()
    def show_settings(self) -> None:
        settings_win = Settings(self, self.settings)
        settings_win.exec_()

    def update_metabar(self) -> bool:
        rowcount = self.table.rowCount()
        self.meta_label.setText(
            self.meta_template %
            (rowcount, self.dl_pagecount * self.dl_pagelinks))
        self.progress.setValue(rowcount)
        self.taskbar.setProgress(rowcount / self.progress.maximum())
        if sys.platform == 'win32':
            self.win_taskbar_button.progress().setValue(self.progress.value())
        return True

    def start_scraping(self) -> None:
        self.init_threads('scrape')
        self.rows = 0
        self.table.clearContents()
        self.table.setRowCount(0)
        self.table.setSortingEnabled(False)
        self.update_metabar()
        self.scrapeThread.start()

    @pyqtSlot()
    def about_app(self) -> None:
        about_html = '''<style>
        a { color:#441d4e; text-decoration:none; font-weight:bold; }
        a:hover { text-decoration:underline; }
    </style>
    <p style="font-size:24pt; font-weight:bold; color:#6A687D;">%s</p>
    <p>
        <span style="font-size:13pt;"><b>Version: %s</b></span>
        <span style="font-size:10pt;position:relative;left:5px;">( %s )</span>
    </p>
    <p style="font-size:13px;">
        Copyright &copy; %s <a href="mailto:[email protected]">Pete Alexandrou</a>
        <br/>
        Web: <a href="%s">%s</a>
    </p>
    <p style="font-size:11px;">
        This program is free software; you can redistribute it and/or
        modify it under the terms of the GNU General Public License
        as published by the Free Software Foundation; either version 2
        of the License, or (at your option) any later version.
    </p>''' % (qApp.applicationName(), qApp.applicationVersion(),
               platform.architecture()[0], datetime.now().year,
               qApp.organizationDomain(), qApp.organizationDomain())
        QMessageBox.about(self, 'About %s' % qApp.applicationName(),
                          about_html)

    @pyqtSlot(int)
    def update_pagecount(self, index: int) -> None:
        self.dl_pagecount = int(self.dlpages_field.itemText(index))
        self.scrapeWorker.maxpages = self.dl_pagecount
        self.progress.setMaximum(self.dl_pagecount * self.dl_pagelinks)
        self.settings.setValue('dl_pagecount', self.dl_pagecount)
        if sys.platform == 'win32':
            self.win_taskbar_button.progress().setMaximum(self.dl_pagecount *
                                                          self.dl_pagelinks)
        if self.scrapeThread.isRunning():
            self.scrapeThread.requestInterruption()
        self.start_scraping()

    @pyqtSlot()
    def show_progress(self):
        self.progress.show()
        self.taskbar.setProgress(0.0, True)
        if sys.platform == 'win32':
            self.win_taskbar_button.setWindow(self.windowHandle())
            self.win_taskbar_button.progress().setRange(
                0, self.dl_pagecount * self.dl_pagelinks)
            self.win_taskbar_button.progress().setVisible(True)
            self.win_taskbar_button.progress().setValue(self.progress.value())

    @pyqtSlot()
    def scrape_finished(self) -> None:
        self.progress.hide()
        self.taskbar.setProgress(0.0, False)
        if sys.platform == 'win32':
            self.win_taskbar_button.progress().setVisible(False)
        self.table.setSortingEnabled(True)
        self.filter_table(text='')

    @pyqtSlot(list)
    def add_row(self, row: list) -> None:
        if self.scrapeThread.isInterruptionRequested():
            self.scrapeThread.terminate()
        else:
            self.cols = 0
            self.table.setRowCount(self.rows + 1)
            if self.table.cursor() != Qt.PointingHandCursor:
                self.table.setCursor(Qt.PointingHandCursor)
            for item in row:
                table_item = QTableWidgetItem(item)
                table_item.setToolTip(
                    '%s\n\nDouble-click to view hoster links.' % row[1])
                table_item.setFont(QFont('Open Sans', weight=QFont.Normal))
                if self.cols == 2:
                    if sys.platform == 'win32':
                        table_item.setFont(
                            QFont('Open Sans Semibold', pointSize=10))
                    elif sys.platform == 'darwin':
                        table_item.setFont(
                            QFont('Open Sans Bold', weight=QFont.Bold))
                    else:
                        table_item.setFont(
                            QFont('Open Sans',
                                  weight=QFont.DemiBold,
                                  pointSize=10))
                    table_item.setText('  ' + table_item.text())
                elif self.cols in (0, 3):
                    table_item.setTextAlignment(Qt.AlignCenter)
                self.table.setItem(self.rows, self.cols, table_item)
                self.update_metabar()
                self.cols += 1
            self.rows += 1

    @pyqtSlot(list)
    def add_hosters(self, links: list) -> None:
        self.hosters_win.show_hosters(links)

    @pyqtSlot(QModelIndex)
    def show_hosters(self, index: QModelIndex) -> None:
        qApp.setOverrideCursor(Qt.BusyCursor)
        self.hosters_win = HosterLinks(self)
        self.hosters_win.downloadLink.connect(self.download_link)
        self.hosters_win.copyLink.connect(self.copy_download_link)
        self.links = HostersThread(
            self.table.item(self.table.currentRow(), 1).text(),
            self.user_agent)
        self.links.setHosters.connect(self.add_hosters)
        self.links.noLinks.connect(self.no_links)
        self.links.start()

    @pyqtSlot()
    def no_links(self) -> None:
        self.hosters_win.loading_progress.cancel()
        self.hosters_win.close()
        QMessageBox.warning(
            self, 'No Links Available',
            'No links are available yet for the chosen TV show. ' +
            'This is most likely due to the files still being uploaded. This is normal if the '
            +
            'link was published 30-45 mins ago.\n\nPlease check back again in 10-15 minutes.'
        )

    @pyqtSlot(bool)
    def filter_faves(self, checked: bool) -> None:
        self.settings.setValue('faves_filter', checked)
        # if hasattr(self, 'scrapeWorker') and (sip.isdeleted(self.scrapeWorker) or self.scrapeWorker.complete):
        if not self.firstrun:
            self.filter_table()

    @pyqtSlot(str)
    @pyqtSlot()
    def filter_table(self, text: str = '') -> None:
        filters = []
        if self.favorites_button.isChecked():
            filters = self.favorites
            self.table.sortItems(2, Qt.AscendingOrder)
        else:
            self.table.sortItems(0, Qt.DescendingOrder)
        if len(text):
            filters.append(text)
        if not len(filters) or not hasattr(self, 'valid_rows'):
            self.valid_rows = []
        for search_term in filters:
            for item in self.table.findItems(search_term, Qt.MatchContains):
                self.valid_rows.append(item.row())
        for row in range(0, self.table.rowCount()):
            if not len(filters):
                self.table.showRow(row)
            else:
                if row not in self.valid_rows:
                    self.table.hideRow(row)
                else:
                    self.table.showRow(row)

    @pyqtSlot()
    def clear_filters(self):
        if not len(self.search_field.text()):
            self.filter_table('')

    @pyqtSlot(bool)
    def aria2_confirmation(self, success: bool) -> None:
        qApp.restoreOverrideCursor()
        if success:
            if sys.platform.startswith('linux'):
                self.notify(
                    title=qApp.applicationName(),
                    msg='Your download link has been unrestricted and now ' +
                    'queued in Aria2 RPC Daemon',
                    icon=self.NotifyIcon.SUCCESS)
            else:
                QMessageBox.information(
                    self, qApp.applicationName(),
                    'Download link has been queued in Aria2.', QMessageBox.Ok)
        else:
            QMessageBox.critical(
                self, 'Aria2 RPC Daemon',
                'Could not connect to Aria2 RPC Daemon. ' +
                'Check your %s settings and try again.' %
                qApp.applicationName(), QMessageBox.Ok)

    @pyqtSlot(str)
    def download_link(self, link: str) -> None:
        if len(self.realdebrid_api_token) > 0 and 'real-debrid.com' not in link \
            and 'rdeb.io' not in link:
            qApp.setOverrideCursor(Qt.BusyCursor)
            self.unrestrict_link(link, True)
        else:
            if self.download_manager == 'aria2':
                self.aria2 = Aria2Thread(settings=self.settings, link_url=link)
                self.aria2.aria2Confirmation.connect(self.aria2_confirmation)
                self.aria2.start()
                self.hosters_win.close()
            elif self.download_manager == 'pyload':
                self.pyload_conn = PyloadConnection(self.pyload_host,
                                                    self.pyload_username,
                                                    self.pyload_password)
                pid = self.pyload_conn.addPackage(name='TVLinker',
                                                  links=[link])
                qApp.restoreOverrideCursor()
                self.hosters_win.close()
                if sys.platform.startswith('linux'):
                    self.notify(title='Download added to %s' %
                                self.download_manager,
                                icon=self.NotifyIcon.SUCCESS)
                else:
                    QMessageBox.information(
                        self, self.download_manager,
                        'Your link has been queued in %s.' %
                        self.download_manager, QMessageBox.Ok)
                # open_pyload = msgbox.addButton('Open pyLoad', QMessageBox.AcceptRole)
                # open_pyload.clicked.connect(self.open_pyload)
            elif self.download_manager in ('kget', 'persepolis'):
                provider = self.kget_cmd if self.download_manager == 'kget' else self.persepolis_cmd
                cmd = '{0} "{1}"'.format(provider, link)
                if self.cmdexec(cmd):
                    qApp.restoreOverrideCursor()
                    self.hosters_win.close()
                    if sys.platform.startswith('linux'):
                        self.notify(title='Download added to %s' %
                                    self.download_manager,
                                    icon=self.NotifyIcon.SUCCESS)
                    else:
                        QMessageBox.information(
                            self, self.download_manager,
                            'Your link has been queued in %s.' %
                            self.download_manager, QMessageBox.Ok)
            elif self.download_manager == 'idm':
                cmd = '"%s" /n /d "%s"' % (self.idm_exe_path, link)
                if self.cmdexec(cmd):
                    qApp.restoreOverrideCursor()
                    self.hosters_win.close()
                    QMessageBox.information(
                        self, 'Internet Download Manager',
                        'Your link has been queued in IDM.')
                else:
                    print('IDM QProcess error = %s' %
                          self.ProcError(self.idm.error()).name)
                    qApp.restoreOverrideCursor()
                    self.hosters_win.close()
                    QMessageBox.critical(
                        self, 'Internet Download Manager',
                        '<p>Could not connect to your local IDM application instance. '
                        +
                        'Please check your settings and ensure the IDM executable path is correct '
                        +
                        'according to your installation.</p><p>Error Code: %s</p>'
                        % self.ProcError(self.idm.error()).name,
                        QMessageBox.Ok)
            else:
                dlpath, _ = QFileDialog.getSaveFileName(
                    self, 'Save File',
                    link.split('/')[-1])
                if dlpath != '':
                    self.directdl_win = DirectDownload(parent=self)
                    self.directdl = DownloadThread(link_url=link,
                                                   dl_path=dlpath)
                    self.directdl.dlComplete.connect(
                        self.directdl_win.download_complete)
                    if sys.platform.startswith('linux'):
                        self.directdl.dlComplete.connect(
                            lambda: self.notify(qApp.applicationName(
                            ), 'Download complete', self.NotifyIcon.SUCCESS))
                    else:
                        self.directdl.dlComplete.connect(
                            lambda: QMessageBox.information(
                                self, qApp.applicationName(),
                                'Download complete', QMessageBox.Ok))
                    self.directdl.dlProgressTxt.connect(
                        self.directdl_win.update_progress_label)
                    self.directdl.dlProgress.connect(
                        self.directdl_win.update_progress)
                    self.directdl_win.cancelDownload.connect(
                        self.cancel_download)
                    self.directdl.start()
                    self.hosters_win.close()

    def _init_notification_icons(self):
        for icon in self.NotifyIcon:
            icon_file = QPixmap(icon.value, 'PNG')
            icon_file.save(
                os.path.join(FixedSettings.config_path,
                             os.path.basename(icon.value)), 'PNG', 100)

    def notify(self,
               title: str,
               msg: str = '',
               icon: Enum = None,
               urgency: int = 1) -> bool:
        icon_path = icon.value if icon is not None else self.NotifyIcon.DEFAULT.value
        icon_path = os.path.join(FixedSettings.config_path,
                                 os.path.basename(icon_path))
        if not os.path.exists(icon_path):
            self._init_notification_icons()
        notification = notify.Notification(title, msg, icon_path)
        notification.set_urgency(urgency)
        return notification.show()

    def cmdexec(self, cmd: str) -> bool:
        self.proc = QProcess()
        self.proc.setProcessChannelMode(QProcess.MergedChannels)
        if hasattr(self.proc, 'errorOccurred'):
            self.proc.errorOccurred.connect(lambda error: print(
                'Process error = %s' % self.ProcError(error).name))
        if self.proc.state() == QProcess.NotRunning:
            self.proc.start(cmd)
            self.proc.waitForFinished(-1)
            rc = self.proc.exitStatus(
            ) == QProcess.NormalExit and self.proc.exitCode() == 0
            self.proc.deleteLater()
            return rc
        return False

    @pyqtSlot()
    def cancel_download(self) -> None:
        self.directdl.cancel_download = True
        self.directdl.quit()
        self.directdl.deleteLater()

    def open_pyload(self) -> None:
        QDesktopServices.openUrl(QUrl(self.pyload_config.host))

    @pyqtSlot(str)
    def copy_download_link(self, link: str) -> None:
        if len(self.realdebrid_api_token) > 0 and 'real-debrid.com' not in link \
            and 'rdeb.io' not in link:
            qApp.setOverrideCursor(Qt.BusyCursor)
            self.unrestrict_link(link, False)
        else:
            clip = qApp.clipboard()
            clip.setText(link)
            self.hosters_win.close()
            qApp.restoreOverrideCursor()

    def unrestrict_link(self, link: str, download: bool = True) -> None:
        caller = inspect.stack()[1].function
        self.realdebrid = RealDebridThread(
            settings=self.settings,
            api_url=FixedSettings.realdebrid_api_url,
            link_url=link,
            action=RealDebridThread.RealDebridAction.UNRESTRICT_LINK)
        self.realdebrid.errorMsg.connect(self.error_handler)
        if download:
            self.realdebrid.unrestrictedLink.connect(self.download_link)
        else:
            self.realdebrid.unrestrictedLink.connect(self.copy_download_link)
        self.realdebrid.start()

    def closeEvent(self, event: QCloseEvent) -> None:
        if hasattr(self, 'scrapeThread'):
            if not sip.isdeleted(
                    self.scrapeThread) and self.scrapeThread.isRunning():
                self.scrapeThread.requestInterruption()
                self.scrapeThread.quit()
        qApp.quit()

    def error_handler(self, props: list) -> None:
        qApp.restoreOverrideCursor()
        QMessageBox.critical(self, props[0], props[1], QMessageBox.Ok)

    @staticmethod
    def get_path(path: str = None, override: bool = False) -> str:
        if override:
            if getattr(sys, 'frozen', False):
                return os.path.join(sys._MEIPASS, path)
            return os.path.join(QFileInfo(__file__).absolutePath(), path)
        return ':assets/%s' % path

    @staticmethod
    def get_version(filename: str = '__init__.py') -> str:
        with open(TVLinker.get_path(filename, override=True), 'r') as initfile:
            for line in initfile.readlines():
                m = re.match('__version__ *= *[\'](.*)[\']', line)
                if m:
                    return m.group(1)
コード例 #11
0
class ArchivViewer(QMainWindow, ArchivviewerUi):
    def __init__(self, con, parent=None):
        super(ArchivViewer, self).__init__(parent)
        self._config = ConfigReader.get_instance()
        self._con = con
        self.taskbar_button = None
        self.taskbar_progress = None
        self.setupUi(self)
        iconpath = os.sep.join(
            [os.path.dirname(os.path.realpath(__file__)), 'icon128.png'])
        self.setWindowIcon(QIcon(iconpath))
        self.setWindowTitle("Archiv Viewer")
        self.refreshFiles.setIcon(self.style().standardIcon(
            QStyle.SP_BrowserReload))
        self.exportPdf.setIcon(self.style().standardIcon(
            QStyle.SP_DialogSaveButton))
        self.savePreset.setIcon(self.style().standardIcon(
            QStyle.SP_DialogSaveButton))
        self.savePreset.setText('')
        self.clearPreset.setIcon(self.style().standardIcon(
            QStyle.SP_TrashIcon))
        self.clearPreset.setText('')
        self.readSettings()
        self.delegate = FilesTableDelegate()
        self.documentView.setItemDelegate(self.delegate)
        self.actionStayOnTop.changed.connect(self.stayOnTopChanged)
        self.actionShowPDFAfterExport.changed.connect(
            self.showPDFAfterExportChanged)
        self.actionUseImg2pdf.changed.connect(self.useImg2pdfChanged)
        self.actionUseGimpForTiff.changed.connect(self.useGimpForTiffChanged)
        self.actionOptimizeExport.changed.connect(self.optimizeExportChanged)
        self.actionFitToA4.changed.connect(self.fitToA4Changed)
        self.presetModel = PresetModel(self.categoryList)
        self.presets.setModel(self.presetModel)
        self.presets.editTextChanged.connect(self.presetsEditTextChanged)
        self.clearPreset.clicked.connect(self.clearPresetClicked)
        self.savePreset.clicked.connect(self.savePresetClicked)
        self.presets.currentIndexChanged.connect(self.presetsIndexChanged)
        try:
            self.categoryListModel = CategoryModel(self._con)
        except Exception as e:
            displayErrorMessage(
                "Fehler beim Laden der Kategorien: {}".format(e))
            sys.exit()
        self.categoryList.setModel(self.categoryListModel)
        self.categoryListModel.dataChanged.connect(
            self.categoryListModelDataChanged)

    def displayErrorMessage(self, msg):
        QMessageBox.critical(self, "Fehler", str(msg))

    def stayOnTopChanged(self):
        ontop = self.actionStayOnTop.isChecked()
        self._config.setValue('stayontop', ontop)
        if ontop:
            self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        else:
            self.setWindowFlags(self.windowFlags()
                                & (~Qt.WindowStaysOnTopHint))
        self.show()

    def clearPresetClicked(self):
        buttonReply = QMessageBox.question(
            self, 'Voreinstellung löschen',
            "Soll die aktive Voreinstellung wirklich gelöscht werden?",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if (buttonReply == QMessageBox.Yes):
            curidx = self.presets.findText(self.presets.currentText())
            self.presets.removeItem(curidx)
            self.presets.setCurrentIndex(0)
        else:
            return

    def savePresetClicked(self):
        curidx = self.presets.findText(self.presets.currentText())
        if curidx != 0:
            ids = [
                self.categoryList.model().idAtRow(idx.row())
                for idx in self.categoryList.selectionModel().selectedRows()
            ]

            if curidx > 0:
                self.presetModel.setSelectedCategories(curidx, ids)
            else:
                curidx = self.presetModel.insertPreset(
                    self.presets.currentText(), ids)
            self.presets.setCurrentIndex(curidx)

    def presetsEditTextChanged(self, text):
        idx = self.presets.findText(text)
        self.savePreset.setEnabled(idx != 0 and len(text) > 0)
        self.clearPreset.setEnabled(idx > 0)

    def presetsIndexChanged(self, idx):
        self.savePreset.setEnabled(idx > 0)
        self.clearPreset.setEnabled(idx > 0)
        categories = self.presetModel.categoriesAtIndex(idx)
        selm = self.categoryList.selectionModel()
        cmodel = self.categoryList.model()
        catidxs = [
            cmodel.createIndex(row, 0, None)
            for row in range(cmodel.rowCount(None))
        ]

        qis = QItemSelection()
        for cidx in catidxs:
            if cmodel.idAtRow(cidx.row()) in categories:
                qir = QItemSelectionRange(cidx)
                qis.append(qir)
        selm.select(qis, QItemSelectionModel.ClearAndSelect)

    def categoryListModelDataChanged(self, begin, end):
        self.presetsIndexChanged(self.presetList.currentIndex())

    def showPDFAfterExportChanged(self):
        show = self.actionShowPDFAfterExport.isChecked()
        self._config.setValue('showPDFAfterExport', show)

    def useImg2pdfChanged(self):
        use = self.actionUseImg2pdf.isChecked()
        self._config.setValue('useImg2pdf', use)

    def optimizeExportChanged(self):
        optimize = self.actionOptimizeExport.isChecked()
        self._config.setValue('shrinkPDF', optimize)

    def useGimpForTiffChanged(self):
        use = self.actionUseGimpForTiff.isChecked()
        self._config.setValue('useGimpForTiff', use)

    def fitToA4Changed(self):
        self._config.setValue('fitToA4', self.actionFitToA4.isChecked())

    def event(self, evt):
        ontop = self._config.getValue('stayontop')
        if evt.type() == QEvent.WindowActivate or evt.type(
        ) == QEvent.HoverEnter:
            self.setWindowOpacity(1.0)
        elif (evt.type() == QEvent.WindowDeactivate or evt.type()
              == QEvent.HoverLeave) and not self.isActiveWindow() and ontop:
            self.setWindowOpacity(0.6)
        return QMainWindow.event(self, evt)

    def showEvent(self, evt):
        self.taskbar_button = QWinTaskbarButton()
        self.taskbar_progress = self.taskbar_button.progress()
        self.taskbar_button.setWindow(self.windowHandle())

    def closeEvent(self, evt):
        settings = QSettings(QSettings.UserScope, "cortex", "ArchivViewer")
        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("windowState", self.saveState())
        settings.setValue("splitter", self.splitter.saveState())
        settings.sync()
        QMainWindow.closeEvent(self, evt)

    def readSettings(self):
        settings = QSettings(QSettings.UserScope, "cortex", "ArchivViewer")
        try:
            self.restoreGeometry(settings.value("geometry"))
            self.restoreState(settings.value("windowState"))
            self.splitter.restoreState(settings.value("splitter"))
        except Exception as e:
            pass
コード例 #12
0
class Assist(QWidget):
    # logsig = pyqtSignal(str)

    WAITING_MESSAGE = 'AWAITING IMAGE - TAKE SCREENSHOT USING ALT+PRINTSCREEN'

    VERSION_FILENAME = 'version-number.txt'

    def __init__(self, config):
        super().__init__()
        self.config = config
        self.initUI()

    def initUI(self):
        self.mainIcon = QIcon('AssistIcon.ico')
        self.setGeometry(100, 100, 1000, 500)
        self.setWindowTitle('Assist')
        self.setWindowIcon(self.mainIcon)

        self.processingLogo = RCLogoView()

        version_number = ''
        with open(self.VERSION_FILENAME) as f:
            version_number = f.readline()

        output_string_names = [
            x['name'] for x in self.config['main']['output_strings']
        ]
        self.formatComboBox = QComboBox()
        self.formatComboBox.addItems(output_string_names)
        default_output_string = self.config['main']['default_output_string']
        if default_output_string in output_string_names:
            self.formatComboBox.setCurrentIndex(
                output_string_names.index(default_output_string))
        self.formatComboBox.currentIndexChanged.connect(
            self.handleOutputStringChanged)

        self.formatType = QLabel()
        self.formatText = QTextEdit()
        self.formatText.setReadOnly(True)
        self.formatText.document().setDefaultStyleSheet(
            'span.sv { color: #CC44FF; }')

        self.handleOutputStringChanged()

        self.log_lock = QMutex()
        self.log = QTextEdit()
        self.log.setReadOnly(True)
        self.log.document().setDefaultStyleSheet(
            '* { font-family: Consolas; } span.logmessage { color: #FFFFFF; white-space: pre; }'
        )

        self.layout = QVBoxLayout(self)
        # self.layout.addWidget(self.processingLogo)

        self.statusWidget = QWidget(self)
        self.statusWidget.setObjectName('statusWidget')
        # self.statusWidget.setStyleSheet('font-family: "Arame Mono"; ')
        self.statusWidget.setStyleSheet(
            '* { font-family: "Arame Mono"; font-size: 14px; } #statusWidget { border: none; }'
        )
        self.statusLayout = QHBoxLayout(self.statusWidget)
        self.statusLayout.setContentsMargins(3, 3, 3, 3)
        self.statusLayout.addStretch()
        self.statusInitialLabel = QLabel('STATUS:')
        self.statusInitialLabel.setStyleSheet('color: #AAAAAA;')
        self.statusLayout.addWidget(self.statusInitialLabel)
        self.statusMessageLabel = QLabel(self.WAITING_MESSAGE)
        self.statusLayout.addWidget(self.statusMessageLabel)
        self.statusLayout.addStretch()
        self.layout.addWidget(self.statusWidget)

        self.repeatButton = QPushButton('&Re-copy last text', self)
        # self.repeatButton.setFlat(True)
        self.repeatButton.setStyleSheet('min-height: 30px; max-width: 100px;')
        self.repeatButton.clicked.connect(self.handleRepeatButtonClicked)

        self.taskbarButton = None
        self.taskbarProgress = None
        """
        self.taskbarButton.setWindow(self.windowHandle())
        self.taskbarProgress = self.taskbarButton.progress()
        self.taskbarProgress.setVisible(True)
        # self.taskbarProgress.setRange(0, 100)
        """

        self.versionLabel = QLabel(version_number)
        self.versionLabel.setStyleSheet('font-size: 10px;')
        self.versionLabel.setAlignment(Qt.AlignRight)

        self.layout.addWidget(QLabel('Output format:'))
        self.layout.addWidget(self.formatComboBox)
        self.layout.addWidget(self.formatType)
        self.layout.addWidget(self.formatText)
        self.layout.addWidget(QLabel('Debug log:'))
        self.layout.addWidget(self.log)
        self.layout.addWidget(self.repeatButton)
        self.layout.addWidget(self.versionLabel)

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setIcon(self.mainIcon)
        self.trayIcon.show()

        QApplication.clipboard().dataChanged.connect(
            self.handleClipboardChanged)

        self.image_queue = queue.Queue()
        self.image_queue_lock = QMutex()

        self.image_processing_thread = ProcessClipboardImageThread(
            self, self.config)
        self.image_processing_thread.message.connect(
            self.handleProcessThreadMessage)
        self.image_processing_thread.log.connect(self.handleLogMessage)
        self.image_processing_thread.clipboard.connect(
            self.handleClipboardMessage)
        self.image_processing_thread.processing.connect(
            self.handleProcessingStateChange)
        self.image_processing_thread.lines_complete.connect(
            self.handleLinesComplete)
        self.image_processing_thread.start()

        self.header_line_window = None

        self.last_clipboard_content = ''

        # self.setStyleSheet("background-color: rgba(53,53,53,255);")

        self.show()

        self.rtf_clipboard_code = RegisterClipboardFormat(win32con.CF_RTF)
        print("RTF clipboard type identified: {}".format(
            self.rtf_clipboard_code))

        closeShortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
        closeShortcut.activated.connect(self.shortcutClose)

    def shortcutClose(self):
        self.close()

    def handleLinesComplete(self):
        def ndarray_to_qlabel(ndarray):
            data = ndarray.copy()
            qimage = QImage(data, data.shape[1], data.shape[0],
                            data.strides[0], QImage.Format_Grayscale8)
            qpixmap = QPixmap(qimage)
            qlabel = QLabel()
            qlabel.setPixmap(qpixmap)
            qlabel.resize(qpixmap.width(), qpixmap.height())
            return qlabel

        self.logMessage("Displaying lines...")

        auslab_image = self.image_processing_thread.auslab_image

        self.header_line_window = QWidget()
        self.header_line_window.setWindowTitle("Header Lines")
        layout = QVBoxLayout(self.header_line_window)
        # self.header_line_window.setGeometry(500, 500, 500, 500)

        for auslab_image_line in auslab_image.header_line_images:
            layout.addWidget(ndarray_to_qlabel(auslab_image_line.line_image))
            layout.addSpacing(5)

        for auslab_image_line in auslab_image.center_line_images:
            layout.addWidget(ndarray_to_qlabel(auslab_image_line.line_image))
            layout.addSpacing(5)

        # print(self.header_line_window.layout.sizeHint())

        self.header_line_window.adjustSize()
        self.header_line_window.show()

    def getCurrentOutputStringEntry(self):
        output_string_config_entry = [
            x for x in self.config['main']['output_strings']
            if x['name'] == self.formatComboBox.currentText()
        ]
        if len(output_string_config_entry) > 0:
            return output_string_config_entry[0]
        else:
            return None

    def getCurrentOutputString(self):
        output_string = [
            x['string'] for x in self.config["main"]["output_strings"]
            if x["name"] == self.formatComboBox.currentText()
        ]
        if len(output_string) > 0:
            output_string = output_string[0]
            return output_string
        else:
            return None

    def handleOutputStringChanged(self):
        def highlightOutputString(output_string):
            return re.sub(r'([^\{]|^)\{([^\{].*?)(\}|$)',
                          r'\1<span class="sv">{\2}</span>', output_string)

        entry = self.getCurrentOutputStringEntry()
        print(entry)
        output_string = ''
        if entry is None:
            output_string = ''
        else:
            output_string = entry['string']
            self.formatType.setText(entry['type'])
        output_string = output_string.replace('\\n', '\n')
        output_string = output_string.replace('\n', '<br />')
        output_html = highlightOutputString(output_string)
        # print(output_html)
        self.formatText.setHtml(output_html)

    def handleProcessingStateChange(self, message_str, step, total_steps):
        COMPLETED_CHAR = '*'
        # COMPLETED_CHAR = '█'
        UNCOMPLETED_CHAR = '.'
        if message_str == 'start':
            # self.processingLogo.animationStart()
            self.statusMessageLabel.setText(
                'AUSLAB IMAGE - PROCESSING 000% [{}]'.format(UNCOMPLETED_CHAR *
                                                             10))
        elif message_str == 'stop':
            # self.processingLogo.animationStop()
            self.statusMessageLabel.setText(self.WAITING_MESSAGE)
            self.taskbarProgress.setValue(0)
        elif message_str == 'update':
            percentage = int((step / total_steps) * 100)
            completed_blocks = percentage // 10
            uncompleted_blocks = 10 - completed_blocks
            completed_text = COMPLETED_CHAR * completed_blocks
            uncompleted_text = UNCOMPLETED_CHAR * uncompleted_blocks

            self.statusMessageLabel.setText(
                'AUSLAB IMAGE - PROCESSING {:03d}% [{}{}]'.format(
                    percentage, completed_text, uncompleted_text))
            self.taskbarProgress.setValue(int((step / total_steps) * 100))

    def handleProcessThreadMessage(self, message_str):
        # self.logMessage('Message signal')
        self.trayIcon.showMessage('Assist', message_str, 3000)

    def handleLogMessage(self, message):
        self.logMessage(message)

    @pyqtSlot()
    def handleRepeatButtonClicked(self):
        self.handleClipboardMessage(self.last_clipboard_content)
        # print('Repeat button pressed.')

    @pyqtSlot()
    def handleClipboardChanged(self):
        self.logMessage('Clipboard changed')

        if self.config['main']['log_clipboard_events']:
            clipboardLogFile = QFile('clipboard-log-{}-{}.txt'.format(
                datetime.datetime.now().strftime('%Y-%m-%d'),
                datetime.datetime.now().strftime('%H%M%S%f')))
            clipboardLogFile.open(QFile.WriteOnly | QFile.Text)
            outputStream = QTextStream(clipboardLogFile)
            mimeData = QApplication.clipboard().mimeData()
            outputStream << "Begin logging clipboard event.  Formats:" << "\n"

            for mimeFormat in QApplication.clipboard().mimeData().formats():
                outputStream << mimeFormat << "\n"

                if mimeFormat == 'text/html':
                    outputStream << mimeData.html() << "\n"
                elif mimeFormat == 'text/plain':
                    outputStream << mimeData.text() << "\n"
                elif mimeFormat == 'application/x-qt-windows-mime;value="Rich Text Format"':
                    outputStream << mimeData.data(mimeFormat).data() << "\n"
                else:
                    outputStream << "Unsupported data type."

                outputStream << "\n"

            # outputStream << QApplication.clipboard().text()

            clipboardLogFile.close()

        # self.clipboard.emit('Test')

        qimage = QApplication.clipboard().image()
        if qimage.isNull():
            return
        self.image_queue_lock.lock()
        self.image_queue.put(qimage)
        self.image_queue_lock.unlock()
        self.logMessage('Image waiting in queue...')

    def logMessage(self, message):
        self.log_lock.lock()
        dt = datetime.datetime.now()
        datestr = dt.strftime('%d-%m-%Y %H:%M:%S')
        # print('At End: {}'.format(self.log.textCursor().atEnd()))
        textCursor = self.log.textCursor()
        textCursor.movePosition(QTextCursor.End)
        self.log.setTextCursor(textCursor)
        # print('Move successful: {}'.format(self.log.textCursor().movePosition(QTextCursor.End)))
        self.log.insertHtml(
            '<span style=""><span style="color: #888888;">[{0}]</span> <span class="logmessage">{1}</span></span><br />'
            .format(datestr, html.escape(message)))
        self.log.verticalScrollBar().setValue(
            self.log.verticalScrollBar().maximum())
        self.log_lock.unlock()

    def handleClipboardMessage(self, content):
        self.last_clipboard_content = content
        cp = QApplication.clipboard()
        cp.setText(content, cp.Clipboard)

        if content.startswith(r'{\rtf'):
            OpenClipboard()
            EmptyClipboard()
            if self.config['main']['paste_text_with_rtf']:
                SetClipboardData(CF_TEXT, content.encode('mbcs'))
            SetClipboardData(self.rtf_clipboard_code, content.encode('ascii'))
            CloseClipboard()

    def showEvent(self, event):
        self.taskbarButton = QWinTaskbarButton(self)
        self.taskbarButton.setWindow(self.windowHandle())
        self.taskbarProgress = self.taskbarButton.progress()
        self.taskbarProgress.setVisible(True)

    def closeEvent(self, event):
        # print('Close Event')
        if self.header_line_window is not None:
            self.header_line_window.close()
        self.trayIcon.hide()

    def bringFocus(self):
        current_flags = self.windowFlags()
        self.setWindowFlags(current_flags | Qt.WindowStaysOnTopHint)
        # self.activateWindow()
        # self.windowHandle().requestActivate()
        self.show()
        self.setWindowFlags(current_flags)
        self.show()
コード例 #13
0
class BVMQMainWindow(QMainWindow, main_window_design.Ui_MainWindow):
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.platform = get_platform()

        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setupUi(self)

        # Read icons
        self.icon_app = QIcon(":/icons/app.svg")
        self.icon_star = QIcon(":/icons/star.png")
        self.icon_trash = QIcon(":/icons/delete.png")
        self.icon_quit = QIcon(":/icons/quit.png")
        self.icon_fake = QIcon(":/icons/fake.svg")

        # Read settings
        self.settings = QSettings('b3d_version_manager', 'settings')

        is_register_blend = self.settings.value('is_register_blend', type=bool)
        self.is_run_minimized = self.settings.value('is_run_minimized',
                                                    type=bool)
        is_run_on_startup = self.settings.value('is_run_on_startup', type=bool)
        root_folder = self.settings.value('root_folder')
        if (not root_folder) or (not os.path.isdir(root_folder)):
            exe_path = os.path.dirname(sys.executable)
            self.settings.setValue('root_folder', exe_path)

        # Custom title bar
        self.btnWiki.clicked.connect(lambda: webbrowser.open(
            "https://github.com/DotBow/Blender-Version-Manager/wiki"))
        self.title.setText("%s %s" % (QApplication.applicationName(),
                                      QApplication.applicationVersion()))
        self.btnClose.clicked.connect(self.hide)
        self.btnMinimize.clicked.connect(self.showMinimized)

        # Custom menu bar
        self.actionToggleRegisterBlend.setChecked(is_register_blend)
        self.actionToggleRegisterBlend.triggered.connect(
            self.toggle_register_blend)

        self.actionToggleRunMinimized.setChecked(self.is_run_minimized)
        self.actionToggleRunMinimized.triggered.connect(
            self.toggle_run_minimized)

        self.actionToggleRunOnStartup.setChecked(is_run_on_startup)
        self.actionToggleRunOnStartup.triggered.connect(
            self.toggle_run_on_startup)

        self.menubar.hide()
        self.btnSettings.setMenu(self.menuFile)

        self.menuFile.installEventFilter(self)

        # Root folder layout
        self.labelRootFolder.setText(self.settings.value('root_folder'))
        self.btnSetRootFolder.clicked.connect(self.set_root_folder)
        self.btnOpenRootFolder.clicked.connect(self.open_root_folder)

        # Tray layout
        self.blender_action = QAction(self.icon_star, "Blender    ", self)
        show_action = QAction("Show", self)
        hide_action = QAction("Hide", self)
        quit_action = QAction(self.icon_quit, "Quit", self)

        self.blender_action.triggered.connect(self.open_latest_b3d)
        show_action.triggered.connect(self.bring_to_front)
        hide_action.triggered.connect(self.hide)
        quit_action.triggered.connect(self.quit)

        self.tray_menu = QMenu()
        self.tray_menu.setStyleSheet(self.menuFile.styleSheet())
        self.tray_menu.addAction(self.blender_action)
        self.tray_menu.addAction(show_action)
        self.tray_menu.addAction(hide_action)
        self.tray_menu.addAction(quit_action)

        self.tray_icon = QSystemTrayIcon(self.icon_app, self)
        self.tray_icon.setToolTip("Blender Version Manager")
        self.tray_icon.setContextMenu(self.tray_menu)
        self.tray_icon.messageClicked.connect(self.bring_to_front)
        self.tray_icon.activated.connect(self.onTrayIconActivated)
        self.tray_icon.show()

        # Version layout
        self.btnUpdate.clicked.connect(self.update)
        self.set_task_visible(False)
        self.zeroBuildsWarning.hide()
        self.layouts = []
        self.collect_versions()
        self.draw_list_versions()

        # Custom drag behaviour
        self.old_pos = self.pos()
        self.pressed = False

        # Update task
        self.is_update_running = False
        self.start_uptodate_thread()

        self.taskbar_progress = None

        self.left_click_timer = QTimer(self)
        self.left_click_timer.setSingleShot(True)
        self.left_click_timer.timeout.connect(self.bring_to_front)

    def start_uptodate_thread(self):
        self.uptodate_thread = CheckForUpdates(self)
        self.uptodate_thread.new_version_obtained.connect(
            self.show_new_version)
        self.uptodate_thread.start()

    def stop_uptodate_thread(self):
        self.uptodate_thread.is_running = False
        self.uptodate_thread.terminate()
        self.uptodate_thread.wait()

    def onTrayIconActivated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            self.left_click_timer.start(QApplication.doubleClickInterval())
        elif reason == QSystemTrayIcon.DoubleClick:
            self.left_click_timer.stop()
            self.open_latest_b3d()

    def showEvent(self, event):
        # Setup taskbar
        if self.platform == 'Windows':
            if not self.taskbar_progress:
                self.task_button = QWinTaskbarButton(self)
                self.task_button.setWindow(self.windowHandle())

                self.taskbar_progress = self.task_button.progress()
                self.taskbar_progress.setVisible(True)
                self.taskbar_progress.setValue(0)

    def open_latest_b3d(self):
        if self.layouts:
            self.layouts[0].open()

    def show_new_version(self, display_name):
        self.set_task_visible(True)
        self.set_progress_bar(0, 0, display_name)

        if self.isHidden():
            self.tray_icon.showMessage("Blender Version Manager",
                                       "New build of Blender is available!",
                                       QSystemTrayIcon.Information, 4000)

    def set_task_visible(self, is_visible):
        if is_visible:
            self.progressBar.show()
            self.btnUpdate.show()
            self.btnCancel.hide()
        else:
            self.progressBar.hide()
            self.btnUpdate.hide()
            self.btnCancel.hide()

    def is_running_task(self):
        if self.is_update_running:
            QMessageBox.information(self, "Warning",
                                    "Update task in progress!", QMessageBox.Ok)
            return True
        else:
            return False

    def toggle_run_minimized(self, is_checked):
        self.settings.setValue('is_run_minimized', is_checked)

    def toggle_register_blend(self, is_checked):
        self.settings.setValue('is_register_blend', is_checked)

    def cleanup_layout(self, layout):
        while layout.count():
            item = layout.takeAt(0)
            widget = item.widget()

            if widget is not None:
                widget.deleteLater()
            else:
                self.cleanup_layout(item.layout())

    def collect_versions(self):
        self.layouts.clear()
        root_folder = self.settings.value('root_folder')
        dirs = next(os.walk(root_folder))[1]
        versions = []

        if self.platform == 'Windows':
            blender_exe = "blender.exe"
        elif self.platform == 'Linux':
            blender_exe = "blender"

        for dir in dirs:
            if os.path.isfile(os.path.join(root_folder, dir, blender_exe)):
                versions.append(dir)

        for ver in versions:
            b3d_item_layout = B3dItemLayout(root_folder, ver, False, self)
            self.layouts.append(b3d_item_layout)

    def draw_list_versions(self):
        if len(self.layouts) > 0:
            self.zeroBuildsWarning.hide()
            self.layouts.sort(key=lambda ver: ver.mtime, reverse=True)
            self.blender_action.setVisible(True)

            for b3d_item_layout in self.layouts:
                self.layoutListVersions.removeItem(b3d_item_layout)
                b3d_item_layout.set_is_latest(False)

            self.layouts[0].set_is_latest(True)

            for b3d_item_layout in self.layouts:
                self.layoutListVersions.addLayout(b3d_item_layout)
        else:
            self.zeroBuildsWarning.show()
            self.blender_action.setVisible(False)

    def set_root_folder(self):
        root_folder = self.settings.value('root_folder')
        dir = QFileDialog.getExistingDirectory(self, "Choose Root Folder",
                                               root_folder)

        if dir and (dir != root_folder):
            self.stop_uptodate_thread()
            self.zeroBuildsWarning.hide()
            self.set_task_visible(False)
            self.settings.setValue('root_folder', dir)
            self.labelRootFolder.setText(dir)
            self.cleanup_layout(self.layoutListVersions)
            self.collect_versions()
            self.draw_list_versions()
            self.start_uptodate_thread()

    def open_root_folder(self):
        root_folder = self.settings.value('root_folder')

        if self.platform == 'Windows':
            os.startfile(root_folder)
        elif self.platform == 'Linux':
            subprocess.call(["xdg-open", root_folder])

    def update(self):
        self.is_update_running = True
        self.stop_uptodate_thread()

        self.btnUpdate.hide()
        self.btnCancel.show()
        self.btnSetRootFolder.hide()
        self.set_progress_bar(0, 0, "Downloading: %p%")

        self.build_loader = BuildLoader(self,
                                        self.uptodate_thread.download_url,
                                        self.uptodate_thread.strptime)
        self.build_loader.finished.connect(self.finished)
        self.build_loader.progress_changed.connect(self.set_progress_bar)
        self.build_loader.block_abortion.connect(lambda: self.btnCancel.hide())
        self.btnCancel.clicked.connect(self.build_loader.stop)
        self.build_loader.start()

    def finished(self, version):
        self.build_loader.terminate()
        self.build_loader.wait()

        self.btnSetRootFolder.show()
        self.set_task_visible(False)
        self.is_update_running = False

        if version:
            root_folder = self.settings.value('root_folder')
            b3d_item_layout = B3dItemLayout(root_folder, version, True, self)
            self.layouts.append(b3d_item_layout)

            self.tray_icon.showMessage("Blender Version Manager",
                                       "Download finished!",
                                       QSystemTrayIcon.Information, 4000)

        self.draw_list_versions()
        self.set_progress_bar(0, 0, "")
        self.start_uptodate_thread()

    def set_progress_bar(self, progress_bar_val, taskbar_val, format):
        self.progressBar.setFormat(format)
        self.progressBar.setValue(progress_bar_val * 100)

        if self.taskbar_progress:
            self.taskbar_progress.setValue(taskbar_val * 100)

    def quit(self):
        if not self.is_running_task():
            self.tray_icon.hide()
            self.app.quit()

    def closeEvent(self, event):
        self.hide()
        event.ignore()

    def mousePressEvent(self, event):
        self.old_pos = event.globalPos()
        self.pressing = True

    def mouseMoveEvent(self, event):
        if self.pressing:
            delta = QPoint(event.globalPos() - self.old_pos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.old_pos = event.globalPos()

    def mouseReleaseEvent(self, QMouseEvent):
        self.pressing = False

    def toggle_run_on_startup(self, is_checked):
        if (self.platform == 'Windows'):
            key = winreg.OpenKey(
                winreg.HKEY_CURRENT_USER,
                r'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run', 0,
                winreg.KEY_SET_VALUE)

            if (is_checked):
                path = sys.executable
                winreg.SetValueEx(key, 'Blender Version Manager', 0,
                                  winreg.REG_SZ, path)
            else:
                try:
                    winreg.DeleteValue(key, 'Blender Version Manager')
                except:
                    pass

            key.Close()
            self.settings.setValue('is_run_on_startup', is_checked)

    def bring_to_front(self):
        self.setWindowState(self.windowState() & ~Qt.WindowMinimized
                            | Qt.WindowActive)
        self.show()
        self.activateWindow()

    # Prevent QMenu from closing
    def eventFilter(self, obj, event):
        if event.type() in [QEvent.MouseButtonRelease]:
            if isinstance(obj, QMenu):
                if obj.activeAction():
                    if not obj.activeAction().menu():
                        obj.activeAction().trigger()
                        return True

        return super(BVMQMainWindow, self).eventFilter(obj, event)
コード例 #14
0
class MainWindow(QtWidgets.QMainWindow):
    """ Window of the RenderKnecht Preset Editor """

    # Report user inactivity
    idle_timer = QtCore.QTimer()
    idle_timer.setSingleShot(True)
    idle_timer.setTimerType(QtCore.Qt.VeryCoarseTimer)
    idle_timer.setInterval(10000)

    def __init__(self, app_class):
        super(MainWindow, self).__init__()

        # Avoid UIC Debug messages
        log_level = LOGGER.getEffectiveLevel()
        logging.root.setLevel(40)

        # Load Ui file
        loadUi(UI_FILE_PRESET_EDITOR, self)

        # Add LED Overlay to tabWidget
        # Spams the log with Debug messages, so initialize in this block
        self.led_ovr = LedCornerWidget(parent=self.tabWidget)

        logging.root.setLevel(log_level)

        # Windows taskbar progress indicator
        self.taskbar_btn = QWinTaskbarButton(self)
        self.progress = QWinTaskbarProgress()

        # Clipboard
        self.clipboard = []
        self.clipboard_src = None

        # Undo Redo Menu
        self.actionUndo.setText('Rückgängig\tCtrl+Z')
        self.actionRedo.setText('Wiederherstellen\tCtrl+Y')
        self.actionUndo.setEnabled(False)
        self.actionRedo.setEnabled(False)

        # Add version to Window title
        self.ver = InfoMessage.ver
        self.title = self.windowTitle()
        self.set_window_title()

        self.load_settings()

        # Unsaved changes status
        self.unsaved_changes_present = False
        self.unsaved_changes_auto_save = True

        # App class so we can override the window exit method [x] with the exit method of the app class
        self.app_class = app_class

        self.tree_widget_list = [
            self.treeWidget_SrcPreset, self.treeWidget_DestPreset,
            self.treeWidget_Variants, self.treeWidget_render
        ]

        # Set tree column width to content resize and sort ascending by order
        tree_setup_header_format(self.tree_widget_list)

        # Icons
        self.icon = dict()
        for icon_name, icon_path in Itemstyle.ICON_PATH.items():
            self.icon[icon_name] = QIcon(icon_path)

        # Tree iterator instance
        self.iter_tree = iterate_item_childs(self.treeWidget_DestPreset)

        # Make reference modules instance available to the working classes
        self.find_reference_items = FindReferenceItems(self, self.iter_tree)
        self.add_selected_top_level_items = CopySelectedToDest(self)

        # Worker class instances
        self.sort_tree_widget = SortTree(self, self.treeWidget_SrcPreset)
        self.context_menus = []
        self.widget_pairs = [
            (self.treeWidget_SrcPreset, self.pushButton_Src_sort,
             self.lineEdit_Src_filter, [1, 2]),
            (self.treeWidget_DestPreset, self.pushButton_Dest_sort,
             self.lineEdit_Dest_filter, [1, 2]),
            (self.treeWidget_Variants, self.pushButton_Var_sort,
             self.lineEdit_Var_filter, 1),
            (self.treeWidget_render, self.pushButton_Ren_sort,
             self.lineEdit_Ren_filter, 1)
        ]

        # Init widget pairs
        for w_tuple in self.widget_pairs:
            tree_widget, sort_button, line_edit, filter_col = w_tuple

            # Sorting and reference highlight
            tree_widget.sortBtn = sort_button

            # Store Id's to missing items as strings
            # so we avoid to assign them to new items
            tree_widget.missing_ids = set()

            # Store topLevelItem names
            tree_widget.unique_names = set()

            # Default Filter column
            tree_widget.filter_column = filter_col

            # Store LineEdit text filter widget as tree attribute
            tree_widget.filter_txt_widget = line_edit

            # Init Filter
            tree_widget.filter = filter_on_timer(line_edit,
                                                 tree_widget,
                                                 filter_column=filter_col)
            line_edit.textChanged.connect(tree_widget.filter.start_timer)

            # Default preset visibility
            tree_widget.toggle_preset_vis = False

            # Info Overlay
            tree_widget.info_overlay = InfoOverlay(tree_widget)
            # Animated overlay
            tree_widget.overlay = Overlay(tree_widget)
            # Update overlay position on event
            tree_widget.overlayPos.connect(tree_widget.overlay.update_position)

            # Context menu
            tree_widget.context = TreeContextMenu(tree_widget, self)

            # Bind keys
            tree_widget.keys = TreeKeyEvents(tree_widget, self, self.app_class)
            tree_widget.keys.add_event_filter()

            # Report conflict shortcut
            tree_widget.report_conflict = self.report_conflict

        # Forbid editing in SrcWidget
        self.treeWidget_SrcPreset.keys.no_edit = True

        # Search Replace dialog
        self.search_replace = SearchReplace(self, [
            self.treeWidget_SrcPreset, self.treeWidget_DestPreset,
            self.treeWidget_Variants
        ],
                                            current_tree=1)

        # Copy variants key events
        variant_keys_tree = CopyVariantsKeyEvents(self.treeWidget_Variants,
                                                  self)
        variants_keys_txt = CopyVariantsKeyEvents(
            self.plainTextEdit_addVariant_Setname, self)
        variant_keys_add = CopyVariantsKeyEvents(
            self.plainTextEdit_addVariant_Variant, self)

        # Internal drag_drop worker
        self.treeWidget_DestPreset.internal_drag_drop.setup_ui(self)
        self.treeWidget_Variants.internal_drag_drop.setup_ui(self)
        self.treeWidget_render.internal_drag_drop.setup_ui(self, True)

        # hide variants type column
        self.treeWidget_Variants.header().hideSection(ItemColumn.TYPE)

        # Reset treeWidgets splitter to equal sizes
        self.presetTreeSplitter.setSizes([100, 100])

        # Detect inactivity for automatic session save
        self.idle_timer.timeout.connect(self.set_inactive)
        self.__idle = False

        self.app_class.installEventFilter(self)

    @property
    def idle(self):
        return self.__idle

    @idle.setter
    def idle(self, val: bool = False):
        self.__idle = val

    def set_active(self):
        self.idle = False
        self.idle_timer.stop()

    def set_inactive(self):
        self.idle = True

    def eventFilter(self, obj, eve):
        if eve is None or obj is None:
            return False

        if eve.type() == QtCore.QEvent.KeyPress or \
           eve.type() == QtCore.QEvent.MouseMove or \
           eve.type() == QtCore.QEvent.MouseButtonPress:
            self.set_active()
            return False

        if not self.idle_timer.isActive():
            self.idle_timer.start()

        return False

    def enable_load_actions(self, enabled: bool = True):
        self.menuImport.setEnabled(enabled)
        self.actionOpen.setEnabled(enabled)

    def load_settings(self):
        # Load Viewer Size
        for idx in range(0, self.comboBox_ViewerSize.count()):
            self.comboBox_ViewerSize.setCurrentIndex(idx)
            if knechtSettings.dg[
                    'viewer_size'] == self.comboBox_ViewerSize.currentText():
                # Set current viewer size
                SendToDeltaGen.viewer_size = self.comboBox_ViewerSize.currentText(
                )
                LOGGER.debug('Setting viewer combo box to index: %s', idx)
                break

    @staticmethod
    def add_file_string(message, file_path):
        if file_path:
            message = message + '<br><i>' + str(file_path) + '</i>'
        return message

    def generic_error_msg(self, msg: str = ''):
        __msg = Msg.GENERIC_ERROR + msg
        self.warning_box(Msg.GENERIC_ERROR_TITLE, __msg)

    def warning_box(self,
                    title_txt: str = Msg.ERROR_BOX_TITLE,
                    message: str = '',
                    file_path=False,
                    parent=None):
        message = self.add_file_string(message, file_path)

        if not parent:
            parent = self

        QtWidgets.QMessageBox.warning(parent, title_txt, message)

    def question_box(self,
                     title_txt: str = Msg.ERROR_BOX_TITLE,
                     message: str = '',
                     file_path=False,
                     parent=None):
        """ Pop-Up Warning Box ask to continue or break, returns True on abort """
        message = self.add_file_string(message, file_path)

        if not parent:
            parent = self

        msg_box = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Question,
                                        title_txt,
                                        message,
                                        parent=parent)
        msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok
                                   | QtWidgets.QMessageBox.Abort)

        msg_box.button(QtWidgets.QMessageBox.Ok).setText(Msg.QUESTION_OK)
        msg_box.button(QtWidgets.QMessageBox.Abort).setText(Msg.QUESTION_ABORT)

        answer = msg_box.exec()

        if answer == QtWidgets.QMessageBox.Abort:
            # User selected abort
            return True

        # User selected -Ok-, continue
        return False

    def info_box(self,
                 title_txt: str = Msg.INFO_TITLE,
                 message: str = '',
                 file_path=False,
                 parent=None):
        message = self.add_file_string(message, file_path)

        if not parent:
            parent = self

        QtWidgets.QMessageBox.information(parent, title_txt, message)

    # noinspection PyArgumentList
    def unsaved_changes(self,
                        file_path=False,
                        title_txt: str = Msg.UNSAVED_CHANGES_TITLE,
                        message: str = Msg.UNSAVED_CHANGES):
        """ Creates Question Box if unchanged changes in Dest widget detected """
        if not self.unsaved_changes_present:
            return QtWidgets.QMessageBox.No

        message = self.add_file_string(message, file_path)

        msg_box = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning,
                                        title_txt,
                                        message,
                                        parent=self)
        msg_box.setStandardButtons(QtWidgets.QMessageBox.Yes
                                   | QtWidgets.QMessageBox.No
                                   | QtWidgets.QMessageBox.Cancel)

        msg_box.button(QtWidgets.QMessageBox.Yes).setText(
            Msg.UNSAVED_CHANGES_YES)
        msg_box.button(QtWidgets.QMessageBox.Yes).setIcon(
            self.icon[Itemstyle.MAIN['save']])
        msg_box.button(QtWidgets.QMessageBox.No).setText(
            Msg.UNSAVED_CHANGES_NO)
        msg_box.button(QtWidgets.QMessageBox.No).setIcon(
            self.icon[Itemstyle.MAIN['sad']])
        msg_box.button(QtWidgets.QMessageBox.Cancel).setText(
            Msg.UNSAVED_CHANGES_CANCEL)

        answer = msg_box.exec_()
        LOGGER.debug('Asked for unsaved changes, user answered: %s', answer)
        return answer

    def set_window_title(self, file_info: str = ''):
        self.setWindowTitle(self.title + ' - ' + self.ver)

        if file_info != '':
            if self.unsaved_changes_present:
                file_info = '*' + file_info + '*'
                self.setWindowTitle('*' + self.title + ' ' + self.ver + '*')
            self.label_Dest_File.setText(file_info)
        else:
            self.label_Dest_File.setText(Msg.NO_FILE_INFO)

    def report_conflict(self, item=False, new_id=False):
        """ Report ID Conflict """
        if item:
            err_msg = Msg.OVERLAY_MISSING_REF[0] + item.text(
                ItemColumn.NAME) + Msg.OVERLAY_MISSING_REF[1]
            err_msg += item.text(ItemColumn.ID)

            if not new_id:
                # Copy not created
                err_msg += Msg.OVERLAY_MISSING_REF[2]
            else:
                # New Id created instead
                err_msg += Msg.OVERLAY_MISSING_REF[3] + new_id
        else:
            err_msg = Msg.OVERLAY_MISSING_REF[4]

        self.treeWidget_DestPreset.info_overlay.display_confirm(err_msg,
                                                                ('[X]', None),
                                                                immediate=True)

    def report_name_clash(self, name, id, new_id):
        self.treeWidget_DestPreset.info_overlay.display(
            Msg.OVERLAY_NAME_CLASH.format(name=name, id=id, new_id=new_id),
            3500)

    def get_tree_name(self, widget):
        """ Return user readable tree names """
        if widget is self.treeWidget_SrcPreset:
            return 'Preset Vorgaben'
        if widget is self.treeWidget_DestPreset:
            return 'Benutzer Vorgaben'
        if widget is self.treeWidget_Variants:
            return 'Varianten Liste'
        if widget is self.treeWidget_render:
            return 'Render Liste'
        return ''

    def tree_with_focus(self):
        """ Return the current QTreeWidget in focus """
        widget_in_focus = self.focusWidget()

        if widget_in_focus in self.tree_widget_list:
            return widget_in_focus

        return False

    class send_to_dg:
        """ Pseudeo clas.s if send_to_dg was never called from GUI """
        def thread_running(self=None):
            """
                This will return true from overwritten
                clas.s if rendering / sending to deltagen is in progress.
            """
            return False

    def check_item_name(self, item, item_name, __new_item_name: str = None):
        """ If item name already in unique names, return new name """
        unique_names = item.treeWidget().unique_names

        if not unique_names:
            return

        if item_name in unique_names:
            __new_item_name = create_unique_item_name(item_name, unique_names)

        if __new_item_name:
            item.setText(ItemColumn.NAME, __new_item_name)

        self.sort_tree_widget.sort_current_level(item, item.treeWidget())

        return __new_item_name

    def init_taskbar(self):
        """ Initializes the MS Windows taskbar button"""
        # Needs to be called after window is created/shown
        self.taskbar_btn.setWindow(self.windowHandle())

        self.progress = self.taskbar_btn.progress()
        self.progress.setRange(0, 100)
        self.progress.valueChanged.connect(self.progress.show)

    def end_threads(self):
        for w_tuple in self.widget_pairs:
            tree_widget, sort_button, line_edit, filter_col = w_tuple
            tree_widget.filter.end_thread()

    def closeEvent(self, QCloseEvent):
        LOGGER.debug(
            'Preset Editor SchnuffiWindow close event triggered. Closing SchnuffiWindow.'
        )
        QCloseEvent.ignore()
        self.app_class.exit_preset_editor()
コード例 #15
0
class _Taskbar:
    def __init__(self):
        self.window = None
        self.taskbarButton = None
        self.taskbarProgress = None

    def setWindow(self, window):
        self.window = window
        self.resetTaskbar()

    def clearWindow(self):
        self.window = None

    def resetTaskbar(self):
        self.taskbarButton = QWinTaskbarButton()
        self.taskbarButton.setWindow(self.window.windowHandle())
        self.taskbarProgress = self.taskbarButton.progress()

    def show(self, indeterminate=False):
        self.resetTaskbar()
        if indeterminate:
            self.taskbarProgress.setMaximum(0)
        else:
            self.taskbarProgress.setMaximum(100)
        self.taskbarProgress.show()

    def hide(self):
        self.taskbarProgress.hide()

    def isVisible(self):
        return self.taskbarProgress.isVisible()

    def pause(self):
        if self.taskbarProgress.maximum() == 0:
            self.taskbarProgress.setMaximum(100)
            self.taskbarProgress.setValue(100)
        self.taskbarProgress.pause()

    def isPaused(self):
        return self.taskbarProgress.isPaused()

    def stop(self):
        if self.taskbarProgress.maximum() == 0:
            self.taskbarProgress.setMaximum(100)
            self.taskbarProgress.setValue(100)
        self.taskbarProgress.stop()

    def isStopped(self):
        return self.taskbarProgress.isStopped()

    def resume(self):
        self.taskbarProgress.resume()

    def setValue(self, value):
        if self.isPaused() or self.isStopped():
            self.resume()
        self.taskbarProgress.setValue(value)

    def complete(self):
        self.hide()
        self.alert()

    def alert(self):
        QtCore.QCoreApplication.instance().alert(self.window)
コード例 #16
0
class DownloadManager(QDialog, Ui_DownloadManager):
    """
    Class implementing the download manager.
    
    @signal downloadsCountChanged() emitted to indicate a change of the
        count of download items
    """
    RemoveNever = 0
    RemoveExit = 1
    RemoveSuccessFullDownload = 2

    UpdateTimerTimeout = 1000

    downloadsCountChanged = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(DownloadManager, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(Qt.Window)

        self.__winTaskbarButton = None

        self.__saveTimer = AutoSaver(self, self.save)

        self.__model = DownloadModel(self)
        self.__manager = WebBrowserWindow.networkManager()

        self.__iconProvider = None
        self.__downloads = []
        self.__downloadDirectory = ""
        self.__loaded = False

        self.__rowHeightMultiplier = 1.1

        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))

        self.downloadsView.setShowGrid(False)
        self.downloadsView.verticalHeader().hide()
        self.downloadsView.horizontalHeader().hide()
        self.downloadsView.setAlternatingRowColors(True)
        self.downloadsView.horizontalHeader().setStretchLastSection(True)
        self.downloadsView.setModel(self.__model)
        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.downloadsView.customContextMenuRequested.connect(
            self.__customContextMenuRequested)

        self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self)
        self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked)

        self.__load()

        self.__updateTimer = QBasicTimer()

    def __customContextMenuRequested(self, pos):
        """
        Private slot to handle the context menu request for the bookmarks tree.
        
        @param pos position the context menu was requested (QPoint)
        """
        menu = QMenu()

        selectedRowsCount = len(
            self.downloadsView.selectionModel().selectedRows())

        if selectedRowsCount == 1:
            row = self.downloadsView.selectionModel().selectedRows()[0].row()
            itm = self.__downloads[row]
            if itm.downloadedSuccessfully():
                menu.addAction(UI.PixmapCache.getIcon("open.png"),
                               self.tr("Open"), self.__contextMenuOpen)
            elif itm.downloading():
                menu.addAction(UI.PixmapCache.getIcon("stopLoading.png"),
                               self.tr("Cancel"), self.__contextMenuCancel)
                menu.addSeparator()
            menu.addAction(self.tr("Open Containing Folder"),
                           self.__contextMenuOpenFolder)
            menu.addSeparator()
            menu.addAction(self.tr("Go to Download Page"),
                           self.__contextMenuGotoPage)
            menu.addAction(self.tr("Copy Download Link"),
                           self.__contextMenuCopyLink)
            menu.addSeparator()
        menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
        if (selectedRowsCount > 1
                or (selectedRowsCount == 1
                    and not self.__downloads[self.downloadsView.selectionModel(
                    ).selectedRows()[0].row()].downloading())):
            menu.addSeparator()
            menu.addAction(self.tr("Remove From List"),
                           self.__contextMenuRemoveSelected)

        menu.exec_(QCursor.pos())

    def shutdown(self):
        """
        Public method to stop the download manager.
        """
        self.save()
        self.close()

    def activeDownloadsCount(self):
        """
        Public method to get the number of active downloads.
        
        @return number of active downloads (integer)
        """
        count = 0

        for download in self.__downloads:
            if download.downloading():
                count += 1
        return count

    def allowQuit(self):
        """
        Public method to check, if it is ok to quit.
        
        @return flag indicating allowance to quit (boolean)
        """
        if self.activeDownloadsCount() > 0:
            res = E5MessageBox.yesNo(
                self,
                self.tr(""),
                self.tr(
                    """There are %n downloads in progress.\n"""
                    """Do you want to quit anyway?""", "",
                    self.activeDownloadsCount()),
                icon=E5MessageBox.Warning)
            if not res:
                self.show()
                return False

        self.close()
        return True

    def __testWebBrowserView(self, view, url):
        """
        Private method to test a web browser view against an URL.
        
        @param view reference to the web browser view to be tested
        @type WebBrowserView
        @param url URL to test against
        @type QUrl
        @return flag indicating, that the view is the one for the URL
        @rtype bool
        """
        if view.tabWidget().count() < 2:
            return False

        page = view.page()
        if page.history().count() != 0:
            return False

        if (not page.url().isEmpty() and page.url().host() == url.host()):
            return True

        requestedUrl = page.requestedUrl()
        if requestedUrl.isEmpty():
            requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text())
        return requestedUrl.isEmpty() or requestedUrl.host() == url.host()

    def __closeDownloadTab(self, url):
        """
        Private method to close an empty tab, that was opened only for loading
        the download URL.
        
        @param url download URL
        @type QUrl
        """
        if self.__testWebBrowserView(
                WebBrowserWindow.getWindow().currentBrowser(), url):
            WebBrowserWindow.getWindow().closeCurrentBrowser()
            return

        for window in WebBrowserWindow.mainWindows():
            for browser in window.browsers():
                if self.__testWebBrowserView(browser, url):
                    window.closeBrowser(browser)
                    return

    def download(self, downloadItem):
        """
        Public method to download a file.
        
        @param downloadItem reference to the download object containing the
        download data.
        @type QWebEngineDownloadItem
        """
        url = downloadItem.url()
        if url.isEmpty():
            return

        self.__closeDownloadTab(url)

        # Safe Browsing
        from WebBrowser.SafeBrowsing.SafeBrowsingManager import (
            SafeBrowsingManager)
        if SafeBrowsingManager.isEnabled():
            threatLists = (
                WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0])
            if threatLists:
                threatMessages = (WebBrowserWindow.safeBrowsingManager().
                                  getThreatMessages(threatLists))
                res = E5MessageBox.warning(
                    WebBrowserWindow.getWindow(),
                    self.tr("Suspicuous URL detected"),
                    self.tr("<p>The URL <b>{0}</b> was found in the Safe"
                            " Browsing database.</p>{1}").format(
                                url.toString(), "".join(threatMessages)),
                    E5MessageBox.StandardButtons(E5MessageBox.Abort
                                                 | E5MessageBox.Ignore),
                    E5MessageBox.Abort)
                if res == E5MessageBox.Abort:
                    downloadItem.cancel()
                    return

        window = WebBrowserWindow.getWindow()
        if window:
            pageUrl = window.currentBrowser().url()
        else:
            pageUrl = QUrl()
        from .DownloadItem import DownloadItem
        itm = DownloadItem(downloadItem=downloadItem,
                           pageUrl=pageUrl,
                           parent=self)
        self.__addItem(itm)

        if Preferences.getWebBrowser("DownloadManagerAutoOpen"):
            self.show()
        else:
            self.__startUpdateTimer()

    def show(self):
        """
        Public slot to show the download manager dialog.
        """
        self.__startUpdateTimer()

        super(DownloadManager, self).show()
        self.activateWindow()
        self.raise_()

    def __addItem(self, itm, append=False):
        """
        Private method to add a download to the list of downloads.
        
        @param itm reference to the download item
        @type DownloadItem
        @param append flag indicating to append the item
        @type bool
        """
        itm.statusChanged.connect(lambda: self.__updateRow(itm))
        itm.downloadFinished.connect(self.__finished)

        # insert at top of window
        if append:
            row = self.downloadsCount()
        else:
            row = 0
        self.__model.beginInsertRows(QModelIndex(), row, row)
        if append:
            self.__downloads.append(itm)
        else:
            self.__downloads.insert(0, itm)
        self.__model.endInsertRows()

        self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
        icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)
        self.downloadsView.setRowHeight(
            row,
            itm.sizeHint().height() * self.__rowHeightMultiplier)
        # just in case the download finished before the constructor returned
        self.__updateRow(itm)
        self.changeOccurred()

        self.downloadsCountChanged.emit()

    def __updateRow(self, itm):
        """
        Private slot to update a download item.
        
        @param itm reference to the download item
        @type DownloadItem
        """
        if itm not in self.__downloads:
            return

        row = self.__downloads.index(itm)

        if self.__iconProvider is None:
            self.__iconProvider = QFileIconProvider()

        icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
        if icon.isNull():
            icon = self.style().standardIcon(QStyle.SP_FileIcon)
        itm.setIcon(icon)

        self.downloadsView.setRowHeight(
            row,
            itm.minimumSizeHint().height() * self.__rowHeightMultiplier)

        remove = False

        if (itm.downloadedSuccessfully() and self.removePolicy()
                == DownloadManager.RemoveSuccessFullDownload):
            remove = True

        if remove:
            self.__model.removeRow(row)

        self.cleanupButton.setEnabled(
            (self.downloadsCount() - self.activeDownloadsCount()) > 0)

        # record the change
        self.changeOccurred()

    def removePolicy(self):
        """
        Public method to get the remove policy.
        
        @return remove policy (integer)
        """
        return Preferences.getWebBrowser("DownloadManagerRemovePolicy")

    def setRemovePolicy(self, policy):
        """
        Public method to set the remove policy.
        
        @param policy policy to be set
            (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
             DownloadManager.RemoveSuccessFullDownload)
        """
        assert policy in (DownloadManager.RemoveExit,
                          DownloadManager.RemoveNever,
                          DownloadManager.RemoveSuccessFullDownload)

        if policy == self.removePolicy():
            return

        Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy)

    def save(self):
        """
        Public method to save the download settings.
        """
        if not self.__loaded:
            return

        Preferences.setWebBrowser("DownloadManagerSize", self.size())
        Preferences.setWebBrowser("DownloadManagerPosition", self.pos())
        if self.removePolicy() == DownloadManager.RemoveExit:
            return

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        if WebBrowserWindow.isPrivate():
            return

        downloads = []
        for download in self.__downloads:
            downloads.append(download.getData())
        Preferences.setWebBrowser("DownloadManagerDownloads", downloads)

    def __load(self):
        """
        Private method to load the download settings.
        """
        if self.__loaded:
            return

        size = Preferences.getWebBrowser("DownloadManagerSize")
        if size.isValid():
            self.resize(size)
        pos = Preferences.getWebBrowser("DownloadManagerPosition")
        self.move(pos)

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        if not WebBrowserWindow.isPrivate():
            downloads = Preferences.getWebBrowser("DownloadManagerDownloads")
            for download in downloads:
                if (not download["URL"].isEmpty()
                        and bool(download["Location"])):
                    from .DownloadItem import DownloadItem
                    itm = DownloadItem(parent=self)
                    itm.setData(download)
                    self.__addItem(itm, append=True)
            self.cleanupButton.setEnabled(
                (self.downloadsCount() - self.activeDownloadsCount()) > 0)

        self.__loaded = True

        self.downloadsCountChanged.emit()

    def closeEvent(self, evt):
        """
        Protected event handler for the close event.
        
        @param evt reference to the close event
        @type QCloseEvent
        """
        self.save()

    def cleanup(self):
        """
        Public slot to cleanup the downloads.
        """
        self.on_cleanupButton_clicked()

    @pyqtSlot()
    def on_cleanupButton_clicked(self):
        """
        Private slot to cleanup the downloads.
        """
        if self.downloadsCount() == 0:
            return

        self.__model.removeRows(0, self.downloadsCount())
        if (self.downloadsCount() == 0 and self.__iconProvider is not None):
            self.__iconProvider = None

        self.changeOccurred()

        self.downloadsCountChanged.emit()

    def __finished(self, success):
        """
        Private slot to handle a finished download.
        
        @param success flag indicating a successful download
        @type bool
        """
        if self.isVisible():
            QApplication.alert(self)

        self.downloadsCountChanged.emit()

        if self.activeDownloadsCount() == 0:
            # all active downloads are done
            if success and e5App().activeWindow() is not self:
                if WebBrowserWindow.notificationsEnabled():
                    WebBrowserWindow.showNotification(
                        UI.PixmapCache.getPixmap("downloads48.png"),
                        self.tr("Downloads finished"),
                        self.tr("All files have been downloaded."))
                if not Preferences.getWebBrowser("DownloadManagerAutoClose"):
                    self.raise_()
                    self.activateWindow()

            self.__stopUpdateTimer()
            self.infoLabel.clear()
            self.setWindowTitle(self.tr("Download Manager"))
            if Globals.isWindowsPlatform():
                self.__taskbarButton().progress().hide()

            if Preferences.getWebBrowser("DownloadManagerAutoClose"):
                self.close()

    def setDownloadDirectory(self, directory):
        """
        Public method to set the current download directory.
        
        @param directory current download directory (string)
        """
        self.__downloadDirectory = directory
        if self.__downloadDirectory != "":
            self.__downloadDirectory += "/"

    def downloadDirectory(self):
        """
        Public method to get the current download directory.
        
        @return current download directory (string)
        """
        return self.__downloadDirectory

    def downloadsCount(self):
        """
        Public method to get the number of downloads.
        
        @return number of downloads
        @rtype int
        """
        return len(self.__downloads)

    def downloads(self):
        """
        Public method to get a reference to the downloads.
        
        @return reference to the downloads (list of DownloadItem)
        """
        return self.__downloads

    def changeOccurred(self):
        """
        Public method to signal a change.
        """
        self.__saveTimer.changeOccurred()

    def __taskbarButton(self):
        """
        Private method to get a reference to the task bar button (Windows
        only).
        
        @return reference to the task bar button
        @rtype QWinTaskbarButton or None
        """
        if Globals.isWindowsPlatform():
            from PyQt5.QtWinExtras import QWinTaskbarButton
            if self.__winTaskbarButton is None:
                window = WebBrowserWindow.mainWindow()
                self.__winTaskbarButton = QWinTaskbarButton(
                    window.windowHandle())
                self.__winTaskbarButton.progress().setRange(0, 100)

        return self.__winTaskbarButton

    def timerEvent(self, evt):
        """
        Protected event handler for timer events.
        
        @param evt reference to the timer event
        @type QTimerEvent
        """
        if evt.timerId() == self.__updateTimer.timerId():
            if self.activeDownloadsCount() == 0:
                self.__stopUpdateTimer()
                self.infoLabel.clear()
                self.setWindowTitle(self.tr("Download Manager"))
                if Globals.isWindowsPlatform():
                    self.__taskbarButton().progress().hide()
            else:
                progresses = []
                for itm in self.__downloads:
                    if (itm is None or itm.downloadCanceled()
                            or not itm.downloading()):
                        continue

                    progresses.append(
                        (itm.downloadProgress(), itm.remainingTime(),
                         itm.currentSpeed()))

                if not progresses:
                    return

                remaining = 0
                progress = 0
                speed = 0.0

                for progressData in progresses:
                    if progressData[1] > remaining:
                        remaining = progressData[1]
                    progress += progressData[0]
                    speed += progressData[2]
                progress = progress / len(progresses)

                if self.isVisible():
                    self.infoLabel.setText(
                        self.tr("{0}% of %n file(s) ({1}) {2}", "",
                                len(progresses)).format(
                                    progress,
                                    speedString(speed),
                                    timeString(remaining),
                                ))
                    self.setWindowTitle(self.tr("{0}% - Download Manager"))

                if Globals.isWindowsPlatform():
                    self.__taskbarButton().progress().show()
                    self.__taskbarButton().progress().setValue(progress)

        super(DownloadManager, self).timerEvent(evt)

    def __startUpdateTimer(self):
        """
        Private slot to start the update timer.
        """
        if self.activeDownloadsCount() and not self.__updateTimer.isActive():
            self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self)

    def __stopUpdateTimer(self):
        """
        Private slot to stop the update timer.
        """
        self.__updateTimer.stop()

    ###########################################################################
    ## Context menu related methods below
    ###########################################################################

    def __currentItem(self):
        """
        Private method to get a reference to the current item.
        
        @return reference to the current item (DownloadItem)
        """
        index = self.downloadsView.currentIndex()
        if index and index.isValid():
            row = index.row()
            return self.__downloads[row]

        return None

    def __contextMenuOpen(self):
        """
        Private method to open the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFile()

    def __contextMenuOpenFolder(self):
        """
        Private method to open the folder containing the downloaded file.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.openFolder()

    def __contextMenuCancel(self):
        """
        Private method to cancel the current download.
        """
        itm = self.__currentItem()
        if itm is not None:
            itm.cancelDownload()

    def __contextMenuGotoPage(self):
        """
        Private method to open the download page.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl()
            WebBrowserWindow.mainWindow().openUrl(url, "")

    def __contextMenuCopyLink(self):
        """
        Private method to copy the download link to the clipboard.
        """
        itm = self.__currentItem()
        if itm is not None:
            url = itm.getPageUrl().toDisplayString(QUrl.FullyDecoded)
            QApplication.clipboard().setText(url)

    def __contextMenuSelectAll(self):
        """
        Private method to select all downloads.
        """
        self.downloadsView.selectAll()

    def __contextMenuRemoveSelected(self):
        """
        Private method to remove the selected downloads from the list.
        """
        self.downloadsView.removeSelected()
コード例 #17
0
class MainWindow(QMainWindow):
    EXIT_CODE_REBOOT = 666
    TEMP_PROJECT_FILE = 'vidcutter_reboot.vcp'
    WORKING_FOLDER = os.path.join(QDir.tempPath(), 'vidcutter')

    def __init__(self):
        super(MainWindow, self).__init__()
        self.video, self.resizeTimer = '', 0
        self.parse_cmdline()
        self.init_settings()
        self.init_logger()
        self.init_scale()
        self.init_cutter()
        self.setWindowTitle(qApp.applicationName())
        self.setContentsMargins(0, 0, 0, 0)
        self.statusBar().showMessage('Ready')
        self.statusBar().setStyleSheet('border: none; padding: 0; margin: 0;')
        self.setAcceptDrops(True)
        self.show()
        if sys.platform == 'win32' and TaskbarProgress.isValidWinVer():
            self.win_taskbar_button = QWinTaskbarButton(self)
            self.win_taskbar_button.setWindow(self.windowHandle())
            self.win_taskbar_button.progress().setVisible(True)
            self.win_taskbar_button.progress().setValue(0)
        self.console.setGeometry(int(self.x() - (self.width() / 2)),
                                 self.y() + int(self.height() / 3), 750, 300)
        if not self.video and os.path.isfile(
                os.path.join(QDir.tempPath(), MainWindow.TEMP_PROJECT_FILE)):
            self.video = os.path.join(QDir.tempPath(),
                                      MainWindow.TEMP_PROJECT_FILE)
        if self.video:
            self.file_opener(self.video)

    def init_scale(self) -> None:
        screen_size = qApp.desktop().availableGeometry(-1)
        self.scale = 'LOW' if screen_size.width() <= 1024 else 'NORMAL'
        self.setMinimumSize(self.get_size(self.scale))
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    @pyqtSlot(str)
    def file_opener(self, filename: str) -> None:
        try:
            if QFileInfo(filename).suffix() == 'vcp':
                self.cutter.openProject(project_file=filename)
                if filename == os.path.join(QDir.tempPath(),
                                            MainWindow.TEMP_PROJECT_FILE):
                    os.remove(
                        os.path.join(QDir.tempPath(),
                                     MainWindow.TEMP_PROJECT_FILE))
            else:
                self.cutter.loadMedia(filename)
        except (FileNotFoundError, PermissionError):
            QMessageBox.critical(self, 'Error loading file', sys.exc_info()[0])
            logging.exception('Error loading file')
            qApp.restoreOverrideCursor()
            self.restart()

    @staticmethod
    def get_size(mode: str = 'NORMAL') -> QSize:
        modes = {
            'LOW': QSize(800, 425),
            'NORMAL': QSize(930, 680),
            'HIGH': QSize(1850, 1300)
        }
        return modes[mode]

    def init_logger(self) -> None:
        try:
            log_path = self.get_app_config_path()
        except AttributeError:
            if sys.platform == 'win32':
                log_path = os.path.join(QDir.homePath(), 'AppData', 'Local',
                                        qApp.applicationName().lower())
            elif sys.platform == 'darwin':
                log_path = os.path.join(QDir.homePath(), 'Library',
                                        'Preferences',
                                        qApp.applicationName().lower())
            else:
                log_path = os.path.join(QDir.homePath(), '.config',
                                        qApp.applicationName().lower())
        os.makedirs(log_path, exist_ok=True)
        self.console = ConsoleWidget(self)
        self.consoleLogger = ConsoleHandler(self.console)
        handlers = [
            logging.handlers.RotatingFileHandler(os.path.join(
                log_path, '%s.log' % qApp.applicationName().lower()),
                                                 maxBytes=1000000,
                                                 backupCount=1),
            self.consoleLogger
        ]
        if self.parser.isSet(self.debug_option) or self.verboseLogs:
            # noinspection PyTypeChecker
            handlers.append(logging.StreamHandler())
        logging.setLoggerClass(VideoLogger)
        logging.basicConfig(
            handlers=handlers,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            datefmt='%Y-%m-%d %H:%M',
            level=logging.INFO)
        logging.captureWarnings(capture=True)
        sys.excepthook = MainWindow.log_uncaught_exceptions
        if os.getenv('DEBUG', False):
            logging.info('appconfig folder: {}'.format(log_path))

    def init_settings(self) -> None:
        try:
            settings_path = self.get_app_config_path()
        except AttributeError:
            if sys.platform == 'win32':
                settings_path = os.path.join(QDir.homePath(), 'AppData',
                                             'Local',
                                             qApp.applicationName().lower())
            elif sys.platform == 'darwin':
                settings_path = os.path.join(QDir.homePath(), 'Library',
                                             'Preferences',
                                             qApp.applicationName().lower())
            else:
                settings_path = os.path.join(QDir.homePath(), '.config',
                                             qApp.applicationName().lower())
        os.makedirs(settings_path, exist_ok=True)
        settings_file = '{}.ini'.format(qApp.applicationName().lower())
        self.settings = QSettings(os.path.join(settings_path, settings_file),
                                  QSettings.IniFormat)
        if self.settings.value('geometry') is not None:
            self.restoreGeometry(self.settings.value('geometry'))
        if self.settings.value('windowState') is not None:
            self.restoreState(self.settings.value('windowState'))
        self.theme = self.settings.value('theme', 'light', type=str)
        self.startupvol = self.settings.value('volume', 100, type=int)
        self.verboseLogs = self.settings.value('verboseLogs', 'off',
                                               type=str) in {'on', 'true'}

    @staticmethod
    def log_uncaught_exceptions(cls, exc, tb) -> None:
        logging.critical(''.join(traceback.format_tb(tb)))
        logging.critical('{0}: {1}'.format(cls, exc))

    def parse_cmdline(self) -> None:
        self.parser = QCommandLineParser()
        self.parser.setApplicationDescription(
            '\nVidCutter - the simplest + fastest media cutter & joiner')
        self.parser.addPositionalArgument('video', 'Preload video file',
                                          '[video]')
        self.parser.addPositionalArgument(
            'project', 'Open VidCutter project file (.vcp)', '[project]')
        self.debug_option = QCommandLineOption(
            ['debug'], 'debug mode; verbose console output & logging. '
            'This will basically output what is being logged to file to the '
            'console stdout. Mainly useful for debugging problems with your '
            'system video and/or audio stack and codec configuration.')
        self.parser.addOption(self.debug_option)
        self.parser.addVersionOption()
        self.parser.addHelpOption()
        self.parser.process(qApp)
        self.args = self.parser.positionalArguments()
        if self.parser.isSet(self.debug_option):
            os.environ['DEBUG'] = '1'
        if len(self.args) > 0:
            file_path = QFileInfo(self.args[0]).absoluteFilePath()
            if not os.path.exists(file_path):
                sys.stderr.write('\nERROR: File not found: %s\n' % file_path)
                self.close()
                qApp.exit(1)
            self.video = file_path

    def init_cutter(self) -> None:
        self.cutter = VideoCutter(self)
        self.cutter.errorOccurred.connect(self.errorHandler)
        self.setCentralWidget(self.cutter)
        qApp.setWindowIcon(VideoCutter.getAppIcon(encoded=False))

    @staticmethod
    def get_bitness() -> int:
        from struct import calcsize
        return calcsize('P') * 8

    @pyqtSlot()
    def reboot(self) -> None:
        if self.cutter.mediaAvailable:
            self.cutter.saveProject(reboot=True)
        self.save_settings()
        qApp.exit(MainWindow.EXIT_CODE_REBOOT)

    def save_settings(self) -> None:
        self.settings.setValue('lastFolder', self.cutter.lastFolder)
        self.settings.setValue('geometry', self.saveGeometry())
        self.settings.setValue('windowState', self.saveState())
        self.settings.sync()

    @pyqtSlot(bool)
    def lock_gui(self, locked: bool = True) -> None:
        if locked:
            qApp.setOverrideCursor(Qt.WaitCursor)
            self.cutter.cliplist.setEnabled(False)
            self.setEnabled(False)
        else:
            self.setEnabled(True)
            self.cutter.cliplist.setEnabled(True)
            qApp.restoreOverrideCursor()
        qApp.processEvents()

    @property
    def flatpak(self) -> bool:
        return sys.platform.startswith('linux') and QFileInfo(
            __file__).absolutePath().startswith('/app/')

    def get_app_config_path(self) -> str:
        if self.flatpak:
            confpath = QProcessEnvironment.systemEnvironment().value(
                'XDG_CONFIG_HOME', '')
            if len(confpath):
                return confpath
            else:
                return os.path.join(QDir.homePath(), '.var', 'app',
                                    vidcutter.__desktopid__, 'config')
        return QStandardPaths.writableLocation(
            QStandardPaths.AppConfigLocation).replace(
                qApp.applicationName(),
                qApp.applicationName().lower())

    @staticmethod
    def get_path(path: str = None, override: bool = False) -> str:
        if override:
            if getattr(sys, 'frozen', False) and getattr(
                    sys, '_MEIPASS', False):
                # noinspection PyProtectedMember, PyUnresolvedReferences
                return os.path.join(sys._MEIPASS, path)
            return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
                                path)
        return ':{}'.format(path)

    @pyqtSlot(str)
    def errorHandler(self, msg: str, title: str = None) -> None:
        qApp.restoreOverrideCursor()
        QMessageBox.critical(self,
                             'An error occurred' if title is None else title,
                             msg, QMessageBox.Ok)
        logging.error(msg)

    @staticmethod
    @pyqtSlot()
    def cleanup():
        shutil.rmtree(MainWindow.WORKING_FOLDER, ignore_errors=True)

    def contextMenuEvent(self, event: QContextMenuEvent) -> None:
        if event.reason() in {
                QContextMenuEvent.Mouse, QContextMenuEvent.Keyboard
        }:
            self.cutter.appmenu.popup(event.globalPos())
        super(MainWindow, self).contextMenuEvent(event)

    def mousePressEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.LeftButton and self.cutter.mediaAvailable:
            self.cutter.cliplist.clearSelection()
            self.cutter.timeCounter.clearFocus()
            self.cutter.frameCounter.clearFocus()
            # noinspection PyBroadException
            try:
                if hasattr(self.cutter, 'notify'):
                    self.cutter.notify.close()
            except BaseException:
                pass
            event.accept()

    def dragEnterEvent(self, event: QDragEnterEvent) -> None:
        if event.mimeData().hasUrls():
            event.accept()

    def dropEvent(self, event: QDropEvent) -> None:
        filename = event.mimeData().urls()[0].toLocalFile()
        self.file_opener(filename)
        event.accept()

    def resizeEvent(self, event: QResizeEvent) -> None:
        try:
            if self.isEnabled(
            ) and self.cutter.mediaAvailable and self.cutter.thumbnailsButton.isChecked(
            ):
                if self.cutter.seekSlider.thumbnailsOn:
                    self.cutter.sliderWidget.setLoader(True)
                    self.cutter.sliderWidget.hideThumbs()
                if self.resizeTimer:
                    self.killTimer(self.resizeTimer)
                self.resizeTimer = self.startTimer(500)
        except AttributeError:
            pass

    def timerEvent(self, event: QTimerEvent) -> None:
        try:
            self.cutter.seekSlider.reloadThumbs()
            self.killTimer(self.resizeTimer)
            self.resizeTimer = 0
        except AttributeError:
            pass

    def closeEvent(self, event: QCloseEvent) -> Optional[Callable]:
        event.accept()
        try:
            if not self.isEnabled():
                exitwarn = VCMessageBox('Warning',
                                        'Media is currently being processed',
                                        'Are you sure you want to exit now?',
                                        parent=self)
                exitwarn.addButton('Yes', QMessageBox.NoRole)
                cancelbutton = exitwarn.addButton('No', QMessageBox.RejectRole)
                exitwarn.exec_()
                res = exitwarn.clickedButton()
                if res == cancelbutton:
                    event.ignore()
                    return
            noexit, callback = self.cutter.saveWarning()
            if noexit:
                event.ignore()
                if callback is not None:
                    return callback()
                else:
                    return
        except AttributeError:
            logging.exception('warning dialogs on app exit exception',
                              exc_info=True)
        self.console.deleteLater()
        if hasattr(self, 'cutter'):
            self.save_settings()
            try:
                if hasattr(self.cutter.videoService, 'smartcut_jobs'):
                    [
                        self.cutter.videoService.cleanup(job.files.values())
                        for job in self.cutter.videoService.smartcut_jobs
                    ]
                if hasattr(self.cutter, 'mpvWidget'):
                    self.cutter.mpvWidget.shutdown()
            except AttributeError:
                pass
        try:
            qApp.exit(0)
        except mpv.MPVError:
            pass
コード例 #18
0
class B3dVersionMangerMainWindow(QMainWindow,
                                 main_window_design.Ui_MainWindow):
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.setupUi(self)

        # Read icons
        self.app_icon = QIcon(":/icons/app.svg")
        self.star_icon = QIcon(":/icons/star.svg")
        self.star_inv_icon = QIcon(":/icons/star_inv.svg")
        self.trash_icon = QIcon(":/icons/delete.svg")
        self.quit_icon = QIcon(":/icons/quit_inv.svg")
        self.fake_icon = QIcon(":/icons/fake.svg")

        # Read settings
        self.settings = QSettings('b3d_version_manager', 'settings')

        is_register_blend = self.settings.value('is_register_blend', type=bool)
        self.is_run_minimized = self.settings.value('is_run_minimized',
                                                    type=bool)
        is_run_on_startup = self.settings.value('is_run_on_startup', type=bool)
        root_folder = self.settings.value('root_folder')
        if (not root_folder) or (not os.path.isdir(root_folder)):
            exe_path = os.path.dirname(sys.executable)
            self.settings.setValue('root_folder', exe_path)

        # Custom title bar
        self.title.setText("%s %s" % (QApplication.applicationName(),
                                      QApplication.applicationVersion()))
        self.btnClose.clicked.connect(self.close)
        self.btnMinimize.clicked.connect(self.showMinimized)

        # Custom menu bar
        self.actionToggleRegisterBlend.setChecked(is_register_blend)
        self.actionToggleRegisterBlend.triggered.connect(
            self.toggle_register_blend)

        self.actionToggleRunMinimized.setChecked(self.is_run_minimized)
        self.actionToggleRunMinimized.triggered.connect(
            self.toggle_run_minimized)

        self.actionToggleRunOnStartup.setChecked(is_run_on_startup)
        self.actionToggleRunOnStartup.triggered.connect(
            self.toggle_run_on_startup)

        self.actionQuit.triggered.connect(self.quit)

        self.menubar.hide()
        self.btnFile.setMenu(self.menuFile)

        # Root folder layout
        self.labelRootFolder.setText(self.settings.value('root_folder'))
        self.btnSetRootFolder.clicked.connect(self.set_root_folder)
        self.btnOpenRootFolder.clicked.connect(
            lambda: os.startfile(self.settings.value('root_folder')))

        # Tray layout
        self.blender_action = QAction(self.star_inv_icon, "Blender", self)
        show_action = QAction("Show", self)
        hide_action = QAction("Hide", self)
        quit_action = QAction(self.quit_icon, "Quit", self)

        self.blender_action.triggered.connect(self.open_latest_b3d)
        show_action.triggered.connect(self.bring_to_front)
        hide_action.triggered.connect(self.hide)
        quit_action.triggered.connect(self.quit)

        self.tray_menu = QMenu()
        self.tray_menu.addAction(self.blender_action)
        self.tray_menu.addSeparator()
        self.tray_menu.addAction(show_action)
        self.tray_menu.addAction(hide_action)
        self.tray_menu.addAction(quit_action)

        self.tray_icon = QSystemTrayIcon(self.app_icon, self)
        self.tray_icon.setContextMenu(self.tray_menu)
        self.tray_icon.messageClicked.connect(self.bring_to_front)
        self.tray_icon.activated.connect(lambda btn: self.bring_to_front()
                                         if btn == 3 else False)
        self.tray_icon.show()

        # Version layout
        self.btnUpdate.clicked.connect(self.update)
        self.set_task_visible(False)
        self.layouts = []
        self.latest_local = None
        self.collect_versions()
        self.draw_list_versions()

        # Custom drag behaviour
        self.old_pos = self.pos()
        self.pressed = False

        # Update task
        self.is_update_running = False
        self.uptodate_silent = False
        self.uptodate_thread = CheckForUpdates(self)
        self.uptodate_thread.new_version_obtained.connect(
            self.show_new_version)
        self.uptodate_thread.start()

        self.taskbar_progress = None

    def showEvent(self, event):
        # Setup taskbar
        if not self.taskbar_progress:
            self.task_button = QWinTaskbarButton(self)
            self.task_button.setWindow(self.windowHandle())

            self.taskbar_progress = self.task_button.progress()
            self.taskbar_progress.setVisible(True)
            self.taskbar_progress.setValue(0)

    def open_latest_b3d(self):
        if self.layouts:
            self.layouts[0].open()

    def show_new_version(self, display_name):
        if (display_name == self.progressBar.text()):
            return

        self.set_task_visible(True)
        self.set_progress_bar(0, 0, display_name)

        if self.isHidden() and not self.uptodate_silent:
            self.tray_icon.showMessage(
                "Blender Version Manager",
                "New version of Blender 2.8 is avaliable!",
                QSystemTrayIcon.Information, 4000)

    def set_task_visible(self, is_visible):
        if is_visible:
            self.progressBar.show()
            self.btnUpdate.show()
            self.btnCancel.hide()
        else:
            self.progressBar.hide()
            self.btnUpdate.hide()
            self.btnCancel.hide()

    def is_running_task(self):
        if self.is_update_running:
            QMessageBox.information(self, "Warning",
                                    "Update task in progress!", QMessageBox.Ok)
            return True
        else:
            return False

    def toggle_run_minimized(self, is_checked):
        self.settings.setValue('is_run_minimized', is_checked)

    def toggle_register_blend(self, is_checked):
        self.settings.setValue('is_register_blend', is_checked)

    def cleanup_layout(self, layout):
        while layout.count():
            item = layout.takeAt(0)
            widget = item.widget()

            if widget is not None:
                widget.deleteLater()
            else:
                self.cleanup_layout(item.layout())

    def collect_versions(self):
        self.layouts.clear()
        root_folder = self.settings.value('root_folder')
        dirs = next(os.walk(root_folder))[1]
        versions = []

        for dir in dirs:
            if os.path.isfile(os.path.join(root_folder, dir, "blender.exe")):
                versions.append(dir)

        for ver in versions:
            b3d_item_layout = B3dItemLayout(root_folder, ver, False, self)
            self.layouts.append(b3d_item_layout)

    def draw_list_versions(self):
        if len(self.layouts) > 0:
            self.layouts.sort(key=lambda ver: ver.mtime, reverse=True)
            self.latest_local = self.layouts[0].git
            self.blender_action.setVisible(True)

            for b3d_item_layout in self.layouts:
                self.layoutListVersions.removeItem(b3d_item_layout)
                b3d_item_layout.set_is_latest(False)

            self.layouts[0].set_is_latest(True)

            for b3d_item_layout in self.layouts:
                self.layoutListVersions.addLayout(b3d_item_layout)
        else:
            self.latest_local = None
            label = QLabel("No Local Versions Found!")
            label.setStyleSheet("color: white; font-size: 10pt;")
            label.setAlignment(Qt.AlignCenter)
            label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            self.layoutListVersions.addWidget(label)
            self.blender_action.setVisible(False)

    def set_root_folder(self):
        root_folder = self.settings.value('root_folder')
        dir = QFileDialog.getExistingDirectory(self, "Choose Root Folder",
                                               root_folder)

        if dir and (dir != root_folder):
            self.settings.setValue('root_folder', dir)
            self.labelRootFolder.setText(dir)
            self.cleanup_layout(self.layoutListVersions)
            self.collect_versions()
            self.draw_list_versions()

    def update(self):
        self.is_update_running = True
        self.uptodate_thread.is_running = False
        self.uptodate_thread.terminate()
        self.uptodate_thread.wait()

        self.btnUpdate.hide()
        self.btnCancel.show()
        self.btnSetRootFolder.hide()
        self.set_progress_bar(0, 0, "Downloading: %p%")

        self.build_loader = BuildLoader(self,
                                        self.uptodate_thread.download_url)
        self.build_loader.finished.connect(self.finished)
        self.build_loader.progress_changed.connect(self.set_progress_bar)
        self.build_loader.block_abortion.connect(lambda: self.btnCancel.hide())
        self.btnCancel.clicked.connect(self.build_loader.stop)
        self.build_loader.start()

    def finished(self, version):
        self.build_loader.terminate()
        self.build_loader.wait()

        self.btnSetRootFolder.show()
        self.set_task_visible(False)
        self.is_update_running = False

        if version:
            root_folder = self.settings.value('root_folder')
            b3d_item_layout = B3dItemLayout(root_folder, version, True, self)
            self.layouts.append(b3d_item_layout)

            self.tray_icon.showMessage("Blender Version Manager",
                                       "Update finished!",
                                       QSystemTrayIcon.Information, 4000)

        self.draw_list_versions()
        self.uptodate_thread = CheckForUpdates(self)
        self.uptodate_thread.new_version_obtained.connect(
            self.show_new_version)
        self.set_progress_bar(0, 0, "")
        self.uptodate_thread.start()

    def set_progress_bar(self, progress_bar_val, taskbar_val, format):
        self.progressBar.setFormat(format)
        self.progressBar.setValue(progress_bar_val * 100)

        if self.taskbar_progress:
            self.taskbar_progress.setValue(taskbar_val * 100)

    def quit(self):
        if not self.is_running_task():
            self.tray_icon.hide()
            self.app.quit()

    def closeEvent(self, event):
        if self.actionToggleRunMinimized.isChecked():
            event.ignore()
            self.hide()
        elif self.is_running_task():
            event.ignore()
        else:
            self.tray_icon.hide()
            event.accept()

    def mousePressEvent(self, event):
        self.old_pos = event.globalPos()
        self.pressing = True

    def mouseMoveEvent(self, event):
        if self.pressing:
            delta = QPoint(event.globalPos() - self.old_pos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.old_pos = event.globalPos()

    def mouseReleaseEvent(self, QMouseEvent):
        self.pressing = False

    def toggle_run_on_startup(self, is_checked):
        key = winreg.OpenKey(
            winreg.HKEY_CURRENT_USER,
            r'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run', 0,
            winreg.KEY_SET_VALUE)

        if (is_checked):
            path = sys.executable
            winreg.SetValueEx(key, 'Blender Version Manager', 0, winreg.REG_SZ,
                              path)
        else:
            try:
                winreg.DeleteValue(key, 'Blender Version Manager')
            except:
                pass

        key.Close()
        self.settings.setValue('is_run_on_startup', is_checked)

    def bring_to_front(self):
        self.setWindowState(self.windowState() & ~Qt.WindowMinimized
                            | Qt.WindowActive)
        self.activateWindow()
        self.show()