diff --git a/src/clear_pycache_dirs.sh b/src/clear_pycache_dirs.sh
new file mode 100755
index 0000000..7987a3d
--- /dev/null
+++ b/src/clear_pycache_dirs.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# set -o xtrace ## To debug scripts
+# set -o errexit ## To exit on error
+# set -o errunset ## To exit if a variable is referenced but not set
+
+
+function main() {
+ find . -name "__pycache__" -exec rm -rf $1 {} \;
+ find . -name "*.pyc" -exec rm -rf $1 {} \;
+}
+main
diff --git a/src/debs/build.sh b/src/debs/build.sh
new file mode 100644
index 0000000..48e85d7
--- /dev/null
+++ b/src/debs/build.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# Fixes ownershp
+function main() {
+ sudo find . -type f -exec chmod 644 {} +
+ sudo find . -type d -exec chmod 755 {} +
+
+ # Set postrm permissions
+ for i in `find . -name postrm`; do
+ sudo chmod 755 "${i}"
+ done
+
+ # Set pytop permissions
+ for i in `find . -name pytop`; do
+ sudo chmod 755 "${i}"
+ done
+
+ sudo chown -R root:root ./*/
+
+ builder;
+ bash ./chownAll.sh
+}
+
+#builds debs
+function builder() {
+ for i in `ls`; do
+ if [[ -d "${i}" ]]; then
+ dpkg --build "${i}"
+ else
+ echo "Not a dir."
+ fi
+ done
+}
+main;
diff --git a/src/debs/chownAll.sh b/src/debs/chownAll.sh
new file mode 100644
index 0000000..44bef62
--- /dev/null
+++ b/src/debs/chownAll.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+function main() {
+ sudo chown -R abaddon:abaddon .
+}
+main;
diff --git a/src/debs/pytop-0-0-1-x64/DEBIAN/control b/src/debs/pytop-0-0-1-x64/DEBIAN/control
new file mode 100644
index 0000000..d7afe4f
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/DEBIAN/control
@@ -0,0 +1,8 @@
+Package: pytop64
+Version: 0.0-1
+Section: python
+Priority: optional
+Architecture: amd64
+Depends: python3, wget, steamcmd, ffmpegthumbnailer (>= 2.0.10-0.1), gir1.2-wnck-1.0
+Maintainer: Maxim Stewart <1itdominator@gmail.com>
+Description: Pytop is a custom desktop GUI.
diff --git a/src/debs/pytop-0-0-1-x64/DEBIAN/postrm b/src/debs/pytop-0-0-1-x64/DEBIAN/postrm
new file mode 100755
index 0000000..4962c4c
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/DEBIAN/postrm
@@ -0,0 +1,11 @@
+#!/bin/bash
+#postrm (script executed after uninstalling the package)
+#set -e
+
+if [ -f /bin/pytop ]; then
+ rm /bin/pytop
+fi
+
+if [ -d /opt/Pytop ]; then
+ rm -rf /opt/Pytop
+fi
diff --git a/src/debs/pytop-0-0-1-x64/bin/pytop b/src/debs/pytop-0-0-1-x64/bin/pytop
new file mode 100755
index 0000000..d4cb9c9
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/bin/pytop differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/__init__.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/__init__.py
new file mode 100644
index 0000000..054b613
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/__init__.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python3
+
+# Python imports
+import inspect
+
+from setproctitle import setproctitle
+
+# Gtk imports
+import gi, faulthandler, signal
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import Gtk as gtk
+from gi.repository import Gdk as gdk
+from gi.repository import GLib
+
+# Application imports
+from utils import Settings
+from signal_classes import Signals
+
+
+class Main:
+ def __init__(self, args):
+ setproctitle('Pytop')
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit)
+ faulthandler.enable() # For better debug info
+
+ builder = gtk.Builder()
+ settings = Settings()
+ settings.attachBuilder(builder)
+ self.connectBuilder(settings, builder)
+
+ window = settings.createWindow()
+ window.fullscreen()
+ window.show()
+
+ monitors = settings.returnMonitorsInfo()
+ i = 1
+ if len(monitors) > 1:
+ for mon in monitors[1:]:
+ subBuilder = gtk.Builder()
+ subSettings = Settings(i)
+ subSettings.attachBuilder(subBuilder)
+ self.connectBuilder(subSettings, subBuilder)
+
+ win = subSettings.createWindow()
+ win.set_default_size(mon.width, mon.height)
+ win.set_size_request(mon.width, mon.height)
+ win.set_resizable(False)
+ win.move(mon.x, mon.y)
+
+ win.show()
+ i += 1
+
+
+ def connectBuilder(self, settings, builder):
+ # Gets the methods from the classes and sets to handler.
+ # Then, builder connects to any signals it needs.
+ classes = [Signals(settings)]
+
+ handlers = {}
+ for c in classes:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+
+ builder.connect_signals(handlers)
+
+
+if __name__ == "__main__":
+ try:
+ main = Main()
+ gtk.main()
+ except Exception as e:
+ print( repr(e) )
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/__main__.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/__main__.py
new file mode 100644
index 0000000..06514d9
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/__main__.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python3
+
+
+# Python imports
+import argparse
+import pdb # For trace debugging
+from setproctitle import setproctitle
+
+# Gtk imports
+import gi, faulthandler, signal
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk as gtk
+from gi.repository import GLib
+
+# Application imports
+from __init__ import Main
+
+
+
+if __name__ == "__main__":
+ try:
+ # pdb.set_trace()
+ setproctitle('Pytop')
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit)
+ faulthandler.enable() # For better debug info
+ parser = argparse.ArgumentParser()
+ # Add long and short arguments
+ parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
+
+ # Read arguments (If any...)
+ args = parser.parse_args()
+ main = Main(args)
+ gtk.main()
+ except Exception as e:
+ print( repr(e) )
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/Main_Window.glade b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/Main_Window.glade
new file mode 100644
index 0000000..f0222ae
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/Main_Window.glade
@@ -0,0 +1,849 @@
+
+
+
+
+
+
+
+
+
+
+
+ 500
+ 0
+ False
+ taskBarWorkspacesVer
+ bottom
+
+
+ True
+ False
+
+
+ True
+ True
+ True
+ True
+ rgb(138,226,52)
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 60
+ True
+ False
+
+
+
+
+ True
+ True
+ 2
+
+
+
+
+ True
+ True
+ brushSizeProp
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+
+ True
+ False
+ icons/trash.png
+
+
+ False
+ False
+ True
+ center
+ True
+ splashscreen
+ True
+ True
+ False
+ False
+ center
+
+
+
+
+
+
+ 500
+ True
+ False
+ vertical
+
+
+ 500
+ 26
+ True
+ True
+ gtk-edit
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ gtk-copy
+ True
+ True
+ True
+ Copy...
+ True
+ True
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ gtk-cut
+ True
+ True
+ True
+ Cut...
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ gtk-delete
+ True
+ True
+ True
+ Delete...
+ True
+ True
+
+
+
+ False
+ True
+ 4
+
+
+
+
+ Trash
+ True
+ True
+ True
+ Move to Trash...
+ trashImage
+ True
+
+
+
+ False
+ True
+ end
+ 3
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/archive.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/archive.png
new file mode 100644
index 0000000..7943e4e
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/archive.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/audio.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/audio.png
new file mode 100644
index 0000000..c010134
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/audio.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/bin.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/bin.png
new file mode 100644
index 0000000..d6954e3
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/bin.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/dir.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/dir.png
new file mode 100644
index 0000000..a9b5e9f
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/dir.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/doc.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/doc.png
new file mode 100644
index 0000000..f838826
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/doc.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/pdf.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/pdf.png
new file mode 100644
index 0000000..9f40122
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/pdf.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/presentation.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/presentation.png
new file mode 100644
index 0000000..3a339af
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/presentation.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/spreadsheet.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/spreadsheet.png
new file mode 100644
index 0000000..710efa6
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/spreadsheet.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/text.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/text.png
new file mode 100644
index 0000000..2546fcd
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/text.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/trash.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/trash.png
new file mode 100644
index 0000000..c6514b9
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/trash.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/video.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/video.png
new file mode 100644
index 0000000..55afa98
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/video.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/web.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/web.png
new file mode 100644
index 0000000..17017ce
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/icons/web.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_128x128.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_128x128.png
new file mode 100644
index 0000000..d565a6a
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_128x128.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_256x256.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_256x256.png
new file mode 100644
index 0000000..29810bb
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_256x256.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_32x32.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_32x32.png
new file mode 100644
index 0000000..ba53a44
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_32x32.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_64x64.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_64x64.png
new file mode 100644
index 0000000..07eae0a
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_64x64.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_72x72.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_72x72.png
new file mode 100644
index 0000000..221cca5
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_72x72.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_96x96.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_96x96.png
new file mode 100644
index 0000000..74020a7
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon2_96x96.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_128x128.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_128x128.png
new file mode 100644
index 0000000..f11fb1b
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_128x128.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_256x256.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_256x256.png
new file mode 100644
index 0000000..f7b5320
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_256x256.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_32x32.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_32x32.png
new file mode 100644
index 0000000..f5abcf9
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_32x32.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_64x64.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_64x64.png
new file mode 100644
index 0000000..7250d7c
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_64x64.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_72x72.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_72x72.png
new file mode 100644
index 0000000..2d4e7c1
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_72x72.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_96x96.png b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_96x96.png
new file mode 100644
index 0000000..9ea489d
Binary files /dev/null and b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/start_menu_icons/start_menu_icon_96x96.png differ
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/stylesheet.css b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/stylesheet.css
new file mode 100644
index 0000000..d8263f5
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/resources/stylesheet.css
@@ -0,0 +1,96 @@
+/* Menu css properties */
+.menu_scroller label {
+ font-size: 120%;
+}
+
+
+/* Grid css properties */
+
+viewport,
+treeview,
+treeview > header,
+notebook > stack,
+notebook > header {
+ background-color: rgba(0, 0, 0, 0.24);
+}
+
+
+notebook > header {
+ background-color: rgba(0, 0, 0, 0.24);
+ border-color: rgba(0, 232, 255, 0.64);
+}
+
+box,
+iconview {
+ background-color: rgba(0, 0, 0, 0.2);
+ background: rgba(0, 0, 0, 0.2);
+}
+
+treeview,
+treeview.view {
+ background: rgba(0, 0, 0, 0.2);
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+cell {
+ margin: 0em;
+ padding: 0em;
+ /* float: left; */
+}
+
+cell:focus {
+ outline-style: solid;
+ outline-color: rgba(0, 232, 255, 0.64);
+}
+
+
+/* Ivonview and children default color */
+.view {
+ background-color: rgba(0, 0, 0, 0.22);
+ color: #ebebeb;
+}
+
+
+/* Hover over color when not selected */
+.view:hover {
+ box-shadow: inset 0 0 0 9999px alpha(rgba(0, 232, 255, 0.64), 0.54);
+}
+
+/* Handles the icon selection hover and selected hover color. */
+.view:selected,
+.view:selected:hover {
+ box-shadow: inset 0 0 0 9999px rgba(15, 134, 13, 0.49);
+}
+
+/* Rubberband coloring */
+.rubberband,
+rubberband,
+flowbox rubberband,
+treeview.view rubberband,
+.content-view rubberband,
+.content-view .rubberband,
+XfdesktopIconView.view .rubberband {
+ border: 1px solid #6c6c6c;
+ background-color: rgba(21, 158, 167, 0.57);
+}
+
+XfdesktopIconView.view:active {
+ background-color: rgba(172, 102, 21, 1);
+}
+
+
+XfdesktopIconView.view {
+ border-radius: 4px;
+ background-color: transparent;
+ color: white;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
+}
+
+XfdesktopIconView.view:active {
+ box-shadow: none;
+ text-shadow: none;
+}
+
+XfdesktopIconView.view .rubberband {
+ border-radius: 0;
+}
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/Signals.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/Signals.py
new file mode 100644
index 0000000..97dd312
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/Signals.py
@@ -0,0 +1,89 @@
+# Python imports
+from datetime import datetime
+import os
+
+# Gtk imports
+
+
+# Application imports
+from .mixins import CPUDrawMixin, MainMenuMixin, TaskbarMixin, GridMixin
+from widgets import Grid
+from widgets import Icon
+from utils import FileHandler
+
+
+
+class Signals(CPUDrawMixin, MainMenuMixin, TaskbarMixin, GridMixin):
+ def __init__(self, settings):
+ self.settings = settings
+ self.builder = self.settings.returnBuilder()
+
+ self.timeLabel = self.builder.get_object("timeLabel")
+ self.drawArea = self.builder.get_object("drawArea")
+ self.brushSizeProp = self.builder.get_object("brushSizeProp")
+ self.brushColorProp = self.builder.get_object("brushColorProp")
+
+ # Menu bar setup - Note: Must be before setting clock
+ self.orientation = 1 # 0 = horizontal, 1 = vertical
+ self.setPagerWidget()
+ self.setTasklistWidget()
+
+ # Must be after pager and task list inits
+ self.displayclock()
+ self.startClock()
+
+ # CPUDrawMixin Parts
+ self.cpu_percents = []
+ self.doDrawBackground = False
+ self.isDrawing = False
+ self.surface = None
+ self.aw = None # Draw area width
+ self.ah = None # Draw area height
+ self.xStep = None # For x-axis 60 sec steps
+ self.yStep = None # For y-axis %s
+
+ rgba = self.brushColorProp.get_rgba()
+ self.brushColorVal = [rgba.red, rgba.green, rgba.blue, rgba.alpha]
+ self.brushSizeVal = self.brushSizeProp.get_value()
+ self.updateSpeed = 100 # 1 sec = 1000ms
+
+ self.good = [0.53, 0.8, 0.15, 1.0]
+ self.warning = [1.0, 0.66, 0.0, 1.0]
+ self.danger = [1.0, 0.0, 0.0, 1.0]
+
+
+ # GridMixin Parts
+ self.filehandler = FileHandler(settings)
+
+ self.builder = self.settings.returnBuilder()
+ self.gridObj = self.builder.get_object("Desktop")
+ selectDirDialog = self.builder.get_object("selectDirDialog")
+ filefilter = self.builder.get_object("Folders")
+
+ self.currentPath = self.settings.returnSettings()[0]
+ self.copyCutArry = []
+ self.selectedFiles = []
+ self.gridClss = Grid(self.gridObj, self.settings)
+ self.pasteType = 1 # copy == 1 and cut == 2
+
+
+ # Add filter to allow only folders to be selected
+ selectDirDialog.add_filter(filefilter)
+ selectDirDialog.set_filename(self.currentPath)
+ self.setNewDirectory(selectDirDialog)
+
+
+ # Program Menu Parts
+ self.menuWindow = self.builder.get_object("menuWindow")
+ self.menuWindow.set_keep_above(True);
+ self.showIcons = False
+
+ self.iconFactory = Icon(self.settings)
+ self.grpDefault = "Accessories"
+ self.progGroup = self.grpDefault
+ HOME_APPS = os.path.expanduser('~') + "/.local/share/applications/"
+ paths = ["/opt/", "/usr/share/applications/", HOME_APPS]
+ self.menuData = self.getDesktopFilesInfo(paths)
+ self.desktopObjs = []
+ self.getSubgroup()
+ self.generateListView()
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/__init__.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/__init__.py
new file mode 100644
index 0000000..f5b8431
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/__init__.py
@@ -0,0 +1,4 @@
+from .mixins import CPUDrawMixin
+from .mixins import TaskbarMixin
+from .mixins import GridMixin
+from signal_classes.Signals import Signals
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/CPUDrawMixin.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/CPUDrawMixin.py
new file mode 100644
index 0000000..e921777
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/CPUDrawMixin.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python3
+
+
+# Python Imports
+from __future__ import division
+import cairo, psutil
+
+
+# GTK Imports
+from gi.repository import GObject
+from gi.repository import GLib
+
+
+
+
+
+class CPUDrawMixin:
+ # Note: y-axis on draw area goes from top to bottom when increasing.
+ # Need to do the math such that you subtract from max height to start from bottom to go up
+ # self.linePoints.append([1 * xStep, ah - (23 * yStep)]) # 23%
+ # self.linePoints.append([2 * xStep, ah - (60 * yStep)]) # 60%
+ # self.drawPointLine()
+ # del self.linePoints[0:1]
+ # self.linePoints.append([3 * xStep, ah - (44 * yStep)]) # 44%
+ def updateCPUPoints(self):
+ # Clears screen when enough points exist and unshifts the
+ # first point to keep fixed range
+ self.drawBackground(self.brush, self.aw, self.ah)
+ del self.cpu_percents[0:1]
+
+ precent = psutil.cpu_percent()
+ self.cpu_percents.append(precent)
+ self.drawPointLine() # Will re-draw every point
+ self.drawArea.queue_draw()
+
+ return True
+
+
+ def drawPointLine(self):
+ self.brush.set_line_width(self.brushSizeVal)
+ self.brush.set_line_cap(1) # 0 = BUTT, 1 = ROUND, 2 = SQUARE
+
+ oldX = 0.0
+ oldP = 0.0
+ i = 1
+ for p in self.cpu_percents:
+ # set color depending on usage...
+ rgba = self.brushColorVal
+ if p > 50.0:
+ rgba = self.warning
+ if p > 85.0:
+ rgba = self.danger
+
+ self.brush.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3])
+
+ # Movbe to prev. point if any
+ if oldP is not 0.0 and oldX is not 0.0:
+ x = oldX
+ y = float(self.ah) - (oldP * self.yStep)
+ self.brush.move_to(x, y)
+
+ # Draw line to the new point from old point
+ x2 = i * self.xStep
+ y2 = float(self.ah) - (p * self.yStep)
+ self.brush.line_to(x2, y2)
+ self.brush.stroke()
+
+ # Collect info to use as prev. pint
+ oldX = x2
+ oldP = p
+ i += 1
+
+
+ def onConfigure(self, area, eve, data = None):
+ aw = area.get_allocated_width()
+ ah = area.get_allocated_height()
+ self.aw = aw
+ self.ah = ah
+ self.xStep = aw / 200 # For x-axis
+ self.yStep = ah / 100 # For y-axis %s
+ self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, aw, ah)
+ self.brush = cairo.Context(self.surface)
+
+ self.drawBackground(self.brush, aw, ah)
+ if not self.isDrawing:
+ self.fillCPUPercents()
+ self.startCPUGraph()
+ self.isDrawing = True
+
+ return False
+
+ # Fill with y bank with 50%s
+ def fillCPUPercents(self):
+ self.cpu_percents = [50.0] * 198
+
+
+ # Draw background white
+ def drawBackground(self, brush, aw, ah):
+ brush.rectangle(0, 0, aw, ah) # x, y, width, height
+ brush.set_source_rgba(1, 1, 1, 1.0) # x, y, width, height
+
+ if not self.doDrawBackground: # If transparent or white
+ self.brush.set_operator(0);
+
+ brush.fill()
+ self.brush.set_operator(1); # reset the brush after filling bg...
+
+
+ # Starting graph generation
+ def startCPUGraph(self):
+ GObject.timeout_add(self.updateSpeed, self.updateCPUPoints)
+
+
+ def onDraw(self, area, brush):
+ if self.surface is not None:
+ brush.set_source_surface(self.surface, 0.0, 0.0)
+ brush.paint()
+ else:
+ print("No surface info...")
+
+ return False
+
+
+ def onColorSet(self, widget):
+ rgba = widget.get_rgba()
+ self.brushColorVal = [rgba.red, rgba.green, rgba.blue, rgba.alpha]
+
+ def onBrushSizeChange(self, widget):
+ self.brushSizeVal = self.brushSizeProp.get_value()
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/GridMixin.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/GridMixin.py
new file mode 100644
index 0000000..a10cf42
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/GridMixin.py
@@ -0,0 +1,77 @@
+# Gtk imports
+
+# Python imports
+
+# Application imports
+
+
+class GridMixin:
+ # Calls the Grid widget class' method
+ def setNewDirectory(self, widget, data=None):
+ newPath = widget.get_filename()
+ self.gridClss.setNewDirectory(newPath)
+ self.settings.saveSettings(newPath)
+
+
+ # File control events
+ def create(self, wdget):
+ self.currentPath = self.gridClss.returnCurrentPath()
+ fileName = self.builder.get_object("filenameInput").get_text().strip()
+ type = self.builder.get_object("createSwitch").get_state()
+
+ if fileName != "":
+ fileName = self.currentPath + "/" + fileName
+ status = self.filehandler.create(fileName, type)
+ if status == 0:
+ self.gridClss.setNewDirectory(self.currentPath)
+
+ def copy(self, widget):
+ self.pasteType = 1
+ self.copyCutArry = self.gridClss.returnSelectedFiles()
+
+ def cut(self, widget):
+ self.pasteType = 2
+ self.copyCutArry = self.gridClss.returnSelectedFiles()
+
+ def paste(self, widget):
+ self.currentPath = self.gridClss.returnCurrentPath()
+ status = self.filehandler.paste(self.copyCutArry, self.currentPath, self.pasteType)
+
+ if status == 0:
+ self.gridClss.setNewDirectory(self.currentPath)
+ if self.pasteType == 2: # cut == 2
+ self.copyCutArry = []
+
+ def delete(self, widget):
+ self.getGridInfo()
+ status = self.filehandler.delete(self.selectedFiles)
+
+ if status == 0:
+ self.selectedFiles = []
+ self.gridClss.setNewDirectory(self.currentPath)
+
+ def trash(self, widget):
+ self.getGridInfo()
+ status = self.filehandler.trash(self.selectedFiles)
+
+ if status == 0:
+ self.selectedFiles = []
+ self.gridClss.setNewDirectory(self.currentPath)
+
+ def rename(self, widget, data):
+ if data.keyval == 65293: # Enter key press
+ self.getGridInfo()
+ file = widget.get_text();
+ if len(self.selectedFiles) == 1:
+ newName = self.currentPath + "/" + file
+ print("Old Name: " + self.selectedFiles[0])
+ print("New Name: " + newName.strip())
+
+ status = self.filehandler.rename(self.selectedFiles[0], newName.strip())
+ if status == 0:
+ self.selectedFiles = [newName]
+ self.gridClss.setNewDirectory(self.currentPath)
+
+ def getGridInfo(self):
+ self.selectedFiles = self.gridClss.returnSelectedFiles()
+ self.currentPath = self.gridClss.returnCurrentPath()
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/MainMenuMixin.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/MainMenuMixin.py
new file mode 100644
index 0000000..85c786f
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/MainMenuMixin.py
@@ -0,0 +1,290 @@
+# Python imports
+import subprocess
+import threading
+import os
+import json
+
+from os.path import isdir, isfile, join
+from os import listdir
+
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import Gtk as gtk
+from gi.repository import GLib as glib
+from xdg.DesktopEntry import DesktopEntry
+
+
+# Application imports
+
+
+
+
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+ return wrapper
+
+
+class MainMenuMixin:
+ def toggleProgramMenu(self, widget):
+ pos = self.menuWindow.get_position()
+ posX = pos[0] + 32
+ posY = pos[1] + 72
+ if self.menuWindow.get_visible() == False:
+ self.menuWindow.move(posX, posY)
+ glib.idle_add(self.menuWindow.show_all)
+ else:
+ glib.idle_add(self.menuWindow.hide)
+
+
+ def setListGroup(self, widget):
+ self.progGroup = widget.get_label().strip()
+ self.getSubgroup(self.progGroup)
+ self.generateListView()
+
+
+ def searchForEntry(self, widget, data=None):
+ self.progGroup = "[ Search ]"
+ query = widget.get_text().strip()
+ if not query:
+ self.progGroup = self.grpDefault
+ self.getSubgroup()
+ self.generateListView()
+ return
+
+ self.getSubgroup(query)
+ self.generateListView()
+
+
+ @threaded
+ def generateListView(self):
+ widget = self.builder.get_object("programListBttns")
+
+ # Should have this as a useful method...But, I don't want to import Glib everywhere
+ children = widget.get_children()
+ for child in children:
+ glib.idle_add(widget.remove, (child))
+
+ for obj in self.desktopObjs:
+ title = obj[0]
+ dirPath = obj[1]
+ if self.showIcons:
+ image = self.iconFactory.parseDesktopFiles(dirPath) # .get_pixbuf()
+ self.addToProgramListView(widget, title, image)
+ else:
+ self.addToProgramListViewAsText(widget, title)
+
+
+ @threaded
+ def addToProgramListView(self, widget, title, icon):
+ button = gtk.Button(label=title)
+ button.set_image(icon)
+ button.connect("clicked", self.executeProgram)
+
+ children = button.get_children()
+ alignment1 = children[0]
+ box = alignment1.get_children()[0]
+ label = box.get_children()[1]
+
+ alignment1.set(0.0, 0.0, 0.0, 0.0)
+ label.set_halign(1)
+ label.set_line_wrap(True)
+ label.set_max_width_chars(38)
+ label.set_size_request(640, 64)
+
+ button.show_all()
+ glib.idle_add(widget.add, (button))
+
+ @threaded
+ def addToProgramListViewAsText(self, widget, title):
+ button = gtk.Button(label=title)
+ button.connect("clicked", self.executeProgram)
+
+ children = button.get_children()
+ label = children[0]
+
+ label.set_halign(1)
+ label.set_line_wrap(True)
+ label.set_max_width_chars(38)
+ label.set_size_request(640, 64)
+
+ button.show_all()
+ glib.idle_add(widget.add, (button))
+
+
+ def executeProgram(self, widget):
+ """
+ # TODO:
+ Need to refactor and pull out the sub loop that is used in both cases...
+ """
+ entry = widget.get_label().strip()
+ group = self.progGroup
+
+ parts = entry.split("||")
+ program = parts[0].strip()
+ comment = parts[1].strip()
+
+ if "[ Search ]" in group:
+ gkeys = self.menuData.keys()
+ for gkey in gkeys:
+ for opt in self.menuData[gkey]:
+ if program in opt["title"]:
+ keys = opt.keys()
+ if comment in opt["comment"] or comment in opt["fileName"]:
+ DEVNULL = open(os.devnull, 'w')
+ execFailed = False
+ try:
+ command = opt["tryExec"].split("%")[0]
+ # self.logger.debug(command)
+ subprocess.Popen(command.split(), start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)
+ break
+ except Exception as e:
+ execFailed = True
+
+ if execFailed:
+ try:
+ if "exec" in keys and len(opt["exec"]):
+ command = opt["exec"].split("%")[0]
+ # self.logger.debug(command)
+ subprocess.Popen(command.split(), start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)
+ break
+ except Exception as e:
+ print( repr(e) )
+ # self.logger.debug(e)
+ else:
+ for opt in self.menuData[group]:
+ if program in opt["title"]:
+ keys = opt.keys()
+ if comment in opt["comment"] or comment in opt["fileName"]:
+ DEVNULL = open(os.devnull, 'w')
+ execFailed = False
+ try:
+ command = opt["tryExec"].split("%")[0]
+ # self.logger.debug(command)
+ subprocess.Popen(command.split(), start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)
+ except Exception as e:
+ execFailed = True
+
+ if execFailed:
+ try:
+ if "exec" in keys and len(opt["exec"]):
+ command = opt["exec"].split("%")[0]
+ # self.logger.debug(command)
+ subprocess.Popen(command.split(), start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)
+ except Exception as e:
+ print( repr(e) )
+ # self.logger.debug(e)
+
+
+ # Supoport methods
+ def getDesktopFilesInfo(self, paths):
+ menuObjs = {
+ "Accessories": [],
+ "Multimedia": [],
+ "Graphics": [],
+ "Game": [],
+ "Office": [],
+ "Development": [],
+ "Internet": [],
+ "Settings": [],
+ "System": [],
+ "Wine": [],
+ "Other": []
+ }
+
+ for path in paths:
+ if not "/opt/" in path:
+ self.listAndUpdateDesktopFiles(path, menuObjs);
+ else:
+ for folder in listdir(path):
+ try:
+ fPath = path + folder + "/"
+ self.listAndUpdateDesktopFiles(fPath, menuObjs);
+ except Exception as e:
+ print( repr(e) )
+
+ return menuObjs
+
+ def listAndUpdateDesktopFiles(self, path, menuObjs):
+ for f in listdir(path):
+ fPath = path + f
+ if isfile(fPath) and f.endswith(".desktop"):
+ xdgObj = DesktopEntry(fPath)
+
+ title = xdgObj.getName()
+ groups = xdgObj.getCategories()
+ comment = xdgObj.getComment()
+ icon = xdgObj.getIcon()
+ mainExec = xdgObj.getExec()
+ tryExec = xdgObj.getTryExec()
+
+ group = ""
+ if "Accessories" in groups or "Utility" in groups:
+ group = "Accessories"
+ elif "Multimedia" in groups or "Video" in groups or "Audio" in groups:
+ group = "Multimedia"
+ elif "Development" in groups:
+ group = "Development"
+ elif "Game" in groups:
+ group = "Game"
+ elif "Internet" in groups or "Network" in groups:
+ group = "Internet"
+ elif "Graphics" in groups:
+ group = "Graphics"
+ elif "Office" in groups:
+ group = "Office"
+ elif "System" in groups:
+ group = "System"
+ elif "Settings" in groups:
+ group = "Settings"
+ elif "Wine" in groups:
+ group = "Wine"
+ else:
+ group = "Other"
+
+ menuObjs[group].append( {"title": title, "groups": groups,
+ "comment": comment, "exec": mainExec,
+ "tryExec": tryExec, "fileName": f,
+ "filePath": fPath, "icon": icon})
+
+
+ def getSubgroup(self, query = ""):
+ """
+ Need to refactor and pull out the sub logic that is used in both cases...
+ """
+ group = self.progGroup
+ self.desktopObjs.clear()
+ if "[ Search ]" in group:
+ gkeys = self.menuData.keys()
+ for gkey in gkeys:
+ for opt in self.menuData[gkey]:
+ keys = opt.keys()
+
+ if "comment" in keys and len(opt["comment"]) > 0 :
+ if query.lower() in opt["comment"].lower():
+ title = opt["title"] + " || " + opt["comment"]
+ fPath = opt["filePath"]
+ self.desktopObjs.append([title, fPath])
+ continue
+
+ if query.lower() in opt["title"].lower() or \
+ query.lower() in opt["fileName"].lower():
+ title = opt["title"] + " || " + opt["fileName"].replace(".desktop", "")
+ fPath = opt["filePath"]
+ self.desktopObjs.append([title, fPath])
+ else:
+ for opt in self.menuData[group]:
+ keys = opt.keys()
+ if "comment" in keys and len(opt["comment"]) > 0 :
+ title = opt["title"] + " || " + opt["comment"]
+ fPath = opt["filePath"]
+ self.desktopObjs.append([title, fPath])
+ else:
+ title = opt["title"] + " || " + opt["fileName"].replace(".desktop", "")
+ fPath = opt["filePath"]
+ self.desktopObjs.append([title, fPath])
+
+ return self.desktopObjs
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/TaskbarMixin.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/TaskbarMixin.py
new file mode 100644
index 0000000..f8fee3b
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/TaskbarMixin.py
@@ -0,0 +1,90 @@
+# Python imports
+import threading
+from datetime import datetime
+
+# 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
+from gi.repository import GObject
+
+# Application imports
+
+
+
+class MouseButton:
+ LEFT_BUTTON = 1
+ MIDDLE_BUTTON = 2
+ RIGHT_BUTTON = 3
+
+
+class TaskbarMixin:
+ def toggleCalPopover(self, widget, eve):
+ calendarPopup = self.builder.get_object('calendarPopup')
+ if (calendarPopup.get_visible() == False):
+ calendarWid = self.builder.get_object('calendarWid')
+ now = datetime.now()
+ timeStr = now.strftime("%m/%d/%Y")
+ parts = timeStr.split("/")
+ month = int(parts[0]) - 1
+ day = int(parts[1])
+ year = int(parts[2])
+ calendarWid.select_day(day)
+ calendarWid.select_month(month, year)
+ calendarPopup.popup()
+ else:
+ calendarPopup.popdown()
+
+
+ def showSystemStats(self, widget, eve):
+ if eve.type == gdk.EventType.BUTTON_RELEASE and eve.button == MouseButton.RIGHT_BUTTON:
+ self.builder.get_object('systemStats').popup()
+
+ def setPagerWidget(self):
+ pager = wnck.Pager()
+
+ if self.orientation == 0:
+ self.builder.get_object('taskBarWorkspacesHor').add(pager)
+ else:
+ self.builder.get_object('taskBarWorkspacesVer').add(pager)
+
+ pager.show()
+
+
+ def setTasklistWidget(self):
+ tasklist = wnck.Tasklist()
+ tasklist.set_scroll_enabled(False)
+ tasklist.set_button_relief(2) # 0 = normal relief, 2 = no relief
+ tasklist.set_grouping(1) # 0 = mever group, 1 auto group, 2 = always group
+
+ tasklist.set_orientation(self.orientation)
+ if self.orientation == 0:
+ self.builder.get_object('taskBarButtonsHor').add(tasklist)
+ else:
+ self.builder.get_object('taskBarButtonsVer').add(tasklist)
+
+ tasklist.show()
+
+
+ # Displays Timer
+ def displayclock(self):
+ now = datetime.now()
+ hms = now.strftime("%I:%M %p")
+ mdy = now.strftime("%m/%d/%Y")
+ timeStr = hms + "\n" + mdy
+ self.timeLabel.set_label(timeStr)
+ return True
+
+ # Starting or clock
+ def startClock(self):
+ GObject.timeout_add(59000, self.displayclock)
+
+
+ def closePopup(self, widget, data=None):
+ widget.hide()
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/__init__.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/__init__.py
new file mode 100644
index 0000000..7df5562
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/signal_classes/mixins/__init__.py
@@ -0,0 +1,4 @@
+from .MainMenuMixin import MainMenuMixin
+from .TaskbarMixin import TaskbarMixin
+from .CPUDrawMixin import CPUDrawMixin
+from .GridMixin import GridMixin
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Dragging.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Dragging.py
new file mode 100644
index 0000000..abf0a03
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Dragging.py
@@ -0,0 +1,85 @@
+# Gtk imports
+import gi
+
+gi.require_version('Gdk', '3.0')
+
+from gi.repository import Gdk
+from gi.repository import GObject
+
+# Python imports
+import os
+
+# Application imports
+
+
+class Dragging:
+ def __init__(self):
+ # higher values make movement more performant
+ # lower values make movement smoother
+ self.SENSITIVITY = 1
+ self.desktop = None
+ self.EvMask = Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON1_MOTION_MASK
+ self.offsetx = 0
+ self.offsety = 0
+ self.px = 0
+ self.py = 0
+ self.maxx = 0
+ self.maxy = 0
+
+ def connectEvents(self, desktop, widget):
+ self.desktop = desktop
+ widget.set_events(self.EvMask)
+ widget.connect("button_press_event", self.press_event)
+ widget.connect("motion_notify_event", self.draggingEvent)
+ widget.show()
+
+ def press_event(self, w, event):
+ if event.button == 1:
+ p = w.get_parent()
+ # offset == distance of parent widget from edge of screen ...
+ self.offsetx, self.offsety = p.get_window().get_position()
+ # plus distance from pointer to edge of widget
+ self.offsetx += event.x
+ self.offsety += event.y
+ # self.maxx, self.maxy both relative to the parent
+ # note that we're rounding down now so that these max values don't get
+ # rounded upward later and push the widget off the edge of its parent.
+ self.maxx = self.RoundDownToMultiple(p.get_allocation().width - w.get_allocation().width, self.SENSITIVITY)
+ self.maxy = self.RoundDownToMultiple(p.get_allocation().height - w.get_allocation().height, self.SENSITIVITY)
+
+
+ def draggingEvent(self, widget, event):
+ # x_root,x_root relative to screen
+ # x,y relative to parent (fixed widget)
+ # self.px,self.py stores previous values of x,y
+
+ # get starting values for x,y
+ x = event.x_root - self.offsetx
+ y = event.y_root - self.offsety
+ # make sure the potential coordinates x,y:
+ # 1) will not push any part of the widget outside of its parent container
+ # 2) is a multiple of self.SENSITIVITY
+ x = self.RoundToNearestMultiple(self.Max(self.Min(x, self.maxx), 0), self.SENSITIVITY)
+ y = self.RoundToNearestMultiple(self.Max(self.Min(y, self.maxy), 0), self.SENSITIVITY)
+ if x != self.px or y != self.py:
+ self.px = x
+ self.py = y
+ self.desktop.move(widget, x, y)
+
+ def Min(self, a, b):
+ if b < a:
+ return b
+ return a
+
+ def Max(self, a, b):
+ if b > a:
+ return b
+ return a
+
+ def RoundDownToMultiple(self, i, m):
+ return i/m*m
+
+ def RoundToNearestMultiple(self, i, m):
+ if i % m > m / 2:
+ return (i/m+1)*m
+ return i/m*m
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/FileHandler.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/FileHandler.py
new file mode 100644
index 0000000..3c8045d
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/FileHandler.py
@@ -0,0 +1,162 @@
+# Gtk imports
+
+# Python imports
+import os, shutil, subprocess, threading
+
+# Application imports
+
+
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+ return wrapper
+
+class FileHandler:
+ def __init__(self, settings):
+ # 'Filters'
+ self.office = settings.returnOfficeFilter()
+ self.vids = settings.returnVidsFilter()
+ self.txt = settings.returnTextFilter()
+ self.music = settings.returnMusicFilter()
+ self.images = settings.returnImagesFilter()
+ self.pdf = settings.returnPdfFilter()
+
+ # Args
+ self.MEDIAPLAYER = settings.returnMediaProg()
+ self.IMGVIEWER = settings.returnImgVwrProg()
+ self.MUSICPLAYER = settings.returnMusicProg()
+ self.OFFICEPROG = settings.returnOfficeProg()
+ self.TEXTVIEWER = settings.returnTextProg()
+ self.PDFVIEWER = settings.returnPdfProg()
+ self.FILEMANAGER = settings.returnFileMngrProg()
+ self.MPLAYER_WH = settings.returnMplyrWH()
+ self.MPV_WH = settings.returnMpvWHProg()
+ self.TRASHFILESFOLDER = settings.returnTrshFilesPth()
+ self.TRASHINFOFOLDER = settings.returnTrshInfoPth()
+
+
+ def openFile(self, file):
+ print("Opening: " + file)
+ DEVNULL = open(os.devnull, 'w')
+
+ if file.lower().endswith(self.vids):
+ subprocess.Popen([self.MEDIAPLAYER, self.MPV_WH, file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ elif file.lower().endswith(self.music):
+ subprocess.Popen([self.MUSICPLAYER, file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ elif file.lower().endswith(self.images):
+ subprocess.Popen([self.IMGVIEWER, file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ elif file.lower().endswith(self.txt):
+ subprocess.Popen([self.TEXTVIEWER, file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ elif file.lower().endswith(self.pdf):
+ subprocess.Popen([self.PDFVIEWER, file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ elif file.lower().endswith(self.office):
+ subprocess.Popen([self.OFFICEPROG, file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ else:
+ subprocess.Popen(['xdg-open', file], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+
+
+ def create(self, name, type):
+ try:
+ if type == True: # Create File
+ open(name, 'w')
+ else: # Create Folder
+ os.mkdir(name)
+ except Exception as e:
+ print( repr(e) )
+ return 1
+
+ return 0
+
+ def paste(self, files, toPath, pasteType):
+ try:
+ for file in files:
+ parts = file.split("/")
+ toBePath = toPath + "/" + parts[len(parts) - 1] # Used to check for duplicates
+ finalForm = file + self.dedupPathIter(toBePath)
+ isDuplicate = finalForm != file
+
+ if isDuplicate:
+ os.rename(file, finalForm)
+
+ if pasteType == 1: # copy paste = 1
+ shutil.copy2(finalForm, toPath)
+ if isDuplicate:
+ os.rename(finalForm, file) # Rename back after copy completes
+ if pasteType == 2: # cut paste = 2
+ shutil.move(finalForm, toPath)
+
+ except Exception as e:
+ print( repr(e) )
+ return 1
+
+ return 0
+
+ def delete(self, toDeleteFiles):
+ try:
+ print("Deleting...")
+ for file in toDeleteFiles:
+ print(file)
+ if os.path.exists(file):
+ if os.path.isfile(file):
+ os.remove(file)
+ elif os.path.isdir(file):
+ shutil.rmtree(file)
+ else:
+ print("The folder/file does not exist")
+ return 1
+ except Exception as e:
+ print("An error occured deleting the file:")
+ print( repr(e) )
+ return 1
+
+ return 0
+
+ def trash(self, toTrashFiles):
+ try:
+ print("Moving to Trash...")
+ for file in toTrashFiles:
+ print(file)
+ if os.path.exists(file):
+ parts = file.split("/")
+ toBeTrashPath = self.TRASHFILESFOLDER + parts[len(parts) - 1]
+ finalForm = file + self.dedupPathIter(toBeTrashPath)
+
+ if finalForm != file:
+ os.rename(file, finalForm)
+
+ shutil.move(finalForm, self.TRASHFILESFOLDER)
+ else:
+ print("The folder/file does not exist")
+ return 1
+ except Exception as e:
+ print( repr(e) )
+ return 1
+
+ return 0
+
+ def rename(self, oldFileName, newFileName):
+ try:
+ if os.path.exists(oldFileName):
+ print("Renaming...")
+ print(oldFileName + " --> " + newFileName)
+ os.rename(oldFileName, newFileName)
+ else:
+ print("The folder/file does not exist")
+ return 1
+ except Exception as e:
+ print( repr(e) )
+ return 1
+
+ return 0
+
+
+ def dedupPathIter(self, toBeTrashPath):
+ duplicateFix = ""
+ i = 0
+
+ if os.path.exists(toBeTrashPath):
+ while os.path.exists(toBeTrashPath + duplicateFix) == True:
+ i+=1
+ duplicateFix = "-" + str(i)
+
+ return duplicateFix
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Logger.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Logger.py
new file mode 100644
index 0000000..c8dc0db
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Logger.py
@@ -0,0 +1,55 @@
+# Python imports
+import os, logging
+
+# Application imports
+
+
+class Logger:
+ def __init__(self):
+ self.USER_HOME = os.path.expanduser("~")
+
+ def get_logger(self, loggerName = "NO_LOGGER_NAME_PASSED", createFile = True):
+ """
+ Create a new logging object and return it.
+ :note:
+ NOSET # Don't know the actual log level of this... (defaulting or literally none?)
+ Log Levels (From least to most)
+ Type Value
+ CRITICAL 50
+ ERROR 40
+ WARNING 30
+ INFO 20
+ DEBUG 10
+ :param loggerName: Sets the name of the logger object. (Used in log lines)
+ :param createFile: Whether we create a log file or just pump to terminal
+
+ :return: the logging object we created
+ """
+
+ globalLogLvl = logging.DEBUG # Keep this at highest so that handlers can filter to their desired levels
+ chLogLevel = logging.CRITICAL # Prety musch the only one we change ever
+ fhLogLevel = logging.DEBUG
+ log = logging.getLogger(loggerName)
+
+ # Set our log output styles
+ fFormatter = logging.Formatter('[%(asctime)s] %(pathname)s:%(lineno)d %(levelname)s - %(message)s', '%m-%d %H:%M:%S')
+ cFormatter = logging.Formatter('%(pathname)s:%(lineno)d] %(levelname)s - %(message)s')
+
+ ch = logging.StreamHandler()
+ ch.setLevel(level=chLogLevel)
+ ch.setFormatter(cFormatter)
+ log.addHandler(ch)
+
+ if createFile:
+ folder = self.USER_HOME + ".config/pytop/logs"
+ file = folder + "/application.log"
+
+ if not os.path.exists(folder):
+ os.mkdir(folder)
+
+ fh = logging.FileHandler(file)
+ fh.setLevel(level=fhLogLevel)
+ fh.setFormatter(fFormatter)
+ log.addHandler(fh)
+
+ return log
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Settings.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Settings.py
new file mode 100644
index 0000000..c9c4174
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/Settings.py
@@ -0,0 +1,180 @@
+# Gtk imports
+import gi, cairo
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
+
+from gi.repository import Gtk as gtk
+from gi.repository import Gdk as gdk
+
+# Python imports
+import os, json
+
+# Application imports
+
+
+class Settings:
+ def __init__(self, monIndex = 0):
+ self.builder = None
+ self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
+
+ # 'Filters'
+ self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm',
+ '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
+ self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv',
+ '.mpeg', '.mp4', '.webm')
+ self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf')
+ self.music = ('.psf', '.mp3', '.ogg' , '.flac')
+ self.images = ('.png', '.jpg', '.jpeg', '.gif')
+ self.pdf = ('.pdf')
+
+ self.hideHiddenFiles = True
+ self.ColumnSize = 8
+ self.usrHome = os.path.expanduser('~')
+ self.desktopPath = self.usrHome + "/Desktop"
+ self.iconContainerWxH = [128, 128]
+ self.systemIconImageWxH = [56, 56]
+ self.viIconWxH = [256, 128]
+ self.monitors = None
+
+ self.DEFAULTCOLOR = gdk.RGBA(0.0, 0.0, 0.0, 0.0) # ~#00000000
+ self.MOUSEOVERCOLOR = gdk.RGBA(0.0, 0.9, 1.0, 0.64) # ~#00e8ff
+ self.SELECTEDCOLOR = gdk.RGBA(0.4, 0.5, 0.1, 0.84)
+ self.TRASHFOLDER = os.path.expanduser('~') + "/.local/share/Trash/"
+ self.TRASHFILESFOLDER = self.TRASHFOLDER + "files/"
+ self.TRASHINFOFOLDER = self.TRASHFOLDER + "info/"
+ self.THUMB_GENERATOR = "ffmpegthumbnailer"
+ self.MEDIAPLAYER = "mpv";
+ self.IMGVIEWER = "mirage";
+ self.MUSICPLAYER = "/opt/deadbeef/bin/deadbeef";
+ self.OFFICEPROG = "libreoffice";
+ self.TEXTVIEWER = "leafpad";
+ self.PDFVIEWER = "evince";
+ self.FILEMANAGER = "spacefm";
+ self.MPLAYER_WH = " -xy 1600 -geometry 50%:50% ";
+ self.MPV_WH = " -geometry 50%:50% ";
+ self.GTK_ORIENTATION = 1 # HORIZONTAL (0) VERTICAL (1)
+
+ configFolder = os.path.expanduser('~') + "/.config/pytop/"
+ self.configFile = configFolder + "mon_" + str(monIndex) + "_settings.ini"
+
+ if os.path.isdir(configFolder) == False:
+ os.mkdir(configFolder)
+
+ if os.path.isdir(self.TRASHFOLDER) == False:
+ os.mkdir(TRASHFILESFOLDER)
+ os.mkdir(TRASHINFOFOLDER)
+
+ if os.path.isdir(self.TRASHFILESFOLDER) == False:
+ os.mkdir(TRASHFILESFOLDER)
+
+ if os.path.isdir(self.TRASHINFOFOLDER) == False:
+ os.mkdir(TRASHINFOFOLDER)
+
+ if os.path.isfile(self.configFile) == False:
+ open(self.configFile, 'a').close()
+ self.saveSettings(self.desktopPath)
+
+
+ def attachBuilder(self, builder):
+ self.builder = builder
+ self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade")
+
+ def createWindow(self):
+ # Get window and connect signals
+ window = self.builder.get_object("Window")
+ window.connect("delete-event", gtk.main_quit)
+ self.setWindowData(window)
+ return window
+
+ def setWindowData(self, window):
+ screen = window.get_screen()
+ visual = screen.get_rgba_visual()
+ if visual != None and screen.is_composited():
+ window.set_visual(visual)
+
+ # bind css file
+ cssProvider = gtk.CssProvider()
+ cssProvider.load_from_path(self.SCRIPT_PTH + '../resources/stylesheet.css')
+ screen = gdk.Screen.get_default()
+ styleContext = gtk.StyleContext()
+ styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
+
+ window.set_app_paintable(True)
+ self.monitors = self.getMonitorData(screen)
+ window.resize(self.monitors[0].width, self.monitors[0].height)
+
+ def getMonitorData(self, screen):
+ monitors = []
+ for m in range(screen.get_n_monitors()):
+ monitors.append(screen.get_monitor_geometry(m))
+
+ for monitor in monitors:
+ print(str(monitor.width) + "+" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
+
+ return monitors
+
+
+ def returnMonitorsInfo(self):
+ return self.monitors
+
+
+ def saveSettings(self, startPath):
+ data = {}
+ data['pytop_settings'] = []
+
+ data['pytop_settings'].append({
+ 'startPath' : startPath
+ })
+
+ with open(self.configFile, 'w') as outfile:
+ json.dump(data, outfile)
+
+
+ def returnSettings(self):
+ returnData = []
+
+ with open(self.configFile) as infile:
+ try:
+ data = json.load(infile)
+ for obj in data['pytop_settings']:
+ returnData = [obj['startPath']]
+ except Exception as e:
+ returnData = ['~/Desktop/']
+
+
+ if returnData[0] == '':
+ returnData[0] = '~/Desktop/'
+
+ return returnData
+
+
+ def returnBuilder(self): return self.builder
+ def returnUserHome(self): return self.usrHome
+ def returnDesktopPath(self): return self.usrHome + "/Desktop"
+ def returnColumnSize(self): return self.ColumnSize
+ def returnContainerWH(self): return self.iconContainerWxH
+ def returnSystemIconImageWH(self): return self.systemIconImageWxH
+ def returnVIIconWH(self): return self.viIconWxH
+ def isHideHiddenFiles(self): return self.hideHiddenFiles
+
+ # Filter returns
+ def returnOfficeFilter(self): return self.office
+ def returnVidsFilter(self): return self.vids
+ def returnTextFilter(self): return self.txt
+ def returnMusicFilter(self): return self.music
+ def returnImagesFilter(self): return self.images
+ def returnPdfFilter(self): return self.pdf
+
+ def returnIconImagePos(self): return self.GTK_ORIENTATION
+ def getThumbnailGenerator(self): return self.THUMB_GENERATOR
+ def returnMediaProg(self): return self.MEDIAPLAYER
+ def returnImgVwrProg(self): return self.IMGVIEWER
+ def returnMusicProg(self): return self.MUSICPLAYER
+ def returnOfficeProg(self): return self.OFFICEPROG
+ def returnTextProg(self): return self.TEXTVIEWER
+ def returnPdfProg(self): return self.PDFVIEWER
+ def returnFileMngrProg(self): return self.FILEMANAGER
+ def returnMplyrWH(self): return self.MPLAYER_WH
+ def returnMpvWHProg(self): return self.MPV_WH
+ def returnTrshFilesPth(self): return self.TRASHFILESFOLDER
+ def returnTrshInfoPth(self): return self.TRASHINFOFOLDER
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/__init__.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/__init__.py
new file mode 100644
index 0000000..548f6d4
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/utils/__init__.py
@@ -0,0 +1,4 @@
+from utils.Dragging import Dragging
+from .Logger import Logger
+from utils.FileHandler import FileHandler
+from utils.Settings import Settings
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/Grid.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/Grid.py
new file mode 100644
index 0000000..ae67fb7
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/Grid.py
@@ -0,0 +1,209 @@
+# Python imports
+import os, threading, time
+from os.path import isdir, isfile, join
+from os import listdir
+
+
+# Gtk imports
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
+
+from gi.repository import Gtk as gtk
+from gi.repository import Gdk as gdk
+from gi.repository import GLib as glib
+from gi.repository import GdkPixbuf
+
+
+# Application imports
+from .Icon import Icon
+from utils.FileHandler import FileHandler
+
+
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+ return wrapper
+
+
+class Grid:
+ def __init__(self, grid, settings):
+ self.grid = grid
+ self.settings = settings
+ self.fileHandler = FileHandler(self.settings)
+
+ self.store = gtk.ListStore(GdkPixbuf.Pixbuf, str)
+ self.usrHome = settings.returnUserHome()
+ self.hideHiddenFiles = settings.isHideHiddenFiles()
+ self.builder = settings.returnBuilder()
+ self.ColumnSize = settings.returnColumnSize()
+ self.vidsFilter = settings.returnVidsFilter()
+ self.imagesFilter = settings.returnImagesFilter()
+ self.iconFactory = Icon(settings)
+ self.selectedFiles = []
+ self.currentPath = ""
+
+ self.grid.set_model(self.store)
+ self.grid.set_pixbuf_column(0)
+ self.grid.set_text_column(1)
+ self.grid.connect("item-activated", self.iconDblLeftClick)
+ self.grid.connect("button_release_event", self.iconSingleClick, (self.grid,))
+
+ def setNewDirectory(self, path):
+ self.store.clear()
+ self.currentPath = path
+ dirPaths = ['.', '..']
+ vids = []
+ images = []
+ desktop = []
+ files = []
+
+ for f in listdir(path):
+ file = join(path, f)
+ if self.hideHiddenFiles:
+ if f.startswith('.'):
+ continue
+
+ if isfile(file):
+ lowerName = file.lower()
+
+ if lowerName.endswith(self.vidsFilter):
+ vids.append(f)
+ elif lowerName.endswith(self.imagesFilter):
+ images.append(f)
+ elif lowerName.endswith((".desktop",)):
+ desktop.append(f)
+ else:
+ files.append(f)
+ else:
+ dirPaths.append(f)
+
+ dirPaths.sort()
+ vids.sort()
+ images.sort()
+ desktop.sort()
+ files.sort()
+
+ files = dirPaths + vids + images + desktop + files
+ self.generateGridIcons(path, files)
+ self.fillVideoIcons(path, vids, len(dirPaths))
+
+
+ @threaded
+ def generateGridIcons(self, dirPath, files):
+ for file in files:
+ image = self.iconFactory.createIcon(dirPath, file).get_pixbuf()
+ glib.idle_add(self.addToGrid, (image, file,))
+
+
+ @threaded
+ def fillVideoIcons(self, dirPath, files, start):
+ model = self.grid.get_model()
+
+ # Wait till we have a proper index...
+ while len(self.store) < (start + 1):
+ time.sleep(.650)
+
+ i = start
+ for file in files:
+ self.updateGrid(model, dirPath, file, i)
+ i += 1
+
+ @threaded
+ def updateGrid(self, model, dirPath, file, i):
+ try:
+ image = self.iconFactory.createThumbnail(dirPath, file).get_pixbuf()
+ iter = model.get_iter_from_string(str(i))
+ glib.idle_add(self.replaceInGrid, (iter, image,))
+ except Exception as e:
+ # Errors seem to happen when fillVideoIcons index wait check is to low
+ print("widgets/Grid.py sinking errors on updateGrid method...")
+
+ def addToGrid(self, dataSet):
+ self.store.append([dataSet[0], dataSet[1]])
+
+ def replaceInGrid(self, dataSet):
+ # Iter, row column, new pixbuf...
+ self.store.set_value(dataSet[0], 0 , dataSet[1])
+
+
+ def iconDblLeftClick(self, widget, item):
+ try:
+ model = widget.get_model()
+ fileName = model[item][1]
+ dir = self.currentPath
+ file = dir + "/" + fileName
+
+ if fileName == ".":
+ self.setNewDirectory(dir)
+ elif fileName == "..":
+ parentDir = os.path.abspath(os.path.join(dir, os.pardir))
+ self.currentPath = parentDir
+ self.setNewDirectory(parentDir)
+ self.settings.saveSettings(parentDir)
+ elif isdir(file):
+ self.currentPath = file
+ self.setNewDirectory(self.currentPath)
+ self.settings.saveSettings(self.currentPath)
+ elif isfile(file):
+ self.fileHandler.openFile(file)
+ except Exception as e:
+ print(e)
+
+ def iconSingleClick(self, widget, eve, rclicked_icon):
+ try:
+ if eve.type == gdk.EventType.BUTTON_RELEASE and eve.button == 1:
+ self.selectedFiles.clear()
+ items = widget.get_selected_items()
+ model = widget.get_model()
+ dir = self.currentPath
+
+ for item in items:
+ fileName = model[item][1]
+
+ if fileName != "." and fileName != "..":
+ file = dir + "/" + fileName
+ self.selectedFiles.append(file) # Used for return to caller
+
+ elif eve.type == gdk.EventType.BUTTON_RELEASE and eve.button == 3:
+ input = self.builder.get_object("filenameInput")
+ controls = self.builder.get_object("iconControlsWindow")
+ iconsButtonBox = self.builder.get_object("iconsButtonBox")
+ menuButtonBox = self.builder.get_object("menuButtonBox")
+
+
+ if len(self.selectedFiles) == 1:
+ parts = self.selectedFiles[0].split("/")
+ input.set_text(parts[len(parts) - 1])
+ input.show()
+ iconsButtonBox.show()
+ menuButtonBox.hide()
+ controls.show()
+ elif len(self.selectedFiles) > 1:
+ input.set_text("")
+ input.hide()
+ menuButtonBox.hide()
+ iconsButtonBox.show()
+ controls.show()
+ else:
+ input.set_text("")
+ input.show()
+ menuButtonBox.show()
+ iconsButtonBox.hide()
+ controls.show()
+
+ except Exception as e:
+ print(e)
+
+ def returnSelectedFiles(self):
+ # NOTE: Just returning selectedFiles looks like it returns a "pointer"
+ # to the children. This means we lose the list if any left click occures
+ # in this class.
+ files = []
+ for file in self.selectedFiles:
+ files.append(file)
+ return files
+
+ def returnCurrentPath(self):
+ currentPath = self.currentPath
+ return currentPath
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/Icon.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/Icon.py
new file mode 100644
index 0000000..2562a9b
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/Icon.py
@@ -0,0 +1,228 @@
+# Python Imports
+import os, subprocess, hashlib, threading
+from os.path import isdir, isfile, join
+
+
+# Gtk imports
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
+
+from gi.repository import Gtk as gtk
+from gi.repository import Gio as gio
+from xdg.DesktopEntry import DesktopEntry
+
+
+# Application imports
+
+
+
+
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+ return wrapper
+
+class Icon:
+ def __init__(self, settings):
+ self.settings = settings
+ self.thubnailGen = settings.getThumbnailGenerator()
+ self.vidsList = settings.returnVidsFilter()
+ self.imagesList = settings.returnImagesFilter()
+ self.GTK_ORIENTATION = settings.returnIconImagePos()
+ self.usrHome = settings.returnUserHome()
+ self.iconContainerWH = settings.returnContainerWH()
+ self.systemIconImageWH = settings.returnSystemIconImageWH()
+ self.viIconWH = settings.returnVIIconWH()
+ self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
+ self.INTERNAL_ICON_PTH = self.SCRIPT_PTH + "../resources/icons/text.png"
+
+
+ def createIcon(self, dir, file):
+ fullPath = dir + "/" + file
+ return self.getIconImage(file, fullPath)
+
+ def createThumbnail(self, dir, file):
+ fullPath = dir + "/" + file
+ try:
+ # Video thumbnail
+ if file.lower().endswith(self.vidsList):
+ fileHash = hashlib.sha256(str.encode(fullPath)).hexdigest()
+ hashImgPth = self.usrHome + "/.thumbnails/normal/" + fileHash + ".png"
+
+ if isfile(hashImgPth) == False:
+ self.generateVideoThumbnail(fullPath, hashImgPth)
+
+ thumbnl = self.createScaledImage(hashImgPth, self.viIconWH)
+ if thumbnl == None: # If no icon whatsoever, return internal default
+ thumbnl = gtk.Image.new_from_file(self.SCRIPT_PTH + "../resources/icons/video.png")
+
+ return thumbnl
+ except Exception as e:
+ print("Thumbnail generation issue:")
+ print( repr(e) )
+ return gtk.Image.new_from_file(self.SCRIPT_PTH + "../resources/icons/video.png")
+
+
+ def getIconImage(self, file, fullPath):
+ try:
+ thumbnl = None
+
+ # Video icon
+ if file.lower().endswith(self.vidsList):
+ thumbnl = gtk.Image.new_from_file(self.SCRIPT_PTH + "../resources/icons/video.png")
+ # Image Icon
+ elif file.lower().endswith(self.imagesList):
+ thumbnl = self.createScaledImage(fullPath, self.viIconWH)
+ # .desktop file parsing
+ elif fullPath.lower().endswith( ('.desktop',) ):
+ thumbnl = self.parseDesktopFiles(fullPath)
+ # System icons
+ else:
+ thumbnl = self.getSystemThumbnail(fullPath, self.systemIconImageWH[0])
+
+ if thumbnl == None: # If no icon whatsoever, return internal default
+ thumbnl = gtk.Image.new_from_file(self.INTERNAL_ICON_PTH)
+
+ return thumbnl
+ except Exception as e:
+ print("Icon generation issue:")
+ print( repr(e) )
+ return gtk.Image.new_from_file(self.INTERNAL_ICON_PTH)
+
+ def parseDesktopFiles(self, fullPath):
+ try:
+ xdgObj = DesktopEntry(fullPath)
+ icon = xdgObj.getIcon()
+ altIconPath = ""
+
+ if "steam" in icon:
+ steamIconsDir = self.usrHome + "/.thumbnails/steam_icons/"
+ name = xdgObj.getName()
+ fileHash = hashlib.sha256(str.encode(name)).hexdigest()
+
+ if isdir(steamIconsDir) == False:
+ os.mkdir(steamIconsDir)
+
+ hashImgPth = steamIconsDir + fileHash + ".jpg"
+ if isfile(hashImgPth) == True:
+ # Use video sizes since headers are bigger
+ return self.createScaledImage(hashImgPth, self.viIconWH)
+
+ execStr = xdgObj.getExec()
+ parts = execStr.split("steam://rungameid/")
+ id = parts[len(parts) - 1]
+ imageLink = "https://steamcdn-a.akamaihd.net/steam/apps/" + id + "/header.jpg"
+ proc = subprocess.Popen(["wget", "-O", hashImgPth, imageLink])
+ proc.wait()
+
+ # Use video thumbnail sizes since headers are bigger
+ return self.createScaledImage(hashImgPth, self.viIconWH)
+ elif os.path.exists(icon):
+ return self.createScaledImage(icon, self.systemIconImageWH)
+ else:
+ iconsDirs = ["/usr/share/pixmaps", "/usr/share/icons", self.usrHome + "/.icons" ,]
+ altIconPath = ""
+
+ for iconsDir in iconsDirs:
+ altIconPath = self.traverseIconsFolder(iconsDir, icon)
+ if altIconPath is not "":
+ break
+
+ return self.createScaledImage(altIconPath, self.systemIconImageWH)
+ except Exception as e:
+ print(".desktop icon generation issue:")
+ print( repr(e) )
+ return None
+
+
+ def traverseIconsFolder(self, path, icon):
+ altIconPath = ""
+
+ for (dirpath, dirnames, filenames) in os.walk(path):
+ for file in filenames:
+ appNM = "application-x-" + icon
+ if icon in file or appNM in file:
+ altIconPath = dirpath + "/" + file
+ break
+
+ return altIconPath
+
+
+ def getSystemThumbnail(self, filename, size):
+ try:
+ if os.path.exists(filename):
+ gioFile = gio.File.new_for_path(filename)
+ info = gioFile.query_info('standard::icon' , 0, gio.Cancellable())
+ icon = info.get_icon().get_names()[0]
+ iconTheme = gtk.IconTheme.get_default()
+ iconData = iconTheme.lookup_icon(icon , size , 0)
+ if iconData:
+ iconPath = iconData.get_filename()
+ return gtk.Image.new_from_file(iconPath) # This seems to cause a lot of core dump issues...
+ else:
+ return None
+ else:
+ return None
+ except Exception as e:
+ print("system icon generation issue:")
+ print( repr(e) )
+ return None
+
+
+ def createScaledImage(self, path, wxh):
+ try:
+ pixbuf = gtk.Image.new_from_file(path).get_pixbuf()
+ scaledPixBuf = pixbuf.scale_simple(wxh[0], wxh[1], 2) # 2 = BILINEAR and is best by default
+ return gtk.Image.new_from_pixbuf(scaledPixBuf)
+ except Exception as e:
+ print("Image Scaling Issue:")
+ print( repr(e) )
+ return None
+
+ def createFromFile(self, path):
+ try:
+ return gtk.Image.new_from_file(path)
+ except Exception as e:
+ print("Image from file Issue:")
+ print( repr(e) )
+ return None
+
+ def returnGenericIcon(self):
+ return gtk.Image.new_from_file(self.INTERNAL_ICON_PTH)
+
+
+ def generateVideoThumbnail(self, fullPath, hashImgPth):
+ proc = None
+ try:
+ # Stream duration
+ command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath]
+ data = subprocess.run(command, stdout=subprocess.PIPE)
+ duration = data.stdout.decode('utf-8')
+
+ # Format (container) duration
+ if "N/A" in duration:
+ command = ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath]
+ data = subprocess.run(command , stdout=subprocess.PIPE)
+ duration = data.stdout.decode('utf-8')
+
+ # Stream duration type: image2
+ if "N/A" in duration:
+ command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-f", "image2", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath]
+ data = subprocess.run(command, stdout=subprocess.PIPE)
+ duration = data.stdout.decode('utf-8')
+
+ # Format (container) duration type: image2
+ if "N/A" in duration:
+ command = ["ffprobe", "-v", "error", "-f", "image2", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", fullPath]
+ data = subprocess.run(command , stdout=subprocess.PIPE)
+ duration = data.stdout.decode('utf-8')
+
+ # Get frame roughly 35% through video
+ grabTime = str( int( float( duration.split(".")[0] ) * 0.35) )
+ command = ["ffmpeg", "-ss", grabTime, "-an", "-i", fullPath, "-s", "320x180", "-vframes", "1", hashImgPth]
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE)
+ proc.wait()
+ except Exception as e:
+ print("Video thumbnail generation issue in thread:")
+ print( repr(e) )
diff --git a/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/__init__.py b/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/__init__.py
new file mode 100644
index 0000000..148c91c
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/opt/Pytop/widgets/__init__.py
@@ -0,0 +1,2 @@
+from widgets.Grid import Grid
+from widgets.Icon import Icon
diff --git a/src/debs/pytop-0-0-1-x64/usr/share/doc/pytop/copyright b/src/debs/pytop-0-0-1-x64/usr/share/doc/pytop/copyright
new file mode 100644
index 0000000..04b188e
--- /dev/null
+++ b/src/debs/pytop-0-0-1-x64/usr/share/doc/pytop/copyright
@@ -0,0 +1,22 @@
+Pytop is copyright 2019 Maxim Stewart.
+Pytop is currently developed by ITDominator <1itdominator@gmail.com>.
+
+License: GPLv2+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+See /usr/share/common-licenses/GPL-2, or
+ for the terms of the latest version
+of the GNU General Public License.