Пример #1
0
    def getProfiles(shortGameName, zEditInstallFolder) -> List[Merges]:
        """ This returns the content of each profiles 'merges.json' """

        result = []
        zEditProfileDir = QDir(zEditInstallFolder + "/" +
                               ZEditConfig.RELATIVE_PROFILE_DIR)
        if not zEditProfileDir.exists():
            qDebug("Profiles path does not exist: {}".format(
                zEditProfileDir.absolutePath()))
            return result

        profiles = ZEditConfig.parseProfileList(shortGameName)
        for name in profiles:
            relName = name + "/merges.json"
            if not zEditProfileDir.exists(relName):
                continue
            try:
                filePath = zEditProfileDir.absoluteFilePath(relName)
                with open(filePath) as f:
                    m = Merges(json.load(f))
                    m.profileName = name
                    m.profilePath = filePath
                    result.append(m)
            except ValueError as ex:
                qWarning('Invalid file "{}": {}'.format(filePath, str(ex)))
        return result
Пример #2
0
    def __load(self):
        """
        Private slot to load the available scripts into the manager.
        """
        scriptsDir = QDir(self.scriptsDirectory())
        if not scriptsDir.exists():
            scriptsDir.mkpath(self.scriptsDirectory())

        if not scriptsDir.exists("requires"):
            scriptsDir.mkdir("requires")

        self.__disabledScripts = Preferences.getWebBrowser(
            "GreaseMonkeyDisabledScripts")

        from .GreaseMonkeyScript import GreaseMonkeyScript
        for fileName in scriptsDir.entryList(["*.js"], QDir.Files):
            absolutePath = scriptsDir.absoluteFilePath(fileName)
            script = GreaseMonkeyScript(self, absolutePath)

            if not script.isValid():
                del script
                continue

            self.__scripts.append(script)

            if script.fullName() in self.__disabledScripts:
                script.setEnabled(False)
            else:
                collection = WebBrowserWindow.webProfile().scripts()
                collection.insert(script.webScript())

        self.__jsObject.setSettingsFile(
            os.path.join(Utilities.getConfigDir(), "web_browser",
                         "greasemonkey_values.ini"))
        ExternalJsObject.registerExtraObject("GreaseMonkey", self.__jsObject)
Пример #3
0
    def copyFiles(self, src, dst, names=None, overwrite=True):
        """ Copy files from src directory to dst directory. Subfolders are ignored.
        If names is provided, copies only files matching those names. Symlinks are not
        copied """
        moDebug("Copying files from {} to {}. Names: {}".format(
            src, dst, names))
        srcDir = QDir(src, "", QDir.IgnoreCase, QDir.Files | QDir.NoSymLinks)
        dstDir = QDir(dst)

        if not srcDir.exists() or not dstDir.exists():
            moWarn("Failed to copy files with non-existant directories")
            return False

        success = True
        if names:
            srcDir.setNameFilters(names)
        fileNames = srcDir.entryList()
        for name in fileNames:
            if dstDir.exists(name):
                if not overwrite:
                    continue
                dstDir.remove(name)
            success = QFile.copy(srcDir.filePath(name), dstDir.filePath(name))
            if not success:
                moWarn('Failed to copy "{}" from "{}" to "{}"'.format(
                    name, src, dst))
        return success
Пример #4
0
 def __load(self):
     """
     Private slot to load the available scripts into the manager.
     """
     scriptsDir = QDir(self.scriptsDirectory())
     if not scriptsDir.exists():
         scriptsDir.mkpath(self.scriptsDirectory())
     
     if not scriptsDir.exists("requires"):
         scriptsDir.mkdir("requires")
     
     self.__disabledScripts = \
         Preferences.getHelp("GreaseMonkeyDisabledScripts")
     
     from .GreaseMonkeyScript import GreaseMonkeyScript
     for fileName in scriptsDir.entryList(["*.js"], QDir.Files):
         absolutePath = scriptsDir.absoluteFilePath(fileName)
         script = GreaseMonkeyScript(self, absolutePath)
         
         if script.fullName() in self.__disabledScripts:
             script.setEnabled(False)
         
         if script.startAt() == GreaseMonkeyScript.DocumentStart:
             self.__startScripts.append(script)
         else:
             self.__endScripts.append(script)
Пример #5
0
    def loadProfile(shortGameName, profileName, zEditInstallFolder) -> Merges:
        zEditProfileDir = QDir(zEditInstallFolder + "/" +
                               ZEditConfig.RELATIVE_PROFILE_DIR)
        if not zEditProfileDir.exists():
            moWarn("Profiles path does not exist: {}".format(
                zEditProfileDir.absolutePath()))
            return

        profiles = ZEditConfig.parseProfileList(shortGameName)
        for name in profiles:
            if name == profileName:
                relName = name + "/merges.json"
                if not zEditProfileDir.exists(relName):
                    moWarn('Profile "{}" does not have a "merges.json" file.'.
                           format(name))
                    return
                try:
                    filePath = zEditProfileDir.absoluteFilePath(relName)
                    with open(filePath) as f:
                        m = Merges(json.load(f))
                        m.profileName = name
                        m.profilePath = filePath
                        return m
                except ValueError as ex:
                    moWarn(
                        'Failed to read zEdit profile. Invalid file: "{}": {}'.
                        format(filePath, str(ex)))
        moError('Profile "{}" was not found'.format(profileName))
        return Merges()
Пример #6
0
    def __load(self):
        """
        Private slot to load the available scripts into the manager.
        """
        scriptsDir = QDir(self.scriptsDirectory())
        if not scriptsDir.exists():
            scriptsDir.mkpath(self.scriptsDirectory())

        if not scriptsDir.exists("requires"):
            scriptsDir.mkdir("requires")

        self.__disabledScripts = \
            Preferences.getHelp("GreaseMonkeyDisabledScripts")

        from .GreaseMonkeyScript import GreaseMonkeyScript
        for fileName in scriptsDir.entryList(["*.js"], QDir.Files):
            absolutePath = scriptsDir.absoluteFilePath(fileName)
            script = GreaseMonkeyScript(self, absolutePath)

            if script.fullName() in self.__disabledScripts:
                script.setEnabled(False)

            if script.startAt() == GreaseMonkeyScript.DocumentStart:
                self.__startScripts.append(script)
            else:
                self.__endScripts.append(script)
Пример #7
0
def findStampFileName(name, currentFileName=''):
    invalidChars = QRegularExpression("[^\\w -]+")
    prefs = preferences.Preferences.instance()
    stampsDir = QDir(prefs.stampsDirectory())
    suggestedFileName = name.toLower().remove(invalidChars)
    fileName = suggestedFileName + ".stamp"
    if (fileName == currentFileName or not stampsDir.exists(fileName)):
        return fileName
    n = 2
    while (fileName != currentFileName and stampsDir.exists(fileName)):
        fileName = suggestedFileName + QString.number(n) + ".stamp"
        n += 1
    
    return fileName
