class ImageViewer(): def __init__(self, config_folder): self.config_folder = config_folder if not os.path.exists(self.config_folder): os.mkdir(self.config_folder) self.loadConfig() self.setupInterface() self.current_image = None self.files_in_folder = [] # Inotify if INOTIFY: self.has_inotify = True self.pyinotify_wm = pyinotify.WatchManager() self.pyinotify_mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE | pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO handler = InotifyEventHandler(self) self.pyinotify_notifier = pyinotify.Notifier(self.pyinotify_wm, handler, timeout=INOTIFY_TIMEOUT) self.pyinotify_wdd = {} else: self.has_inotify = False def loadConfig(self): self.config = configparser.SafeConfigParser(DEFAULT_CONFIG) self.config_file = os.path.join(self.config_folder, 'config.txt') self.config.read(self.config_file) def getConfig(self, param): return self.config.get(CONFIG_SECTION_DEFAULT, param) def getConfigInt(self, param): return int(self.config.get(CONFIG_SECTION_DEFAULT, param)) def getConfigColour(self, param): col_str = self.getConfig(param) colour = Gdk.RGBA() colour.parse(col_str) return colour def getConfigBool(self, param): if self.config.get(CONFIG_SECTION_DEFAULT, param).lower() == 'true': return True else: return False def setConfig(self, param, value): self.config[CONFIG_SECTION_DEFAULT][param] = str(value) def saveConfig(self): self.config.write(open(self.config_file, 'w')) def setupInterface(self): width = self.getConfigInt(CONFIG_WINDOW_WIDTH) height = self.getConfigInt(CONFIG_WINDOW_HEIGHT) request_size = (width, height) self.interface = Interface(self) self.interface.resize(request_size) if self.getConfigBool(CONFIG_WINDOW_FULLSCREEN): self.interface.modeFullscreen(True) self.interface.show() ## CONFIG INTERFACE GET def getInterfaceBGColour(self): return self.getConfigColour(CONFIG_BG_COLOUR) def isInterfaceImageBGPattern(self): return self.getConfig(CONFIG_IMAGE_BG_TYPE) == IMAGE_BG_TYPE_PATTERN def isInterfaceImageBGAsMain(self): return self.getConfig(CONFIG_IMAGE_BG_TYPE) == IMAGE_BG_TYPE_AS_APP def isInterfaceImageBGColour(self): return self.getConfig(CONFIG_IMAGE_BG_TYPE) == IMAGE_BG_TYPE_COLOUR def getInterfaceImageBGColour(self): return self.getConfigColour(CONFIG_IMAGE_BG_COLOUR) ## CONFIG INTERFACE SET def setInterfaceBGColour(self, colour): self.setConfig(CONFIG_BG_COLOUR, colour.to_string()) def setInterfaceImageBGTypePattern(self): self.setConfig(CONFIG_IMAGE_BG_TYPE, IMAGE_BG_TYPE_PATTERN) def setInterfaceImageBGTypeAsMain(self): self.setConfig(CONFIG_IMAGE_BG_TYPE, IMAGE_BG_TYPE_AS_APP) def setInterfaceImageBGTypeColour(self): self.setConfig(CONFIG_IMAGE_BG_TYPE, IMAGE_BG_TYPE_COLOUR) def setInterfaceImageBGColour(self, colour): self.setConfig(CONFIG_IMAGE_BG_COLOUR, colour.to_string()) ## START def start(self, imagepath=None): if imagepath is not None: current_folder = os.path.dirname(imagepath) self.files_in_folder = self.readFolder(current_folder) self.current_image = self.openImage(imagepath) self.inotifyAdd(current_folder) self.setCurrentImagePosition() self.interface.start(self.current_image) def close(self): # save last window size width, height = self.interface.getSize() fullscreen = self.interface.getFullscreen() self.setConfig(CONFIG_WINDOW_WIDTH, width) self.setConfig(CONFIG_WINDOW_HEIGHT, height) self.setConfig(CONFIG_WINDOW_FULLSCREEN, fullscreen) # save config self.saveConfig() def stop(self): self.interface.close() @ifInotify def inotifyAdd(self, path): self.pyinotify_wdd = self.pyinotify_wm.add_watch(path, self.pyinotify_mask, rec=False) @ifInotify def inotifyRemove(self, path): if self.pyinotify_wdd[path] > 0: self.pyinotify_wm.rm_watch(self.pyinotify_wdd[path]) def openNextImage(self): self.openNearImage(OPEN_NEXT) def openPrevImage(self): self.openNearImage(OPEN_PREV) def openNearImage(self, open_type): current_name = self.current_image.getName() current_folder = self.current_image.getFolder() current_position = self.current_image.getPosition() # set up new image variables new_image = None if open_type == OPEN_NEXT: new_position = current_position + 1 else: new_position = current_position - 1 # get new image if new_position >= 0 and new_position < len(self.files_in_folder): new_image = os.path.join(current_folder, self.files_in_folder[new_position]) else: new_image = None new_position = -1 # open pallel folder if necessary if new_image is None: new_image, new_position = self.openUpperFolder(open_type) if new_image is not None: # update inotify self.inotifyRemove(current_folder) self.inotifyAdd(os.path.dirname(new_image)) # set new current image and open with the interface if new_image is not None: self.current_image = self.openImage(new_image, new_position) self.interface.openImage(self.current_image) def openUpperFolder(self, get): # Get all the files/folders folder = os.path.dirname(self.current_image.getFolder()) current_folder_name = os.path.basename(self.current_image.getFolder()) files = self.readFolder(folder, True) # Reverse the array if get prev if get == OPEN_PREV: files.reverse() # Get the first valid element next = False element = None for filename in files: if filename == current_folder_name: next = True elif next: # check if the element is valid candidate_path = os.path.join(folder, filename) if os.path.isdir(candidate_path): # read folder el_files = self.readFolder(candidate_path) if len(el_files) > 0: self.files_in_folder = el_files position = 0 if get == OPEN_NEXT else -1 element = os.path.join(candidate_path, el_files[position]) break ''' # Note: I do not consider files while reading the upper folder # because I read only files on the same level # i.e. files in the folders ~/Pictures/ and ~/Documents # are on the same level, pictures in # ~/Pictures and ~/Pictures/ex1 are not. else: # reorder the files correctly if get == OPEN_PREV: files.reverse() self.files_in_folder = self.removeFoldersFromArray(files) element = candidate_path break ''' # get element position if element is None: position = -1 elif get == OPEN_NEXT: position = 0 elif get == OPEN_PREV: position = len(self.files_in_folder) - 1 # element is the new image, # position is its index in the folder files return element, position def openImage(self, path, position=None): img = IWImage(path) if position is None: # get image position position = self.getFilePosition(path) img.setPosition(position) return img def getFilePosition(self, path): # NOTE: assume self.files_in_folder is correct basename = os.path.basename(path) if basename in self.files_in_folder: return self.files_in_folder.index(basename) else: # error return -1 def isSupportedExtension(self, ext): return ext.lower() in SUPPORTED_STATIC or ext.lower() in SUPPORTED_ANIMATION def readFolder(self, folder, include_folders=False): all_files = os.listdir(folder) folders = {} folders_keys = [] if include_folders: for filename in all_files: if filename[0] == '.': continue filepath = os.path.join(folder, filename) if os.path.isdir(filepath): f_lower = filename.lower() if f_lower in folders: folders[f_lower].append(filename) else: folders[f_lower] = [filename] folders_keys = natsorted(folders.keys()) valid_files = {} valid_files_keys = [] for filename in all_files: if filename[0] == '.': continue _, ext = os.path.splitext(filename) if self.isSupportedExtension(ext): f_lower = filename.lower() if f_lower in valid_files: valid_files[f_lower].append(filename) else: valid_files[f_lower] = [filename] valid_files_keys = natsorted(valid_files.keys()) # Compose sorted_files = [] for el in folders_keys: fls = folders[el] fls.sort() sorted_files.extend(fls) for el in valid_files_keys: fls = valid_files[el] fls.sort() sorted_files.extend(fls) return sorted_files ''' def removeFoldersFromArray(self, files): # NOTE: the input must be in the format given by # self.readFolder new_array = [] for el in files: if os.path.isdir(el): break else: new_array.append(el) return new_array ''' def setCurrentImagePosition(self): basename = self.current_image.getName() if basename in self.files_in_folder: position = self.files_in_folder.index(basename) self.current_image.setPosition(position) else: self.current_image.setPosition(-1) def getTotImages(self): return len(self.files_in_folder) def addToFilelist(self, path): # TODO: better, this is kinda lazy current_folder = os.path.dirname(path) self.files_in_folder = self.readFolder(current_folder) # update interface self.updateFolderData() def removeFromFilelist(self, path): filename = os.path.basename(path) if filename in self.files_in_folder: self.files_in_folder.remove(filename) # update interface self.updateFolderData() def updateFolderData(self): self.setCurrentImagePosition() self.interface.fillInfo() def folderIsEmpty(self, folder): return len(os.listdir(folder)) == 0