Beispiel #1
0
class DataAnalysisWindows(QMainWindow, AQMainWindow.Ui_MainWindow):
    opendir = ''
    csvDataFrame = ''
    checkMap = {}

    def __init__(self, parent=None):
        super(DataAnalysisWindows, self).__init__(parent)
        self.setupUi(self)
        self.chooseDirToolButton.clicked.connect(self.openWindow)
        self.lodefilePushButton.clicked.connect(self.loadfile)

        children = self.checkGroupBox.children()
        count = len(self.checkGroupBox.children())
        print('check box count {0}'.format(count))
        for child in children:
            if child.staticMetaObject.className() == 'QCheckBox':
                self.checkMap[child.text()] = child.isChecked()

        print('checkMap {0}'.format(self.checkMap))

    def openWindow(self):
        dirpath = QFileDialog.getExistingDirectory(
            self, self.tr(u'打开目录'), "~/",
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        self.dirPathLabel.setText(dirpath)

        filters = ['*.csv']

        self.opendir = QDir(dirpath)
        self.opendir.setNameFilters(filters)
        print('open dir is {0}, dirpath is {1}'.format(self.opendir.path(),
                                                       dirpath))

        model = QStringListModel()
        model.setStringList(self.opendir.entryList())

        self.fileListView.setModel(model)

    # 读取csv文件,到主窗口显示
    def loadfile(self):
        path = os.path.join(self.opendir.path(),
                            self.fileListView.currentIndex().data())
        print('file path is {0}'.format(path))
        self.csvDataFrame = CSVFilter.readCsv(path)
        self.updateCVSDisplay()

    def updateCVSDisplay(self):
        role = {u'搜索人气': [1000, 10000000], u'在线商品数': [1, 5000]}
        dataframe = CSVFilter.filtterRow(self.csvDataFrame, role)

        list = []
        for key in self.checkMap:
            if self.checkMap[key]:
                list.append(key)

        data_frame_column_by_name = dataframe.loc[:, list]
        print('display list {0}'.format(list))

        self.dataTableView.setModel(PandasModel(data_frame_column_by_name))
 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
 def onFileModelDirectoryLoaded(self, path):
     lastDir = str(self._state.get_video_path())
     qDirLastDir = QDir(lastDir)
     qDirLastDir.cdUp()
     if qDirLastDir.path() == path:
         index = self.fileModel.index(lastDir)
         proxyIndex = self.proxyFileModel.mapFromSource(index)
         self.ui.folderView.scrollTo(proxyIndex)
         self.ui.folderView.setCurrentIndex(proxyIndex)
 def findFiles(self, path):
     findName = self.findEdit.text()
     currentDir = QDir(path)
     self.msg("searching in " + currentDir.path(), 0)
     files = currentDir.entryList([findName], QDir.AllEntries | QDir.System
                                  | QDir.Drives)
     for line in files:
         self.lb.insertRow(0)
         self.lb.setItem(0, 0, QTableWidgetItem(line))
         self.lb.setItem(0, 1, QTableWidgetItem(path))  # + "/" + line))
 def onFileModelDirectoryLoaded(self, path):
     settings = QSettings()
     lastDir = settings.value('mainwindow/workingDirectory',
                              QDir.homePath())
     qDirLastDir = QDir(lastDir)
     qDirLastDir.cdUp()
     if qDirLastDir.path() == path:
         index = self.fileModel.index(lastDir)
         proxyIndex = self.proxyFileModel.mapFromSource(index)
         self.ui.folderView.scrollTo(proxyIndex)
         self.ui.folderView.setCurrentIndex(proxyIndex)
    def updateImageRegistries(self):
        self.imageRegistry = []
        self.hiResRegistry = []
        self.i2cRegistry = []

        imageEntryList = self.imageDir.entryList(['??????????'], QDir.Dirs)
        for i in imageEntryList:
            if self.killed is True:
                # kill request received, exit loop early
                break
            iDir = QDir(self.imageDir.path() + '\\' + i)
            # FIXME implement solution for not just jpg but values from ini
            iEntryList = iDir.entryList([i + '_???.jpg'], QDir.Files)
            self.imageRegistry = self.imageRegistry + iEntryList

            hiResDirsEntryList = iDir.entryList(["highres*", "mrsid", "raw"], QDir.Dirs)
            hiResFilters = [i + '_???.' + ext for ext in self.hiResFormats]
            for hr in hiResDirsEntryList:
                if self.killed is True:
                    # kill request received, exit loop early
                    break
                hrDir = QDir(iDir.path() + '\\' + hr)
                hrEntryList = hrDir.entryList(hiResFilters, QDir.Files)
                self.hiResRegistry = self.hiResRegistry + hrEntryList

            i2cDirEntryList = iDir.entryList(["ins2cam"], QDir.Dirs)
            i2cFilters = [i + '_???.' + ext for ext in ['jpg', 'tif', 'tiff']]
            for i2c in i2cDirEntryList:
                if self.killed is True:
                    # kill request received, exit loop early
                    break
                i2cDir = QDir(iDir.path() + '\\' + i2c)
                i2cEntryList = i2cDir.entryList(i2cFilters, QDir.Files)
                self.i2cRegistry = self.i2cRegistry + i2cEntryList

        if self.killed is False:
            self.imageRegistryNE = [img[:14].replace('_', '.') for img in self.imageRegistry]
            self.hiResRegistryNE = [img[:14].replace('_', '.') for img in self.hiResRegistry]
            self.i2cRegistryNE = [img[:14].replace('_', '.') for img in self.i2cRegistry]
 def initUI(self):
     self.setWindowTitle("userFirefox - userChrome tweak manager")
     self.setGeometry(300, 300, 640, 640)
     self.layoutBox = LayoutBox()
     self.setLayout(self.layoutBox)
     currentDir = QDir().current()
     logging.info("Current application dir: %s", currentDir.path())
     currentDir.setFilter(
         QDir.Filters(24577)
     )  #filter only dirs 0x001 and no dots 0x2000 and 0x4000 results to 0d24577
     tweakCats = currentDir.entryList()
     tweaksAvail = {}
     tweaksTree = {}
     logging.debug("List of tweak categoriess: %s", tweakCats)
     for tCat in tweakCats:
         categoryDir = QDir()
         categoryDir.setPath(currentDir.path())
         categoryDir.cd(tCat)
         categoryDir.setFilter(
             QDir.Filters(2))  #filter files 0x002 results to 0d2
         logging.debug("Tweaks in category %s are %s", tCat,
                       categoryDir.entryList())
         tweaksAvail[tCat] = categoryDir.entryList()
     logging.info("Dictionary of all available tweaks: %s", tweaksAvail)
     for tCat in tweaksAvail:
         logging.debug(tCat)
         tweaksTree["_uFFTree"] = {}
         tweaksTree[tCat] = QTreeWidgetItem(
             self.layoutBox.filesBox.availableTweaks)
         tweaksTree[tCat].setText(0, tCat)
         for tName in tweaksAvail[tCat]:
             tweaksTree["_uFFTree"][tName] = QTreeWidgetItem(
                 tweaksTree[tCat])
             tweaksTree["_uFFTree"][tName].setText(0, tName)
     #qtreetop = QTreeWidgetItem(self.layoutBox.filesBox.availableTweaks)
     #qtreetop.setText(0, "baf")
     self.show()
Beispiel #8
0
    def locate_code(self):
        explorerContainer = explorer_container.ExplorerContainer()
        projects_obj = explorerContainer.get_opened_projects()
        projects = [p.path for p in projects_obj]
        if not projects:
            return
        while not self._cancel and projects:
            current_dir = QDir(projects.pop())
            #Skip not readable dirs!
            if not current_dir.isReadable():
                continue

            project_data = json_manager.read_ninja_project(
                current_dir.path())
            extensions = project_data.get('supported-extensions',
                                          settings.SUPPORTED_EXTENSIONS)

            queue_folders = queue.Queue()
            queue_folders.put(current_dir)
            self.__locate_code_in_project(queue_folders, extensions)
        self.dirty = True
        self.get_locations()
Beispiel #9
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
Beispiel #10
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
class DarkestDungeon(mobase.IPluginGame):
    """
    Actual plugin class, extends the IPluginGame interface, meaning it adds support for a new game.
    """

    def __init__(self):
        super(DarkestDungeon, self).__init__()
        self.__featureMap = {}

    """
    Here IPlugin interface stuff. 
    """
    
    def init(self, organizer):
        self.__featureMap[mobase.GamePlugins] = DarkestDungeonGamePlugins(organizer)
        self.m_GameDir=QDir()
        self.m_DataDir=QDir()
        self.m_DocumentsDir=QDir()
        return True

    def name(self):
        """
        @return name of this plugin (used for example in the settings menu).
        @note Please ensure you use a name that will not change. Do NOT include a version number in the name.
        Do NOT use a localizable string (tr()) here.
        Settings for example are tied to this name, if you rename your plugin you lose settings users made.
        """
        return "Darkest Dungeon Plugin"

    def author(self):
        """
        @return author of this plugin.
        AnyOldName3 for initial fake game mock implementation,
        AL12 for generic game support and documentation comments,
        erri120 for adaption to Darkest Dungeon
        """
        return "AnyOldName3, AL12, erri120"

    def description(self):
        """
        @return a short description of the plugin to be displayed to the user
        """
        return self.__tr("Adds support for Darkest Dungeon. Based on the GenericGamePlugin by AnyOldName3 and AL12 version 0.1.0")

    def version(self):
        """
        @return version of the plugin. This can be used to detect outdated versions of plugins.
        """
        return mobase.VersionInfo(0, 1, 0, mobase.ReleaseType.prealpha)

    def isActive(self):
        """
        @brief called to test if this plugin is active. inactive plugins can still be configured
            and report problems but otherwise have no effect.
            For game plugins this is currently ignored during instance creation!
        @return true if this plugin is active.
        """
        return True

    def settings(self):
        """
        @return list of configurable settings for this plugin. The list may be empty.
        This could be used for example to allow users to change some of the parameters of this plugin.
        Example: [mobase.PluginSetting("enabled", self.__tr("Enable this plugin), True)]
        To retrieve it: isEnabled = self.__organizer.pluginSetting(self.name(), "enabled")
        """
        return []    

    """
    Here IPluginGame interface stuff. 
    """

    def gameName(self):
        """
        @return name of the game.
        """
        return "Darkest Dungeon"
    
    def gameShortName(self):
        """
        @brief Get the 'short' name of the game.
        
        The short name of the game is used for savegames, registry entries,
        Nexus API calls and some MO2 internal settings storage.
        """
        return "darkestdungeon"
    
    def gameIcon(self):
        """
        @return an icon for this game (QIcon constructor accepts a path).
        """
        return QIcon()

    def validShortNames(self):
        """
        @brief Get the list of valid game names, this is also used to accept alternative game sources.
            Eg: Skyrim nexus for SSE.
        
        Originally the short name was used for Nexus stuff but then Nexus changed the game identifiers
        forcing Mo2 to add the gameNexusName() to use the correct one.
        """
        return [self.gameShortName()]

    def gameNexusName(self):
        """
        @brief get the Nexus name of the game, used for API calls and for mod pages resolution.
        """
        return "darkestdungeon"
    
    def nexusModOrganizerID(self):
        """
        @brief Get the Nexus ID of the Mod Organizer page for this game.
        Use 0 if no page exists.
        """
        return 0
    
    def nexusGameID(self):
        """
        @brief Get the Nexus Game ID.
        """
        return 804
    
    def steamAPPId(self):
        """
        @return steam app id for this game. Should be empty for games not available on steam
        @note if a game is available in multiple versions those might have different app ids.
            the plugin should try to return the right one
        """
        return "262060"
    
    def binaryName(self):
        """
        @brief Get the name of the executable that gets run
        """
        return "_windows/Darkest.exe"
    
    def getLauncherName(self):
        """
        @brief Get the name of the game launcher
        """
        return ""

    def executables(self):
        """
        @return list of automatically discovered executables of the game itself and tools surrounding it.
        """
        game = mobase.ExecutableInfo("Darkest Dungeon", QFileInfo(self.m_GameDir, "_windows/Darkest.exe"))
        game.withWorkingDirectory(self.m_GameDir)
        return [game]

    def savegameExtension(self):
        """
        @return file extension of save games for this game.
        """
        return ""
    
    def savegameSEExtension(self):
        """
        @return file extension of script extender save game files for this game.
        """
        return ""

    def initializeProfile(self, path, settings):
        """
        @brief initialize a profile for this game.
        @param path the directory where the profile is to be initialized.
        @param settings parameters for how the profile should be initialized.
        @note this function will be used to initially create a profile, potentially to repair it or upgrade/downgrade it so the implementations
            have to gracefully handle the case that the directory already contains files!
        """
        pass
    
    def primaryPlugins(self):
        """
        @return list of plugins that are part of the game and not considered optional.
        """
        return []
    
    def gameVariants(self):
        """
        @return list of game variants
        @note If there are multiple variants of a game (and the variants make a difference to the
            plugin) like a regular one and a GOTY-edition the plugin can return a list of them and
            the user gets to chose which one he owns.
        """
        return []
    
    def setGameVariant(self, variantStr):
        """
        @brief if there are multiple game variants (returned by gameVariants) this will get called
            on start with the user-selected game edition allowing for manual internal adjustments.
        @param variant the game edition selected by the user
        """
        pass

    def gameVersion(self):
        """
        @brief return version of the managed game.
        """
        return "1"
    
    def iniFiles(self):
        """
        @brief Get the list of .ini files this game uses.

        @note just the name, these are all assumed residing in the documentsDirectory().
        @note It is important that the 'main' .ini file comes first in this list.
        """
        return []
    
    def DLCPlugins(self):
        """
        @brief Get a list of esp/esm files that are part of known dlcs.
        """
        return []
    
    def CCPlugins(self):
        """
        @brief Get the current list of active Creation Club plugins.
        """
        return []
    
    def loadOrderMechanism(self):
        """
        @brief determine the load order mechanism used by this game.

        @note this may throw an exception if the mechanism can't be determined.

        Either:
            FileTime,
            PluginsTxt
        
        Leave to PluginsTxt in case the game does not use plugins.
        """
        return mobase.LoadOrderMechanism.PluginsTxt
    
    def sortMechanism(self):
        """
        @brief determine the sorting mech
        Either:
            NONE,
            MLOX,
            BOSS,
            LOOT
        """
        return mobase.SortMechanism.NONE
    
    def looksValid(self, aQDir):
        """
        @brief See if the supplied directory looks like a valid installation of the game.
        """
        return QFileInfo(aQDir.path() + "/_windows/Darkest.exe").exists()
    
    def isInstalled(self):
        """
        @return true if this game has been discovered as installed, false otherwise.
        
        Used to allow fast instance creation. This function can be used to check
        registry keys for the path of the game and setting the internal game/data directories.
        """

        """
        HKEY_CURRENT_USER\\Software\\Valve\\Steam\\Apps\\262060 contains Installed
        HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App 262060 has InstallLocation
        https://github.com/ModOrganizer2/modorganizer-game_gamebryo/blob/master/src/gamebryo/gamegamebryo.cpp#L299
        """
        return False
    
    def gameDirectory(self):
        """
        @return directory to the game installation.
        """
        return self.m_GameDir
    
    def dataDirectory(self):
        """
        @return directory where the game expects to find its data files (virtualization target).
        """
        return self.m_DataDir
    
    def setGamePath(self, pathStr):
        """
        @brief set the path to the managed game.
        @param path to the game.
        @note this will be called by by MO to set the concrete path of the game. This is particularly
            relevant if the path wasn't auto-detected but had to be set manually by the user.
        """
        self.m_GameDir=QDir(pathStr)
        self.m_DataDir=QDir(self.m_GameDir.path() + "/mods/")
    
    def documentsDirectory(self):
        """
        @return directory of the documents folder where configuration files and such for this game reside.
        """
        return QDir("{}/My Games".format(os.getenv("CSIDL_MYDOCUMENTS")))
    
    def savesDirectory(self):
        """
        @return path to where save games are stored.
        """
        return self.documentsDirectory()
    
    def _featureList(self):
        """
        Map of features that the game supports where each feature is a class abiding to
        an interface of one of the supported features found in the game_features project
        on the Modorganizer2 github.

        GamePlugins feature is currently mandatory as Mo2 will otherwise crash.
        """
        return self.__featureMap
    
    def __tr(self, str):
        return QCoreApplication.translate("DarkestDungeon", str)
class LayoutBox(QVBoxLayout):
    def __init__(self):
        super().__init__()
        self.profileGroup = QGroupBox("Profile")
        self.profileLabel = QLabel("none")
        self.profileSelect = QPushButton("Open...")
        self.profileSelect.clicked.connect(self.openProfileClick)
        self.profileSave = QPushButton("Save")
        self.profileSave.clicked.connect(self.saveProfileClick)
        self.profileSave.setEnabled(False)
        self.profileBox = QHBoxLayout()
        self.profileBox.addWidget(self.profileLabel)
        self.profileBox.addStretch()
        self.profileBox.addWidget(self.profileSelect)
        self.profileBox.addWidget(self.profileSave)
        self.profileGroup.setLayout(self.profileBox)

        self.filesBox = FilesBox()
        self.filesBox.buttonBox.setEnable(False)  #
        self.filesBox.availableTweaks.setEnabled(False)  #
        self.filesBox.selectedTweaks.setEnabled(False)  #

        self.filesBox.availableTweaks.itemClicked.connect(self.availClick)
        self.filesBox.selectedTweaks.itemClicked.connect(self.selectClick)

        self.descriptionGroup = QGroupBox("Tweak description")
        self.descriptionGroup.setMinimumHeight(50)
        self.description = QLabel("")
        self.description.setTextInteractionFlags(Qt.TextInteractionFlags(5))

        self.descriptionBox = QHBoxLayout()
        self.descriptionBox.addWidget(self.description)
        self.descriptionGroup.setLayout(self.descriptionBox)

        self.addWidget(self.profileGroup)
        self.addItem(self.filesBox)
        self.addWidget(self.descriptionGroup)
        self.profileDir = QDir()
        self.profilePathDialog = QFileDialog()
        self.profilePath = ""
        self.appDir = QDir().current()

    def openProfileClick(self):
        #Button Open... click
        self.profilePath = self.profilePathDialog.getExistingDirectory(
            self.profileSelect, "Select Firefox profile")
        # self.profilePath = "~/Library/Application Support/Firefox/Profiles/rpk2uobe.default"
        logging.debug("Selected directory: \"%s\"", self.profilePath)
        if self.profilePath != "":
            self.selectedProfile = QDir(self.profilePath)
            self.firefoxDir = QDir(self.profilePath)
            logging.debug("Selected profile qdir: %s",
                          self.selectedProfile.path())
            self.firefoxDir.cdUp()
            self.profilesFile = QFile(self.firefoxDir.path() + "/profiles.ini")
            logging.debug("Profiles file: %s", self.profilesFile.fileName())
            logging.debug("Profiles file exists: %s",
                          self.profilesFile.exists())
            logging.debug("Firefox folder: %s", self.firefoxDir.dirName())
            # Basic check if parent directory is named 'firefox' and contains a file named 'profiles.ini'
            #if self.firefoxDir.dirName() == "firefox" and self.profilesFile.exists():
            if True:
                self.profilePath = self.profilePath
                self.profileLabel.setText(self.profilePath)

                self.filesBox.buttonBox.setEnable(True)
                self.filesBox.availableTweaks.setEnabled(True)
                self.filesBox.selectedTweaks.setEnabled(True)
                self.profileSave.setEnabled(True)

                self.profileDir.setPath(self.profilePath)
                logging.debug("Profile dirs: %s", self.profileDir.entryList())
                self.userChrome = QFile(
                    self.profilePath + "/chrome/userChrome.css")
                self.userFFSettings = QFile(
                    self.profilePath + "/chrome/userFirefox.json")
                logging.debug("userChrome exists: %s",
                              self.userChrome.exists())
                if self.userChrome.exists(
                ) and not self.userFFSettings.exists():
                    self.backupChrome = QMessageBox()
                    self.backupChrome.setIcon(QMessageBox.Question)
                    self.backupChrome.setText(
                        "userChrome.css file already exists in this profile. This may be overwritten. Do you want to make a backup?"
                    )
                    self.backupChrome.setStandardButtons(
                        QMessageBox.StandardButtons(81920))  #yes, no
                    self.backupChrome.exec()
                    logging.debug("Dialog result: %s",
                                  self.backupChrome.result())
                    if self.backupChrome.result() == 16384:  #yes
                        logging.debug("Backing up userChrome")
                        self.backupDone = QMessageBox()
                        self.backupFile = QFile(
                            self.profilePath + "/chrome/userChrome.css~")
                        logging.debug("Backup file: %s",
                                      self.backupFile.fileName())
                        logging.debug("Backup exists: %s",
                                      self.backupFile.exists())
                        if self.backupFile.exists():
                            logging.debug("Backup already exists")
                            self.backupDone.setIcon(QMessageBox.Warning)
                            self.backupDone.setText(
                                "Backup already exists. The file was NOT overwritten and new backup not made."
                            )
                        else:
                            if self.userChrome.copy(self.profilePath +
                                                    "/chrome/userChrome.css~"):
                                self.backupDone.setIcon(
                                    QMessageBox.Information)
                                self.backupDone.setText(
                                    "Backed up to 'userChrome.css~'")
                            else:
                                self.backupDone.setIcon(QMessageBox.Critical)
                                self.backupDone.setText("Backing up failed.")
                        self.backupDone.exec()
                    elif self.backupChrome.result() == 65536:  #no
                        logging.debug("Not backing up userChome")
                # Load existing settings
                try:
                    with open(self.profilePath + "/chrome/userFirefox.json"
                              ) as uFP:
                        savedTweaks = json.load(uFP)
                    logging.debug("Loaded json settings: %s", savedTweaks)
                    for loadedTweak in savedTweaks:
                        logging.debug(
                            "Loaded tweak. check: %i, category: %s, name: %s",
                            loadedTweak["Enabled"], loadedTweak["Category"],
                            loadedTweak["Name"])
                        tweakCat = loadedTweak["Category"]
                        tweakName = loadedTweak["Name"]
                        self.filesBox.tweaksAdded[
                            tweakCat + tweakName] = QTreeWidgetItem(
                                self.filesBox.selectedTweaks)
                        self.filesBox.tweaksAdded[tweakCat
                                                  + tweakName].setCheckState(
                                                      0,
                                                      loadedTweak["Enabled"])
                        self.filesBox.tweaksAdded[tweakCat
                                                  + tweakName].setText(
                                                      1, tweakCat)
                        self.filesBox.tweaksAdded[tweakCat
                                                  + tweakName].setText(
                                                      2, tweakName)
                except FileNotFoundError:
                    pass
                self.filesBox.resizeColumns()
            else:
                self.noProfile = QMessageBox()
                self.noProfile.setIcon(QMessageBox.Warning)
                self.noProfile.setText(
                    "The selected directory does not appear to be a valid Firefox profile. Profiles are usually located at '~/.mozilla/firefox/xxxx' and is most likely a hidden directory."
                )
                self.noProfile.exec()

    def availClick(self):
        if self.filesBox.availableTweaks.currentItem().parent() is not None:
            tweakName = self.filesBox.availableTweaks.currentItem().text(0)
            tweakCat = self.filesBox.availableTweaks.currentItem().parent(
            ).text(0)
            self.showDesc(tweakCat, tweakName)

    def selectClick(self):
        try:
            tweakName = self.filesBox.selectedTweaks.currentItem().text(2)
            tweakCat = self.filesBox.selectedTweaks.currentItem().text(1)
            self.showDesc(tweakCat, tweakName)
        except:
            pass

    def showDesc(self, category, name):
        logging.debug("Showing description for %s/%s: ", category, name)
        currentDir = QDir().current().path()
        tweakPath = currentDir + "/" + category + "/" + name
        logging.debug("Loading %s", tweakPath)
        tweakDesc = ""
        descLines = 0
        with open(tweakPath) as tweakContent:
            for line in tweakContent:
                if (re.search("^(\/\* ?)|( \* ?)|( \*\/)", line) is not None):
                    if len(line) > 3:
                        trimmedLine = line[3:]
                        tweakDesc += trimmedLine
                        descLines += 1
                else:
                    break
        logging.debug("Description: %s", tweakDesc)
        self.description.setText(tweakDesc)
        logging.debug("Desc lines: %i", descLines)

    def saveProfileClick(self):
        logging.debug("Saving profile")
        self.saveQuestion = QMessageBox()
        self.saveQuestion.setIcon(QMessageBox.Question)
        self.saveQuestion.setStandardButtons(
            QMessageBox.StandardButtons(81920))  #yes, no
        self.saveQuestion.setText(
            "Do you want to apply selected tweaks and overwrite userChrome?")
        self.saveQuestion.exec()
        self.saveCSS = ""
        if self.saveQuestion.result() == 16384:  #yes
            logging.debug("Saving userChrome")
            selTweaksNumber = self.filesBox.selectedTweaks.topLevelItemCount()
            savingTweaks = []
            for i in range(selTweaksNumber):
                currentSavingTweak = self.filesBox.selectedTweaks.topLevelItem(
                    i)
                currentSavingData = {}
                currentSavingData["Enabled"] = currentSavingTweak.checkState(0)
                currentSavingData["Category"] = currentSavingTweak.text(1)
                currentSavingData["Name"] = currentSavingTweak.text(2)
                logging.debug("Tweak cat %s, name %s, selected %s",
                              currentSavingData["Category"],
                              currentSavingData["Name"],
                              currentSavingData["Enabled"])
                savingTweaks.append(currentSavingData)
                if currentSavingData["Enabled"] == 2:
                    with open(self.appDir.path() + "/" +
                              currentSavingData["Category"] + "/" +
                              currentSavingData["Name"]) as twkFile:
                        self.saveCSS += twkFile.read() + "\n"
            logging.debug("Selected tweaks: %s", savingTweaks)
            with open(self.profilePath + "/chrome/userFirefox.json",
                      "w") as fp:
                json.dump(savingTweaks, fp)
            logging.debug("userChrome.css: %s", self.saveCSS)
            with open(self.profilePath + "/chrome/userChrome.css", "w") as fp:
                fp.write(self.saveCSS)
        elif self.saveQuestion.result() == 65536:  #no
            logging.debug("Not saving userChrome.")
class UpdateRegistryWorker(QObject):
    '''Background worker for updating Image Registry'''

    finished = pyqtSignal(object)
    error = pyqtSignal(Exception, str)

    def __init__(self):
        QObject.__init__(self)
        self.killed = False
        self.settings = QSettings(QSettings().value("APIS/config_ini"), QSettings.IniFormat)

        # self.registryFile = pluginDir + "\\" + "apis_image_registry.json" #self.settings.value("APIS/image_registry_file", None)

        self.imageDirName = self.settings.value("APIS/image_dir")
        self.orthoDirName = self.settings.value("APIS/ortho_image_dir")
        self.imageDir = QDir(self.imageDirName)
        self.orthoDir = QDir(self.orthoDirName)

        self.imageFormats = self.settings.value("APIS/image_formats", ['jpg'])
        self.hiResFormats = self.settings.value("APIS/hires_formats", ['jpg', 'tif', 'sid', 'nef', 'raf', 'cr2', 'dng'])
        self.orthoFormats = self.settings.value("APIS/ortho_formats", ['jpg', 'tif', 'sid'])

        self.imageFormatsStr = "|".join(self.imageFormats)
        self.hiResFormatsStr = "|".join(self.hiResFormats)
        self.orthoFormatsStr = "|".join(self.orthoFormats)

    def run(self):
        try:
            self.updateImageRegistries()
            self.updateOrthoRegistries()

            if self.killed is False:
                ret = True
                ret = {
                    "imageRegistryNE": self.imageRegistryNE,
                    "hiResRegistryNE": self.hiResRegistryNE,
                    "i2cRegistryNE": self.i2cRegistryNE,
                    "orthoRegistryNE": self.orthoRegistryNE,
                    "mosaicRegistryNE": self.mosaicRegistryNE,
                    "imageRegistry": self.imageRegistry,
                    "hiResRegistry": self.hiResRegistry,
                    "i2cRegistry": self.i2cRegistry,
                    "orthoRegistry": self.orthoRegistry,
                    "mosaicRegistry": self.mosaicRegistry
                }
            else:
                ret = False
        except Exception as e:
            # forward the exception upstream
            self.error.emit(e, traceback.format_exc())
        self.finished.emit(ret)

    def kill(self):
        self.killed = True

    def updateImageRegistries(self):
        self.imageRegistry = []
        self.hiResRegistry = []
        self.i2cRegistry = []

        imageEntryList = self.imageDir.entryList(['??????????'], QDir.Dirs)
        for i in imageEntryList:
            if self.killed is True:
                # kill request received, exit loop early
                break
            iDir = QDir(self.imageDir.path() + '\\' + i)
            # FIXME implement solution for not just jpg but values from ini
            iEntryList = iDir.entryList([i + '_???.jpg'], QDir.Files)
            self.imageRegistry = self.imageRegistry + iEntryList

            hiResDirsEntryList = iDir.entryList(["highres*", "mrsid", "raw"], QDir.Dirs)
            hiResFilters = [i + '_???.' + ext for ext in self.hiResFormats]
            for hr in hiResDirsEntryList:
                if self.killed is True:
                    # kill request received, exit loop early
                    break
                hrDir = QDir(iDir.path() + '\\' + hr)
                hrEntryList = hrDir.entryList(hiResFilters, QDir.Files)
                self.hiResRegistry = self.hiResRegistry + hrEntryList

            i2cDirEntryList = iDir.entryList(["ins2cam"], QDir.Dirs)
            i2cFilters = [i + '_???.' + ext for ext in ['jpg', 'tif', 'tiff']]
            for i2c in i2cDirEntryList:
                if self.killed is True:
                    # kill request received, exit loop early
                    break
                i2cDir = QDir(iDir.path() + '\\' + i2c)
                i2cEntryList = i2cDir.entryList(i2cFilters, QDir.Files)
                self.i2cRegistry = self.i2cRegistry + i2cEntryList

        if self.killed is False:
            self.imageRegistryNE = [img[:14].replace('_', '.') for img in self.imageRegistry]
            self.hiResRegistryNE = [img[:14].replace('_', '.') for img in self.hiResRegistry]
            self.i2cRegistryNE = [img[:14].replace('_', '.') for img in self.i2cRegistry]

    def updateOrthoRegistries(self):
        self.orthoRegistryNE = []
        self.orthoRegistry = []
        self.mosaicRegistryNE = []
        self.mosaicRegistry = []
        orthoEntryList = self.orthoDir.entryList(['??????????'], QDir.Dirs)
        for o in orthoEntryList:
            if self.killed is True:
                # kill request received, exit loop early
                break
            orthoFilters = [o + '_???_op*.' + ext for ext in self.orthoFormats]
            mosaicFilters = [o + '_???_???_op*.' + ext for ext in self.orthoFormats]
            oDir = QDir(self.orthoDir.path() + '\\' + o)
            oEntryList = oDir.entryList(orthoFilters, QDir.Files)
            mEntryList = oDir.entryList(mosaicFilters, QDir.Files)
            #chekc oEntryList if _INT_op
            oEntryList = [img for img in oEntryList if img[11:14].isdigit()]
            #check mEntryList if _INT_INT_op
            mEntryList = [img for img in mEntryList if img[11:14].isdigit() and img[15:18].isdigit()]
            self.orthoRegistry = self.orthoRegistry + oEntryList
            self.mosaicRegistry = self.mosaicRegistry + mEntryList
        if self.killed is False:
            self.orthoRegistryNE = [img[:14].replace('_', '.') for img in self.orthoRegistry]
            self.mosaicRegistryNE = [f"{img[:10]}.{img[11:14]}-{img[15:18]}" for img in self.mosaicRegistry]