Пример #8
0
def findStampFileName(name, currentFileName=''):
    invalidChars = QRegularExpression("[^\\w -]+")
    prefs = preferences.Preferences.instance()
    stampsDir = QDir(prefs.stampsDirectory())
    suggestedFileName = name.toLower().remove(invalidChars)
    fileName = suggestedFileName + ".stamp"
    if (fileName == currentFileName or not stampsDir.exists(fileName)):
        return fileName
    n = 2
    while (fileName != currentFileName and stampsDir.exists(fileName)):
        fileName = suggestedFileName + QString.number(n) + ".stamp"
        n += 1

    return fileName
Пример #9
0
def validate_project(project_dir):
    selected_dir = QDir(project_dir)

    if not selected_dir.exists():
        QMessageBox.critical(None, 'Dir does not  exist', 'Dir does not  exist',
                             QMessageBox.Yes, QMessageBox.Yes)
        return False

    if not selected_dir.exists('src/global.config'):
        QMessageBox.critical(None, 'Project validation error', 'Project dir does not contain src/global.config',
                             QMessageBox.Yes, QMessageBox.Yes)
        return False

    return True
Пример #10
0
 def createBackupFolder(self, profileName: str):
     """ Create a backup folder in the profile. The profile must exist """
     profileDir = QDir(self.profilePath(profileName))
     if not profileDir.exists():
         raise ValueError(
             "Attempted to create backup folder in non-existant profile: {}"
             .format(profileName))
     if profileDir.exists(self.PROFILE_DIR):
         return True
     if not profileDir.mkdir(self.PROFILE_DIR):
         moWarn("Failed to create backup directory in profile: {}".format(
             profileName))
         return False
     return True
Пример #11
0
    def requireScripts(self, urlList):
        """
        Public method to get the sources of all required scripts.
        
        @param urlList list of URLs (list of string)
        @return sources of all required scripts (string)
        """
        requiresDir = QDir(self.requireScriptsDirectory())
        if not requiresDir.exists() or len(urlList) == 0:
            return ""

        script = ""

        settings = QSettings(
            os.path.join(self.requireScriptsDirectory(), "requires.ini"),
            QSettings.IniFormat)
        settings.beginGroup("Files")
        for url in urlList:
            if settings.contains(url):
                fileName = settings.value(url)
                try:
                    f = open(fileName, "r", encoding="utf-8")
                    source = f.read()
                    f.close()
                except (IOError, OSError):
                    source = ""
                script += source.strip() + "\n"

        return script
Пример #12
0
    def accept(self) -> None:
        """Checks if a specified directory is empty. If it isn't warns the user. If the directory doesn't exist attempts
        to create it"""

        accept = True
        path = self.ui.LocationLineEdit.text()
        directory = QDir(path)

        if directory.exists():
            if not directory.isEmpty():
                reply = QMessageBox.question(self, "Create project?", "The directory you specified is not empty." +
                                             "Are you sure that you want to create the project there?",
                                             QMessageBox.Yes | QMessageBox.No)
                if reply == QMessageBox.No:
                    accept = False
        else:
            try:
                directory.mkpath(self.ui.LocationLineEdit.text())
            except OSError as e:
                accept = False
                QMessageBox.critical(self, "Error", f"Failed to create project folder: {str(e)}")

        self.path = self.ui.LocationLineEdit.text()
        self.authors = self.ui.AuthoursLineEdit.text()
        self.project_type = self.ui.ProjectTypeComboBox.currentText()
        self.title = self.ui.TitleLineEdit.text()
        if accept:
            super().accept()
Пример #13
0
 def initUndoRedo(self):
     self.undoStack = QUndoStack()
     temp = QDir(os.path.join(QDir.tempPath(), "FlatSiteBuilder"))
     if temp.exists():
         temp.removeRecursively()
     temp.setPath(QDir.tempPath())
     temp.mkdir("FlatSiteBuilder")
Пример #14
0
 def onMapsDirectoryChanged(self):
     prefs = preferences.Preferences.instance()
     mapsDir = QDir(prefs.mapsDirectory())
     if (not mapsDir.exists()):
         mapsDir.setPath(QDir.currentPath())
     self.model().setRootPath(mapsDir.canonicalPath())
     self.setRootIndex(self.model().index(mapsDir.absolutePath()))
Пример #15
0
 def initUndoRedo(self):
     self.undoStack = QUndoStack()
     temp = QDir(QDir.tempPath() + "/AppBuilder")
     if temp.exists():
         temp.removeRecursively()
     temp.setPath(QDir.tempPath())
     temp.mkdir("AppBuilder")
Пример #16
0
def file_less_suffix(FBTS):
    qd = QDir( FBTS.folderpath() )
    qd.setFilter(QDir.Files | QDir.Readable)
    if qd.exists(FBTS.basename()):
        a_file = QFile( qd.absoluteFilePath(FBTS.basename()) )
        return _qfile_to_stream(a_file, FBTS.open_mode())
    return None
Пример #17
0
    def loadInImageViewer(self, path):
        dir = QDir(path)
        if dir.exists():
            entryList = dir.entryList(['*.jpg'], QDir.Files)
            if len(entryList) > 0:
                # load in thumb viewer
                # QMessageBox.information(None, u"Begehung", u",".join(entryList))
                imagePathList = []
                for image in entryList:
                    imagePathList.append(path + u'\\' + image)

                widget = APISThumbViewer()
                widget.load(imagePathList)
                widget.show()
                if widget.exec_():
                    pass
                    # app.exec_()
            else:
                QMessageBox.information(
                    self, u"Begehung",
                    u"Es wurden keine Dateien [*.jpg] für diesen Fundort gefunden."
                )
        else:
            QMessageBox.information(
                self, u"Begehung",
                u"Das Verzeichnis '{0}' wurde nicht gefunden.".format(path))
Пример #18
0
    def __init__(self):
        QMainWindow.__init__(self)
        self.site = None
        self.editor = ""
        self.install_directory = os.getcwd()
        self.content_after_animation = ""
        self.default_path = ""
        self.method_after_animation = ""

        Generator.install_directory = self.install_directory

        self.initUndoRedo()
        self.initGui()
        self.readSettings()
        self.loadPlugins()

        if self.default_path:
            if self.loadProject(self.default_path + "/Site.qml"):

                # if site has never been generated (after install)
                # then generate the site
                site = QDir(Generator.sitesPath() + "/" + self.site.title)
                if site.exists():
                    gen = Generator()
                    gen.generateSite(self, self.site)

        self.dashboard.setExpanded(True)
        self.showDashboard()
        self.statusBar().showMessage("Ready")
