
I had some fun writing this Gtk Python wrapper for slapt-mod. Search your Slackware packages and combine them together in a module, quickly with your mouse.
Once again, no slop, just 100% organic bad code. It's just a draft, another couple of nights of hobby coding, but I hope it might be useful to someone. Please try it and report any bugs you find.
Gtk Python is quite easy to write once you get used to it and have the Gtk3 docs handy. Thanks to Porteus development team members who already wrote neat distro apps like lsmodules and porteus-settings-centre, establishing the patterns that I just tried to follow.
Only tested on 5.01 Xfce, but hopefully will work on other DEs.
How to use
- Run as root. (If you get the module, it'll enforce that.)
- It might take a few seconds to start up while it loads packages.
- Type a search term in the search bar and press Enter or click Go. Matching packages will appear. You can match the name or the description.
- use package1 OR package2 OR package3... to match multiple packages. This is handy if you want to combine packages manually into one module.
- Or view all packages (might take a sec if you have slackdce).
- The package list widget has its own little search bar if you press Ctrl+F.
- Select one or more packages (Ctrl+click) and either double-click the final one or just click Install selected.
- The little popup has the same settings you will be used to seeing in slapt-mod. All your selected packages are in the text box, separate by spaces. (The text box isn't very wide, but you can move the cursor to check.) You can install with automatic dependencies where the repo supports it.
- Click Install. A terminal window appears, makes the module and saves it in /tmp like usual.
- Move the .xzm to your module folder, or activate it straight away.
Contains the executable, plus menu item (in System folder) and desktop shortcut. Click on the desktop and press F5 to see the desktop shortcut. Run xfce4-panel -r or your DE's equivalent refresh command to ensure the menu item is visible immediately after you activate the module.
https://www.mediafire.com/file/lnzz0q4c ... 1.xzm/file
Sorry, I didn't make a github repo yet for this.
Full source code:
Code: Select all
#!/usr/bin/python3
## Porteus Module Maker
## Author: jssouza, lcpterid
import re
import sys
import os
import glob
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Vte', '2.91')
from gi.repository import Gtk, Vte, GdkPixbuf, Gio, GLib
from subprocess import run
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
class TermWindow(Gtk.Window):
def __init__(self, command_args):
Gtk.Window.__init__(self, title = "Porteus Installer", border_width = 5, height_request = 500, width_request = 550, icon_name = "system-settings")
self.vb = Gtk.Box(spacing = 5, orientation = Gtk.Orientation.VERTICAL)
self.vb.set_homogeneous(False)
terminal = Vte.Terminal()
pty = Vte.Pty.new_sync(Vte.PtyFlags.DEFAULT)
terminal.set_pty(pty)
pty.spawn_async(None, command_args, None, GLib.SpawnFlags.DO_NOT_REAP_CHILD, None, None, -1, None, self.ready)
self.connect("delete-event", self.on_window_close)
scrolledwindow = Gtk.ScrolledWindow()
scrolledwindow.add(terminal)
self.vb.pack_start(scrolledwindow, True, True, 0)
self.hb_bottom = Gtk.Box(spacing = 5)
self.hb_bottom.set_homogeneous(False)
self.ok_button = Gtk.Button.new_with_label("Quit")
self.ok_button.connect("clicked", self.on_quit_button_clicked)
self.hb_bottom.pack_end(self.ok_button, False, False, 6)
self.vb.pack_start(self.hb_bottom, False, False, 6)
self.add(self.vb)
def ready(self, pty, task):
# print('ready')
None
def on_window_close(self):
self.destroy()
def on_quit_button_clicked(self, button):
self.destroy()
class InstallWindow(Gtk.Window):
include_deps = False
compression = "zstd"
def __init__(self, pkgname):
Gtk.Window.__init__(self, title = "Install Module", border_width = 10, height_request = 240, width_request = 250, icon_name = "system-software-update")
self.connect("delete-event", self.on_window_close)
self.vb = Gtk.Box(spacing = 5, orientation = Gtk.Orientation.VERTICAL)
self.vb.set_homogeneous(False)
self.hb_row1 = Gtk.Box(spacing = 2, orientation = Gtk.Orientation.VERTICAL)
self.hb_row1.set_homogeneous(False)
self.top_mod_name = Gtk.Label()
self.top_mod_name.set_markup(f"<b>Module(s) to install:</b>")
self.top_mod_name.set_justify(Gtk.Justification.RIGHT)
self.hb_row1.pack_start(self.top_mod_name, False, False, 0)
self.mod_name_box = Gtk.Entry()
self.mod_name_box.set_max_width_chars(25)
self.mod_name_box.set_text(pkgname)
self.hb_row1.pack_start(self.mod_name_box, False, False, 0)
self.hb_row3 = Gtk.Box(spacing = 3, orientation = Gtk.Orientation.VERTICAL)
self.hb_row3.set_homogeneous(False)
self.compression_label = Gtk.Label()
self.compression_label.set_markup(f"Compression type:")
self.hb_row3.pack_start(self.compression_label, False, False, 0)
self.hb_row3b = Gtk.Box(spacing = 5)
self.hb_row3b.set_homogeneous(False)
self.hb_row3b.set_margin_start(35)
self.hb_row3b.set_margin_end(35)
self.compression_button_1 = Gtk.RadioButton.new_with_label_from_widget(None, "zstd")
self.compression_button_2 = Gtk.RadioButton.new_from_widget(self.compression_button_1)
self.compression_button_2.set_label("xz")
self.hb_row3b.pack_start(self.compression_button_1, True, False, 0)
self.hb_row3b.pack_start(self.compression_button_2, True, False, 0)
self.compression_button_1.connect("toggled", self.on_compression_toggled, "1")
self.compression_button_2.connect("toggled", self.on_compression_toggled, "2")
self.hb_row3.pack_start(self.hb_row3b, False, False, 0)
self.hb_row4 = Gtk.Box(spacing = 5)
self.hb_row4.set_homogeneous(True)
self.deps_checkbox = Gtk.CheckButton(label="Include dependencies")
self.deps_checkbox.connect("toggled", self.on_deps_toggled)
self.deps_checkbox.set_active(True)
self.hb_row4.pack_start(self.deps_checkbox, False, False, 0)
self.hb_row5 = Gtk.Box(spacing = 10)
self.hb_row5.set_homogeneous(False)
self.cancel_button = Gtk.Button.new_with_label("Cancel")
self.cancel_button.connect("clicked", self.on_window_close)
self.hb_row5.pack_start(self.cancel_button, True, False, 0)
self.install_button = Gtk.Button.new_with_label("Install")
self.install_button.connect("clicked", self.on_install_button_clicked)
self.hb_row5.pack_start(self.install_button, True, False, 0)
self.vb.pack_start(self.hb_row1, False, False, 5)
self.vb.pack_start(self.hb_row3, False, False, 5)
self.vb.pack_start(self.hb_row4, False, False, 5)
self.vb.pack_end(self.hb_row5, False, False, 5)
self.add(self.vb)
def on_window_close(self, _arg):
self.destroy()
def on_deps_toggled(self, button):
self.include_deps = button.get_active()
def on_compression_toggled(self, button, name):
if button.get_active():
self.compression = name
def on_install_button_clicked(self, button):
if self.include_deps:
slapt_mod_flag = "m"
else:
slapt_mod_flag = "n"
if self.compression == "2":
slapt_mod_flag = slapt_mod_flag.upper()
slapt_mod_flag = f"-{slapt_mod_flag}"
module_names = self.mod_name_box.get_text()
term_window = TermWindow(["/usr/bin/bash", "-c", f"yes | slapt-mod {slapt_mod_flag} {module_names}"])
term_window.show_all()
self.destroy()
def msg_dialog(self, msg):
dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO,
Gtk.ButtonsType.OK, msg)
dialog.run()
dialog.destroy()
class SlaptModPackages:
'''slapt-mod package list'''
package_list = []
slapt_mod_error_state = 0
def __init__(self):
self.fetch_packages()
def fetch_packages(self):
package_list = []
slapt_mod_process = run(['slapt-mod','-s','-'], capture_output=True)
self.slapt_mod_error_state = slapt_mod_process.returncode
slapt_mod_raw_output = slapt_mod_process.stdout.decode('utf-8')
for this_line in slapt_mod_raw_output.split('\n'):
this_name = this_line.split(' ')[0]
this_desc = this_line.replace(this_name,'').strip()
if this_desc[0:1] == '(' and this_desc[-1:] == ')':
this_desc = this_desc[1:-1]
this_package = {'name': this_name, 'desc': this_desc}
self.package_list.append(this_package)
def get_package_list(self):
return self.package_list
class PackagesWindow(Gtk.Window):
slapt_mod_packages = SlaptModPackages()
column_pkg_name = None
column_pkg_desc = None
needs_refresh = False
def __init__(self):
Gtk.Window.__init__(self, title = "Module Maker", border_width = 10, height_request = 500, width_request = 800, icon_name = "go-down")
self.vb = Gtk.Box(spacing = 5, orientation = Gtk.Orientation.VERTICAL)
self.vb.set_homogeneous(False)
self.hb_top = Gtk.Box(spacing = 5)
self.hb_top.set_homogeneous(False)
self.update_packages_button = Gtk.Button.new_with_label("Update package lists")
self.update_packages_button.connect("clicked", self.on_update_packages_button_clicked)
self.hb_top.pack_start(self.update_packages_button, False, False, 6)
self.search_label = Gtk.Label()
self.search_label.set_markup(f"<b>Search:</b>")
self.search_label.set_justify(Gtk.Justification.RIGHT)
self.hb_top.pack_start(self.search_label, False, False, 0)
self.search_box = Gtk.Entry()
self.search_box.set_max_width_chars(20)
self.search_box.connect("activate", self.on_go_button_clicked)
self.hb_top.pack_start(self.search_box, False, False, 0)
self.go_button = Gtk.Button.new_with_label("Go")
self.go_button.connect("clicked", self.on_go_button_clicked)
self.hb_top.pack_start(self.go_button, False, False, 6)
self.list_all_button = Gtk.Button.new_with_label("List all")
self.list_all_button.connect("clicked", self.on_list_all_button_clicked)
self.hb_top.pack_end(self.list_all_button, False, False, 6)
self.clear_all_button = Gtk.Button.new_with_label("Clear all")
self.clear_all_button.connect("clicked", self.on_clear_all_button_clicked)
self.hb_top.pack_end(self.clear_all_button, False, False, 6)
self.vb.pack_start(self.hb_top, False, False, 6)
# Columns in order:
# 0 - str - package name
# 1 - str - package description
self.slapt_mod_packages_model = Gtk.TreeStore(str, str)
self.tree_view = Gtk.TreeView(model = self.slapt_mod_packages_model)
self.tree_view.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
renderer_pkg_name = Gtk.CellRendererText()
self.column_pkg_name = Gtk.TreeViewColumn("Package Name", renderer_pkg_name, text=0)
self.column_pkg_name.set_sort_column_id(0)
self.column_pkg_name.set_fixed_width(350)
self.column_pkg_name.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
self.column_pkg_name.set_resizable(True)
self.tree_view.append_column(self.column_pkg_name)
renderer_pkg_desc = Gtk.CellRendererText()
self.column_pkg_desc = Gtk.TreeViewColumn("Description", renderer_pkg_desc, text=1)
self.column_pkg_desc.set_sort_column_id(1)
self.column_pkg_desc.set_fixed_width(200)
self.column_pkg_desc.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
self.column_pkg_desc.set_resizable(True)
self.tree_view.append_column(self.column_pkg_desc)
self.tree_view.connect('row-activated', self.on_row_double_clicked)
self.scrolled_win = Gtk.ScrolledWindow()
self.scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
self.scrolled_win.add(self.tree_view)
self.vb.pack_start(self.scrolled_win, True, True, 0)
self.hb_bottom = Gtk.Box(spacing = 5)
self.hb_bottom.set_homogeneous(False)
self.quit_button = Gtk.Button.new_with_label("Quit")
self.quit_button.connect("clicked", self.on_quit_button_clicked)
self.hb_bottom.pack_end(self.quit_button, False, False, 6)
self.install_button = Gtk.Button.new_with_label("Install selected")
self.install_button.connect("clicked", self.on_install_button_clicked)
self.hb_bottom.pack_end(self.install_button, False, False, 6)
self.vb.pack_end(self.hb_bottom, False, False, 6)
self.add(self.vb)
self.search_box.grab_focus()
def on_update_packages_button_clicked(self, _button):
self.needs_refresh = True
term_window = TermWindow(["/usr/local/bin/slapt-mod", "-u"])
term_window.show_all()
def on_clear_all_button_clicked(self, _button):
self.slapt_mod_packages_model.clear()
def on_list_all_button_clicked(self, _button):
self.package_search('')
def on_go_button_clicked(self,_button):
if self.needs_refresh:
self.needs_refresh = False
self.slapt_mod_packages.fetch_packages()
search_term = self.search_box.get_text()
self.package_search(search_term)
def on_quit_button_clicked(self, _button):
Gtk.main_quit()
def on_row_double_clicked(self, _tree_view, _path, _column):
self.launch_install()
def on_install_button_clicked(self, _button):
self.launch_install()
def launch_install(self):
selected_package_paths = self.tree_view.get_selection().get_selected_rows()[1]
selected_package_list = []
for pkgpath in selected_package_paths:
selected_package_list.append(self.slapt_mod_packages_model[pkgpath][0])
selected_package_string = ' '.join(selected_package_list)
install_window = InstallWindow(selected_package_string)
install_window.set_modal(True)
install_window.show_all()
def package_search(self, search_term):
self.slapt_mod_packages_model.clear()
the_package_list = self.slapt_mod_packages.get_package_list()
search_array = search_term.split(' OR ')
package_match = False
for pkg in the_package_list:
for sterm in search_array:
if sterm in pkg['name'] or sterm in pkg['desc']:
package_match = True
self.slapt_mod_packages_model.append(None, [pkg['name'], pkg['desc']])
win = PackagesWindow()
win.connect("delete-event", Gtk.main_quit)
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()










