Ejemplo n.º 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()
Ejemplo n.º 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()
        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)()
Ejemplo n.º 4
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
Ejemplo n.º 5
0
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
Ejemplo n.º 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()
Ejemplo n.º 7
0
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)
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()
Ejemplo n.º 9
0
 def __init__(self, window):
     super().__init__()
     taskbar_button = QWinTaskbarButton(window)
     taskbar_button.setWindow(window)
     self._progress = taskbar_button.progress()
Ejemplo n.º 10
0
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)
Ejemplo n.º 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
Ejemplo n.º 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()
Ejemplo n.º 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)
Ejemplo n.º 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()
Ejemplo n.º 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)