Пример #19
0
	def Seal(dir_path):
		"""
Seal a folder: count recursively MD5 checksum, write it to all.md5, then make the folder read-only.
Shell function, need package md5deep.
"""
		METHOD_NAME = f"Blister.Seal"
		qdir = QDir(dir_path)
		if not qdir.exists():
			print(f"{METHOD_NAME}: Path doesn't exist or isn't a dir.", end='\n')
			return False
		qfiles = QFileInfo(qdir, "**/*")
		file_list = glob.glob(qfiles.absoluteFilePath(), recursive=True)
		for _file in file_list:
			fileinfo = QFileInfo(_file)
			if not fileinfo.isReadable():
				print(f"{METHOD_NAME}: There is input cannot be checked (unreadable files). Stopped.", end='\n')
				return False
		start_time = time.time()
		sp = subprocess.Popen(f"cd {qdir.absolutePath()}; (md5deep -lr * > all.md5); chmod 555 -R {qdir.absolutePath()}", shell=True, stderr=subprocess.PIPE)
		out, err = sp.communicate()
		if err != b'':
			print(f"{METHOD_NAME}: Shell error: {str(err)}", end='\n')
			return False
		print(f"{METHOD_NAME}: Dir was sealed [%s]:\n\t{qdir.absolutePath()}" % (Blister.SecToTime(time.time() - start_time)), end='\n')
		return True
Пример #20
0
	def getExportExtensionsList(self):
		extensions = []
		for extsprefix in datadirs:
			extsdir = QDir(extsprefix+'/export-extensions/')
			if extsdir.exists():
				for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'],
				QDir.Files | QDir.Readable):
					extensions.append(self.readExtension(fileInfo.filePath()))
		locale = QLocale.system().name()
		self.extensionActions = []
		for extension in extensions:
			try:
				if ('Name[%s]' % locale) in extension:
					name = extension['Name[%s]' % locale]
				elif ('Name[%s]' % locale.split('_')[0]) in extension:
					name = extension['Name[%s]' % locale.split('_')[0]]
				else:
					name = extension['Name']
				data = {}
				for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
					if 'X-ReText-'+prop in extension:
						data[prop] = extension['X-ReText-'+prop]
					elif prop in extension:
						data[prop] = extension[prop]
					else:
						data[prop] = ''
				action = self.act(name, trig=self.extensionFunction(data))
				if 'Icon' in extension:
					action.setIcon(self.actIcon(extension['Icon']))
				mimetype = extension['MimeType'] if 'MimeType' in extension else None
			except KeyError:
				print('Failed to parse extension: Name is required', file=sys.stderr)
			else:
				self.extensionActions.append((action, mimetype))
Пример #21
0
 def new_campaign(self):
     dialog = QFileDialog(self)
     dialog.setDirectory(helper.one_up(self.current_dir))
     dialog.setAcceptMode(QFileDialog.AcceptSave)
     dialog.setFileMode(QFileDialog.AnyFile)
     dialog.setOption(QFileDialog.ShowDirsOnly)
     dialog.setWindowTitle('Select folder name')
     if dialog.exec_():
         directory = dialog.selectedFiles()[0]
         qdir = QDir(directory)
         if not qdir.exists():
             self.current_dir = qdir.path()
             for folder_name in resource.folders:
                 qdir.mkpath('./' + folder_name)
             helper.save_json_data(
                 '{}/{}/health{}'.format(self.current_dir,
                                         resource.health_stat.folder,
                                         resource.health_stat.ext),
                 resource.health_stat.default)
             qdir.mkpath('./.settings/std')
             qdir.mkpath('./.settings/debug')
             resource.create_config_files(self.current_dir, qdir.dirName())
             self.refresh_tree_view()
         else:
             helper.display_error('Directory for campaign already exists.'
                                  )  # This shouldn't happen
Пример #22
0
    def scan_for_csl_styles(self) -> dict:
        """Scans <app data>/csl_styles subdirectories for csl styles. Returns a dictionary with information about the
        styles that were found"""

        # Get all files in the csl_styles directory
        path = self.get_appdata_path() + "/csl_styles"
        directory = QDir(path)

        # Create directory if it doesn't exist
        if not directory.exists():
            try:
                directory.mkpath(path)
            except Exception:
                print(f"Failed to create path: {path}")
                return dict()

        entries = directory.entryList(QDir.Files)

        # Iterate over files
        styles = dict()
        for entry in entries:
            if entry.endswith(".csl"):
                filepath = path + "/" + entry
                title = self.extract_csl_style_name(filepath)
                identifier = common.generate_identifier(title)
                if title is not None:
                    styles[identifier] = {"name": title, "path": filepath}

        return styles
Пример #23
0
    def parseProfileList(shortGameName: str):
        # the profile list is stored in Roaming/zEdit/profiles.json
        result = []
        gameMode = ZEditConfig.getZEditGameMode(shortGameName)
        if gameMode is None:
            qWarning("Game type is not supported by zMerge.")
            return result

        appData = QDir(
            QStandardPaths.writableLocation(QStandardPaths.AppDataLocation))
        profilesPath = appData.absoluteFilePath(
            ZEditConfig.RELATIVE_ZEDIT_PROFILES_FILE)
        if not appData.exists(ZEditConfig.RELATIVE_ZEDIT_PROFILES_FILE):
            qDebug('"{}" does not exist'.format(profilesPath))
            return result

        try:
            file = open(profilesPath, "r", encoding="utf8")
            profiles = json.load(file)
            for profile in profiles:
                if profile["gameMode"] == gameMode:
                    result.append(profile["name"])

        except OSError:
            qWarning('Failed to read "profiles.json".')
            return []
        except (TypeError, ValueError):
            qWarning('"profiles.json" has unknown file structure.')
        return result
Пример #24
0
    def initUI(self):

        self.setStyleSheet('background-color: silver')
        image_folder = QDir('images')

        if not image_folder.exists():
            logging.error('Image foulder not found')
            sys.exit(1)

        self.layout_h = QHBoxLayout()

        self.scheduler = MasterTool(self, 'BinanceSched', 1)
        self.scheduler.setPixmap(
            QPixmap('images/BinanceSched.png').scaled(120, 60))

        self.ohlc = MasterTool(self, 'BinanceOHLC', 1)
        self.ohlc.setPixmap(QPixmap('images/BinanceOHLC.png').scaled(120, 60))

        self.order = MasterTool(self, 'BinanceOrder', 1)
        self.order.setPixmap(
            QPixmap('images/BinanceOrder.png').scaled(120, 60))

        self.layout_h.addWidget(self.scheduler)
        self.layout_h.addWidget(self.ohlc)
        self.layout_h.addWidget(self.order)
        self.layout_h.addStretch(1)

        self.setLayout(self.layout_h)
