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.')
Example #2
0
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
Example #3
0
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_())
Example #5
0
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())
Example #6
0
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())
Example #7
0
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()
Example #8
0
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
    }
Example #9
0
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_())
Example #10
0
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!"))
Example #12
0
        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:
Example #13
0
        '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)
Example #14
0
    - 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
Example #15
0
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)
Example #16
0
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)
Example #17
0
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]
Example #18
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
Example #19
0
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)
Example #21
0
    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:
Example #22
0
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())