From 9a889ff093b6e514dea116d01891b77803932287 Mon Sep 17 00:00:00 2001 From: guinux Date: Wed, 12 Dec 2012 17:12:27 +0100 Subject: [PATCH] first commit --- .gitignore | 2 + config.py | 210 +++++++++++++++++ gui/dialogs.glade | 84 +++++++ gui/pamac.glade | 515 +++++++++++++++++++++++++++++++++++++++++ gui/pamac_update.glade | 150 ++++++++++++ pamac.py | 368 +++++++++++++++++++++++++++++ pamac_update.py | 60 +++++ transaction.py | 161 +++++++++++++ 8 files changed, 1550 insertions(+) create mode 100644 .gitignore create mode 100644 config.py create mode 100644 gui/dialogs.glade create mode 100644 gui/pamac.glade create mode 100644 gui/pamac_update.glade create mode 100755 pamac.py create mode 100755 pamac_update.py create mode 100755 transaction.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..171845e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pychache__ +..goutputstream* diff --git a/config.py b/config.py new file mode 100644 index 0000000..20725ab --- /dev/null +++ b/config.py @@ -0,0 +1,210 @@ +#! /usr/bin/python +# -*-coding:utf-8 -* + +import io +import os +import glob +import sys +import argparse +import collections +import warnings + +import pyalpm + +class InvalidSyntax(Warning): + def __init__(self, filename, problem, arg): + self.filename = filename + self.problem = problem + self.arg = arg + + def __str__(self): + return "unable to parse %s, %s: %r" % (self.filename, self.problem, self.arg) + +# Options that may occur several times in a section. Their values should be +# accumulated in a list. +LIST_OPTIONS = ( + 'CacheDir', + 'HoldPkg', + 'SyncFirst', + 'IgnoreGroup', + 'IgnorePkg', + 'NoExtract', + 'NoUpgrade', + 'Server' +) + +SINGLE_OPTIONS = ( + 'RootDir', + 'DBPath', + 'GPGDir', + 'LogFile', + 'Architecture', + 'XferCommand', + 'CleanMethod', + 'SigLevel' +) + +BOOLEAN_OPTIONS = ( + 'UseSyslog', + 'ShowSize', + 'UseDelta', + 'TotalDownload', + 'CheckSpace', + 'VerbosePkgLists', + 'ILoveCandy' +) + +def pacman_conf_enumerator(path): + filestack = [] + current_section = None + filestack.append(open(path)) + while len(filestack) > 0: + f = filestack[-1] + line = f.readline() + if len(line) == 0: + # end of file + filestack.pop() + continue + + line = line.strip() + if len(line) == 0: continue + if line[0] == '#': + continue + if line[0] == '[' and line[-1] == ']': + current_section = line[1:-1] + continue + if current_section is None: + raise InvalidSyntax(f.name, 'statement outside of a section', line) + # read key, value + key, equal, value = [x.strip() for x in line.partition('=')] + + # include files + if equal == '=' and key == 'Include': + filestack.extend(open(f) for f in glob.glob(value)) + continue + if current_section != 'options': + # repos only have the Server option + if key == 'Server' and equal == '=': + yield (current_section, 'Server', value) + elif key == 'SigLevel' and equal == '=': + yield (current_section, 'SigLevel', value) + else: + raise InvalidSyntax(f.name, 'invalid key for repository configuration', line) + continue + if equal == '=': + if key in LIST_OPTIONS: + for val in value.split(): + yield (current_section, key, val) + elif key in SINGLE_OPTIONS: + yield (current_section, key, value) + else: + warnings.warn(InvalidSyntax(f.name, 'unrecognized option', key)) + else: + if key in BOOLEAN_OPTIONS: + yield (current_section, key, True) + else: + warnings.warn(InvalidSyntax(f.name, 'unrecognized option', key)) + +_logmask = pyalpm.LOG_ERROR | pyalpm.LOG_WARNING + +def cb_log(level, line): + if not (level & _logmask): + return + if level & pyalpm.LOG_ERROR: + line = "ERROR: " + line + elif level & pyalpm.LOG_WARNING: + line = "WARNING: " + line + elif level & pyalpm.LOG_DEBUG: + line = "DEBUG: " + line + elif level & pyalpm.LOG_FUNCTION: + line = "FUNC: " + line + sys.stderr.write(line) + +class PacmanConfig(object): + def __init__(self, conf = None, options = None): + self.options = {} + self.repos = collections.OrderedDict() + self.options["RootDir"] = "/" + self.options["DBPath"] = "/var/lib/pacman" + self.options["GPGDir"] = "/etc/pacman.d/gnupg/" + self.options["LogFile"] = "/var/log/pacman.log" + self.options["Architecture"] = os.uname()[-1] + if conf is not None: + self.load_from_file(conf) + if options is not None: + self.load_from_options(options) + + def load_from_file(self, filename): + for section, key, value in pacman_conf_enumerator(filename): + if section == 'options': + if key == 'Architecture' and value == 'auto': + continue + if key in LIST_OPTIONS: + self.options.setdefault(key, []).append(value) + else: + self.options[key] = value + else: + servers = self.repos.setdefault(section, []) + if key == 'Server': + servers.append(value) + if "CacheDir" not in self.options: + self.options["CacheDir"]= ["/var/cache/pacman/pkg"] + + def load_from_options(self, options): + global _logmask + if options.root is not None: + self.options["RootDir"] = options.root + if options.dbpath is not None: + self.options["DBPath"] = options.dbpath + if options.gpgdir is not None: + self.options["GPGDir"] = options.gpgdir + if options.arch is not None: + self.options["Architecture"] = options.arch + if options.logfile is not None: + self.options["LogFile"] = options.logfile + if options.cachedir is not None: + self.options["CacheDir"] = [option.cachedir] + if options.debug: + _logmask = 0xffff + + def apply(self, h): + h.arch = self.options["Architecture"] + h.logfile = self.options["LogFile"] + h.gpgdir = self.options["GPGDir"] + h.cachedirs = self.options["CacheDir"] + if "IgnoreGroup" in self.options: + h.ignoregrps = self.options["IgnoreGroup"] + if "IgnorePkg" in self.options: + h.ignorepkgs = self.options["IgnorePkg"] + if "NoExtract" in self.options: + h.noextracts = self.options["NoExtract"] + if "NoUpgrade" in self.options: + h.noupgrades = self.options["NoUpgrade"] + h.logcb = cb_log + + # set sync databases + for repo, servers in self.repos.items(): + db = h.register_syncdb(repo, 0) + db_servers = [] + for rawurl in servers: + url = rawurl.replace("$repo", repo) + url = url.replace("$arch", self.options["Architecture"]) + db_servers.append(url) + db.servers = db_servers + + def initialize_alpm(self): + h = pyalpm.Handle(self.options["RootDir"], self.options["DBPath"]) + self.apply(h) + return h + + def __str__(self): + return("PacmanConfig(options=%s, repos=%s)" % (str(self.options), str(self.repos))) + +pacman_conf = PacmanConfig(conf = "/etc/pacman.conf") +handle = pacman_conf.initialize_alpm() +holpkg = None +syncfirst = None +if 'HoldPkg' in pacman_conf.options: + holdpkg = pacman_conf.options['HoldPkg'] +if 'SyncFirst' in pacman_conf.options: + syncfirst = pacman_conf.options['SyncFirst'] diff --git a/gui/dialogs.glade b/gui/dialogs.glade new file mode 100644 index 0000000..5b84285 --- /dev/null +++ b/gui/dialogs.glade @@ -0,0 +1,84 @@ + + + + + False + 5 + + False + center-on-parent + dialog + True + error + ok + + + False + vertical + 2 + + + False + + + False + True + end + 0 + + + + + + + + + + 250 + 60 + False + + False + center-on-parent + 250 + 60 + + + True + False + 5 + 5 + 5 + 5 + vertical + + + True + False + + + + False + True + 0 + + + + + True + False + 0.050000000000000003 + + True + middle + + + False + True + 1 + + + + + + diff --git a/gui/pamac.glade b/gui/pamac.glade new file mode 100644 index 0000000..d4a4a91 --- /dev/null +++ b/gui/pamac.glade @@ -0,0 +1,515 @@ + + + + + 250 + 150 + False + True + True + 5 + + center-on-parent + 200 + 100 + dialog + True + other + ok-cancel + <b>Transaction summary</b> + True + + + 240 + 140 + False + True + True + vertical + + + True + True + True + end + + + False + True + end + 0 + + + + + 200 + 120 + True + True + True + True + in + + + True + False + True + transaction_desc + False + False + False + + + + + + column + + + 0 + 600 + + + 0 + + + + + + + column + + + + 1 + + + + + + + + + True + True + 2 + + + + + True + False + label + + + False + True + 4 + + + + + + + + + + + + + 800 + 500 + True + False + Pamac + 800 + 500 + + + + True + False + vertical + + + True + False + True + True + + + True + False + True + vertical + + + True + False + + + True + True + True + True + + 25 + gtk-find + + + + + False + True + 0 + + + + + gtk-find + True + True + True + True + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + groups_list + False + + + + + + + + Groups + + + + 0 + + + + + + + + + True + True + 2 + + + + + False + True + 6 + 0 + + + + + True + False + True + True + vertical + 6 + + + True + True + True + True + in + + + 500 + True + True + True + True + packages_list + True + False + 0 + False + + + + + + + + autosize + Installed + True + True + + + + #000000000000 + rgba(0,0,0,0) + 2 + 2 + + + + 2 + 1 + + + + + + + True + autosize + Name + True + True + + + + + 0 + + + + + + + + + True + True + 0 + + + + + True + True + True + True + in + + + True + True + package_desc + False + False + True + False + + + none + + + + + column + + + 0 + 600 + word + + + 0 + + + + + + + column + + + word + + + 1 + + + + + + + + + False + True + 1 + + + + + True + True + 6 + 1 + + + + + False + True + 6 + 0 + + + + + True + False + vertical + + + True + False + 5 + True + end + + + gtk-refresh + True + True + True + True + + + + False + True + 0 + + + + + gtk-apply + True + True + True + True + + + + False + True + 1 + + + + + gtk-undo + True + True + True + True + + + + False + True + 2 + + + + + gtk-quit + True + True + True + True + + + + False + True + 3 + + + + + False + True + 4 + 2 + + + + + False + True + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + False + + + + + + + + + + + + diff --git a/gui/pamac_update.glade b/gui/pamac_update.glade new file mode 100644 index 0000000..367bf39 --- /dev/null +++ b/gui/pamac_update.glade @@ -0,0 +1,150 @@ + + + + + False + + center + + + + False + True + True + vertical + 2 + + + False + 5 + end + + + gtk-refresh + True + True + True + True + + + + False + True + 0 + + + + + gtk-quit + True + True + True + True + + + + False + True + 1 + + + + + gtk-apply + True + True + True + True + + + + False + True + 2 + + + + + False + True + 4 + end + 0 + + + + + True + False + label + + + False + True + 1 + + + + + 390 + 490 + True + True + True + True + in + + + True + True + True + update_list + False + False + 0 + + + + + + column + + + + 0 + + + + + + + column + + + + 1 + + + + + + + + + False + True + 2 + + + + + + + + + + + + + + diff --git a/pamac.py b/pamac.py new file mode 100755 index 0000000..a8c1460 --- /dev/null +++ b/pamac.py @@ -0,0 +1,368 @@ +#! /usr/bin/python +# -*-coding:utf-8 -* + +from gi.repository import Gtk, GdkPixbuf, Gdk, GObject + +import pyalpm +import math +import sys +from time import strftime, localtime +from os import geteuid +import config +import transaction +import traceback + +interface = Gtk.Builder() +interface.add_from_file('gui/pamac.glade') +interface.add_from_file('gui/dialogs.glade') + +packages_list = interface.get_object('packages_list') +groups_list = interface.get_object('groups_list') +transaction_desc = interface.get_object('transaction_desc') +package_desc = interface.get_object('package_desc') +conf_label = interface.get_object('conf_label') +toggle = interface.get_object('cellrenderertoggle1') +search_entry = interface.get_object('search_entry') +tree2 = interface.get_object('treeview2_selection') +tree1 = interface.get_object('treeview1_selection') +installed_column = interface.get_object('installed_column') +name_column = interface.get_object('name_column') +ConfDialog = interface.get_object('ConfDialog') +ErrorDialog = interface.get_object('ErrorDialog') +down_label = interface.get_object('down_label') + +installed_column.set_sort_column_id(1) +name_column.set_sort_column_id(0) + +tmp_list = [] +for repo in config.handle.get_syncdbs(): + for name, pkgs in repo.grpcache: + if not name in tmp_list: + tmp_list.append(name) +tmp_list = sorted(tmp_list) +for name in tmp_list: + groups_list.append([name]) + +pkg_name_list = [] +pkg_object_dict = {} +pkg_installed_dict = {} +list_dict = None +current_group = None +transaction_type = None +transaction_dict = {} +t = None + +def set_list_dict_search(*patterns): + global pkg_name_list + global pkg_object_dict + global pkg_installed_dict + pkg_name_list = [] + pkg_object_dict = {} + pkg_installed_dict = {} + for db in config.handle.get_syncdbs(): + for pkg_object in db.search(*patterns): + if not pkg_object.name in pkg_name_list: + pkg_name_list.append(pkg_object.name) + pkg_object_dict[pkg_object.name] = pkg_object + pkg_installed_dict[pkg_object.name] = False + for pkg_object in config.handle.get_localdb().search(*patterns): + if not pkg_object.name in pkg_name_list: + pkg_name_list.append(pkg_object.name) + pkg_installed_dict[pkg_object.name] = True + pkg_object_dict[pkg_object.name] = pkg_object + pkg_name_list = sorted(pkg_name_list) + +def set_list_dict_group(group): + global pkg_name_list + global pkg_object_dict + global pkg_installed_dict + pkg_name_list = [] + pkg_object_dict = {} + pkg_installed_dict = {} + for db in config.handle.get_syncdbs(): + grp = db.read_grp(group) + if grp is not None: + name, pkg_list = grp + for pkg_object in pkg_list: + if not pkg_object.name in pkg_name_list: + pkg_name_list.append(pkg_object.name) + pkg_object_dict[pkg_object.name] = pkg_object + pkg_installed_dict[pkg_object.name] = False + db = config.handle.get_localdb() + grp = db.read_grp(group) + if grp is not None: + name, pkg_list = grp + for pkg_object in pkg_list: + if not pkg_object.name in pkg_name_list: + pkg_name_list.append(pkg_object.name) + pkg_installed_dict[pkg_object.name] = True + pkg_object_dict[pkg_object.name] = pkg_object + pkg_name_list = sorted(pkg_name_list) + +def refresh_packages_list(): + global packages_list + packages_list.clear() + if not pkg_name_list: + packages_list.append([" ", False, False]) + else: + for name in pkg_name_list: + if name in config.holdpkg: + packages_list.append([name, pkg_installed_dict[name], False]) + break + elif transaction_type is "install": + if pkg_installed_dict[name] is True: + packages_list.append([name, pkg_installed_dict[name], False]) + elif name in transaction_dict.keys(): + packages_list.append([name, True, True]) + else: + packages_list.append([name, pkg_installed_dict[name], True]) + elif transaction_type is "remove": + if pkg_installed_dict[name] is False: + packages_list.append([name, pkg_installed_dict[name], False]) + elif name in transaction_dict.keys(): + packages_list.append([name, False, True]) + else: + packages_list.append([name, pkg_installed_dict[name], True]) + else: + packages_list.append([name, pkg_installed_dict[name], True]) + +def set_packages_list(): + global list_dict + if list_dict == "search": + search_strings_list = search_entry.get_text().split() + set_list_dict_search(*search_strings_list) + if list_dict == "group": + set_list_dict_group(current_group) + refresh_packages_list() + +def set_desc(pkg, style): + """ + Args : + pkg_object -- the package to display + style -- 'local' or 'sync' + """ + + if style not in ['local', 'sync', 'file']: + raise ValueError('Invalid style for package info formatting') + + package_desc.clear() + + if style == 'sync': + package_desc.append(['Repository:', pkg.db.name]) + package_desc.append(['Name:', pkg.name]) + package_desc.append(['Version:', pkg.version]) + package_desc.append(['Description:', pkg.desc]) + package_desc.append(['URL:', pkg.url]) + package_desc.append(['Licenses:', ' '.join(pkg.licenses)]) + package_desc.append(['Groups:', ' '.join(pkg.groups)]) + package_desc.append(['Provides:', ' '.join(pkg.provides)]) + package_desc.append(['Depends On:', ' '.join(pkg.depends)]) + package_desc.append(['Optional Deps:', '\n'.join(pkg.optdepends)]) + if style == 'local': + package_desc.append(['Required By:', ' '.join(pkg.compute_requiredby())]) + package_desc.append(['Conflicts With:', ' '.join(pkg.conflicts)]) + package_desc.append(['Replaces:', ' '.join(pkg.replaces)]) + if style == 'sync': + package_desc.append(['Download Size:', transaction.format_size(pkg.size)]) + if style == 'file': + package_desc.append(['Compressed Size:', transaction.format_size(pkg.size)]) + package_desc.append(['Installed Size:', transaction.format_size(pkg.isize)]) + package_desc.append(['Packager:', pkg.packager]) + package_desc.append(['Architecture:', pkg.arch]) + package_desc.append(['Build Date:', strftime("%a %d %b %Y %X %Z", localtime(pkg.builddate))]) + + if style == 'local': + package_desc.append(['Install Date:', strftime("%a %d %b %Y %X %Z", localtime(pkg.installdate))]) + if pkg.reason == pyalpm.PKG_REASON_EXPLICIT: + reason = 'Explicitly installed' + elif pkg.reason == pyalpm.PKG_REASON_DEPEND: + reason = 'Installed as a dependency for another package' + else: + reason = 'N/A' + package_desc.append(['Install Reason:', reason]) + if style != 'sync': + package_desc.append(['Install Script:', 'Yes' if pkg.has_scriptlet else 'No']) + if style == 'sync': + package_desc.append(['MD5 Sum:', pkg.md5sum]) + package_desc.append(['SHA256 Sum:', pkg.sha256sum]) + package_desc.append(['Signatures:', 'Yes' if pkg.base64_sig else 'No']) + + if style == 'local': + if len(pkg.backup) == 0: + package_desc.append(['Backup files:', '']) + else: + package_desc.append(['Backup files:', '\n'.join(["%s %s" % (md5, file) for (file, md5) in pkg.backup])]) + +class Handler: + def on_MainWindow_delete_event(self, *arg): + Gtk.main_quit() + + def on_QuitButton_clicked(self, *arg): + Gtk.main_quit() + + def on_ValidButton_clicked(self, *arg): + global t + global transaction_type + global transaction_dict + global transaction_desc + if not geteuid() == 0: + ErrorDialog.format_secondary_text("You need to be root to run packages transactions") + response = ErrorDialog.run() + if response: + ErrorDialog.hide() + elif not transaction_dict: + ErrorDialog.format_secondary_text("No package is selected") + response = ErrorDialog.run() + if response: + ErrorDialog.hide() + else: + transaction_desc.clear() + t = transaction.init_transaction(config.handle) + if transaction_type is "install": + for pkg in transaction_dict.values(): + t.add_pkg(pkg) + if transaction_type is "remove": + for pkg in transaction_dict.values(): + t.remove_pkg(pkg) + try: + t.prepare() + except pyalpm.error: + ErrorDialog.format_secondary_text(traceback.format_exc()) + response = ErrorDialog.run() + if response: + ErrorDialog.hide() + t.release() + transaction.to_remove = t.to_remove + transaction.to_add = t.to_add + if transaction.to_remove: + transaction_desc.append(['To remove:', transaction.to_remove[0].name]) + i = 1 + while i < len(transaction.to_remove): + transaction_desc.append([' ', transaction.to_remove[i].name]) + i += 1 + down_label.set_markup('') + if transaction.to_add: + transaction_desc.append(['To install:', transaction.to_add[0].name]) + i = 1 + dsize = transaction.to_add[0].size + while i < len(transaction.to_add): + transaction_desc.append([' ', transaction.to_add[i].name]) + dsize += transaction.to_add[i].download_size + i += 1 + down_label.set_markup('Total Download size: '+transaction.format_size(dsize)) + response = ConfDialog.run() + if response == Gtk.ResponseType.OK: + ConfDialog.hide() + try: + t.commit() + except pyalpm.error: + ErrorDialog.format_secondary_text(traceback.format_exc()) + response = ErrorDialog.run() + if response: + ErrorDialog.hide() + t.release() + transaction_dict.clear() + transaction_type = None + set_packages_list() + transaction.ProgressWindow.hide() + if response == Gtk.ResponseType.CANCEL or Gtk.ResponseType.CLOSE or Gtk.ResponseType.DELETE_EVENT: + transaction.ProgressWindow.hide() + ConfDialog.hide() + t.release() + + def on_EraseButton_clicked(self, *arg): + global transaction_type + global transaction_dict + transaction_dict.clear() + transaction_type = None + refresh_packages_list() + + def on_RefreshButton_clicked(self, *arg): + transaction.do_refresh() + refresh_packages_list() + + def on_search_button_clicked(self, widget): + global list_dict + list_dict = "search" + set_packages_list() + + def on_search_entry_icon_press(self, *arg): + global list_dict + list_dict = "search" + set_packages_list() + + def on_search_entry_activate(self, widget): + global list_dict + list_dict = "search" + set_packages_list() + + def on_treeview2_selection_changed(self, widget): + liste, line = tree2.get_selected() + if line is not None: + if packages_list[line][0] in pkg_object_dict.keys(): + pkg_object = pkg_object_dict[packages_list[line][0]] + if pkg_installed_dict[packages_list[line][0]] is True: + style = "local" + else: + style = "sync" + set_desc(pkg_object, style) + + def on_treeview1_selection_changed(self, widget): + global list_dict + global current_group + liste, line = tree1.get_selected() + if line is not None: + list_dict = "group" + current_group = groups_list[line][0] + set_packages_list() + + def on_installed_column_clicked(self, widget): + installed_column.set_sort_column_id(1) + + def on_name_column_clicked(self, widget): + name_column.set_sort_column_id(0) + + def on_cellrenderertoggle1_toggled(self, widget, line): + global transaction_type + global transaction_dict + global pkg_object_dict + if packages_list[line][0] in transaction_dict.keys(): + transaction_dict.pop(packages_list[line][0]) + if not transaction_dict: + transaction_type = None + lin = 0 + while lin < len(packages_list): + if packages_list[lin][0] in config.holdpkg: + packages_list[lin][2] = False + else: + packages_list[lin][2] = True + lin += 1 + pass + else: + if packages_list[line][1] is True: + transaction_type = "remove" + transaction_dict[packages_list[line][0]] = pkg_object_dict[packages_list[line][0]] + lin = 0 + while lin < len(packages_list): + if not packages_list[lin][0] in transaction_dict.keys(): + if packages_list[lin][1] is False: + packages_list[lin][2] = False + lin += 1 + if packages_list[line][1] is False: + transaction_type = "install" + transaction_dict[packages_list[line][0]] = pkg_object_dict[packages_list[line][0]] + lin = 0 + while lin < len(packages_list): + if not packages_list[lin][0] in transaction_dict.keys(): + if packages_list[lin][1] is True: + packages_list[lin][2] = False + lin += 1 + packages_list[line][1] = not packages_list[line][1] + packages_list[line][2] = True + +#if __name__ == "__main__": +transaction.do_refresh() +interface.connect_signals(Handler()) +MainWindow = interface.get_object("MainWindow") +MainWindow.show_all() +Gtk.main() diff --git a/pamac_update.py b/pamac_update.py new file mode 100755 index 0000000..00bffaa --- /dev/null +++ b/pamac_update.py @@ -0,0 +1,60 @@ +#! /usr/bin/python +# -*-coding:utf-8 -* + +from gi.repository import Gtk, GdkPixbuf, Gdk + +import pyalpm +from time import strftime, localtime +from os import geteuid +import sys +import config +import transaction + +interface = Gtk.Builder() +interface.add_from_file('gui/pamac_update.glade') +interface.add_from_file('gui/dialogs.glade') + +update_listore = interface.get_object('update_list') +top_label = interface.get_object('top_label') + +def have_updates(): + available_updates = transaction.get_updates() + if not available_updates: + update_listore.append(["", ""]) + return False + else: + for pkg in available_updates: + pkgname = pkg.name + newversion = transaction.get_new_version_available(pkgname) + pkgname = pkg.name+" "+newversion + update_listore.append([pkgname, transaction.format_size(pkg.size)]) + return True + +class Handler: + def on_MainWindow_delete_event(self, *arg): + Gtk.main_quit() + + def on_QuitButton_clicked(self, *arg): + Gtk.main_quit() + + def on_ApplyButton_clicked(self, *arg): + print("Apply") + + def on_RefreshButton_clicked(self, *arg): + transaction.do_refresh() + have_updates() + +def main(): + update = have_updates() + top_label.set_justify(Gtk.Justification.CENTER) + if update is False: + top_label.set_markup("No update available") + else: + top_label.set_markup("Available updates") + interface.connect_signals(Handler()) + MainWindow = interface.get_object("MainWindow") + MainWindow.show_all() + Gtk.main() + +if __name__ == "__main__": + main() diff --git a/transaction.py b/transaction.py new file mode 100755 index 0000000..45dfac0 --- /dev/null +++ b/transaction.py @@ -0,0 +1,161 @@ +#! /usr/bin/python +# -*-coding:utf-8 -* + +from gi.repository import Gtk + +import pyalpm +import math +import sys +import config + +interface = Gtk.Builder() +interface.add_from_file('gui/dialogs.glade') + +ProgressWindow = interface.get_object('ProgressWindow') +progress_bar = interface.get_object('progressbar2') +progress_label = interface.get_object('progresslabel2') + +to_remove = None +to_add = None + +def init_transaction(handle): + "Transaction initialization" + handle.dlcb = cb_dl + handle.totaldlcb = totaldlcb + handle.eventcb = cb_event + handle.questioncb = cb_conv + handle.progresscb = cb_progress + try: + t = handle.init_transaction(cascade = True) + return t + except pyalpm.error: + ErrorDialog.format_secondary_text(traceback.format_exc()) + response = ErrorDialog.run() + if response: + ErrorDialog.hide() + return False + +def do_refresh(): + """Sync databases like pacman -Sy""" + ProgressWindow.show_all() + for db in config.handle.get_syncdbs(): + t = init_transaction(config.handle) + try: + db.update(force=False) + except pyalpm.error: + ErrorDialog.format_secondary_text(traceback.format_exc()) + response = ErrorDialog.run() + if response: + ErrorDialog.hide() + t.release() + ProgressWindow.hide() + progress_label.set_text('') + progress_bar.set_text('') + +def do_sysupgrade(): + """Upgrade a system like pacman -Su""" + t = init_transaction(config.handle) + t.sysupgrade(downgrade=False) + if len(t.to_add) + len(t.to_remove) == 0: + print("Nothing to do") + t.release() + return 0 + else: + ok = finalize(t) + return (0 if ok else 1) + +def get_updates(): + """Return a list of package objects in local db which can be updated""" + installed_pkglist = config.handle.get_localdb().pkgcache + result = [] + for pkg in installed_pkglist: + candidate = pyalpm.sync_newversion(pkg, config.handle.get_syncdbs()) + if candidate is not None: + result.append(candidate) + return result + +def get_new_version_available(pkgname): + for repo in config.handle.get_syncdbs(): + pkg = repo.get_pkg(pkgname) + if pkg is not None: + return pkg.version + break + +def format_size(size): + KiB_size = size / 1024 + if KiB_size < 1000: + size_string = '%.1f KiB' % (KiB_size) + return size_string + else: + size_string = '%.2f MiB' % (KiB_size / 1024) + return size_string + +# Callbacks +event_text = ' ' +def cb_event(ID, event, tupel): + global event_text + ProgressWindow.show_all() + while Gtk.events_pending(): + Gtk.main_iteration() + for i in [1,3,5,7,9,11,15]: + if ID is i: + progress_label.set_text(event) + print(event) + break + else : + progress_label.set_text(' ') + if ID is 27: + progress_label.set_text('Downloading '+format_size(total_size)) + print('Downloading a file') + progress_bar.set_fraction(0.0) + progress_bar.set_text('') + +def cb_conv(*args): + print("conversation", args) + +total_size = 0 +def totaldlcb(_total_size): + global total_size + total_size = _total_size + +already_transferred = 0 +def cb_dl(_target, _transferred, total): + global already_transferred + while Gtk.events_pending(): + Gtk.main_iteration() + if total_size > 0: + fraction = (_transferred+already_transferred)/total_size + size = 0 + if (to_remove or to_add): + for pkg in to_remove+to_add: + if pkg.name+'-'+pkg.version in _target: + size = pkg.size + if _transferred == size: + already_transferred += size + progress_label.set_text('Downloading '+format_size(total_size)) + progress_bar.set_text(_target) + progress_bar.set_fraction(fraction) + else: + progress_label.set_text('Downloading...') + progress_bar.set_text(_target) + progress_bar.pulse() + + +def cb_progress(_target, _percent, n, i): + while Gtk.events_pending(): + Gtk.main_iteration() + target = _target+' ('+str(i)+'/'+str(n)+')' + progress_bar.set_fraction(_percent/100) + progress_bar.set_text(target) + +if __name__ == "__main__": + do_refresh() + available_updates = get_updates() + if not available_updates: + print("\nNo update available") + else: + for pkg in available_updates: + pkgname = pkg.name + oldversion = pkg.version + newversion = get_new_version_available(pkgname) + print("\n{} {} can be updated to {}".format(pkgname, oldversion, newversion))