Пример #25
0
	def getExportExtensionsList(self):
		extensions = []
		for extsprefix in datadirs:
			extsdir = QDir(extsprefix+'/export-extensions/')
			if extsdir.exists():
				for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'],
				QDir.Files | QDir.Readable):
					extensions.append(self.readExtension(fileInfo.filePath()))
		locale = QLocale.system().name()
		self.extensionActions = []
		for extension in extensions:
			try:
				if ('Name[%s]' % locale) in extension:
					name = extension['Name[%s]' % locale]
				elif ('Name[%s]' % locale.split('_')[0]) in extension:
					name = extension['Name[%s]' % locale.split('_')[0]]
				else:
					name = extension['Name']
				data = {}
				for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
					if 'X-ReText-'+prop in extension:
						data[prop] = extension['X-ReText-'+prop]
					elif prop in extension:
						data[prop] = extension[prop]
					else:
						data[prop] = ''
				action = self.act(name, trig=self.extensionFunction(data))
				if 'Icon' in extension:
					action.setIcon(self.actIcon(extension['Icon']))
				mimetype = extension['MimeType'] if 'MimeType' in extension else None
			except KeyError:
				print('Failed to parse extension: Name is required', file=sys.stderr)
			else:
				self.extensionActions.append((action, mimetype))
Пример #26
0
 def requireScripts(self, urlList):
     """
     Public method to get the sources of all required scripts.
     
     @param urlList list of URLs (list of string)
     @return sources of all required scripts (string)
     """
     requiresDir = QDir(self.requireScriptsDirectory())
     if not requiresDir.exists() or len(urlList) == 0:
         return ""
     
     script = ""
     
     settings = QSettings(
         os.path.join(self.requireScriptsDirectory(), "requires.ini"),
         QSettings.IniFormat)
     settings.beginGroup("Files")
     for url in urlList:
         if settings.contains(url):
             fileName = settings.value(url)
             try:
                 f = open(fileName, "r", encoding="utf-8")
                 source = f.read()
                 f.close()
             except (IOError, OSError):
                 source = ""
             script += source.strip() + "\n"
     
     return script
Пример #27
0
def file_less_suffix(FBTS):
    qd = QDir(FBTS.folderpath())
    qd.setFilter(QDir.Files | QDir.Readable)
    if qd.exists(FBTS.basename()):
        a_file = QFile(qd.absoluteFilePath(FBTS.basename()))
        return _qfile_to_stream(a_file, FBTS.open_mode())
    return None
Пример #28
0
	def __init__(self, parent=None):
		super(MainApp, self).__init__(parent)
		self.setupUi(self)
		
		#init statusbar
		self.setStyleSheet("QStatusBar::item { border: 0px solid black }; ")
		self.statusBar = QLabel()
		self.statusbar.addWidget(self.statusBar)
		self.statusBar.setText("No configuration file loaded.")
		
		#object variables
		self.currentPlugin = False
		self.pluginContainer = None
		self.selectedElement = None
		self.unsavedContent = False
		self.editorContainer = None
		self.ignoreWidgetsToSettingsData = False
		self.fileHandler = None
		self.lastFile = ""
		
		#buttons
		self.button_add.setEnabled(False)
		self.button_del.setEnabled(False)
		
		#signals
		self.actionLoad_Configuration.triggered.connect(self.loadFileDialog)
		self.actionSave_Configuration.triggered.connect(self.saveFile)
		self.actionSave_Configuration_File_As.triggered.connect(self.saveFileAs)
		self.actionNew_Configuration_File.triggered.connect(self.newFile)
		self.actionExit.triggered.connect(self.closeEventButton)
		self.actionAbout_This_Program.triggered.connect(self.showAboutWidget)
		self.button_add.clicked.connect(self.showAddSettingWidget)
		self.button_del.clicked.connect(self.deleteSetting)
		
		#treemodel
		self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
		self.treeView.customContextMenuRequested.connect(self.treeViewOpenMenu)
		self.treeView.mousePressEvent = MethodType(mousePressEvent, self.treeView)
		self.activeModel = SettingTree(self)
		self.proxyModel = FilterProxyModel(self)
		self.proxyModel.setSourceModel(self.activeModel)
		self.treeView.setModel(self.proxyModel)
		self.treeView.setColumnHidden(1, True)
		self.completeModel = SettingTree(HeaderData(True), self)
		
		#plugin content
		self.contentXML = None
		
		#icon
		self.icon = ""
		dataDir = QDir(os.path.dirname(os.path.realpath(__file__)))
		dataDir.cd("data")
		if dataDir.exists("icon.png"):
			self.icon = dataDir.absoluteFilePath("icon.png")
			self.setWindowIcon(QIcon(self.icon))
			
		#programm configuration for window size and last filename
		self.programConfig = ProgramConfiguration(self)
		self.programConfig.loadConfiguration()
Пример #29
0
    def get_cwd():
        """Get the current working directory (default for file open and save-as)"""
        settings = QSettings()
        cwd = QDir(settings.value(Settings.CWD, defaultValue=Settings.CWD_DFLT))

        if not cwd.exists():
            cwd = QDir(str(Path.home()))

        return cwd.canonicalPath()  # resolve symlinks and relative paths
Пример #30
0
def removeDir(dirName):
    d = QDir(dirName)
    if d.exists():
        for info in d.entryInfoList(QDir.Dirs | QDir.Files | QDir.NoDotAndDotDot):
            if info.isDir():
                removeDir(info.absoluteFilePath())
            else:
                d.remove(info.fileName())
        d.rmdir(dirName)
Пример #31
0
    def tempDirCreate(self, basedir, name=None):
        tmpdir = QDir(basedir)

        if not tmpdir.exists():
            return

        uid = name if name else str(uuid.uuid4())

        path = tmpdir.absoluteFilePath(uid)
        if tmpdir.mkpath(path):
            return path
Пример #32
0
 def run(self):
     watch_dir = QDir(self.path)
     if self.path and watch_dir.exists():
         self.running = True
         event_handler = Handler()
         event_handler.store = self.store
         self.observer.schedule(event_handler, self.path, recursive=True)
         self.observer.start()
     else:
         Logger.Logger().debug(
             'Error no path set, or path is invalid {}'.format(self.path))
Пример #33
0
def getSettingsFile():
    appPath = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
    appDir = QDir(appPath)
    if not appDir.exists():
        logger.debug('Creating app directory')
        appDir.mkpath(appDir.absolutePath())
    else:
        logger.debug('App directory already exists')
    if appDir.setCurrent(appDir.absolutePath()):
        logger.debug('Changing cwd to app data directory')
    else:
        logger.debug('Changing cwd to app data directory failed')
    return QFile('settings.json')
Пример #34
0
    def on_btnDir_exists_clicked(self):
        self.__showBtnInfo(self.sender())
        sous = self.ui.editDir.text()
        ##      if sous=="":
        ##         self.ui.textEdit.appendPlainText("请先选择一个目录")
        ##         return

        dirObj = QDir(sous)  #若sous为空,则使用其当前目录
        self.ui.textEdit.appendPlainText(dirObj.absolutePath() + "\n")
        if dirObj.exists():
            self.ui.textEdit.appendPlainText("True \n")
        else:
            self.ui.textEdit.appendPlainText("False \n")
