def _delayed_recalc(self): self._timeout = None tv = self.get_tree_view() assert tv is not None range_ = tv.get_visible_range() if not range_: return start, end = range_ start = start[0] end = end[0] # compute the cell width for all drawn cells in range +/- 3 for key, value in listitems(self._texts): if not (start - 3) <= key <= (end + 3): del self._texts[key] elif isinstance(value, string_types): self._texts[key] = self._cell_width(value) # resize if too small or way too big and above the minimum width = self.get_width() needed_width = max([self._get_min_width()] + listvalues(self._texts)) if width < needed_width: self._resize(needed_width) elif width - needed_width >= self._cell_width("0"): self._resize(needed_width)
def sanitize(self, filename=None): """Fill in metadata defaults. Find ~mountpoint, ~#mtime, ~#filesize and ~#added. Check for null bytes in tags. Does not raise. """ # Replace nulls with newlines, trimming zero-length segments for key, val in listitems(self): self[key] = val if isinstance(val, string_types) and '\0' in val: self[key] = '\n'.join(filter(lambda s: s, val.split('\0'))) # Remove unnecessary defaults if key in NUMERIC_ZERO_DEFAULT and val == 0: del self[key] if filename: self["~filename"] = filename elif "~filename" not in self: raise ValueError("Unknown filename!") assert isinstance(self["~filename"], fsnative) if self.is_file: self["~filename"] = normalize_path( self["~filename"], canonicalise=True) # Find mount point (terminating at "/" if necessary) head = self["~filename"] while "~mountpoint" not in self: head, tail = os.path.split(head) # Prevent infinite loop without a fully-qualified filename # (the unit tests use these). head = head or fsnative(u"/") if ismount(head): self["~mountpoint"] = head else: self["~mountpoint"] = fsnative(u"/") # Fill in necessary values. self.setdefault("~#added", int(time.time())) # For efficiency, do a single stat here. See Issue 504 try: stat = os.stat(self['~filename']) self["~#mtime"] = stat.st_mtime self["~#filesize"] = stat.st_size # Issue 342. This is a horrible approximation (due to headers) but # on FLACs, the most common case, this should be close enough if "~#bitrate" not in self: try: # kbps = bytes * 8 / seconds / 1000 self["~#bitrate"] = int(stat.st_size / (self["~#length"] * (1000 / 8))) except (KeyError, ZeroDivisionError): pass except OSError: self["~#mtime"] = 0
def sanitize(self, filename=None): """Fill in metadata defaults. Find ~mountpoint, ~#mtime, ~#filesize and ~#added. Check for null bytes in tags. Does not raise. """ # Replace nulls with newlines, trimming zero-length segments for key, val in listitems(self): self[key] = val if isinstance(val, string_types) and '\0' in val: self[key] = '\n'.join(filter(lambda s: s, val.split('\0'))) # Remove unnecessary defaults if key in NUMERIC_ZERO_DEFAULT and val == 0: del self[key] if filename: self["~filename"] = filename elif "~filename" not in self: raise ValueError("Unknown filename!") assert isinstance(self["~filename"], fsnative) if self.is_file: self["~filename"] = normalize_path(self["~filename"], canonicalise=True) # Find mount point (terminating at "/" if necessary) head = self["~filename"] while "~mountpoint" not in self: head, tail = os.path.split(head) # Prevent infinite loop without a fully-qualified filename # (the unit tests use these). head = head or fsnative(u"/") if ismount(head): self["~mountpoint"] = head else: self["~mountpoint"] = fsnative(u"/") # Fill in necessary values. self.setdefault("~#added", int(time.time())) # For efficiency, do a single stat here. See Issue 504 try: stat = os.stat(self['~filename']) self["~#mtime"] = stat.st_mtime self["~#filesize"] = stat.st_size # Issue 342. This is a horrible approximation (due to headers) but # on FLACs, the most common case, this should be close enough if "~#bitrate" not in self: try: # kbps = bytes * 8 / seconds / 1000 self["~#bitrate"] = int(stat.st_size / (self["~#length"] * (1000 / 8))) except (KeyError, ZeroDivisionError): pass except OSError: self["~#mtime"] = 0
def rescan(self): """Rescan all folders for changed/new/removed modules. The caller should release all references to removed modules. Returns a tuple: (removed, added) """ print_d("Rescanning..") info = {} # get what is there atm for folder in self.__folders: for name, path, deps in get_importables(folder, True): # take the basename as module key, later modules win info[name] = (path, deps) # python can not unload a module, so we can only add new ones # or reload if the path is the same and mtime changed, # but we can still pretend we removed something removed = [] added = [] # remove those that are gone and changed ones for name, mod in listitems(self.__modules): # not here anymore, remove if name not in info: del self.__modules[name] removed.append(name) continue # check if any dependency has changed path, new_deps = info[name] if mod.has_changed(new_deps): del self.__modules[name] removed.append(name) self.__failures.clear() # add new ones for (name, (path, deps)) in iteritems(info): if name in self.__modules: continue try: # add a real module, so that pickle works # https://github.com/quodlibet/quodlibet/issues/1093 parent = "quodlibet.fake" if parent not in sys.modules: sys.modules[parent] = imp.new_module(parent) vars(sys.modules["quodlibet"])["fake"] = sys.modules[parent] mod = load_module(name, parent + ".plugins", dirname(path), reload=True) if mod is None: continue except Exception as err: text = format_exception(*sys.exc_info()) self.__failures[name] = ModuleImportError(name, err, text) else: added.append(name) self.__modules[name] = Module(name, mod, deps, path) print_d("Rescanning done: %d added, %d removed, %d error(s)" % (len(added), len(removed), len(self.__failures))) return removed, added