Create PackageListDialog.py

This commit is contained in:
D3V1L0N
2024-04-24 04:48:22 +05:30
committed by GitHub
parent 3b317e6d60
commit a197f7aad6

View File

@@ -0,0 +1,283 @@
# This class is used to create a modal dialog window to display currently installed packages
import os
import gi
import Functions as fn
from ui.MessageDialog import MessageDialog
from queue import Queue
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 PackageListDialog(Gtk.Dialog):
def __init__(self):
Gtk.Dialog.__init__(self)
# Create a queue for storing package list exports to display inside PackageListDialog
self.pkg_export_queue = Queue()
self.filename = "%s/packages-x86_64.txt" % (fn.export_dir,)
self.set_resizable(True)
self.set_size_request(1050, 700)
self.set_modal(True)
self.set_border_width(10)
self.set_icon_from_file(os.path.join(base_dir, "images/blackbox.png"))
self.connect("delete-event", self.on_close)
self.installed_packages_list = None
self.headerbar = Gtk.HeaderBar()
self.headerbar.set_title("Loading please wait ..")
self.headerbar.set_show_close_button(True)
self.set_titlebar(self.headerbar)
self.grid_packageslst = Gtk.Grid()
self.grid_packageslst.set_column_homogeneous(True)
self.lbl_info = Gtk.Label(xalign=0, yalign=0)
self.lbl_info.set_text("Export destination %s" % self.filename)
# get a list of installed packages on the system
self.pacman_export_cmd = ["pacman", "-Qien"]
fn.Thread(
target=fn.get_installed_package_data,
args=(self,),
daemon=True,
).start()
fn.Thread(target=self.check_queue, daemon=True).start()
def setup_gui(self):
if len(self.installed_packages_list) > 0:
self.set_title(
"Showing %s installed packages" % len(self.installed_packages_list)
)
search_entry = Gtk.SearchEntry()
search_entry.set_placeholder_text("Search...")
# remove the focus on startup from search entry
self.headerbar.set_property("can-focus", True)
Gtk.Window.grab_focus(self.headerbar)
treestore_packages = Gtk.TreeStore(str, str, str, str, str)
for item in sorted(self.installed_packages_list):
treestore_packages.append(None, list(item))
treeview_packages = Gtk.TreeView()
treeview_packages.set_search_entry(search_entry)
treeview_packages.set_model(treestore_packages)
for i, col_title in enumerate(
[
"Name",
"Installed Version",
"Latest Version",
"Installed Size",
"Installed Date",
]
):
renderer = Gtk.CellRendererText()
col = Gtk.TreeViewColumn(col_title, renderer, text=i)
treeview_packages.append_column(col)
# allow sorting by installed date
col_installed_date = treeview_packages.get_column(4)
col_installed_date.set_sort_column_id(4)
treestore_packages.set_sort_func(4, self.compare_install_date, None)
path = Gtk.TreePath.new_from_indices([0])
selection = treeview_packages.get_selection()
selection.select_path(path)
treeview_packages.expand_all()
treeview_packages.columns_autosize()
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_vexpand(True)
scrolled_window.set_hexpand(True)
self.grid_packageslst.attach(scrolled_window, 0, 0, 8, 10)
lbl_padding1 = Gtk.Label(xalign=0, yalign=0)
lbl_padding1.set_text("")
self.grid_packageslst.attach_next_to(
lbl_padding1, scrolled_window, Gtk.PositionType.BOTTOM, 1, 1
)
btn_dialog_export = Gtk.Button(label="Export")
btn_dialog_export.connect("clicked", self.on_dialog_export_clicked)
btn_dialog_export.set_size_request(100, 30)
btn_dialog_export.set_halign(Gtk.Align.END)
btn_dialog_export_close = Gtk.Button(label="Close")
btn_dialog_export_close.connect("clicked", self.on_close, "delete-event")
btn_dialog_export_close.set_size_request(100, 30)
btn_dialog_export_close.set_halign(Gtk.Align.END)
scrolled_window.add(treeview_packages)
grid_btn = Gtk.Grid()
grid_btn.attach(btn_dialog_export, 0, 1, 1, 1)
lbl_padding2 = Gtk.Label(xalign=0, yalign=0)
lbl_padding2.set_text(" ")
grid_btn.attach_next_to(
lbl_padding2, btn_dialog_export, Gtk.PositionType.RIGHT, 1, 1
)
grid_btn.attach_next_to(
btn_dialog_export_close, lbl_padding2, Gtk.PositionType.RIGHT, 1, 1
)
grid_btn.set_halign(Gtk.Align.END)
vbox_btn = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
vbox_btn.pack_start(grid_btn, True, True, 1)
lbl_padding3 = Gtk.Label(xalign=0, yalign=0)
lbl_padding3.set_text("")
self.vbox.add(search_entry)
self.vbox.add(lbl_padding3)
self.vbox.add(self.grid_packageslst)
self.vbox.add(self.lbl_info)
self.vbox.add(vbox_btn)
self.show_all()
def check_queue(self):
while True:
self.installed_packages_list = self.pkg_export_queue.get()
if self.installed_packages_list is not None:
break
self.pkg_export_queue.task_done()
GLib.idle_add(self.setup_gui, priority=GLib.PRIORITY_DEFAULT)
def on_close(self, dialog, event):
self.hide()
self.destroy()
def on_dialog_export_clicked(self, dialog):
try:
if not os.path.exists(fn.export_dir):
fn.makedirs(fn.export_dir)
fn.permissions(fn.export_dir)
with open(self.filename, "w", encoding="utf-8") as f:
f.write(
"# This file was auto-generated by BlackBox on %s at %s\n"
% (
fn.datetime.today().date(),
fn.datetime.now().strftime("%H:%M:%S"),
)
)
f.write(
"# Exported explicitly installed packages using %s\n"
% " ".join(self.pacman_export_cmd)
)
for package in sorted(self.installed_packages_list):
f.write("%s\n" % (package[0]))
if os.path.exists(self.filename):
fn.logger.info("Export completed")
# fix permissions, file is owned by root
fn.permissions(self.filename)
message_dialog = MessageDialog(
"Info",
"Package export complete",
"Package list exported to %s" % self.filename,
"",
"info",
False,
)
message_dialog.show_all()
message_dialog.run()
message_dialog.hide()
message_dialog.destroy()
else:
fn.logger.error("Export failed")
message_dialog = MessageDialog(
"Error",
"Package export failed",
"Failed to export package list to %s." % self.filename,
"",
"error",
False,
)
message_dialog.show_all()
message_dialog.run()
message_dialog.hide()
message_dialog.destroy()
except Exception as e:
fn.logger.error("Exception in on_dialog_export_clicked(): %s" % e)
# noqa: any locales other than en_GB.UTF-8 / en_US.UTF-8 are untested
def compare_install_date(self, model, row1, row2, user_data):
try:
sort_column, _ = model.get_sort_column_id()
str_value1 = model.get_value(row1, sort_column)
str_value2 = model.get_value(row2, sort_column)
datetime_value1 = None
datetime_value2 = None
# convert string into datetime object, check if time format is 12H format with AM/PM
if str_value1.lower().find("am") > 0 or str_value1.lower().find("pm") > 0:
# 12H format
datetime_value1 = fn.datetime.strptime(
str_value1, "%a %d %b %Y %I:%M:%S %p %Z"
).replace(tzinfo=None)
datetime_value2 = fn.datetime.strptime(
str_value2, "%a %d %b %Y %I:%M:%S %p %Z"
).replace(tzinfo=None)
else:
# 24H format
datetime_value1 = fn.datetime.strptime(
str_value1, "%a %d %b %Y %H:%M:%S %Z"
).replace(tzinfo=None)
datetime_value2 = fn.datetime.strptime(
str_value2, "%a %d %b %Y %H:%M:%S %Z"
).replace(tzinfo=None)
if datetime_value1 is not None and datetime_value2 is not None:
if datetime_value1 < datetime_value2:
return -1
elif datetime_value1 == datetime_value2:
return 0
else:
return 1
except ValueError as ve:
# fn.logger.error("ValueError in compare_install_date: %s" % ve)
# compare fails due to the format of the datetime string, which hasn't been tested
pass
except Exception as e:
fn.logger.error("Exception in compare_install_date: %s" % e)