Пример #35
0
 def getpath(self):
     self.tableWidget.setRowCount(0)
     path = self.lineEdit.text()
     if path != "":
         dir = QDir()
         if not dir.exists(path):
             dir.mkdir(path)
         dir = QDir(path)
         flag = 0
         for d in dir.entryList(QDir.Dirs | QDir.NoDotAndDotDot):
             self.tableWidget.insertRow(flag)
             self.tableWidget.setItem(flag, 0, QtWidgets.QTableWidgetItem(os.path.join(path, d)))
             flag += 1
Пример #36
0
def preloadMimeIcons():
    icons = {}
    mIconsDir = QDir(':/share/icons/mimetypes')

    if not mIconsDir.exists():
        return icons

    entries = mIconsDir.entryList()

    for entry in entries:
        mType = entry.replace('.png', '').replace('-', '/', 1)
        icons[mType] = getIcon('mimetypes/{}'.format(entry))

    return icons
Пример #37
0
    def Successful_Tmp(self):# CheckTmpDir, StateTmpDir
        failed = lambda: not self.tdir or not self.tdir.isValid()
        if failed():# not successfully
            self.tdir = QTemporaryDir()
            if failed():
                QMessageBox.critical(self, "Unexpected Failurer", "The application has detected a problem trying to\nCreate or Access a Temporary File!.")
                return None
            else:
                self.tdir.setAutoRemove(True)

        d = QDir(self.tdir.path())
        if not d.exists("ninja-ide"):
            if not d.mkdir("ninja-ide"):
                self.tdir = None
        d.cd("ninja-ide")
        return d
Пример #38
0
    def __init__(self, mainWindow, parent = None):
        super().__init__(parent)
        self.mMainWindow = mainWindow

        self.setRootIsDecorated(False)
        self.setHeaderHidden(True)
        self.setItemsExpandable(False)
        self.setUniformRowHeights(True)
        self.setDragEnabled(True)
        self.setDefaultDropAction(Qt.MoveAction)
        prefs = preferences.Preferences.instance()
        prefs.mapsDirectoryChanged.connect(self.onMapsDirectoryChanged)
        mapsDir = QDir(prefs.mapsDirectory())
        if (not mapsDir.exists()):
            mapsDir.setPath(QDir.currentPath())
        self.mFSModel = FileSystemModel(self)
        self.mFSModel.setRootPath(mapsDir.absolutePath())

        nameFilters = QStringList("*.tmx")
        # The file system model name filters are plain, whereas the plugins expose
        # a filter as part of the file description
        filterFinder = QRegExp("\\((\\*\\.[^\\)\\s]*)")
        for format in PluginManager.objects(MapFormat):
            if not (format.capabilities() & MapFormat.Read):
                continue

            filter = format.nameFilter()
            if (filterFinder.indexIn(filter) != -1):
                nameFilters.append(filterFinder.cap(1))

        self.mFSModel.setFilter(QDir.AllDirs | QDir.Files | QDir.NoDot)
        self.mFSModel.setNameFilters(nameFilters)
        self.mFSModel.setNameFilterDisables(False) # hide filtered files
        self.setModel(self.mFSModel)
        headerView = self.header()
        headerView.hideSection(1) # Size column
        headerView.hideSection(2)
        headerView.hideSection(3)
        self.setRootIndex(self.mFSModel.index(mapsDir.absolutePath()))
        self.header().setStretchLastSection(False)
        self.header().setSectionResizeMode(0, QHeaderView.Stretch)
        self.activated.connect(self.onActivated)

        self.mMainWindow = None
        self.mFSModel = None
Пример #39
0
 def saveStamp(self, stamp):
     # make sure we have a stamps directory
     prefs = preferences.Preferences.instance()
     stampsDirectory = prefs.stampsDirectory()
     stampsDir = QDir(stampsDirectory)
     if (not stampsDir.exists() and not stampsDir.mkpath(".")):
         qDebug("Failed to create stamps directory" + stampsDirectory)
         return
     
     filePath = stampsDir.filePath(stamp.fileName())
     file = QSaveFile(filePath)
     if (not file.open(QIODevice.WriteOnly)):
         qDebug("Failed to open stamp file for writing" + filePath)
         return
     
     stampJson = stamp.toJson(QFileInfo(filePath).dir())
     file.write(QJsonDocument(stampJson).toJson(QJsonDocument.Compact))
     if (not file.commit()):
         qDebug() << "Failed to write stamp" << filePath
Пример #40
0
    def _scan(self, _):
        """ Invoked when the user clicks on the scan button. """

        project = self.project
        package = self.package

        # Get the root directory to scan.
        root = self.get_root_dir()
        if root == '':
            return

        # Save the included state of any existing contents so that they can be
        # restored after the scan.
        old_state = {}

        for itm in self._get_items():
            rel_path = [itm.data(0, Qt.DisplayRole)]

            parent = itm.parent()
            while parent is not None:
                rel_path.append(parent.data(0, Qt.DisplayRole))
                parent = parent.parent()

            rel_path.reverse()

            if self._show_root:
                rel_path = rel_path[1:]

            old_state['/'.join(rel_path)] = (itm.checkState(0) == Qt.Checked)

        # Walk the package.
        root_dir = QDir(root)
        if not root_dir.exists():
            QMessageBox.warning(self.parentWidget(), "Scan Directory",
                    "{0} is not a valid directory.".format(
                            QDir.toNativeSeparators(root)))
            return

        self._add_to_container(package, root_dir, [], old_state)
        self._visualise()

        self.package_changed.emit()
Пример #41
0
def search_dir(base_path, theme_name):
    """ Search for theme name """

    # Search each entry in this directory
    base_dir = QDir(base_path)
    for e in base_dir.entryList():
        # Path to current item
        path = base_dir.path() + "/" + e
        base_filename = e.split('.')[0]

        # If file matches theme name, return
        if base_filename == theme_name:
            return path

        # If this is a directory, search within it
        dir = QDir(path)
        if dir.exists():
            # If found below, return it
            res = search_dir(path, theme_name)
            if res:
                return res

    # If no match found in dir, return None
    return None
Пример #42
0
 def set_path(self,book_folder_path):
     book_dir = QDir(book_folder_path)
     if book_dir.exists('pngs') :
         self.png_dir = QDir(book_dir.absoluteFilePath('pngs'))
         self._enable()
         self.cursor_move()
