def init_cutter(self) -> None: self.cutter = VideoCutter(self) self.cutter.errorOccurred.connect(self.errorHandler) # qApp.setWindowIcon(QIcon(':/images/vidcutter.png')) qApp.setWindowIcon( QIcon.fromTheme(qApp.applicationName().lower(), QIcon(':/images/vidcutter.png'))) self.setCentralWidget(self.cutter)
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
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))
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.edl, self.video = '', '' self.parse_cmdline() self.init_logger() self.init_cutter() self.setWindowTitle('%s' % qApp.applicationName()) self.setContentsMargins(0, 0, 0, 0) self.statusBar().showMessage('Ready') statuslogo = QLabel(pixmap=QPixmap(':/images/vidcutter-emboss.png'), objectName='logowidget') self.statusBar().addPermanentWidget(statuslogo) self.statusBar().setStyleSheet('border:none;') self.setAcceptDrops(True) self.setMinimumSize(900, 640) self.show() try: if len(self.video): self.cutter.loadMedia(self.video) if len(self.edl): self.cutter.openEDL(edlfile=self.edl) except (FileNotFoundError, PermissionError) as e: QMessageBox.critical(self, 'Error loading file', sys.exc_info()[0]) logging.exception('Error loading file') qApp.restoreOverrideCursor() self.cutter.startNew() if not self.cutter.ffmpeg_check(): self.close() sys.exit(1) def init_logger(self) -> None: try: log_path = QStandardPaths.writableLocation( QStandardPaths.AppConfigLocation).lower() 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) handlers = [ logging.handlers.RotatingFileHandler(os.path.join( log_path, '%s.log' % qApp.applicationName().lower()), maxBytes=1000000, backupCount=1) ] if os.getenv('DEBUG', False): handlers.append(logging.StreamHandler()) 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 = self.log_uncaught_exceptions @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( 'The simply FAST & ACCURATE video cutter & joiner') self.parser.addPositionalArgument('video', 'Preloads the video file in app.', '[video]') self.edl_option = QCommandLineOption( 'edl', 'Preloads clip index from a previously saved EDL file.\n' + 'NOTE: You must also set the video argument for this to work.', 'edl file') self.debug_option = QCommandLineOption( ['d', 'debug'], 'Output all info, warnings and errors to the console. ' + '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.edl_option) self.parser.addOption(self.debug_option) self.parser.addVersionOption() self.parser.addHelpOption() self.parser.process(qApp) self.args = self.parser.positionalArguments() if self.parser.value('edl').strip() and not os.path.exists( self.parser.value('edl')): print('\n ERROR: EDL file not found.\n', file=sys.stderr) self.close() sys.exit(1) if self.parser.value('edl').strip() and len(self.args) == 0: print('\n ERROR: Video file argument is missing.\n', file=sys.stderr) self.close() sys.exit(1) if self.parser.value('edl').strip(): self.edl = self.parser.value('edl') if self.parser.isSet(self.debug_option): os.environ['DEBUG'] = '1' if len(self.args) > 0 and not os.path.exists(self.args[0]): print('\n ERROR: Video file not found.\n', file=sys.stderr) self.close() sys.exit(1) if len(self.args) > 0: self.video = self.args[0] def init_cutter(self) -> None: self.cutter = VideoCutter(self) qApp.setWindowIcon(self.cutter.appIcon) self.setCentralWidget(self.cutter) @staticmethod def get_bitness() -> int: from struct import calcsize return calcsize('P') * 8 def restart(self) -> None: self.cutter.deleteLater() self.init_cutter() @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 ':%s' % path @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()) @staticmethod def get_version(filename: str = '__init__.py') -> str: with open(MainWindow.get_path(filename, override=True), 'r', encoding='utf-8') as initfile: for line in initfile.readlines(): m = re.match('__version__ *= *[\'](.*)[\']', line) if m: return m.group(1) def contextMenuEvent(self, event: QContextMenuEvent) -> None: if event.reason() == QContextMenuEvent.Mouse: self.cutter.appMenu.exec_(event.globalPos()) event.accept() super(MainWindow, self).contextMenuEvent(event) def mousePressEvent(self, event: QMouseEvent): if event.button() == Qt.LeftButton: self.cutter.cliplist.clearSelection() 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.cutter.loadMedia(filename) event.accept() def closeEvent(self, event: QCloseEvent) -> None: if hasattr(self, 'cutter'): if hasattr(self.cutter, 'mediaPlayer'): self.cutter.mediaPlayer.terminate() qApp.quit()
def init_cutter(self) -> None: self.cutter = VideoCutter(self) qApp.setWindowIcon(self.cutter.appIcon) self.setCentralWidget(self.cutter)
class MainWindow(QMainWindow): EXIT_CODE_REBOOT = 666 def __init__(self): super(MainWindow, self).__init__() self.video, self.devmode = '', False self.parse_cmdline() self.init_logger() self.init_settings() self.init_scale() self.init_cutter() self.setWindowTitle('%s' % 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() self.console.setGeometry(int(self.x() - (self.width() / 2)), self.y() + int(self.height() / 3), 750, 300) try: if len(self.video): if QFileInfo(self.video).suffix() == 'vcp': self.cutter.openProject(project_file=self.video) else: self.cutter.loadMedia(self.video) except (FileNotFoundError, PermissionError) as e: QMessageBox.critical(self, 'Error loading file', sys.exc_info()[0]) logging.exception('Error loading file') qApp.restoreOverrideCursor() self.restart() if not self.cutter.ffmpeg_check(): qApp.exit(1) 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) @staticmethod def get_size(mode: str = 'NORMAL') -> QSize: modes = { 'LOW': QSize(800, 425), 'NORMAL': QSize(915, 680), 'HIGH': QSize(1850, 1300) } return modes[mode] def init_logger(self) -> None: try: log_path = QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation).lower() 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): handlers.append(logging.StreamHandler()) 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 = self.log_uncaught_exceptions def init_settings(self) -> None: if sys.platform == 'darwin': QSettings.setDefaultFormat(QSettings.IniFormat) self.settings = QSettings(self) else: try: settings_path = QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation).lower() 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 = '%s.ini' % 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) @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 video 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.dev_option = QCommandLineOption(['dev'], 'developer mode; disables the use of compiled resource files ' + 'so that all app resources & assets are accessed directly from the file ' + 'system allowing you to see UI changes immediately. this typically ' + 'relates to changes made to Qt stylesheets (.qss), layout/templates, ' + 'content includes and images. basically all assets defined in .qrc ' + 'files throughout the codebase.') self.parser.addOption(self.debug_option) self.parser.addOption(self.dev_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 self.parser.isSet(self.dev_option): self.devmode = True 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() sys.exit(1) self.video = file_path def init_cutter(self) -> None: self.cutter = VideoCutter(self) self.cutter.errorOccurred.connect(self.errorHandler) # qApp.setWindowIcon(QIcon(':/images/vidcutter.png')) qApp.setWindowIcon(QIcon.fromTheme(qApp.applicationName().lower(), QIcon(':/images/vidcutter.png'))) self.setCentralWidget(self.cutter) @staticmethod def get_bitness() -> int: from struct import calcsize return calcsize('P') * 8 @pyqtSlot() def reboot(self) -> None: 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() @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 ':%s' % path @staticmethod def get_version(filename: str = '__init__.py') -> str: with open(MainWindow.get_path(filename, override=True), 'r', encoding='utf-8') as initfile: for line in initfile.readlines(): m = re.match('__version__ *= *[\'](.*)[\']', line) if m: return m.group(1) @pyqtSlot(str) def errorHandler(self, msg: str) -> None: QMessageBox.critical(self, 'An error occurred', msg, QMessageBox.Ok) logging.error(msg) def contextMenuEvent(self, event: QContextMenuEvent) -> None: if event.reason() == QContextMenuEvent.Mouse: self.cutter.appMenu.exec_(event.globalPos()) event.accept() 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() 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.cutter.loadMedia(filename) event.accept() def resizeEvent(self, event: QResizeEvent) -> None: try: if self.cutter.mediaAvailable and self.cutter.thumbnailsButton.isChecked(): self.cutter.seekSlider.reloadThumbs() except AttributeError: pass def closeEvent(self, event: QCloseEvent) -> None: event.accept() self.console.deleteLater() if hasattr(self, 'cutter'): self.save_settings() if hasattr(self.cutter, 'mpvWidget'): self.cutter.mpvWidget.shutdown() qApp.quit()