def runmain(): import sys app = QApplication(sys.argv) cmdParser = QCommandLineParser() cmdParser.setApplicationDescription( 'Room file merger utility script for Basement Renovator. Takes a config file and feeds it to the cli roommerger script' ) cmdParser.addHelpOption() cmdParser.addPositionalArgument( 'configFile', '''json config file to grab configuration from; sets current working directory to its directory. Format: { files: [ { outputFile: path to file to output, paths: [ path to file/folder to replace, ... ], skipSTB: optional, true to skip generating the stb, noRecomputeIds: optional, true to skip recompute room ids, startingId: starting room id to recompute from }... ] } ''') fileEditedOpt = QCommandLineOption( 'fileEdited', 'optional file to limit which rooms get merged from the config', 'file') cmdParser.addOption(fileEditedOpt) cmdParser.process(app) configArg = cmdParser.positionalArguments()[0] configPath = Path(configArg).absolute().resolve() if not configArg or not configPath.is_file(): print('Invalid config path!') return fileEditedArg = cmdParser.value(fileEditedOpt) fileEditedPath = None if fileEditedArg: fileEditedPath = Path(fileEditedArg).absolute().resolve() if not fileEditedPath.is_file(): print('Invalid edited file path!') return scriptPath = Path(__file__ + '/../roommerger.py').absolute().resolve() with open(configPath) as configFile: config = json.load(configFile) mergeRooms(config['files'], str(scriptPath), configPath.parent, fileEdited=fileEditedPath) print('Success! Merged all.')
def process_args(app): args = {} parser = QCommandLineParser() parser.setApplicationDescription( ('PyMemorise is a tool to help memorise tables of data.' + ' It was built as an exam revision aid.')) parser.addHelpOption() parser.addVersionOption() dbOption = QCommandLineOption( ["database-file", "d"], "path to database, will be created if does not exist", "db", str(Path.home().joinpath(".pymem.db"))) parser.addOption(dbOption) parser.process(app) args["database"] = parser.value(dbOption) if parser.positionalArguments(): print(parser.positionalArguments()) parser.showHelp() return args
def processArguments(arguments): parser = QCommandLineParser() parser.addHelpOption() parser.addVersionOption() delayOption = QCommandLineOption(["d", "delay"], "Take a screenshot after NUM seconds", "NUM") fullscreenOption = QCommandLineOption(["f", "fullscreen"], "Take a screenshot of the whole screen") topWindowOption = QCommandLineOption(["w", "top-window"], "Take a screenshot of the most top window") savePathOption = QCommandLineOption(["s", "save-path"], "Specify a path to save the screenshot", "PATH") startFromDesktopOption = QCommandLineOption(["i", "icon"], "Indicate that this program's started by clicking desktop file.") parser.addOption(delayOption) parser.addOption(fullscreenOption) parser.addOption(topWindowOption) parser.addOption(savePathOption) parser.addOption(startFromDesktopOption) parser.process(arguments) delay = int(parser.value(delayOption) or 0) fullscreen = bool(parser.isSet(fullscreenOption) or False) topWindow = bool(parser.isSet(topWindowOption) or False) savePath = str(parser.value(savePathOption) or "") startFromDesktop = bool(parser.isSet(startFromDesktopOption) or False) return {"delay": delay, "fullscreen": fullscreen, "topWindow": topWindow, "savePath": savePath, "startFromDesktop": startFromDesktop}
def directory(app): #app = QApplication(sys.argv) QCoreApplication.setApplicationVersion(QT_VERSION_STR) parser = QCommandLineParser() parser.setApplicationDescription("File Directory") parser.addHelpOption() parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption( 'C', "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument('', "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath('') filter = ['*.db'] #filtering out just by db model.setNameFilters(filter) model.setNameFilterDisables(0) #Only show the filtered .db paths #filename = model.filePath() #print(filename) if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None: rootIndex = model.index(QDir.cleanPath(rootPath)) if rootIndex.isValid(): tree.setRootIndex(rootIndex) # Demonstrating look and feel features. tree.setAnimated(False) tree.setIndentation(20) tree.setSortingEnabled(True) availableSize = QApplication.desktop().availableGeometry(tree).size() tree.resize(availableSize / 2) tree.setColumnWidth(0, tree.width() / 3) tree.setWindowTitle("Directory View") tree.show() sys.exit(app.exec_())
def main(): app = QCoreApplication(sys.argv) # Set some application details. app.setApplicationName("MyApp") app.setApplicationVersion("0.1.0") app.setOrganizationName("My Organization") app.setOrganizationDomain("www.my-organization.org") # Create a verbosity command line option. verbose_option = QCommandLineOption( "verbose", "Verbose mode. Print out debug messages.") # Setup the commandline parser. command_parser = QCommandLineParser() command_parser.addHelpOption() command_parser.addVersionOption() # Add the commandline options. command_parser.addOption(verbose_option) # Process the command line. command_parser.process(app) # Set the basic logger mnessage format and verbosity level. logger = logging.getLogger() # This dictates how the messages are formatted. formatter = logging.Formatter(fmt=MESSAGE_FORMAT, style='{') # This handler sends everything to stdout. handler = logging.StreamHandler() handler.setFormatter(formatter) # Add the handler to the logger. logger.addHandler(handler) # Check if the verbosity flag is set and set the log level to debug if it is. if command_parser.isSet(verbose_option): logger.setLevel(logging.DEBUG) logger.debug("Setting loglevel to debug.") else: logger.setLevel(logging.ERROR) # Start the event loop. sys.exit(app.exec())
def main(): app = QCoreApplication(sys.argv) # Set some application details. app.setApplicationName("MyApp") app.setApplicationVersion("0.1.0") app.setOrganizationName("My Organization") app.setOrganizationDomain("www.my-organization.org") # Create a verbosity command line option. verbose_option = QCommandLineOption("verbose", "Verbose mode. Print out debug messages.") # Setup the commandline parser. command_parser = QCommandLineParser() command_parser.addHelpOption() command_parser.addVersionOption() # Add the commandline options. command_parser.addOption(verbose_option) # Process the command line. command_parser.process(app) # Set the basic logger mnessage format and verbosity level. logger = logging.getLogger() # This dictates how the messages are formatted. formatter = logging.Formatter(fmt=MESSAGE_FORMAT, style="{") # This handler sends everything to stdout. handler = logging.StreamHandler() handler.setFormatter(formatter) # Add the handler to the logger. logger.addHandler(handler) # Check if the verbosity flag is set and set the log level to debug if it is. if command_parser.isSet(verbose_option): logger.setLevel(logging.DEBUG) logger.debug("Setting loglevel to debug.") else: logger.setLevel(logging.ERROR) # Start the event loop. sys.exit(app.exec())
class CliParser: def __init__(self, app): self.log = logs.logger.add_module("CliParser") QApplication.setApplicationName("fotobox") QApplication.setApplicationVersion("1.0") self.parser = QCommandLineParser() self.parser.setApplicationDescription("Fotobox") self.parser.addHelpOption() self.parser.addVersionOption() self.cursorOption = QCommandLineOption(["m", "mouse-cursor"], "Maus anzeigen") self.parser.addOption(self.cursorOption) self.configOption = QCommandLineOption(["c", "config"], "Systemkonfiguration setzen") self.parser.addOption(self.configOption) self.loggingOption = QCommandLineOption(["log"], "Logging aktivieren", "level", "INFO") self.parser.addOption(self.loggingOption) self.parser.process(app) def is_mouse_cursor(self): return self.parser.isSet(self.cursorOption) def is_config_mode(self): return self.parser.isSet(self.configOption) def log_level(self): return self.parser.value(self.loggingOption).lower()
def processArguments(arguments): parser = QCommandLineParser() parser.addHelpOption() parser.addVersionOption() delayOption = QCommandLineOption(["d", "delay"], "Take a screenshot after NUM seconds", "NUM") fullscreenOption = QCommandLineOption( ["f", "fullscreen"], "Take a screenshot of the whole screen") topWindowOption = QCommandLineOption( ["w", "top-window"], "Take a screenshot of the most top window") savePathOption = QCommandLineOption( ["s", "save-path"], "Specify a path to save the screenshot", "PATH") startFromDesktopOption = QCommandLineOption( ["i", "icon"], "Indicate that this program's started by clicking desktop file.") parser.addOption(delayOption) parser.addOption(fullscreenOption) parser.addOption(topWindowOption) parser.addOption(savePathOption) parser.addOption(startFromDesktopOption) parser.process(arguments) delay = int(parser.value(delayOption) or 0) fullscreen = bool(parser.isSet(fullscreenOption) or False) topWindow = bool(parser.isSet(topWindowOption) or False) savePath = str(parser.value(savePathOption) or "") startFromDesktop = bool(parser.isSet(startFromDesktopOption) or False) return { "delay": delay, "fullscreen": fullscreen, "topWindow": topWindow, "savePath": savePath, "startFromDesktop": startFromDesktop }
def main(use_resources=False): parser = QCommandLineParser() geometryOpt = QCommandLineOption('geometry', 'Main window geometry', 'geometry') parser.addOption(geometryOpt) parser.process(sys.argv) if parser.isSet('geometry'): try: geometry = tuple( int(val) for val in parser.value('geometry').split('x')) if len(geometry) != 4: raise if any(val < 1 for val in geometry): raise except: print( 'The --geometry argument value must have a format such as 1x1x640x320 (x, y, width, height).' ) exit(0) else: geometry = None app = QApplication(sys.argv) app.setApplicationName('pyrMExplorer') app.setOrganizationName('rMTools') if use_resources: icon_path = resource_path('icon.ico') else: icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon.ico') app.setWindowIcon(QIcon(icon_path)) mainWindow = RmExplorerWindow() if geometry: mainWindow.setGeometry(*geometry) mainWindow.show() sys.exit(app.exec_())
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()
parser = QCommandLineParser() parser.addHelpOption() parser.addVersionOption() delayOption = QCommandLineOption(["d", "delay"], "Take a screenshot after NUM seconds", "NUM") fullscreenOption = QCommandLineOption(["f", "fullscreen"], "Take a screenshot of the whole screen") topWindowOption = QCommandLineOption(["w", "top-window"], "Take a screenshot of the most top window") savePathOption = QCommandLineOption(["s", "save-path"], "Specify a path to save the screenshot", "PATH") startFromDesktopOption = QCommandLineOption(["i", "icon"], "Indicate that this program's started by clicking desktop file.") parser.addOption(delayOption) parser.addOption(fullscreenOption) parser.addOption(topWindowOption) parser.addOption(savePathOption) parser.addOption(startFromDesktopOption) parser.process(app) delayValue = int(parser.value(delayOption) or 0) fullscreenValue = bool(parser.isSet(fullscreenOption) or False) topWindowValue = bool(parser.isSet(topWindowOption) or False) savePathValue = str(parser.value(savePathOption) or "") startFromDesktopValue = bool(parser.isSet(startFromDesktopOption) or False) if is_service_exist(): notificationsInterface.notify("Deepin Screenshot", _("Deepin Screenshot has been started!"))
QCoreApplication, QDir, QT_VERSION_STR) from PyQt5.QtWidgets import (QApplication, QFileIconProvider, QFileSystemModel, QTreeView) app = QApplication(sys.argv) QCoreApplication.setApplicationVersion(QT_VERSION_STR) parser = QCommandLineParser() parser.setApplicationDescription("Qt Dir View Example") parser.addHelpOption() parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption('c', "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument('directory', "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath('') if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None:
'configFile', '''json config file to grab configuration from. Format: { files: [ absolute path to file to replace, ... ], entities: [ { from: [ type, variant, subtype (can be -1 for wild card) ], to: [ type, variant, subtype (can be -1 for wild card) ], }, ... ] } ''') stbOpt = QCommandLineOption( 'stb', 'whether to save an stb version of the file next to it') cmdParser.addOption(stbOpt) cmdParser.process(app) args = cmdParser.positionalArguments() configArg = args[0] stbArg = cmdParser.isSet(stbOpt) with open(configArg) as configFile: config = json.load(configFile) totalRooms = 0 print('Replacing entities:', config['entities']) for rf in config['files']: print('File: ', rf)
- the application will process messages until it is told to exit """ import sys, os from subprocess import Popen, PIPE from configparser import ConfigParser app = QApplication(sys.argv) app.setApplicationName("gitStatus.py") app.setApplicationVersion("1.0.0") clp = QCommandLineParser() # handle command line options clp.addHelpOption() clp.addVersionOption() verboseOption = QCommandLineOption("verbose", "Issue progress messages to console.") clp.addOption(verboseOption) clp.process(sys.argv) GitStatus = QtWidgets.QDialog() # create QDialog ui = Ui_GitStatus() # instantiate ui ui.setupUi(GitStatus) # configure all widgets ui.isVerbose = clp.isSet(verboseOption) if not ui.parseConfigFile(): # read the .ini file sys.exit(1) ui.populateDialog() # populate the dialog's widgets GitStatus.show() # make the dialog visible sys.exit(app.exec_()) # handle all messages until exit
from menu_controller import MenuController from dbus_services import (DeepinMovieServie, check_multiple_instances, DeepinMovieInterface, session_bus, DBUS_PATH) if __name__ == "__main__": windowView = None menu_controller = None database = None movie_info = None try: parser = QCommandLineParser() parser.setApplicationDescription(PROJECT_NAME) parser.addHelpOption() parser.addVersionOption() parser.addOption(QCommandLineOption("full-screen", "Play in full screen mode")) parser.addPositionalArgument("element", "Element path") parser.process(app) result = check_multiple_instances() if result: dbus_service = DeepinMovieServie(app) session_bus.registerObject(DBUS_PATH, dbus_service) else: if not config.playerMultipleProgramsAllowed: dbus_interface = DeepinMovieInterface() dbus_interface.play(json.dumps(sys.argv[1:])) os._exit(0) windowView = Window(result or len(sys.argv) > 1) menu_controller = MenuController(windowView)
def runmain(): import sys app = QApplication(sys.argv) cmdParser = QCommandLineParser() cmdParser.setApplicationDescription( 'Room file merger utility script for Basement Renovator. Takes a set of file paths' ) cmdParser.addHelpOption() cmdParser.addPositionalArgument('file', 'xml files to merge') outputFileOpt = QCommandLineOption('output', 'output filename, must be xml', 'file') cmdParser.addOption(outputFileOpt) stbOpt = QCommandLineOption( 'stb', 'whether to save an stb version of the file next to it') cmdParser.addOption(stbOpt) noRecompIdsOpt = QCommandLineOption( 'noRecomputeIds', 'turn off recomputing room ids; useful for special room merging') cmdParser.addOption(noRecompIdsOpt) idOpt = QCommandLineOption( 'startingId', 'optional starting id to use when recomputing room ids', 'id') cmdParser.addOption(idOpt) skipOpt = QCommandLineOption( 'roommerge', 'placeholder argument used to prevent recursive execution') cmdParser.addOption(skipOpt) cmdParser.process(app) if cmdParser.isSet(skipOpt): print('Recursive execution from save hook, skipping') return paths = cmdParser.positionalArguments() if not paths: print('Must specify at least one file to merge!') return outputFileArg = cmdParser.value(outputFileOpt) outputFilePath = Path(outputFileArg).absolute().resolve() if not outputFileArg or outputFilePath.suffix != '.xml': print('Must specify xml output file!') return lastModified = None if outputFilePath.exists(): lastModified = outputFilePath.stat().st_mtime idArg = cmdParser.value(idOpt) noRecompIdsArg = cmdParser.isSet(noRecompIdsOpt) stbArg = cmdParser.isSet(stbOpt) mergeRoomFile = None i = -1 while (i + 1) < len(paths): i += 1 file = paths[i] path = Path(file) if path.is_dir(): files = list(filter(lambda f: f.suffix == '.xml', path.iterdir())) print('Adding xml files to queue from: ', path) del paths[i] i -= 1 paths.extend(files) paths = list(filter(lambda f: Path(f).exists(), paths)) if lastModified: anyModified = next( (file for file in paths if file.stat().st_mtime > lastModified), None) is not None if not anyModified: print('----') print( 'Skipping since no xmls in folder have been modified since last update' ) return for file in paths: print('----') print('Path:', file) path = Path(file) print('Merging file...') if path.suffix != '.xml': print('Must be xml! Skipping!') continue roomFile = cvt.xmlToCommon(path) if not mergeRoomFile: mergeRoomFile = roomFile else: mergeRoomFile.rooms.extend(roomFile.rooms) print('----') if not mergeRoomFile: print('No rooms files to merge') return if not noRecompIdsArg: recomputeRoomIDs(mergeRoomFile.rooms, idArg and int(idArg)) cvt.commonToXML(outputFilePath, mergeRoomFile.rooms, file=mergeRoomFile) if stbArg: cvt.commonToSTBAB(outputFileArg.replace('.xml', '.stb'), mergeRoomFile.rooms) settings = QSettings(__file__ + '/../../settings.ini', QSettings.IniFormat) saveHooks = settings.value('HooksSave') if saveHooks: fullPath = str(outputFilePath) for hook in saveHooks: hook = Path(hook).absolute().resolve() try: subprocess.run([str(hook), fullPath, '--save', '--roommerge'], cwd=hook.parent, timeout=60) except Exception as e: print('Save hook failed! Reason:', e) print('Success! Merged to', outputFileArg)
class PyCirkuitParser(QObject): def _initStrings(self): self._appDescriptionStr = _translate( "CommandLine", "\n" "{productName} is a front-end for Circuit Macros by Dwight Aplevich,\n" "which are a set of macros for drawing high-quality line diagrams\n" "to be included in TeX, LaTeX, web or similar documents.", "Commandline application description. Don't translate '{productName}'." ) self._batchOptionStr = _translate( "CommandLine", "Activates batch (unattended) mode, and convert files specified by <path> to {formatID} format. Several output formats can be used together.", "Commandline option description. Don't translate the '{formatID}' variable." ) self._dpiOptionStr = _translate( "CommandLine", "Sets the resolution of output raster images (png, jpg), in dots per inch. Value <N> is mandatory. If option is not set, default is {defaultDPI}dpi (defined in 'settings' dialog).", "Commandline argument description. Don't translate the '{defaultDPI}' variable." ) self._qualityOptionStr = _translate( "CommandLine", "Sets the quality of output raster lossy images (jpg), in percent. Value <Q> is mandatory. If option is not set, default is {defaultQuality}% (defined in 'settings' dialog).", "Commandline option description. Don't translate the '{defaultQuality}' variable." ) self._overwriteOptionStr = _translate( "CommandLine", "Overwrite by default the converted files if they are present. If not set, user will be asked at runtime.", "Commandline option description.") self._followLinksOptionStr = _translate( "CommandLine", "Follow symbolic links.\n" "- If not set, destination file will be saved into the same directory where the source file is, whether it's a real file or a symbolic link.\n" "- If set, destination file will be saved into the directory where the real source file is located.", "Commandline option description.") self._destDirOptionStr = _translate( "CommandLine", "Save all converted files into the same destination directory <D> (value is mandatory).", "Commandline option description. Don't translate '<D>'.") self._recurseOptionStr = _translate( "CommandLine", "Using this option the pattern '**' will match any files and zero or more subdirs, so '**/*.ckt' will match all files with 'ckt' extension in the current directory and all its subdirectories.", "Commandline option description.") self._pathDescriptionStr = _translate( "CommandLine", "Path to source drawing file(s) to process. Wildcards accepted.\n" "- If no <path> is given, the GUI is opened.\n" "- If <path> points to only one file and no batch conversion options are present, this file is opened into the GUI for editing.\n" "- If <path>s point to more than one valid file and a combination of output formats options are present, these source files are processed sequentially in batch (unattended) mode and converted into the requested formats.\n" "- Specifying more than one file to process with no output format options present is not allowed.", "Commandline argument description. If you translate <path>, translate the name and path syntax accordingly." ) self._seeHelpStr = _translate( "CommandLine", 'Try "{appName} --help" to get more information.', "Command-line error message. Don't translate '{appName}'.") def __init__(self, args): super().__init__() self.args = args settings = QSettings() self.imageParam = { Option.DPI: settings.value("Export/exportDPI", 150, type=int), Option.QUAL: settings.value("Export/exportQuality", 80, type=int), } self.cli_mode = False self.requestedRecursive = False self.overwrite = Overwrite.UNSET self.dstDir = "" self._initStrings() self.parser = QCommandLineParser() #self.parser.setSingleDashWordOptionMode(QCommandLineself.parser.ParseAsLongOptions) self.parser.setApplicationDescription( self._appDescriptionStr.format(productName=__productname__)) # Adding the '-h, --help' option self.parser.addHelpOption() # Adding the '-v --version' option self.parser.addVersionOption() # Allowing positional arguments (the file/files to open or process) self.parser.addPositionalArgument( _translate( "CommandLine", "path", "Commandline argument name. If you translate this name, translate it accordingly into the path description and path syntax." ), self._pathDescriptionStr, _translate( "CommandLine", "[<path> [ <path2>...]]", "Commandline argument syntax. If you translate <path>, translate the name and path description accordingly." )) # Adding command line options self.options = { Option.TIKZ: QCommandLineOption( ["t", "tikz"], self._batchOptionStr.format(formatID='TIkZ'), ), Option.PDF: QCommandLineOption( ["f", "pdf"], self._batchOptionStr.format(formatID='PDF'), ), Option.PNG: QCommandLineOption( ["p", "png"], self._batchOptionStr.format(formatID='PNG'), ), Option.JPEG: QCommandLineOption( ["j", "jpeg"], self._batchOptionStr.format(formatID='JPEG'), ), Option.SVG: QCommandLineOption( ["s", "svg"], self._batchOptionStr.format(formatID='SVG'), ), Option.DPI: QCommandLineOption( ["dpi"], self._dpiOptionStr.format( defaultDPI=self.imageParam[Option.DPI]), "N", ), Option.QUAL: QCommandLineOption( ["quality"], self._qualityOptionStr.format( defaultQuality=self.imageParam[Option.QUAL]), "Q", ), Option.DEST: QCommandLineOption( ["destination"], self._destDirOptionStr, "D", ), Option.LINK: QCommandLineOption( ["links"], self._followLinksOptionStr, ), Option.OVER: QCommandLineOption( ["overwrite"], self._overwriteOptionStr, ), Option.REC: QCommandLineOption( ["r"], self._recurseOptionStr, ), } # Adding the options in the list for option in self.options.values(): self.parser.addOption(option) def _checkFiles(self): # Test for some path passed as parameters, or none paths = self.parser.positionalArguments() # User gave some filespec to process? pathPresent = (len(paths) > 0) # User may have entered more than one path, and these can contain wildcards # We have to expand them into files prior to process self.requestedFilesToProcess = list() for pathSpec in paths: for f in glob.iglob(pathSpec, recursive=self.requestedRecursive): if isfile(realpath(f)): f = realpath(f) if self.followSymlinks else abspath(f) self.requestedFilesToProcess.append(f) NumFiles = len(self.requestedFilesToProcess) if (NumFiles == 0) and pathPresent: print( QCoreApplication.applicationName() + ": " + _translate("CommandLine", "The given path does not match any existing file.", "Commandline error message")) print( self._seeHelpStr.format( appName=QCoreApplication.applicationName())) sys.exit(-1) return NumFiles def _checkFileOptions(self): self.followSymlinks = self.parser.isSet(self.options[Option.LINK]) self.overwrite = Overwrite.ALL if self.parser.isSet( self.options[Option.OVER]) else Overwrite.UNSET if self.parser.isSet(self.options[Option.DEST]): self.dstDir = abspath( self.parser.value((self.options[Option.DEST]))) if not isdir(self.dstDir): print(QCoreApplication.applicationName() + ": " + _translate( "CommandLine", "The given path does not match any existing file.", "Commandline error message")) print( self._seeHelpStr.format( appName=QCoreApplication.applicationName())) sys.exit(-1) else: self.dstDir = "" def _checkFormats(self): # Test for requested output formats. If any, cli_mode is set. validOutputFormats = { Option.TIKZ, Option.PNG, Option.PDF, Option.JPEG, Option.SVG } self.requestedOutputFormats = set() for outputFormat in validOutputFormats: if self.parser.isSet(self.options[outputFormat]): self.cli_mode = True self.requestedOutputFormats.add(outputFormat) NumFormats = len(self.requestedOutputFormats) return NumFormats def _checkRecursive(self): self.requestedRecursive = self.parser.isSet(self.options[Option.REC]) def _checkRasterOptions(self): # Set the "dpi" and "quality" parameters to the values provided by user, if any # process the "dpi" option if self.parser.isSet(self.options[Option.DPI]): try: self.imageParam[Option.DPI] = int( self.parser.value(self.options[Option.DPI])) if self.imageParam[Option.DPI] not in range(25, 3001): raise Exception() except: print(QCoreApplication.applicationName() + ": " + _translate( "CommandLine", "The --dpi parameter must be an integer between 25 and 3000.", "Error message")) print( self._seeHelpStr.format( appName=QCoreApplication.applicationName())) sys.exit(-1) # Process the "quality" option if self.parser.isSet(self.options[Option.QUAL]): try: self.imageParam[Option.QUAL] = int( self.parser.value(self.options[Option.QUAL])) if self.imageParam[Option.QUAL] not in range(0, 101): raise Exception() except: print(QCoreApplication.applicationName() + ": " + _translate( "CommandLine", "The --quality parameter must be an integer between 0 and 100.", "Error message")) print( self._seeHelpStr.format( appName=QCoreApplication.applicationName())) sys.exit(-1) def parseCmdLine(self): self.parser.process(self.args) ##### FETCH OPTIONS AND ARGUMENTS # Test if "recursive" flag is set self._checkRecursive() # Check if user set some image raster parameters self._checkRasterOptions() # Test how many output formats were requested. self._checkFormats() # Test for various file options self._checkFileOptions() # Find files to process NumFiles = self._checkFiles() ##### Process CLI mode if self.cli_mode: # Is an error to call pycirkuit with a batch option and no filenames if (NumFiles == 0): print(QCoreApplication.applicationName() + ": " + _translate( "CommandLine", "Batch processing requested with no files.", "Commandline error message")) print( self._seeHelpStr.format( appName=QCoreApplication.applicationName())) sys.exit(-1) # Instantiate a processor object for this CLI session try: processor = PyCirkuitProcessor() for fileName in self.requestedFilesToProcess: processed = False print( _translate( "CommandLine", "Processing file:", "Command line message. Will be followed by an absolute file path" ), fileName) while not processed: processor.beginProcessing(fileName) for format in self.requestedOutputFormats: try: processor.requestResult( format, dstDir=self.dstDir, overwrite=self.overwrite, dpi=self.imageParam[Option.DPI], quality=self.imageParam[Option.QUAL]) except PyCktToolExecutionError as err: answer = self._askOnError(err) if answer == Decision.ABORT: # Terminate program sys.exit(-1) elif answer == Decision.SKIP: # Consider file processed and jump to the next one processed = True continue elif answer == Decision.OPEN: # Open a IGU try: run(["pycirkuit", fileName], shell=False, check=False) # Breaking here puts us out of the "for" loop and source is re-read in "beginProcesing" break except: processed = True else: processed = True print("") except PyCirkuitError as err: print("\npycirkuit:", err) sys.exit(-1) print( _translate( "CommandLine", "Files processed: {N}.", "Command line message. {N} will be an integer, don't translate it." ).format(N=NumFiles)) sys.exit(0) ##### Process GUI mode. Perform some final checks and exit. if NumFiles == 0: return None elif NumFiles == 1: return abspath(self.requestedFilesToProcess[0]) else: print(QCoreApplication.applicationName() + ": " + _translate( "CommandLine", "More than one file to process with no batch option given.", "Commandline error message")) print( self._seeHelpStr.format( appName=QCoreApplication.applicationName())) sys.exit(-1) def _askOnError(self, err): # Test if we have GUI or not, and ask accordingly print("\npycirkuit:", err) question = _translate( "CommandLine-UserInput2", "Please choose what to do: [a]bort processing, [s]kip file, [o]pen in GUI for editing: ", "WARNING!! Critical translation. You should translate this message to your language, enclosing into brackets one single DIFFERENT character for each option, and translate accordingly the characters in the next message." ) answerAbort = _translate( "CommandLine-UserInput2", "a", "WARNING!! Critical translation. This char must match one of those of the message 'Please choose what to do:'" ) answerSkip = _translate( "CommandLine-UserInput2", "s", "WARNING!! Critical translation. This char must match one of those of the message 'Please choose what to do:'" ) answerOpen = _translate( "CommandLine-UserInput2", "o", "WARNING!! Critical translation. This char must match one of those of the message 'Please choose what to do:'" ) if pycirkuit.__haveGUI__: question = _translate( "CommandLine-UserInput2", "Please choose what to do: [a]bort processing, [s]kip file, [o]pen in GUI for editing: ", "WARNING!! Critical translation. You should translate this message to your language, enclosing into brackets one single DIFFERENT character for each option, and translate accordingly the characters in the next message." ) answers = { answerAbort: Decision.ABORT, answerSkip: Decision.SKIP, answerOpen: Decision.OPEN } else: question = _translate( "CommandLine-UserInput2", "Please choose what to do: [a]bort processing, [s]kip file: ", "WARNING!! Critical translation. You should translate this message to your language, enclosing into brackets one single DIFFERENT character for each option, and translate accordingly the characters in the next message." ) answers = { answerAbort: Decision.ABORT, answerSkip: Decision.SKIP, } # Ask for user decision, loop until answered while True: answer = input(question) if answer.lower() in answers: return answers[answer]
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
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()
import sys app = QApplication(sys.argv) cmdParser = QCommandLineParser() cmdParser.setApplicationDescription( "Icon generator utility script for Basement Renovator. Takes an anm2 " ) cmdParser.addHelpOption() cmdParser.addPositionalArgument("file", "anm2 file to generate the icon from") frameOpt = QCommandLineOption( ["f", "frame"], "frame in the anm2 to use, defaults to 0", "f", "0" ) cmdParser.addOption(frameOpt) animOpt = QCommandLineOption( ["n", "anim"], "name of the animation in the anm2 to use, defaults to the default anim", "n", ) cmdParser.addOption(animOpt) overlayOpt = QCommandLineOption( ["o", "overlay-anim"], "name of an animation in the anm2 to use as an overlay (optional)", "o", ) cmdParser.addOption(overlayOpt)
QDir, QT_VERSION_STR, ) from PyQt5.QtWidgets import QApplication, QFileIconProvider, QFileSystemModel, QTreeView app = QApplication(sys.argv) QCoreApplication.setApplicationVersion(QT_VERSION_STR) parser = QCommandLineParser() parser.setApplicationDescription("Qt Dir View Example") parser.addHelpOption() parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption( "c", "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument("directory", "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath("") if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None:
def main(): multiprocessing.set_start_method('spawn') if markups.__version_tuple__ < (2, ): sys.exit('Error: ReText needs PyMarkups 2.0 or newer to run.') # If we're running on Windows without a console, then discard stdout # and save stderr to a file to facilitate debugging in case of crashes. if sys.executable.endswith('pythonw.exe'): sys.stdout = open(devnull, 'w') sys.stderr = open('stderr.log', 'w') try: # See https://github.com/retext-project/retext/issues/399 # and https://launchpad.net/bugs/941826 ctypes.CDLL('libGL.so.1', ctypes.RTLD_GLOBAL) except OSError: pass # Needed for Qt WebEngine on Windows QApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts) QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps) app = QApplication(sys.argv) app.setOrganizationName("ReText project") app.setApplicationName("ReText") app.setApplicationDisplayName("ReText") app.setApplicationVersion(app_version) app.setOrganizationDomain('mitya57.me') app.setDesktopFileName('me.mitya57.ReText.desktop') QNetworkProxyFactory.setUseSystemConfiguration(True) initializeDataDirs() RtTranslator = QTranslator() for path in datadirs: if RtTranslator.load('retext_' + globalSettings.uiLanguage, join(path, 'locale')): break QtTranslator = QTranslator() QtTranslator.load("qtbase_" + globalSettings.uiLanguage, QLibraryInfo.location(QLibraryInfo.LibraryLocation.TranslationsPath)) app.installTranslator(RtTranslator) app.installTranslator(QtTranslator) parser = QCommandLineParser() parser.addHelpOption() parser.addVersionOption() previewOption = QCommandLineOption('preview', QApplication.translate('main', 'Open the files in preview mode')) newWindowOption = QCommandLineOption('new-window', QApplication.translate('main', 'Create a new window even if there is an existing one')) parser.addOption(previewOption) parser.addOption(newWindowOption) parser.addPositionalArgument('files', QApplication.translate('main', 'List of files to open'), '[files...]') parser.process(app) filesToOpen = parser.positionalArguments() print('Using configuration file:', settings.fileName()) if globalSettings.appStyleSheet: sheetfile = QFile(globalSettings.appStyleSheet) sheetfile.open(QIODevice.OpenModeFlag.ReadOnly) app.setStyleSheet(QTextStream(sheetfile).readAll()) sheetfile.close() window = ReTextWindow() openInExistingWindow = (globalSettings.openFilesInExistingWindow and not parser.isSet(newWindowOption)) connection = QDBusConnection.sessionBus() if connection.isConnected() and openInExistingWindow: connection.registerObject('/', window, QDBusConnection.RegisterOption.ExportAllSlots) serviceName = 'me.mitya57.ReText' if not connection.registerService(serviceName) and filesToOpen: print('Opening the file(s) in the existing window of ReText.') iface = QDBusInterface(serviceName, '/', '', connection) for fileName in filesToOpen: iface.call('openFileWrapper', fileName) qWidgetIface = QDBusInterface(serviceName, '/', 'org.qtproject.Qt.QWidget', connection) qWidgetIface.call('raise') sys.exit(0) window.show() # ReText can change directory when loading files, so we # need to have a list of canonical names before loading fileNames = list(map(canonicalize, filesToOpen)) readStdIn = False if globalSettings.openLastFilesOnStartup: window.restoreLastOpenedFiles() for fileName in fileNames: if QFile.exists(fileName): window.openFileWrapper(fileName) if parser.isSet(previewOption): window.actionPreview.setChecked(True) window.preview(True) elif fileName == '-': readStdIn = True inputData = '' if readStdIn and sys.stdin is not None: if sys.stdin.isatty(): print('Reading stdin, press ^D to end...') inputData = sys.stdin.read() if inputData or not window.tabWidget.count(): window.createNew(inputData) signal.signal(signal.SIGINT, lambda sig, frame: window.close()) sys.exit(app.exec())