Пример #43
0
class ImageDisplay(QWidget):
    def __init__(self, my_book, parent=None):
        super().__init__(parent)
        self.my_book = my_book
        # register metadata readers and writers
        md = my_book.get_meta_manager()
        md.register(C.MD_IZ,self._zoom_read,self._zoom_write)
        md.register(C.MD_IX,self._link_read,self._link_write)
        # Create our widgets including cursor_to_image and
        # image_to_cursor pushbuttons.
        self._uic()
        # set defaults in case no metadata
        self.cursor_to_image.setChecked(True)
        self.image_to_cursor.setChecked(False)
        self.zoom_factor = 0.25
        self.png_path = None
        # disable all widgetry until we get some metadata
        self._disable()
        # end of __init__()

    # Disable our widgets because we have no image to show.
    def _disable(self):
        self.no_image = True
        self.last_index = None # compares unequal to any
        self.pix_map = QPixmap()
        self.image = QImage()
        self.cursor_to_image.setEnabled(False)
        self.image_to_cursor.setEnabled(False)
        self.zoom_pct.setEnabled(False)
        self.zoom_to_width.setEnabled(False)
        self.zoom_to_height.setEnabled(False)
        self.image_display.setPixmap(self.gray_image)
        self.image_display.setToolTip(
            _TR('Image view tooltip',
                'Display of one scanned page (no images available)')
            )

    # Enable our widgets, we have images to show. At this time the Book
    # has definitely created an edit view and a page model.
    def _enable(self):
        self.edit_view = self.my_book.get_edit_view()
        self.editor = self.edit_view.Editor # access to actual QTextEdit
        self.page_data = self.my_book.get_page_model()
        self.cursor_to_image.setEnabled(True)
        self.image_to_cursor.setEnabled(True)
        self.zoom_to_width.setEnabled(True)
        self.zoom_to_height.setEnabled(True)
        self.image_display.setToolTip(
            _TR('Image view tooltip',
                'Display of one scanned page from the book')
            )
        self.no_image = False
        self.zoom_pct.setEnabled(True)
        # the following triggers entry to _new_zoom_pct() below
        self.zoom_pct.setValue(int(100*self.zoom_factor))

    # Metadata: read or write the {{IMAGEZOOM f}} section.
    # Parameter f should be a decimal number between 0.15 and 2.0
    # but we do not depend on text the user could edit.
    def _zoom_read(self, qts, section, vers, parm):
        try:
            z = float(parm) # throws exception on a bad literal
            if math.isnan(z) or (z < 0.15) or (z > 2.0) :
                raise ValueError
            self.zoom_factor = z
        except:
            imageview_logger.error('Invalid IMAGEZOOM "{0}" ignored'.format(parm))

    def _zoom_write(self, qts, section):
        qts << metadata.open_line(section, str(self.zoom_factor))

    # Metadata: read or write the {{IMAGELINK b}} section. The parameter should
    # be an int 0/1/2/3. Bit 0 represents the state of cursor_to_image
    # (usually 1); bit 1 represents the state of image_to_cursor (usually 0).
    def _link_read(self, qts, section, vers, parm):
        try:
            b = int(parm) # exception on a bad literal
            if (b < 0) or (b > 3) : raise ValueError
            self.cursor_to_image.setChecked( True if b & 1 else False )
            self.image_to_cursor.setChecked( True if b & 2 else False )
        except :
            imageview_logger.error('Invalid IMAGELINKING "{0}" ignored'.format(parm))

    def _link_write(self, qts, section):
        b = 0
        if self.cursor_to_image.isChecked() : b |= 1
        if self.image_to_cursor.isChecked() : b |= 2
        qts << metadata.open_line(section, str(b))

    # The Book calls here after it has loaded a book with defined page data,
    # passing the path to the folder containing the book. If we can find a
    # folder named 'pngs' in it we record that path and enable our widgets,
    # and fake a cursorMoved signal to display the current edit page.
    def set_path(self,book_folder_path):
        book_dir = QDir(book_folder_path)
        if book_dir.exists('pngs') :
            self.png_dir = QDir(book_dir.absoluteFilePath('pngs'))
            self._enable()
            self.cursor_move()

    # Come here to display or re-display an image. The last-displayed
    # page image index (if any) is in self.last_index. The desired page
    # index is passed as the argument, which may be:
    # * the same as last_index, for example on a change of zoom%. Just
    #   redisplay the current page.
    # * negative or None if the cursor is "above" the first available page or on
    #   a Page-Up keystroke. Display the gray image.
    # * greater than page_data.page_count() on a Page-Down keystroke,
    #   display the last available page.
    # If different from last_index, try to load the .png file for that
    # page. If that fails, use the gray image. Otherwise display that
    # page and save it as last_index.

    def _show_page(self, page_index):
        if page_index != self.last_index :
            self.last_index = page_index
            # change of page, see if we have a filename for it
            self.pix_map = self.gray_image # assume failure...
            im_name = self.page_data.filename(page_index)
            if im_name :
                # pagedata has a filename; of course there is no guarantee
                # such a file exists now or ever did.
                im_name += '.png'
                if self.png_dir.exists(im_name) :
                    self.image = QImage(self.png_dir.absoluteFilePath(im_name))
                    if not self.image.isNull():
                        # we loaded it ok, make a full-scale pixmap for display
                        self.pix_map = QPixmap.fromImage(self.image,Qt.ColorOnly)
        # Whether new page or not, rescale to current zoom. The .resize method
        # takes a QSize; pix_map.size() returns one, and it supports * by a real.
        self.image_display.setPixmap(self.pix_map)
        self.image_display.resize( self.zoom_factor * self.pix_map.size() )

    # Slot to receive the cursorMoved signal from the editview widget. If we
    # are in no_image state, do nothing. If the cursor_to_image switch is
    # not checked, do nothing. Else get the character position of
    # the high-end of the current edit selection, and use that to get the
    # current page index from pagedata, and pass that to _show_page.
    def cursor_move(self):
        if self.no_image : return
        if self.cursor_to_image.isChecked() :
            pos = self.editor.textCursor().selectionEnd()
            self._show_page( self.page_data.page_index(pos) )

    # Slots to receive the signals from our zoom percent and zoom-to buttons.
    # The controls are disabled while we are in no_image state, so if a signal
    # arrives, we are not in that state.
    #
    # These are strictly internal hence _names.

    # Any change in the value of the zoom % spin-box including setValue().
    def _new_zoom_pct(self,new_value):
        self.zoom_factor = self.zoom_pct.value() / 100
        self._show_page(self.last_index)

    # Set a new zoom factor (a real) and update the zoom pct spinbox.
    # Setting zoom_pct triggers a signal to _new_zoom_pct above, and
    # thence to _show_page which repaints the page at the new scale value.
    def _set_zoom_real(self,new_value):
        zoom = max(new_value, ZOOM_FACTOR_MIN)
        zoom = min(zoom, ZOOM_FACTOR_MAX)
        self.zoom_factor = zoom
        self.zoom_pct.setValue(int(100*zoom))

    # Re-implement keyPressEvent in order to provide zoom and page up/down.
    #   ctrl-plus increases the image size by 1.25
    #   ctrl-minus decreases the image size by 0.8
    #   page-up displays the next-higher page
    #   page-down displays the next-lower page

    def keyPressEvent(self, event):
        # assume we will not handle this key and clear its accepted flag
        event.ignore()
        if self.no_image or (self.last_index is None) :
            return # ignore keys until we are showing some image
        # We have images to show, check the key value.
        modkey = int( int(event.key() | (int(event.modifiers()) & C.KEYPAD_MOD_CLEAR)) )
        if modkey in C.KEYS_ZOOM :
            event.accept()
            fac = (0.8) if (modkey == C.CTL_MINUS) else (1.25)
            self._set_zoom_real( fac * self.zoom_factor)
        elif (event.key() == Qt.Key_PageUp) or (event.key() == Qt.Key_PageDown) :
            event.accept()
            pgix = self.last_index + (1 if (event.key() == Qt.Key_PageDown) else -1)
            # If not paging off either end, show that page
            if pgix >= 0 and pgix < self.page_data.page_count() :
                self._show_page(pgix)
                if self.image_to_cursor.isChecked():
                    self.edit_view.show_position(self.page_data.position(pgix))

    # Zoom to width and zoom to height are basically the same thing:
    # 1. Using the QImage of the current page in self.image,
    #    scan its pixels to find the width (height) of the nonwhite area.
    # 2. Get the ratio of that to our image label's viewport width (height).
    # 3. Set that ratio as the zoom factor and redraw the image.
    # 5. Set the scroll position(s) of our scroll area to left-justify the text.
    #
    # We get access to the pixel data using QImage.bits() which gives us a
    # "sip.voidptr" object that we can index to get byte values.
    def _zoom_to_width(self):

        # Generic loop to scan inward from the left or right edge of one
        # column inward until a dark pixel is seen, returning that margin.
        def inner_loop(row_range, col_start, margin, col_step):
            pa, pb = 255, 255 # virtual white outside column
            for row in row_range:
                for col in range(col_start, margin, col_step):
                    pc = color_table[ bytes_ptr[row+col] ]
                    if (pa + pb + pc) < 24 : # black or dark gray trio
                        margin = col # new, narrower, margin
                        break # no need to look further on this row
                    pa, pb = pb, pc # else shift 3-pixel window
            return margin - (2*col_step) # allow for 3-px window

        if self.no_image or self.image.isNull() :
            return # nothing to do
        scale_factor = 4
        orig_rows = self.image.height() # number of pixels high
        orig_cols = self.image.width() # number of logical pixels across
        # Scale the image to 1/4 size (1/16 the pixel count) and then force
        # it to indexed-8 format, one byte per pixel.
        work_image = self.image.scaled(
            QSize(int(orig_cols/scale_factor),int(orig_rows/scale_factor)),
            Qt.KeepAspectRatio, Qt.FastTransformation)
        work_image = work_image.convertToFormat(QImage.Format_Indexed8,Qt.ColorOnly)
        # Get a reduced version of the color table by extracting just the GG
        # values of each entry, as a dict keyed by the pixel byte value. For
        # PNG-2, this gives [0,255] but it could have 8, 16, even 256 elements.
        color_table = { bytes([c]): int((work_image.color(c) >> 8) & 255)
                         for c in range(work_image.colorCount()) }
        # Establish limits for the inner loop
        rows = work_image.height() # number of pixels high
        cols = work_image.width() # number of logical pixels across
        stride = (cols + 3) & (-4) # scan-line width in bytes
        bytes_ptr = work_image.bits() # uchar * a_bunch_o_pixels
        bytes_ptr.setsize(stride * rows) # make the pointer indexable

        # Scan in from left and from right to find the outermost dark spots.
        # Pages tend to start with many lines of white pixels so in hopes of
        # establishing a narrow margin quickly, scan from the middle to the
        # end, then do the top half.
        left_margin = inner_loop(
                        range(int(rows/2)*stride, (rows-1)*stride, stride*2),
                        0, int(cols/2), 1
        )
        left_margin = inner_loop(
                        range(0, int(rows/2)*stride, stride*2),
                        0, left_margin, 1
                        )
        # Now do exactly the same but for the right margin.
        right_margin = inner_loop(
                        range(int(rows/2)*stride, (rows-1)*stride, stride*2),
                        cols-1, int(cols/2), -1
                        )
        right_margin = inner_loop(
                        range(0, int(rows/2)*stride, stride*2),
                        cols-1, right_margin, -1
                        )
        # Adjust the margins by the scale factor to fit the full size image.
        #left_margin = max(0,left_margin*scale_factor-scale_factor)
        #right_margin = min(orig_cols,right_margin*scale_factor+scale_factor)
        left_margin = left_margin*scale_factor
        right_margin = right_margin*scale_factor
        text_size = right_margin - left_margin + 2
        port_width = self.scroll_area.viewport().width()
        # Set the new zoom factor, after limiting by min/max values
        self._set_zoom_real(port_width/text_size)
        # Set the scrollbar to show the page from its left margin.
        self.scroll_area.horizontalScrollBar().setValue(
                             int( left_margin * self.zoom_factor)
                         )
        # and that completes zoom-to-width

    def _zoom_to_height(self):
        def dark_row(row_start, cols):
            '''
            Scan one row of pixels and return True if it contains
            at least one 3-pixel blob of darkness, or False if not.
            '''
            pa, pb = 255, 255
            for c in range(row_start,row_start+cols):
                pc = color_table[ bytes_ptr[c] ]
                if (pa + pb + pc) < 24 : # black or dark gray trio
                    return True
                pa, pb = pb, pc
            return False # row was all-white-ish

        if self.no_image or self.image.isNull() :
            return # nothing to do
        scale_factor = 4
        orig_rows = self.image.height() # number of pixels high
        orig_cols = self.image.width() # number of logical pixels across
        # Scale the image to 1/4 size (1/16 the pixel count) and then force
        # it to indexed-8 format, one byte per pixel.
        work_image = self.image.scaled(
            QSize(int(orig_cols/scale_factor),int(orig_rows/scale_factor)),
            Qt.KeepAspectRatio, Qt.FastTransformation)
        work_image = work_image.convertToFormat(QImage.Format_Indexed8,Qt.ColorOnly)
        # Get a reduced version of the color table by extracting just the GG
        # values of each entry, as a dict keyed by the pixel byte value. For
        # PNG-2, this gives [0,255] but it could have 8, 16, even 256 elements.
        color_table = { bytes([c]): int((work_image.color(c) >> 8) & 255)
                         for c in range(work_image.colorCount()) }
        rows = work_image.height() # number of pixels high
        cols = work_image.width() # number of logical pixels across
        stride = (cols + 3) & (-4) # scan-line width in bytes
        bytes_ptr = work_image.bits() # uchar * a_bunch_o_pixels
        bytes_ptr.setsize(stride * rows) # make the pointer indexable
        # Scan the image rows from the top down looking for one with darkness
        for top_row in range(rows):
            if dark_row(top_row*stride, cols): break
        if top_row > (rows/2) : # too much white, skip it
            return
        for bottom_row in range(rows-1, top_row, -1):
            if dark_row(bottom_row*stride, cols) : break
        # bottom_row has to be >= top_row. if they are too close together
        # set_zoom_real will limit the zoom to 200%.
        top_row = top_row*scale_factor
        bottom_row = bottom_row*scale_factor
        text_height = bottom_row - top_row + 1
        port_height = self.scroll_area.viewport().height()
        self._set_zoom_real(port_height/text_height)
        self.scroll_area.verticalScrollBar().setValue(
                         int( top_row * self.zoom_factor ) )
        # and that completes zoom-to-height

    # Build the widgetary contents. The widget consists mostly of a vertical
    # layout with two items: A scrollArea containing a QLabel used to display
    # an image, and a horizontal layout containing the zoom controls.
    # TODO: figure out design and location of two cursor-link tool buttons.
    def _uic(self):

        # Function to return the actual width of the label text
        # of a widget. Get the fontMetrics and ask it for the width.
        def _label_width(widget):
            fm = widget.fontMetrics()
            return fm.width(widget.text())

        # Create a gray field to use when no image is available
        self.gray_image = QPixmap(700,900)
        self.gray_image.fill(QColor("gray"))

        # Build the QLabel that displays the image pixmap. It gets all
        # available space and scales its contents to fit that space.
        self.image_display = QLabel()
        self.image_display.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.image_display.setScaledContents(True)

        # Create a scroll area within which to display the image. It will
        # create a horizontal and/or vertical scroll bar when
        # the image_display size exceeds the size of the scroll area.
        self.scroll_area = QScrollArea()
        self.scroll_area.setBackgroundRole(QPalette.Dark)
        self.scroll_area.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
        self.scroll_area.setWidget(self.image_display)
        # Make sure the scroll area does not swallow user keystrokes
        self.setFocusPolicy(Qt.ClickFocus) # focus into whole widget
        self.scroll_area.setFocusProxy(self) # you, pass it on.

        # Create the image-linking toolbuttons.
        # Cursor-to-image uses left-hands.
        c2i_on = QPixmap(':/hand-left-closed.png')
        c2i_off = QPixmap(':/hand-left-open.png')
        c2i_con = QIcon()
        c2i_con.addPixmap(c2i_on,QIcon.Normal,QIcon.On)
        c2i_con.addPixmap(c2i_off,QIcon.Normal,QIcon.Off)
        self.cursor_to_image = QToolButton()
        self.cursor_to_image.setCheckable(True)
        self.cursor_to_image.setContentsMargins(0,0,0,0)
        self.cursor_to_image.setIconSize(QSize(30,24))
        self.cursor_to_image.setMaximumSize(QSize(32,26))
        self.cursor_to_image.setIcon(c2i_con)
        # Image-to-cursor uses right-hands.
        i2c_on = QPixmap(':/hand-right-closed.png')
        i2c_off = QPixmap(':/hand-right-open.png')
        i2c_con = QIcon()
        i2c_con.addPixmap(i2c_on,QIcon.Normal,QIcon.On)
        i2c_con.addPixmap(i2c_off,QIcon.Normal,QIcon.Off)
        self.image_to_cursor = QToolButton()
        self.image_to_cursor.setCheckable(True)
        self.image_to_cursor.setContentsMargins(0,0,0,0)
        self.image_to_cursor.setIconSize(QSize(30,24))
        self.image_to_cursor.setMaximumSize(QSize(32,26))
        self.image_to_cursor.setIcon(i2c_con)

        # Create a spinbox to set the zoom from 15 to 200 and connect its
        # signal to our slot.
        self.zoom_pct = QSpinBox()
        self.zoom_pct.setRange(
            int(100*ZOOM_FACTOR_MIN),int(100*ZOOM_FACTOR_MAX))
        self.zoom_pct.setToolTip(
            _TR('Imageview zoom control tooltip',
                'Set the magnification of the page image')
            )
        # Connect the valueChanged(int) signal as opposed to the
        # valueChanged(str) signal.
        self.zoom_pct.valueChanged['int'].connect(self._new_zoom_pct)
        # Create a label for the zoom spinbox. (the label is not saved as a
        # class member, its layout will keep it in focus) Not translating
        # the word "Zoom".
        pct_label = QLabel(
            '&Zoom {0}-{1}%'.format(
                str(self.zoom_pct.minimum() ),
                str(self.zoom_pct.maximum() )
                )
            )
        pct_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        pct_label.setBuddy(self.zoom_pct)

        # Create the to-width and to-height zoom buttons. Make
        # sure their widths are equal after translation.
        self.zoom_to_width = QPushButton(
            _TR('Imageview zoom control button name','to Width')
            )
        self.zoom_to_width.setToolTip(
            _TR('Imageview zoom control tooltip',
                'Adjust the image to fill the window side to side.')
            )
        self.zoom_to_width.clicked.connect(self._zoom_to_width)
        self.zoom_to_height = QPushButton(
            _TR('Imageview zoom control button name','to Height')
            )
        self.zoom_to_height.setToolTip(
            _TR('Imageview zoom control tooltip',
                'Adjust the image to fill the window top to bottom.')
            )
        self.zoom_to_height.clicked.connect(self._zoom_to_height)

        w = 20 + max(_label_width(self.zoom_to_height),_label_width(self.zoom_to_width))
        self.zoom_to_height.setMinimumWidth(w)
        self.zoom_to_width.setMinimumWidth(w)

        # Create an HBox for the top of the panel which contains only
        # the cursor-to-image link button.
        tophbox = QHBoxLayout()
        tophbox.setContentsMargins(0,0,0,0)
        tophbox.addWidget(self.cursor_to_image,0)
        tophbox.addStretch() # left-align the button
        # Create an HBox layout to contain the above controls, using
        # spacers left and right to center them and a spacers between
        # to control the spacing.

        zhbox = QHBoxLayout()
        zhbox.setContentsMargins(0,0,0,0)
        zhbox.addWidget(self.image_to_cursor,0)
        zhbox.addStretch(2) # left and right spacers have stretch 2
        zhbox.addWidget(pct_label,0)
        zhbox.addWidget(self.zoom_pct,0)
        zhbox.addStretch(1) # spacers between widgets are stretch 1
        zhbox.addWidget(self.zoom_to_height,0)
        zhbox.addSpacing(10) # juuuust a little space between buttons
        zhbox.addWidget(self.zoom_to_width,0)
        zhbox.addStretch(2) # right side spacer

        # With all the pieces in hand, create our layout with a stack of
        # image over row of controls.
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0,0,0,0)
        vbox.addLayout(tophbox,0)
        # The image gets a high stretch and default alignment.
        vbox.addWidget(self.scroll_area,2)
        vbox.addLayout(zhbox,0)
        self.setLayout(vbox)
        # And that completes the UI setup.