diff --git a/blackbox/defaults/blackbox.yaml b/blackbox/defaults/blackbox.yaml index 82b34aa..7917fa0 100644 --- a/blackbox/defaults/blackbox.yaml +++ b/blackbox/defaults/blackbox.yaml @@ -1,11 +1,11 @@ - name: "Display Package Versions" - description: "" + description: "Show package version labels alongside the package description " enabled: False - name: "Display Package Progress" - description: "" + description: "Show the package install/uninstall progress window " enabled: False - name: "Debug Loggings" - description: "" + description: "Enable debug logging for more verbose app logging " enabled: False \ No newline at end of file diff --git a/blackbox/ui/PackageSearchWindow.py b/blackbox/ui/PackageSearchWindow.py new file mode 100644 index 0000000..ed17411 --- /dev/null +++ b/blackbox/ui/PackageSearchWindow.py @@ -0,0 +1,542 @@ +# This class is used to create a window for package name searches and to display package information + +import os +import gi + +import Functions as fn +from ui.MessageDialog import MessageDialog + +from gi.repository import Gtk, Gdk, GdkPixbuf, Pango, GLib +gi.require_version("Gtk", "3.0") + +base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + + +class PackageSearchWindow(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self) + + self.headerbar = Gtk.HeaderBar() + self.headerbar.set_title("Package Search") + self.headerbar.set_show_close_button(True) + + # remove the focus on startup from search entry + self.headerbar.set_property("can-focus", True) + Gtk.Window.grab_focus(self.headerbar) + + self.set_resizable(True) + self.set_size_request(700, 500) + self.set_border_width(10) + self.set_titlebar(self.headerbar) + self.set_icon_from_file(os.path.join(base_dir, "images/blackbox.png")) + self.search_package_activated = False + self.build_gui() + + def build_gui(self): + self.stack = Gtk.Stack() + self.stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) + self.stack.set_transition_duration(350) + self.stack.set_hhomogeneous(False) + self.stack.set_vhomogeneous(False) + + stack_switcher = Gtk.StackSwitcher() + stack_switcher.set_orientation(Gtk.Orientation.HORIZONTAL) + stack_switcher.set_stack(self.stack) + stack_switcher.set_homogeneous(True) + + searchentry = Gtk.SearchEntry() + searchentry.set_placeholder_text("Search using package name...") + searchentry.set_size_request(400, 0) + searchentry.connect("activate", self.on_search_package_activated) + searchentry.connect("icon-release", self.on_search_package_cleared) + + btn_ok = Gtk.Button(label="OK") + btn_ok.set_size_request(100, 30) + btn_ok.connect("clicked", self.on_close) + btn_ok.set_halign(Gtk.Align.END) + + grid_bottom = Gtk.Grid() + grid_bottom.attach(btn_ok, 0, 1, 1, 1) + grid_bottom.set_halign(Gtk.Align.END) + + vbox_bottom = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) + lbl_padding_bottom = Gtk.Label(xalign=0, yalign=0) + lbl_padding_bottom.set_text("") + + vbox_bottom.pack_start(lbl_padding_bottom, False, True, 0) + vbox_bottom.pack_start(grid_bottom, False, True, 0) + + self.stack.add_titled(searchentry, "Package Search", "Package Search") + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) + vbox.set_border_width(10) + + vbox.pack_start(stack_switcher, False, False, 0) + vbox.pack_start(self.stack, False, False, 0) + vbox.pack_end(vbox_bottom, False, True, 0) + + self.add(vbox) + self.show_all() + + thread_pacman_sync_file_db = fn.threading.Thread( + name="thread_pacman_sync_file_db", + target=fn.sync_file_db, + daemon=True, + ) + thread_pacman_sync_file_db.start() + + def on_close(self, widget): + self.hide() + self.destroy() + + def on_search_package_activated(self, searchentry): + if searchentry.get_text_length() == 0 and self.search_package_activated: + self.search_package_activated = False + self.stack.get_child_by_name("Package Information").destroy() + + self.stack.get_child_by_name("Package Files").destroy() + Gtk.Window.grab_focus(self.headerbar) + else: + self.perform_search(searchentry) + + def on_search_package_cleared(self, searchentry, icon_pos, event): + searchentry.set_placeholder_text("Search using package name...") + if self.search_package_activated is True: + self.search_package_activated = False + self.stack.get_child_by_name("Package Information").destroy() + + self.stack.get_child_by_name("Package Files").destroy() + + Gtk.Window.grab_focus(self.headerbar) + + def perform_search(self, searchentry): + try: + if ( + len(searchentry.get_text().rstrip().lstrip()) > 0 + and not searchentry.get_text().isspace() + ): + term = searchentry.get_text().rstrip().lstrip() + + if len(term) > 0: + fn.logger.info("Searching pacman file database") + + package_metadata = fn.get_package_information(term) + + if package_metadata is not None: + # package information + + if self.search_package_activated is True: + self.stack.get_child_by_name( + "Package Information" + ).destroy() + + self.stack.get_child_by_name("Package Files").destroy() + + box_outer = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=5 + ) + + listbox = Gtk.ListBox() + listbox.set_selection_mode(Gtk.SelectionMode.NONE) + box_outer.pack_start(listbox, True, True, 0) + + # package name + row_package_title = Gtk.ListBoxRow() + vbox_package_title = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_title.add(vbox_package_title) + lbl_package_name_title = Gtk.Label(xalign=0) + lbl_package_name_title.set_markup("Package Name") + + lbl_package_name_value = Gtk.Label(xalign=0) + lbl_package_name_value.set_text(package_metadata["name"]) + vbox_package_title.pack_start( + lbl_package_name_title, True, True, 0 + ) + vbox_package_title.pack_start( + lbl_package_name_value, True, True, 0 + ) + + listbox.add(row_package_title) + + # repository + + row_package_repo = Gtk.ListBoxRow() + vbox_package_repo = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_repo.add(vbox_package_repo) + lbl_package_repo_title = Gtk.Label(xalign=0) + lbl_package_repo_title.set_markup("Repository") + + lbl_package_repo_value = Gtk.Label(xalign=0) + lbl_package_repo_value.set_text(package_metadata["repository"]) + vbox_package_repo.pack_start( + lbl_package_repo_title, True, True, 0 + ) + vbox_package_repo.pack_start( + lbl_package_repo_value, True, True, 0 + ) + + listbox.add(row_package_repo) + + # description + + row_package_description = Gtk.ListBoxRow() + vbox_package_description = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_description.add(vbox_package_description) + lbl_package_description_title = Gtk.Label(xalign=0) + lbl_package_description_title.set_markup("Description") + + lbl_package_description_value = Gtk.Label(xalign=0) + lbl_package_description_value.set_text( + package_metadata["description"] + ) + vbox_package_description.pack_start( + lbl_package_description_title, True, True, 0 + ) + vbox_package_description.pack_start( + lbl_package_description_value, True, True, 0 + ) + + listbox.add(row_package_description) + + # arch + + row_package_arch = Gtk.ListBoxRow() + vbox_package_arch = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_arch.add(vbox_package_arch) + lbl_package_arch_title = Gtk.Label(xalign=0) + lbl_package_arch_title.set_markup("Architecture") + + lbl_package_arch_value = Gtk.Label(xalign=0) + lbl_package_arch_value.set_text(package_metadata["arch"]) + vbox_package_arch.pack_start( + lbl_package_arch_title, True, True, 0 + ) + vbox_package_arch.pack_start( + lbl_package_arch_value, True, True, 0 + ) + + listbox.add(row_package_arch) + + # url + + row_package_url = Gtk.ListBoxRow() + vbox_package_url = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_url.add(vbox_package_url) + lbl_package_url_title = Gtk.Label(xalign=0) + lbl_package_url_title.set_markup("URL") + + lbl_package_url_value = Gtk.Label(xalign=0) + lbl_package_url_value.set_markup( + "%s" + % (package_metadata["url"], package_metadata["url"]) + ) + vbox_package_url.pack_start( + lbl_package_url_title, True, True, 0 + ) + vbox_package_url.pack_start( + lbl_package_url_value, True, True, 0 + ) + + listbox.add(row_package_url) + + # download size + + row_package_size = Gtk.ListBoxRow() + vbox_package_size = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_size.add(vbox_package_size) + lbl_package_size_title = Gtk.Label(xalign=0) + lbl_package_size_title.set_markup("Download size") + + lbl_package_size_value = Gtk.Label(xalign=0) + lbl_package_size_value.set_text( + package_metadata["download_size"] + ) + vbox_package_size.pack_start( + lbl_package_size_title, True, True, 0 + ) + vbox_package_size.pack_start( + lbl_package_size_value, True, True, 0 + ) + + listbox.add(row_package_size) + + # installed size + + row_package_installed_size = Gtk.ListBoxRow() + vbox_package_installed_size = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_installed_size.add(vbox_package_installed_size) + lbl_package_installed_size_title = Gtk.Label(xalign=0) + lbl_package_installed_size_title.set_markup( + "Installed size" + ) + + lbl_package_installed_size_value = Gtk.Label(xalign=0) + lbl_package_installed_size_value.set_text( + package_metadata["installed_size"] + ) + vbox_package_installed_size.pack_start( + lbl_package_installed_size_title, True, True, 0 + ) + vbox_package_installed_size.pack_start( + lbl_package_installed_size_value, True, True, 0 + ) + + listbox.add(row_package_installed_size) + + # build date + + row_package_build_date = Gtk.ListBoxRow() + vbox_package_build_date = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_build_date.add(vbox_package_build_date) + lbl_package_build_date_title = Gtk.Label(xalign=0) + lbl_package_build_date_title.set_markup("Build date") + + lbl_package_build_date_value = Gtk.Label(xalign=0) + lbl_package_build_date_value.set_text( + package_metadata["build_date"] + ) + vbox_package_build_date.pack_start( + lbl_package_build_date_title, True, True, 0 + ) + vbox_package_build_date.pack_start( + lbl_package_build_date_value, True, True, 0 + ) + + listbox.add(row_package_build_date) + + # packager + + row_package_maintainer = Gtk.ListBoxRow() + vbox_package_maintainer = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_maintainer.add(vbox_package_maintainer) + lbl_package_maintainer_title = Gtk.Label(xalign=0) + lbl_package_maintainer_title.set_markup("Packager") + + lbl_package_maintainer_value = Gtk.Label(xalign=0) + lbl_package_maintainer_value.set_text( + package_metadata["packager"] + ) + vbox_package_maintainer.pack_start( + lbl_package_maintainer_title, True, True, 0 + ) + vbox_package_maintainer.pack_start( + lbl_package_maintainer_value, True, True, 0 + ) + + listbox.add(row_package_maintainer) + + # depends on + + expander_depends_on = Gtk.Expander() + expander_depends_on.set_expanded(True) + expander_depends_on.set_use_markup(True) + expander_depends_on.set_resize_toplevel(True) + expander_depends_on.set_label("Depends on") + + row_package_depends_on = Gtk.ListBoxRow() + expander_depends_on.add(row_package_depends_on) + vbox_package_depends_on = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_depends_on.add(vbox_package_depends_on) + + if len(package_metadata["depends_on"]) > 0: + treestore_depends = Gtk.TreeStore(str, str) + + for item in package_metadata["depends_on"]: + treestore_depends.append(None, list(item)) + + treeview_depends = Gtk.TreeView(model=treestore_depends) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn("Package", renderer, text=0) + + treeview_depends.append_column(column) + + vbox_package_depends_on.pack_start( + treeview_depends, True, True, 0 + ) + + else: + lbl_package_depends_value = Gtk.Label(xalign=0, yalign=0) + lbl_package_depends_value.set_text("None") + + vbox_package_depends_on.pack_start( + lbl_package_depends_value, True, True, 0 + ) + + listbox.add(expander_depends_on) + + # conflicts with + + expander_conflicts_with = Gtk.Expander() + expander_conflicts_with.set_use_markup(True) + expander_conflicts_with.set_expanded(True) + expander_conflicts_with.set_resize_toplevel(True) + expander_conflicts_with.set_label("Conflicts with") + + row_package_conflicts_with = Gtk.ListBoxRow() + expander_conflicts_with.add(row_package_conflicts_with) + vbox_package_conflicts_with = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_conflicts_with.add(vbox_package_conflicts_with) + + if len(package_metadata["conflicts_with"]) > 0: + treestore_conflicts = Gtk.TreeStore(str, str) + + for item in package_metadata["conflicts_with"]: + treestore_conflicts.append(None, list(item)) + + treeview_conflicts = Gtk.TreeView(model=treestore_conflicts) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn("Package", renderer, text=0) + + treeview_conflicts.append_column(column) + + vbox_package_conflicts_with.pack_start( + treeview_conflicts, True, True, 0 + ) + + else: + lbl_package_conflicts_with_value = Gtk.Label( + xalign=0, yalign=0 + ) + lbl_package_conflicts_with_value.set_text("None") + + vbox_package_conflicts_with.pack_start( + lbl_package_conflicts_with_value, True, True, 0 + ) + + listbox.add(expander_conflicts_with) + + checkbtn_installed = Gtk.CheckButton(label="Installed") + checkbtn_installed.set_active(False) + checkbtn_installed.set_sensitive(False) + + # is the package installed + installed = fn.check_package_installed(term) + + if installed is True: + checkbtn_installed.set_active(True) + + # box_outer.pack_start(checkbtn_installed, True, True, 0) + + scrolled_window_package_info = Gtk.ScrolledWindow() + scrolled_window_package_info.set_propagate_natural_height(True) + scrolled_window_package_info.add(box_outer) + + vbox_package_info = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + + lbl_padding_vbox = Gtk.Label(xalign=0, yalign=0) + lbl_padding_vbox.set_text("") + + vbox_package_info.pack_start( + scrolled_window_package_info, True, True, 0 + ) + vbox_package_info.pack_start(lbl_padding_vbox, True, True, 0) + vbox_package_info.pack_start(checkbtn_installed, True, True, 0) + + self.stack.add_titled( + vbox_package_info, + "Package Information", + "Package Information", + ) + + # package files + + package_files = fn.get_package_files(term) + if package_files is not None: + lbl_package_title = Gtk.Label(xalign=0, yalign=0) + lbl_package_title.set_markup("Package") + + lbl_package_title_value = Gtk.Label(xalign=0, yalign=0) + + lbl_package_title_value.set_text(package_metadata["name"]) + + treestore_filelist = Gtk.TreeStore(str, str) + + for file in package_files: + treestore_filelist.append(None, list(file)) + + treeview_files = Gtk.TreeView(model=treestore_filelist) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn("Files", renderer, text=0) + + treeview_files.append_column(column) + + vbox_package_files = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + + vbox_package_files.pack_start( + lbl_package_title, True, True, 0 + ) + vbox_package_files.pack_start( + lbl_package_title_value, True, True, 0 + ) + + lbl_padding_package_files = Gtk.Label(xalign=0, yalign=0) + lbl_padding_package_files.set_text("") + + vbox_package_files.pack_start( + lbl_padding_package_files, True, True, 0 + ) + + scrolled_window_package_files = Gtk.ScrolledWindow() + scrolled_window_package_files.set_propagate_natural_height( + True + ) + scrolled_window_package_files.add(treeview_files) + + vbox_package_files.pack_start( + scrolled_window_package_files, True, True, 0 + ) + + self.stack.add_titled( + vbox_package_files, + "Package Files", + "Package Files", + ) + + self.search_package_activated = True + self.show_all() + + else: + message_dialog = MessageDialog( + "Info", + "Search returned 0 results", + "Failed to find package name", + "Are the correct pacman mirrorlists configured ?\nOr try to search again using the exact package name", + "info", + False, + ) + + message_dialog.show_all() + message_dialog.run() + message_dialog.hide() + + except Exception as e: + fn.logger.error("Exception in perform_search(): %s" % e) \ No newline at end of file diff --git a/blackbox/ui/PackagesImportDialog.py b/blackbox/ui/PackagesImportDialog.py new file mode 100644 index 0000000..3415b7f --- /dev/null +++ b/blackbox/ui/PackagesImportDialog.py @@ -0,0 +1,223 @@ +import os +import gi +import Functions as fn +from queue import Queue +from ui.MessageDialog import MessageDialog + +from gi.repository import Gtk, Gdk, GdkPixbuf, Pango, GLib +gi.require_version("Gtk", "3.0") + +base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + + +class PackagesImportDialog(Gtk.Dialog): + """create a gui""" + + def __init__(self, package_file, packages_list, logfile): + Gtk.Dialog.__init__(self) + + # Create a queue for storing package import messages from pacman + self.pkg_import_queue = Queue() + + # Create a queue for storing package install errors + self.pkg_err_queue = Queue() + + # Create a queue for storing package install status + self.pkg_status_queue = Queue() + + self.package_file = package_file + self.packages_list = packages_list + self.logfile = logfile + + self.stop_thread = False + + self.set_resizable(True) + self.set_border_width(10) + self.set_size_request(800, 700) + self.set_modal(True) + + headerbar = Gtk.HeaderBar() + headerbar.set_title("Import packages") + headerbar.set_show_close_button(True) + + self.set_titlebar(headerbar) + + self.set_icon_from_file(os.path.join(base_dir, "images/blackbox.png")) + + hbox_title = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) + lbl_packages_title = Gtk.Label(xalign=0) + lbl_packages_title.set_name("title") + lbl_packages_title.set_text("Packages") + + hbox_title.pack_start(lbl_packages_title, False, False, 0) + + hbox_title_install = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) + label_install_title = Gtk.Label(xalign=0) + label_install_title.set_markup(" Install Packages") + + hbox_title_install.pack_start(label_install_title, False, False, 0) + + hbox_sep = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) + hsep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) + hbox_sep.pack_start(hsep, True, True, 0) + + frame_install = Gtk.Frame(label="") + frame_install_label = frame_install.get_label_widget() + frame_install_label.set_markup("Install Packages") + + hbox_install = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) + label_install_desc = Gtk.Label(xalign=0, yalign=0) + label_install_desc.set_markup( + f"" + f" WARNING: Proceed with caution this will install packages onto your system!\n" + f" Packages from the AUR are not supported \n" + f" This also performs a full system upgrade\n\n" + f" - A list of packages are sourced from {self.package_file}\n" + f" - To ignore a package, add a # in front of the package name\n" + f" - Log file: {self.logfile}\n" + f" - A reboot is recommended when core Linux packages are installed\n" + ) + + self.scrolled_window = Gtk.ScrolledWindow() + + self.textview = Gtk.TextView() + self.textview.set_name("textview_log") + self.textview.set_property("editable", False) + self.textview.set_property("monospace", True) + self.textview.set_border_width(10) + self.textview.set_vexpand(True) + self.textview.set_hexpand(True) + + msg_buffer = self.textview.get_buffer() + msg_buffer.insert( + msg_buffer.get_end_iter(), + "\n Click Yes to confirm install of the following packages:\n\n", + ) + + lbl_title_message = Gtk.Label(xalign=0, yalign=0) + lbl_title_message.set_markup( + "There are %s packages to install, proceed ?" + % len(self.packages_list) + ) + lbl_padding1 = Gtk.Label(xalign=0, yalign=0) + lbl_padding1.set_text("") + + lbl_padding2 = Gtk.Label(xalign=0, yalign=0) + lbl_padding2.set_text("") + + self.infobar = Gtk.InfoBar() + + content = self.infobar.get_content_area() + content.add(lbl_title_message) + + self.infobar.set_revealed(True) + + for package in sorted(self.packages_list): + msg_buffer.insert(msg_buffer.get_end_iter(), " - %s\n" % package) + + # move focus away from the textview, to hide the cursor at load + headerbar.set_property("can-focus", True) + Gtk.Window.grab_focus(headerbar) + + self.scrolled_window.add(self.textview) + + self.button_yes = self.add_button("Yes", Gtk.ResponseType.OK) + self.button_yes.set_size_request(100, 30) + btn_yes_context = self.button_yes.get_style_context() + btn_yes_context.add_class("destructive-action") + + self.button_no = self.add_button("Close", Gtk.ResponseType.CANCEL) + self.button_no.set_size_request(100, 30) + + self.connect("response", self.on_response) + + vbox_log_dir = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) + + btn_open_log_dir = Gtk.Button(label="Open log directory") + btn_open_log_dir.connect("clicked", self.on_open_log_dir_clicked) + btn_open_log_dir.set_size_request(100, 30) + + vbox_log_dir.pack_start(btn_open_log_dir, False, False, 0) + + grid_message = Gtk.Grid() + + grid_message.attach(label_install_desc, 0, 0, 1, 1) + grid_message.attach(self.infobar, 0, 1, 1, 1) + grid_message.attach(lbl_padding1, 0, 2, 1, 1) + + grid_message.attach(self.scrolled_window, 0, 3, 1, 1) + grid_message.attach(lbl_padding2, 0, 4, 1, 1) + grid_message.attach(vbox_log_dir, 0, 5, 1, 1) + + self.vbox.add(grid_message) + + def on_open_log_dir_clicked(self, widget): + fn.open_log_dir() + + def display_progress(self): + self.textview.destroy() + self.infobar.destroy() + self.button_yes.destroy() + + self.label_package_status = Gtk.Label(xalign=0, yalign=0) + self.label_package_count = Gtk.Label(xalign=0, yalign=0) + + label_warning_close = Gtk.Label(xalign=0, yalign=0) + label_warning_close.set_markup( + "Do not close this window during package installation" + ) + + self.textview = Gtk.TextView() + self.textview.set_name("textview_log") + self.textview.set_property("editable", False) + self.textview.set_property("monospace", True) + self.textview.set_border_width(10) + self.textview.set_vexpand(True) + self.textview.set_hexpand(True) + + self.scrolled_window.add(self.textview) + + self.msg_buffer = self.textview.get_buffer() + + self.vbox.add(label_warning_close) + self.vbox.add(self.label_package_status) + self.vbox.add(self.label_package_count) + + fn.Thread( + target=fn.monitor_package_import, + args=(self,), + daemon=True, + ).start() + + self.show_all() + + fn.logger.info("Installing packages") + event = "%s [INFO]: Installing packages\n" % fn.datetime.now().strftime( + "%Y-%m-%d-%H-%M-%S" + ) + + fn.logger.info("Log file = %s" % self.logfile) + + self.pkg_import_queue.put(event) + + # debug install, overrride packages_list + + # starts 2 threads one to install the packages, and another to check install status + + fn.Thread( + target=fn.import_packages, + args=(self,), + daemon=True, + ).start() + + fn.Thread(target=fn.log_package_status, args=(self,), daemon=True).start() + + def on_response(self, dialog, response): + if response in (Gtk.ResponseType.OK, Gtk.ResponseType.YES): + self.stop_thread = False + self.display_progress() + + else: + self.stop_thread = True + dialog.hide() + dialog.destroy() \ No newline at end of file diff --git a/blackbox/ui/PacmanLogWindow.py b/blackbox/ui/PacmanLogWindow.py new file mode 100644 index 0000000..2c47bf6 --- /dev/null +++ b/blackbox/ui/PacmanLogWindow.py @@ -0,0 +1,71 @@ +# This class is used to create a window to monitor the pacman log file inside /var/log/pacman.log + +import os +import gi +import Functions as fn +from gi.repository import Gtk, Gdk, GdkPixbuf, Pango, GLib + +gi.require_version("Gtk", "3.0") + +base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +# base_dir = os.path.dirname(os.path.realpath(__file__)) + + +class PacmanLogWindow(Gtk.Window): + def __init__(self, textview_pacmanlog, btn_pacmanlog): + Gtk.Window.__init__(self) + + self.start_logtimer = True + self.textview_pacmanlog = textview_pacmanlog + self.btn_pacmanlog = btn_pacmanlog + headerbar = Gtk.HeaderBar() + + headerbar.set_show_close_button(True) + + self.set_titlebar(headerbar) + + self.set_title("BlackBox - Pacman log file viewer") + self.set_default_size(800, 600) + self.set_resizable(True) + self.set_border_width(10) + self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) + self.set_icon_from_file(os.path.join(base_dir, "images/blackbox.png")) + self.connect("delete-event", self.on_close) + + btn_pacmanlog_ok = Gtk.Button(label="OK") + btn_pacmanlog_ok.connect("clicked", self.on_response, "response") + btn_pacmanlog_ok.set_size_request(100, 30) + btn_pacmanlog_ok.set_halign(Gtk.Align.END) + + pacmanlog_scrolledwindow = Gtk.ScrolledWindow() + pacmanlog_scrolledwindow.set_size_request(750, 500) + pacmanlog_scrolledwindow.add(self.textview_pacmanlog) + + lbl_padding1 = Gtk.Label(xalign=0, yalign=0) + lbl_padding1.set_text("") + + vbox_pacmanlog = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + + vbox_pacmanlog.pack_start(pacmanlog_scrolledwindow, True, True, 0) + vbox_pacmanlog.pack_start(lbl_padding1, False, False, 0) + vbox_pacmanlog.pack_start(btn_pacmanlog_ok, False, False, 0) + + self.add(vbox_pacmanlog) + + def on_close(self, widget, data): + fn.logger.debug("Closing pacman log monitoring window") + self.start_logtimer = False + self.btn_pacmanlog.set_sensitive(True) + + self.hide() + self.destroy() + + def on_response(self, widget, response): + # stop updating the textview + fn.logger.debug("Closing pacman log monitoring dialog") + self.start_logtimer = False + self.btn_pacmanlog.set_sensitive(True) + + # self.remove(self) + self.hide() + self.destroy() \ No newline at end of file diff --git a/blackbox/ui/ProgressBarWindow.py b/blackbox/ui/ProgressBarWindow.py new file mode 100644 index 0000000..610c8d8 --- /dev/null +++ b/blackbox/ui/ProgressBarWindow.py @@ -0,0 +1,87 @@ +from gi.repository import Gtk, GLib +import gi + + +# Since a system can have multiple versions +# of GTK + installed, we want to make +# sure that we are importing GTK + 3. +gi.require_version("Gtk", "3.0") + + +class ProgressBarWindow(Gtk.Window): + new_value = 0.0 + + def __init__(self): + Gtk.Window.__init__(self, title="Progress Bar") + self.set_border_width(10) + + vbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + self.add(vbox) + + # Create a ProgressBar + self.progressbar = Gtk.ProgressBar() + vbox.pack_start(self.progressbar, True, True, 0) + + # Create CheckButton with labels "Show text", + # "Activity mode", "Right to Left" respectively + # button = Gtk.CheckButton(label="Show text") + # button.connect("toggled", self.on_show_text_toggled) + # vbox.pack_start(button, True, True, 0) + + # button = Gtk.CheckButton(label="Activity mode") + # button.connect("toggled", self.on_activity_mode_toggled) + # vbox.pack_start(button, True, True, 0) + + # button = Gtk.CheckButton(label="Right to Left") + # button.connect("toggled", self.on_right_to_left_toggled) + # vbox.pack_start(button, True, True, 0) + + # self.timeout_id = GLib.timeout_add(5000, self.on_timeout, None) + self.activity_mode = False + + def set_text(self, text): + self.progressbar.set_text(text) + self.progressbar.set_show_text(True) + + def reset_timer(self): + new_value = 0.0 + self.progressbar.set_fraction(new_value) + + def on_activity_mode_toggled(self, button): + self.activity_mode = button.get_active() + if self.activity_mode: + self.progressbar.pulse() + else: + self.progressbar.set_fraction(0.0) + + def on_right_to_left_toggled(self, button): + value = button.get_active() + self.progressbar.set_inverted(value) + + def update(self, fraction): + new_value = self.progressbar.get_fraction() + fraction + self.progressbar.set_fraction(new_value) + if new_value >= 1.0: + return False + return True + + def get_complete(self): + if self.progressbar.get_fraction() >= 1.0: + return True + return False + + def on_timeout(self, user_data=0.01): + """ + Update value on the progress bar + """ + if self.activity_mode: + self.progressbar.pulse() + else: + new_value = self.progressbar.get_fraction() + user_data + + if new_value > 1: + new_value = 0.0 + return False + + self.progressbar.set_fraction(new_value) + return True \ No newline at end of file diff --git a/blackbox/ui/ProgressDialog.py b/blackbox/ui/ProgressDialog.py new file mode 100644 index 0000000..73d42bf --- /dev/null +++ b/blackbox/ui/ProgressDialog.py @@ -0,0 +1,400 @@ +# This class is used to create a modal dialog window showing progress of a package install/uninstall and general package information + +import os +import gi +import Functions as fn +from ui.MessageDialog import MessageDialog +from gi.repository import Gtk, Gdk, GdkPixbuf, Pango, GLib + +gi.require_version("Gtk", "3.0") + +base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +# base_dir = os.path.dirname(os.path.realpath(__file__)) + + +class ProgressDialog(Gtk.Dialog): + def __init__(self, action, package, command, package_metadata): + Gtk.Dialog.__init__(self) + + self.package_found = True + # this gets package information using pacman -Si or pacman -Qi whichever returns output + # package_metadata = fn.get_package_information(pkg.name) + + # if a mirrorlist isn't configured properly, pacman will not be able to query its repository + # so the following is a condition to make sure the data returned isn't an error + + if type(package_metadata) is dict: + package_progress_dialog_headerbar = Gtk.HeaderBar() + package_progress_dialog_headerbar.set_show_close_button(True) + self.set_titlebar(package_progress_dialog_headerbar) + + self.connect("delete-event", package_progress_dialog_on_close, self, action) + + if action == "install": + self.set_title("BlackBox - installing package %s" % package.name) + + elif action == "uninstall": + self.set_title("BlackBox - removing package %s" % package.name) + + self.btn_package_progress_close = Gtk.Button(label="OK") + self.btn_package_progress_close.connect( + "clicked", + on_package_progress_close_response, + self, + ) + self.btn_package_progress_close.set_sensitive(False) + self.btn_package_progress_close.set_size_request(100, 30) + self.btn_package_progress_close.set_halign(Gtk.Align.END) + + self.set_resizable(True) + self.set_size_request(850, 700) + self.set_modal(True) + self.set_border_width(10) + self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) + self.set_icon_from_file(os.path.join(base_dir, "images/blackbox.png")) + + lbl_pacman_action_title = Gtk.Label(xalign=0, yalign=0) + lbl_pacman_action_title.set_text("Running command:") + + lbl_pacman_action_value = Gtk.Label(xalign=0, yalign=0) + lbl_pacman_action_value.set_markup("%s" % command) + + stack = Gtk.Stack() + stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) + stack.set_transition_duration(350) + stack.set_hhomogeneous(False) + stack.set_vhomogeneous(False) + + stack_switcher = Gtk.StackSwitcher() + stack_switcher.set_orientation(Gtk.Orientation.HORIZONTAL) + stack_switcher.set_stack(stack) + stack_switcher.set_homogeneous(True) + + package_progress_grid = Gtk.Grid() + + self.infobar = Gtk.InfoBar() + self.infobar.set_name("infobar_info") + + content = self.infobar.get_content_area() + content.add(lbl_pacman_action_title) + content.add(lbl_pacman_action_value) + + self.infobar.set_revealed(True) + + lbl_padding_header1 = Gtk.Label(xalign=0, yalign=0) + lbl_padding_header1.set_text("") + + package_progress_grid.attach(lbl_padding_header1, 0, 1, 1, 1) + package_progress_grid.attach(self.infobar, 0, 2, 1, 1) + + package_progress_grid.set_property("can-focus", True) + Gtk.Window.grab_focus(package_progress_grid) + + lbl_padding1 = Gtk.Label(xalign=0, yalign=0) + lbl_padding1.set_text("") + + lbl_padding2 = Gtk.Label(xalign=0, yalign=0) + lbl_padding2.set_text("") + lbl_padding2.set_halign(Gtk.Align.END) + + package_progress_grid.attach(lbl_padding1, 0, 3, 1, 1) + + package_progress_scrolled_window = Gtk.ScrolledWindow() + self.package_progress_textview = Gtk.TextView() + self.package_progress_textview.set_property("editable", False) + self.package_progress_textview.set_property("monospace", True) + self.package_progress_textview.set_border_width(10) + self.package_progress_textview.set_vexpand(True) + self.package_progress_textview.set_hexpand(True) + buffer = self.package_progress_textview.get_buffer() + self.package_progress_textview.set_buffer(buffer) + + package_progress_scrolled_window.set_size_request(700, 430) + + package_progress_scrolled_window.add(self.package_progress_textview) + package_progress_grid.attach(package_progress_scrolled_window, 0, 4, 1, 1) + + vbox_close = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + + vbox_close.pack_start(lbl_padding2, True, True, 0) + vbox_close.pack_start(self.btn_package_progress_close, True, True, 0) + + stack.add_titled(package_progress_grid, "Progress", "Package Progress") + + # package information + box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) + + listbox = Gtk.ListBox() + listbox.set_selection_mode(Gtk.SelectionMode.NONE) + box_outer.pack_start(listbox, True, True, 0) + + # package name + row_package_title = Gtk.ListBoxRow() + vbox_package_title = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_title.add(vbox_package_title) + lbl_package_name_title = Gtk.Label(xalign=0) + lbl_package_name_title.set_markup("Package Name") + + lbl_package_name_value = Gtk.Label(xalign=0) + lbl_package_name_value.set_text(package_metadata["name"]) + vbox_package_title.pack_start(lbl_package_name_title, True, True, 0) + vbox_package_title.pack_start(lbl_package_name_value, True, True, 0) + + listbox.add(row_package_title) + + # repository + + row_package_repo = Gtk.ListBoxRow() + vbox_package_repo = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + row_package_repo.add(vbox_package_repo) + lbl_package_repo_title = Gtk.Label(xalign=0) + lbl_package_repo_title.set_markup("Repository") + + lbl_package_repo_value = Gtk.Label(xalign=0) + lbl_package_repo_value.set_text(package_metadata["repository"]) + vbox_package_repo.pack_start(lbl_package_repo_title, True, True, 0) + vbox_package_repo.pack_start(lbl_package_repo_value, True, True, 0) + + listbox.add(row_package_repo) + + # description + + row_package_description = Gtk.ListBoxRow() + vbox_package_description = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_description.add(vbox_package_description) + lbl_package_description_title = Gtk.Label(xalign=0) + lbl_package_description_title.set_markup("Description") + + lbl_package_description_value = Gtk.Label(xalign=0) + lbl_package_description_value.set_text(package_metadata["description"]) + vbox_package_description.pack_start( + lbl_package_description_title, True, True, 0 + ) + vbox_package_description.pack_start( + lbl_package_description_value, True, True, 0 + ) + + listbox.add(row_package_description) + + # arch + + row_package_arch = Gtk.ListBoxRow() + vbox_package_arch = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + row_package_arch.add(vbox_package_arch) + lbl_package_arch_title = Gtk.Label(xalign=0) + lbl_package_arch_title.set_markup("Architecture") + + lbl_package_arch_value = Gtk.Label(xalign=0) + lbl_package_arch_value.set_text(package_metadata["arch"]) + vbox_package_arch.pack_start(lbl_package_arch_title, True, True, 0) + vbox_package_arch.pack_start(lbl_package_arch_value, True, True, 0) + + listbox.add(row_package_arch) + + # url + + row_package_url = Gtk.ListBoxRow() + vbox_package_url = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + row_package_url.add(vbox_package_url) + lbl_package_url_title = Gtk.Label(xalign=0) + lbl_package_url_title.set_markup("URL") + + lbl_package_url_value = Gtk.Label(xalign=0) + lbl_package_url_value.set_markup( + "%s" + % (package_metadata["url"], package_metadata["url"]) + ) + vbox_package_url.pack_start(lbl_package_url_title, True, True, 0) + vbox_package_url.pack_start(lbl_package_url_value, True, True, 0) + + listbox.add(row_package_url) + + # download size + + row_package_size = Gtk.ListBoxRow() + vbox_package_size = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + row_package_size.add(vbox_package_size) + lbl_package_size_title = Gtk.Label(xalign=0) + lbl_package_size_title.set_markup("Download size") + + lbl_package_size_value = Gtk.Label(xalign=0) + lbl_package_size_value.set_text(package_metadata["download_size"]) + vbox_package_size.pack_start(lbl_package_size_title, True, True, 0) + vbox_package_size.pack_start(lbl_package_size_value, True, True, 0) + + listbox.add(row_package_size) + + # installed size + + row_package_installed_size = Gtk.ListBoxRow() + vbox_package_installed_size = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_installed_size.add(vbox_package_installed_size) + lbl_package_installed_size_title = Gtk.Label(xalign=0) + lbl_package_installed_size_title.set_markup("Installed size") + + lbl_package_installed_size_value = Gtk.Label(xalign=0) + lbl_package_installed_size_value.set_text( + package_metadata["installed_size"] + ) + vbox_package_installed_size.pack_start( + lbl_package_installed_size_title, True, True, 0 + ) + vbox_package_installed_size.pack_start( + lbl_package_installed_size_value, True, True, 0 + ) + + listbox.add(row_package_installed_size) + + # build date + + row_package_build_date = Gtk.ListBoxRow() + vbox_package_build_date = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_build_date.add(vbox_package_build_date) + lbl_package_build_date_title = Gtk.Label(xalign=0) + lbl_package_build_date_title.set_markup("Build date") + + lbl_package_build_date_value = Gtk.Label(xalign=0) + lbl_package_build_date_value.set_text(package_metadata["build_date"]) + vbox_package_build_date.pack_start( + lbl_package_build_date_title, True, True, 0 + ) + vbox_package_build_date.pack_start( + lbl_package_build_date_value, True, True, 0 + ) + + listbox.add(row_package_build_date) + + # packager + + row_package_maintainer = Gtk.ListBoxRow() + vbox_package_maintainer = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_maintainer.add(vbox_package_maintainer) + lbl_package_maintainer_title = Gtk.Label(xalign=0) + lbl_package_maintainer_title.set_markup("Packager") + + lbl_package_maintainer_value = Gtk.Label(xalign=0) + lbl_package_maintainer_value.set_text(package_metadata["packager"]) + vbox_package_maintainer.pack_start( + lbl_package_maintainer_title, True, True, 0 + ) + vbox_package_maintainer.pack_start( + lbl_package_maintainer_value, True, True, 0 + ) + + listbox.add(row_package_maintainer) + + # depends on + + expander_depends_on = Gtk.Expander() + expander_depends_on.set_use_markup(True) + expander_depends_on.set_resize_toplevel(True) + expander_depends_on.set_label("Depends on") + + row_package_depends_on = Gtk.ListBoxRow() + expander_depends_on.add(row_package_depends_on) + vbox_package_depends_on = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_depends_on.add(vbox_package_depends_on) + + if len(package_metadata["depends_on"]) > 0: + treestore_depends = Gtk.TreeStore(str, str) + + for item in package_metadata["depends_on"]: + treestore_depends.append(None, list(item)) + + treeview_depends = Gtk.TreeView(model=treestore_depends) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn("Package", renderer, text=0) + + treeview_depends.append_column(column) + + vbox_package_depends_on.pack_start(treeview_depends, True, True, 0) + + else: + lbl_package_depends_value = Gtk.Label(xalign=0, yalign=0) + lbl_package_depends_value.set_text("None") + + vbox_package_depends_on.pack_start( + lbl_package_depends_value, True, True, 0 + ) + + listbox.add(expander_depends_on) + + # conflicts with + + expander_conflicts_with = Gtk.Expander() + expander_conflicts_with.set_use_markup(True) + expander_conflicts_with.set_resize_toplevel(True) + expander_conflicts_with.set_label("Conflicts with") + + row_package_conflicts_with = Gtk.ListBoxRow() + expander_conflicts_with.add(row_package_conflicts_with) + vbox_package_conflicts_with = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, spacing=0 + ) + row_package_conflicts_with.add(vbox_package_conflicts_with) + + if len(package_metadata["conflicts_with"]) > 0: + treestore_conflicts = Gtk.TreeStore(str, str) + + for item in package_metadata["conflicts_with"]: + treestore_conflicts.append(None, list(item)) + + treeview_conflicts = Gtk.TreeView(model=treestore_conflicts) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn("Package", renderer, text=0) + + treeview_conflicts.append_column(column) + + vbox_package_conflicts_with.pack_start( + treeview_conflicts, True, True, 0 + ) + + else: + lbl_package_conflicts_with_value = Gtk.Label(xalign=0, yalign=0) + lbl_package_conflicts_with_value.set_text("None") + + vbox_package_conflicts_with.pack_start( + lbl_package_conflicts_with_value, True, True, 0 + ) + + listbox.add(expander_conflicts_with) + + package_metadata_scrolled_window = Gtk.ScrolledWindow() + + package_metadata_scrolled_window.add(box_outer) + + stack.add_titled( + package_metadata_scrolled_window, "Package Information", "Information" + ) + + self.vbox.add(stack_switcher) + self.vbox.add(stack) + self.vbox.add(vbox_close) + + +def on_package_progress_close_response(self, widget): + self.pkg_dialog_closed = True + fn.logger.debug("Closing package progress dialog") + widget.hide() + widget.destroy() + + +def package_progress_dialog_on_close(widget, data, self, action): + self.pkg_dialog_closed = True + fn.logger.debug("Closing package progress dialog") + widget.hide() + widget.destroy() \ No newline at end of file