Refactored to use better event system

This commit is contained in:
2023-01-16 22:47:28 -06:00
parent e714edcbb7
commit 3911421356
22 changed files with 386 additions and 309 deletions

3
src/core/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""
Core Module
"""

147
src/core/change_view.py Normal file
View File

@@ -0,0 +1,147 @@
# Python imports
import os
import re
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class ChangeView(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(ChangeView, self).__init__()
self._active_path = None
self._from_store = None
self._to_store = None
self._from_changes = []
self._to_changes = []
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_spacing(20)
self.set_border_width(2)
self.set_homogeneous(True)
def _setup_signals(self):
...
def _subscribe_to_events(self):
event_system.subscribe("reset-from-view", self.reset_from_view)
event_system.subscribe("reset-to-view", self.reset_to_view)
event_system.subscribe("get-from", self.get_from_list)
event_system.subscribe("get-to", self.get_to_list)
event_system.subscribe("set-from", self.set_from_list)
event_system.subscribe("set-to", self.set_to_list)
event_system.subscribe("update-from", self.update_from_list)
event_system.subscribe("update-to", self.update_to_list)
event_system.subscribe("get-active-path", self._get_active_path)
event_system.subscribe("set-active-path", self._set_active_path)
def _load_widgets(self):
from_container = Gtk.Box()
to_container = Gtk.Box()
from_scroll_vw, \
self._from_store = self._create_treeview_widget(title="From:")
to_scroll_vw, \
self._to_store = self._create_treeview_widget(title="To:")
from_container.add(from_scroll_vw)
to_container.add(to_scroll_vw)
from_container.set_orientation(1)
to_container.set_orientation(1)
fchild = from_scroll_vw.get_children()[0]
fchild.connect("drag-data-received", self._on_drag_data_received)
URI_TARGET_TYPE = 80
uri_target = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE)
targets = [ uri_target ]
action = Gdk.DragAction.COPY
fchild.enable_model_drag_dest(targets, action)
fchild.enable_model_drag_source(0, targets, action)
self.add(from_container)
self.add(to_container)
def _on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
if info == 80:
uri = data.get_uris()[0].split("file://")[1]
self._set_active_path(uri)
def _get_active_path(self):
return self._active_path
def _set_active_path(self, _file):
if os.path.isdir(_file) :
self._from_changes.clear()
self._active_path = _file
for f in os.listdir(_file):
self._from_changes.append(f)
self._from_changes.sort(key=self._natural_keys)
self._to_changes = self._from_changes
event_system.emit("update-from")
event_system.emit("update-to")
def get_from_list(self):
return self._from_changes
def get_to_list(self):
return self._to_changes
def set_from_list(self, from_list):
self._from_changes = from_list
def set_to_list(self, to_list):
self._to_changes = to_list
def update_from_list(self):
print("Updating From List...")
if self._from_store:
self._from_store.clear()
for i, change in enumerate(self._from_changes):
self._from_store.insert(i, [change])
def update_to_list(self):
print("Updating To List...")
if self._to_store:
self._to_store.clear()
for i, change in enumerate(self._to_changes):
self._to_store.insert(i, [change])
def reset_to_view(self):
self._to_changes = self._from_changes
event_system.emit("update-to")
def reset_from_view(self):
self._set_active_path(self._active_path)
def _atoi(self, text):
return int(text) if text.isdigit() else text
def _natural_keys(self, text):
return [ self._atoi(c) for c in re.split('(\d+)',text) ]

188
src/core/controller.py Normal file
View File

@@ -0,0 +1,188 @@
# Python imports
import os
import sys
# lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from .change_view import ChangeView
from .widgets import *
class Controller(Gtk.Box, CommonWidgetGeneratorMixin):
def __init__(self, args, unknownargs):
super(Controller, self).__init__()
# # Add header
self.change_view = None
self.copy_window = None
self.store = None
self.combo_box = None
self.action_collection = []
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show_all()
if unknownargs:
for arg in unknownargs:
if os.path.isdir(arg):
event_system.emit("set-active-path", (arg,))
if args.path and os.path.isdir(args.path):
event_system.emit("set-active-path", (args.path,))
def _setup_styling(self):
self.set_spacing(20)
self.set_margin_top(5)
self.set_margin_bottom(10)
self.set_margin_left(15)
self.set_margin_right(15)
self.set_orientation(1)
def _setup_signals(self):
...
def _subscribe_to_events(self):
event_system.subscribe("handle-gui-event", self._handle_gui_event)
def _load_widgets(self):
self.change_view = ChangeView()
action_bar = Gtk.Box()
file_choser = Gtk.FileChooserButton(title="Directory Chooser", action=2) # 2 = SELECT_FOLDER
file_filter = Gtk.FileFilter()
file_choser.show()
file_choser.set_filename(USER_HOME)
file_filter.add_mime_type("inode/directory")
file_choser.add_filter(file_filter)
label = Gtk.Label(label="Bulk Action Type: ")
data = ["Insert", "Replace", "Remove", "Remove From / To", "Case", "Time"]
self.store, self.combo_box = self._create_combobox_widget(data)
add_button = Gtk.Button(label="Add Action")
test_all_button = Gtk.Button(label="Preview")
reset_button = Gtk.Button(label="Reset")
run_button = Gtk.Button(label="Run")
action_bar.add(label)
action_bar.add(self.combo_box)
action_bar.add(add_button)
action_bar.add(test_all_button)
action_bar.add(reset_button)
action_bar.set_homogeneous(True)
action_bar.set_spacing(20)
action_bar.show_all()
run_button.connect("clicked", self._run_all)
add_button.connect("clicked", self._add_action)
test_all_button.connect("clicked", self._test_all)
reset_button.connect("clicked", self._reset_to_view)
file_choser.connect("file-set", self.update_dir_path)
actions_scroll_label = Gtk.Label(label="Actions:")
actions_scroll_label.set_xalign(-20)
actions_scroll_view, self.actions_list_view = self._create_listBox_widget()
actions_scroll_view.set_vexpand(True)
self.copy_window = Gtk.Box()
self.add(file_choser)
self.add(action_bar)
self.add(self.change_view)
self.add(actions_scroll_label)
self.add(actions_scroll_view)
self.add(run_button)
def update_dir_path(self, widget):
path = widget.get_filename()
event_system.emit("set-active-path", (path,))
def _handle_gui_event(self, type, target, parameters):
for i, action in enumerate(self.action_collection):
if action == target:
if type == "move-up":
if i > 0:
parent = target.get_parent()
parent.remove(target)
parent.destroy()
self.actions_list_view.insert(target, (i - 1))
self.action_collection[i-1], self.action_collection[i] = self.action_collection[i], self.action_collection[i - 1]
if type == "move-down":
if i < len(self.action_collection):
parent = target.get_parent()
parent.remove(target)
parent.destroy()
self.actions_list_view.insert(target, (i + 1))
self.action_collection[i+1], self.action_collection[i] = self.action_collection[i], self.action_collection[i + 1]
if type == "delete":
self.action_collection.remove(target)
target.delete()
if type == "run":
target.run()
break
def _add_action(self, widget):
itr = self.combo_box.get_active_iter()
text = self.store.get(itr, 0)[0]
widget = self._str_to_class( self._clean_text(text) )
print(f"Adding: {self._clean_text(text)}")
self.actions_list_view.add(widget)
self.action_collection.append(widget)
def _test_all(self, widget=None):
event_system.emit("reset-to-view")
for action in self.action_collection:
action.run()
event_system.emit("update-to")
def _reset_to_view(self, widget):
event_system.emit("reset-to-view")
def _run_all(self, widget):
dir = event_system.emit_and_await("get-active-path")
if not dir:
print("No active path set. Returning...")
return
self._test_all()
to_changes = event_system.emit_and_await("get-to")
for i, file in enumerate(event_system.emit_and_await("get-from")):
fPath = f"{dir}/{file}"
tPath = f"{dir}/{to_changes[i]}"
if fPath != tPath:
try:
os.rename(fPath, tPath)
except Exception as e:
print(f"Cant Move: {fPath}\nTo File: {tPath}")
event_system.emit("reset-from-view")
def _clean_text(self, text):
return text.replace(" ", "") \
.replace("/", "")
def _str_to_class(self, class_name):
return getattr(sys.modules[__name__], class_name)()
# NOTE: Yes, this exists in CommonActionsMixin. But, I don't want to add a while mixin just for one method
def _has_method(self, obj, name):
''' Checks if a given method exists. '''
return callable(getattr(obj, name, None))

View File

@@ -0,0 +1,6 @@
from .case import Case
from .insert import Insert
from .time import Time
from .replace import Replace
from .remove import Remove
from .remove_from_to import RemoveFromTo

50
src/core/widgets/case.py Normal file
View File

@@ -0,0 +1,50 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class Case(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(Case, self).__init__()
data = ["Title Case", "UPPER", "lower", "InVert CaSe --> iNvERT cAsE"]
self.store, self.combo_box = self._create_combobox_widget(data)
self.combo_box.set_hexpand(True)
self.add_widgets([self.combo_box])
self.set_spacing(20)
self.show_all()
def run(self):
new_collection = []
itr = self.combo_box.get_active_iter()
type = self.store.get(itr, 0)[0]
to_changes = event_system.emit_and_await("get-to")
print(f"Changing Case...")
if type == "Title Case":
for name in to_changes:
new_collection.append(name.title())
if type == "UPPER":
for name in to_changes:
new_collection.append(name.upper())
if type == "lower":
for name in to_changes:
new_collection.append(name.lower())
if type == "InVert CaSe --> iNvERT cAsE":
for name in to_changes:
new_collection.append(name.swapcase())
event_system.emit("set-to", (new_collection,))
event_system.emit("update-to")

View File

@@ -0,0 +1,66 @@
# Python imports
import pathlib
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class Insert(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(Insert, self).__init__()
self.insert_entry = Gtk.Entry()
self.insert_entry.set_hexpand(True)
self.insert_entry.set_placeholder_text("Insert...")
data = ["Start", "End", "Position"]
self.store, self.combo_box = self._create_combobox_widget(data)
self.spin_button = self._create_spinbutton_widget()
self.add_widgets([self.insert_entry, self.combo_box, self.spin_button])
self.set_spacing(20)
self.show_all()
def run(self):
new_collection = []
insert_str = self.insert_entry.get_text()
itr = self.combo_box.get_active_iter()
type = self.store.get(itr, 0)[0]
to_changes = event_system.emit_and_await("get-to")
print(f"Inserting...")
if type == "Start":
for name in to_changes:
new_collection.append(f"{insert_str}{name}")
if type == "End":
for name in to_changes:
base, file_extension = self.get_file_parts()
new_collection.append(f"{base}{insert_str}{file_extension}")
if type == "Position":
position = self.spin_button.get_value_as_int()
for name in to_changes:
name = f"{name[:position]}{insert_str}{name[position:]}"
new_collection.append(f"{name}")
event_system.emit("set-to", (new_collection,))
event_system.emit("update-to")
def _combo_box_changed(self, widget, eve=None):
itr = widget.get_active_iter()
type = self.store.get(itr, 0)[0]
if type == "Position":
self.spin_button.set_sensitive(True)
else:
self.spin_button.set_sensitive(False)

View File

@@ -0,0 +1,58 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class Remove(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(Remove, self).__init__()
self.entry_from = Gtk.Entry()
data = ["All", "Word Start", "Word End", "First Instance", "Last Instance", "RegEx"]
self.store, self.combo_box = self._create_combobox_widget(data)
self.entry_from.set_hexpand(True)
self.entry_from.set_placeholder_text("Remove...")
self.add_widgets([self.entry_from, self.combo_box])
self.set_spacing(20)
self.show_all()
def run(self):
from_str = self.entry_from.get_text()
if from_str:
new_collection = []
itr = self.combo_box.get_active_iter()
type = self.store.get(itr, 0)[0]
to_changes = event_system.emit_and_await("get-to")
print(f"To Remove: {from_str}")
if type == "All":
for name in to_changes:
new_collection.append(name.replace(from_str, ''))
if type == "Word Start":
print("Stub...")
if type == "Word End":
print("Stub...")
if type == "First Instance":
for name in to_changes:
new_collection.append( name.replace(from_str, "", 1) )
if type == "Last Instance":
for name in to_changes:
new_collection.append( self._replace_last(name, from_str, "") )
if type == "RegEx":
print("Stub...")
event_system.emit("set-to", (new_collection,))
event_system.emit("update-to")

View File

@@ -0,0 +1,89 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class RemoveFromTo(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(RemoveFromTo, self).__init__()
self.entry_from = Gtk.Entry()
self.entry_to = Gtk.Entry()
self.spin_button_from = self._create_spinbutton_widget()
self.spin_button_to = self._create_spinbutton_widget()
self.entry_from.set_hexpand(True)
self.entry_to.set_hexpand(True)
self.spin_button_from.set_hexpand(True)
self.spin_button_to.set_hexpand(True)
self.spin_button_from.set_sensitive(True)
self.spin_button_to.set_sensitive(True)
self.entry_from.set_placeholder_text("Start...")
self.entry_to.set_placeholder_text("End...")
data = ["Using Sub String", "Using Index"]
self.store, self.combo_box = self._create_combobox_widget(data)
self.add_widgets([self.entry_from, \
self.entry_to, \
self.spin_button_from, \
self.spin_button_to, \
self.combo_box])
self.set_spacing(20)
self.show_all()
self.spin_button_from.hide()
self.spin_button_to.hide()
def run(self):
new_collection = []
itr = self.combo_box.get_active_iter()
type = self.store.get(itr, 0)[0]
to_changes = event_system.emit_and_await("get-to")
if type == "Using Sub String":
fsub = self.entry_from.get_text()
tsub = self.entry_to.get_text()
print(f"From: {fsub}\nTo: {tsub}")
for name in to_changes:
startIndex = name.index(fsub) + 1
endIndex = name.index(tsub)
toRemove = name[startIndex:endIndex]
new_collection.append(name.replace(toRemove, ''))
if type == "Using Index":
fsub = self.spin_button_from.get_value_as_int()
tsub = self.spin_button_to.get_value_as_int()
print(f"From: {fsub}\nTo: {tsub}")
for name in to_changes:
toRemove = name[fsub:tsub]
new_collection.append(name.replace(toRemove, ''))
event_system.emit("set-to", (new_collection,))
event_system.emit("update-to")
def _combo_box_changed(self, widget, eve=None):
itr = widget.get_active_iter()
type = self.store.get(itr, 0)[0]
if type == "Using Sub String":
self.entry_from.show()
self.entry_to.show()
self.spin_button_from.hide()
self.spin_button_to.hide()
else:
self.entry_from.hide()
self.entry_to.hide()
self.spin_button_from.show()
self.spin_button_to.show()

View File

@@ -0,0 +1,45 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class Replace(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(Replace, self).__init__()
self.entry_from = Gtk.Entry()
self.entry_to = Gtk.Entry()
self.entry_from.set_hexpand(True)
self.entry_to.set_hexpand(True)
self.entry_from.set_placeholder_text("Replace From...")
self.entry_to.set_placeholder_text("Replace To...")
self.add_widgets([self.entry_from, self.entry_to])
self.set_spacing(20)
self.show_all()
def run(self):
fsub = self.entry_from.get_text()
tsub = self.entry_to.get_text()
to_changes = event_system.emit_and_await("get-to")
if fsub and tsub:
new_collection = []
print(f"From: {fsub}\nTo: {tsub}")
for name in to_changes:
new_collection.append(name.replace(fsub, tsub))
event_system.emit("set-to", (new_collection,))
event_system.emit("update-to")

67
src/core/widgets/time.py Normal file
View File

@@ -0,0 +1,67 @@
# Python imports
import pathlib
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from mixins import CommonWidgetGeneratorMixin
from mixins import CommonActionsMixin
class Time(Gtk.Box, CommonWidgetGeneratorMixin, CommonActionsMixin):
def __init__(self):
super(Time, self).__init__()
label = Gtk.Label(label="Time: ")
self.insert_entry = Gtk.Entry()
self.insert_entry.set_hexpand(True)
self.insert_entry.set_placeholder_text("HH:MM:SS...")
data = ["Start", "End", "Position"]
self.store, self.combo_box = self._create_combobox_widget(data)
self.spin_button = self._create_spinbutton_widget()
self.add_widgets([label, self.insert_entry, self.combo_box, self.spin_button])
self.set_spacing(20)
self.show_all()
def run(self):
new_collection = []
insert_str = self.insert_entry.get_text()
itr = self.combo_box.get_active_iter()
type = self.store.get(itr, 0)[0]
to_changes = event_system.emit_and_await("get-to")
print(f"Inserting...")
if type == "Start":
for name in to_changes:
new_collection.append(f"{insert_str}{name}")
if type == "End":
for name in to_changes:
base, file_extension = self.get_file_parts()
new_collection.append(f"{base}{insert_str}{file_extension}")
if type == "Position":
position = self.spin_button.get_value_as_int()
for name in to_changes:
name = f"{name[:position]}{insert_str}{name[position:]}"
new_collection.append(f"{name}")
event_system.emit("set-to", (new_collection,))
event_system.emit("update-to")
def _combo_box_changed(self, widget, eve=None):
itr = widget.get_active_iter()
type = self.store.get(itr, 0)[0]
if type == "Position":
self.spin_button.set_sensitive(True)
else:
self.spin_button.set_sensitive(False)

28
src/core/window.py Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/python3
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from .controller import Controller
class Window(Gtk.Window):
"""docstring for Window."""
def __init__(self, args, unknownargs):
super(Window, self).__init__()
self.add(Controller(args, unknownargs))
self.connect("delete-event", Gtk.main_quit)
self.set_default_size(850, 600)
self.set_title(f"{app_name}")
self.set_icon_from_file("/usr/share/bulkr/bulkr.png")
self.set_gravity(5) # 5 = CENTER
self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS
self.show_all()