diff --git a/README.md b/README.md
index 18b7b9a..fd06add 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
# Pytop
Pytop is a Gtk + Python gui to have a custom desktop interface.
+# Updates
+Added task bar.
+
# Notes
```sudo apt-get install python3 wget steamcmd```
diff --git a/bin/pytop-0-0-1-x64.deb b/bin/pytop-0-0-1-x64.deb
index 1fd06cf..491b1d9 100644
Binary files a/bin/pytop-0-0-1-x64.deb and b/bin/pytop-0-0-1-x64.deb differ
diff --git a/src/Pytop/PyTop.py b/src/Pytop/PyTop.py
index 8d1c9d5..d487e46 100755
--- a/src/Pytop/PyTop.py
+++ b/src/Pytop/PyTop.py
@@ -15,12 +15,12 @@ from setproctitle import setproctitle
# Application imports
from utils import Settings
-from signal_classes import CrossClassSignals, GridSignals
+from signal_classes import CrossClassSignals, GridSignals, TaskbarSignals
class Main:
- setproctitle('Pytop')
def __init__(self):
+ setproctitle('Pytop')
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit)
faulthandler.enable() # For better debug info
@@ -31,7 +31,8 @@ class Main:
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
classes = [CrossClassSignals(settings),
- GridSignals(settings)]
+ GridSignals(settings),
+ TaskbarSignals(settings)]
handlers = {}
for c in classes:
diff --git a/src/Pytop/resources/PyTop.glade b/src/Pytop/resources/PyTop.glade
index 2396a89..7764d81 100644
--- a/src/Pytop/resources/PyTop.glade
+++ b/src/Pytop/resources/PyTop.glade
@@ -67,7 +67,7 @@
True
6
multiple
- 6
+ 72
@@ -79,6 +79,62 @@
1
+
+
+
+ False
+ True
+ 2
+
+
@@ -87,6 +143,134 @@
False
gtk-new
+
True
False
diff --git a/src/Pytop/signal_classes/TaskbarSignals.py b/src/Pytop/signal_classes/TaskbarSignals.py
new file mode 100644
index 0000000..5f2d663
--- /dev/null
+++ b/src/Pytop/signal_classes/TaskbarSignals.py
@@ -0,0 +1,181 @@
+# Gtk imports
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
+gi.require_version('Wnck', '3.0')
+
+from gi.repository import Wnck as wnck
+from gi.repository import Gtk as gtk
+from gi.repository import Gdk as gdk
+from gi.repository import GLib
+
+
+# Python imports
+import threading
+
+# Application imports
+
+
+
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+
+ return wrapper
+
+class MouseButtons:
+ LEFT_BUTTON = 1
+ RIGHT_BUTTON = 3
+
+
+class TaskbarSignals:
+ def __init__(self, settings):
+ self.settings = settings
+ self.builder = self.settings.returnBuilder()
+ self.taskBarButtons = self.builder.get_object('taskBarButtons')
+ self.taskbarMenu = self.builder.get_object('taskbarMenu')
+
+ self.SCREEN = wnck.Screen.get_default()
+ self.actv_workspace_num = None
+ self.window = None
+
+ self.SCREEN.force_update() # (Re)populate screen windows list
+ self.refreashTaskbar()
+ self.setScreenSignals()
+ self.setPagerWidget()
+
+
+ def refreashTaskbar(self):
+ workspace = self.SCREEN.get_active_workspace()
+ self.actv_workspace_num = workspace.get_number()
+
+ windows = self.SCREEN.get_windows()
+ for w in windows:
+ if workspace and w.get_workspace():
+ wnum = w.get_workspace().get_number()
+ if not w.is_skip_pager() and not w.is_skip_tasklist() and wnum == self.actv_workspace_num:
+ btn = self.createWinBttn(w)
+ self.setupSignals(btn, w)
+ self.taskBarButtons.add(btn)
+
+ def setupSignals(self, btn, win):
+ btn.connect("button-press-event", self.clickEvent, (win))
+
+ def setScreenSignals(self):
+ self.SCREEN.connect("active-workspace-changed", self.activeWorkspaceChanged)
+ # self.SCREEN.connect("application-opened", self.applicationOpened)
+ # self.SCREEN.connect("application-closed", self.applicationClosed)
+ self.SCREEN.connect("window-opened", self.windowOpened)
+ self.SCREEN.connect("window-closed", self.windowClosed)
+
+ def createWinBttn(self, w):
+ btn = gtk.Button(label=w.get_name(), always_show_image=True)
+ img = gtk.Image()
+ img.set_from_pixbuf( w.get_icon() ) # w.get_mini_icon() or w.get_icon()
+ btn.set_image(img)
+ btn.show()
+ return btn
+
+ def clickEvent(self, widget, e, window):
+ if e.type == gdk.EventType.BUTTON_PRESS and e.button == MouseButtons.LEFT_BUTTON:
+ if not window.is_minimized():
+ window.minimize()
+ else:
+ window.activate(1)
+ if e.type == gdk.EventType.BUTTON_PRESS and e.button == MouseButtons.RIGHT_BUTTON:
+ self.window = window
+ self.taskbarMenu.set_relative_to(widget)
+ self.taskbarMenu.popup()
+
+ def setPagerWidget(self):
+ pager = wnck.Pager()
+ self.builder.get_object('taskBarWorkspaces').add(pager)
+
+ # ---- Screen Events ----
+ # NOTE: This is the worst way of doing this and kids die when these are run.
+ # We need to filter actions and more like add/remove buttons than just
+ # clearing everything. I'm sorry to all the families hurt by this....
+ def activeWorkspaceChanged(self, screen, workspace):
+ self.clearChildren(self.taskBarButtons)
+ self.SCREEN = screen
+ self.SCREEN.force_update() # (Re)populate screen windows list
+ self.refreashTaskbar()
+ def windowOpened(self, screen, window):
+ self.SCREEN.force_update() # (Re)populate screen windows list
+ btn = self.createWinBttn(window)
+ self.setupSignals(btn, window)
+ self.taskBarButtons.add(btn)
+ def windowClosed(self, screen, window):
+ self.clearChildren(self.taskBarButtons)
+ self.SCREEN.force_update() # (Re)populate screen windows list
+ self.refreashTaskbar()
+
+ def clearChildren(self, parent):
+ children = parent.get_children();
+ for child in parent:
+ child.destroy()
+
+ # ---- Taskbar Button Events ----
+ def toggleMinimize(self, widget, data=None):
+ if not self.window.is_minimized():
+ self.window.minimize()
+ widget.set_label("Unminimize")
+ else:
+ self.window.activate(1)
+ widget.set_label("Minimize")
+
+ def toggleMaximize(self, widget, data=None):
+ if not self.window.is_maximized():
+ self.window.maximize()
+ widget.set_label("Unmaximize")
+ else:
+ self.window.unmaximize()
+ widget.set_label("Maximize")
+
+ def startMoveWindow(self, widget, data=None):
+ self.window.keyboard_move()
+
+ def startResizeWindow(self, widget, data=None):
+ self.window.keyboard_size()
+
+ def setTopState(self, widget):
+ if not self.window.is_above():
+ self.window.make_above()
+ else:
+ self.window.unmake_above()
+
+ def setBelowState(self, widget):
+ if not self.window.is_above():
+ self.window.make_below()
+ else:
+ self.window.unmake_below()
+
+ def setWorkspacePin(self, widget):
+ if not self.window.is_pinned():
+ self.window.pin()
+ else:
+ self.window.unpin()
+
+ def closeAppWindow(self, widget, data=None):
+ self.window.close(1)
+
+
+ # WINDOW_SIGNALS
+ # print(w.get_icon())
+ # w.get_name()
+ # w.get_icon()
+ # w.get_mini_icon()
+ # w.make_above()
+ # w. pin()
+ # w. get_state() # https://lazka.github.io/pgi-docs/Wnck-3.0/flags.html#Wnck.WindowState
+ # w.get_workspace()
+ # w.set_workspace(workspace)
+ # w.close()
+ # w.is_above()
+ # w.is_active()
+ # w.is_below()
+ # w.is_fullscreen()
+ # w.is_maximized()
+ # w.is_minimized()
+ # w.is_pinned()
+ # w.is_sticky()
diff --git a/src/Pytop/signal_classes/__init__.py b/src/Pytop/signal_classes/__init__.py
index 4bd914e..6bfa30d 100644
--- a/src/Pytop/signal_classes/__init__.py
+++ b/src/Pytop/signal_classes/__init__.py
@@ -1,2 +1,3 @@
from signal_classes.CrossClassSignals import CrossClassSignals
from signal_classes.GridSignals import GridSignals
+from signal_classes.TaskbarSignals import TaskbarSignals
diff --git a/src/Pytop/utils/FileHandler.py b/src/Pytop/utils/FileHandler.py
index c017ea2..2376792 100644
--- a/src/Pytop/utils/FileHandler.py
+++ b/src/Pytop/utils/FileHandler.py
@@ -35,24 +35,22 @@ class FileHandler:
self.TRASHINFOFOLDER = settings.returnTrshInfoPth()
-
- @threaded
def openFile(self, file):
print("Opening: " + file)
if file.lower().endswith(self.vids):
- subprocess.Popen([self.MEDIAPLAYER, self.MPV_WH, file])
+ subprocess.Popen([self.MEDIAPLAYER, self.MPV_WH, file], stdout=subprocess.PIPE)
elif file.lower().endswith(self.music):
- subprocess.Popen([self.MUSICPLAYER, file])
+ subprocess.Popen([self.MUSICPLAYER, file], stdout=subprocess.PIPE)
elif file.lower().endswith(self.images):
- subprocess.Popen([self.IMGVIEWER, file])
+ subprocess.Popen([self.IMGVIEWER, file], stdout=subprocess.PIPE)
elif file.lower().endswith(self.txt):
- subprocess.Popen([self.TEXTVIEWER, file])
+ subprocess.Popen([self.TEXTVIEWER, file], stdout=subprocess.PIPE)
elif file.lower().endswith(self.pdf):
- subprocess.Popen([self.PDFVIEWER, file])
+ subprocess.Popen([self.PDFVIEWER, file], stdout=subprocess.PIPE)
elif file.lower().endswith(self.office):
- subprocess.Popen([self.OFFICEPROG, file])
+ subprocess.Popen([self.OFFICEPROG, file], stdout=subprocess.PIPE)
else:
- subprocess.Popen(['xdg-open', file])
+ subprocess.Popen(['xdg-open', file], stdout=subprocess.PIPE)
def create(self, name, type):
diff --git a/src/Pytop/widgets/Grid.py b/src/Pytop/widgets/Grid.py
index 16c3ef7..096e2de 100644
--- a/src/Pytop/widgets/Grid.py
+++ b/src/Pytop/widgets/Grid.py
@@ -50,7 +50,7 @@ class Grid:
self.grid.connect("item-activated", self.iconDblLeftClick)
self.grid.connect("button_release_event", self.iconSingleClick, (self.grid,))
-
+ # @threaded
def setNewDirectory(self, path):
self.currentPath = path
dirPaths = ['.', '..']
@@ -112,7 +112,8 @@ class Grid:
for dataSet in self.toWorkPool:
self.store.append([dataSet[0].get_pixbuf(), dataSet[1]])
- self.toWorkPool.clear()
+ self.toWorkPool.clear()
+
if len(self.store) == len(files): # Processed all files
return False
else: # Check again when idle