Added debs builder

This commit is contained in:
itdominator 2021-11-20 03:28:45 -06:00
parent ab9fca8c33
commit 4819e0de8c
50 changed files with 2754 additions and 0 deletions

12
src/clear_pycache_dirs.sh Executable file
View File

@ -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

34
src/debs/build.sh Normal file
View File

@ -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;

6
src/debs/chownAll.sh Normal file
View File

@ -0,0 +1,6 @@
#!/bin/bash
function main() {
sudo chown -R abaddon:abaddon .
}
main;

View File

@ -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.

View File

@ -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

Binary file not shown.

View File

@ -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) )

View File

@ -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) )

View File

@ -0,0 +1,849 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkFileFilter" id="Folders">
<mime-types>
<mime-type>inode/directory</mime-type>
</mime-types>
</object>
<object class="GtkAdjustment" id="brushSizeProp">
<property name="lower">1</property>
<property name="upper">100</property>
<property name="value">1</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<signal name="value-changed" handler="onBrushSizeChange" swapped="no"/>
</object>
<object class="GtkImage" id="createImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-new</property>
</object>
<object class="GtkImage" id="menuImage">
<property name="width_request">64</property>
<property name="height_request">64</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixbuf">start_menu_icons/start_menu_icon2_32x32.png</property>
</object>
<object class="GtkWindow" id="Window">
<property name="can_focus">False</property>
<property name="default_width">800</property>
<property name="default_height">600</property>
<property name="type_hint">desktop</property>
<property name="decorated">False</property>
<property name="gravity">center</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox" id="box1">
<property name="width_request">256</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="progMenu">
<property name="label" translatable="yes">Menu</property>
<property name="width_request">64</property>
<property name="height_request">64</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">menuImage</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="toggleProgramMenu" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child type="center">
<object class="GtkButtonBox" id="taskBarWorkspacesVer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">start</property>
<signal name="button-release-event" handler="showSystemStats" swapped="no"/>
<child>
<placeholder/>
</child>
<child type="center">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="selectDirDialog">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action">select-folder</property>
<property name="filter">Folders</property>
<property name="title" translatable="yes">Directory Chooser</property>
<signal name="file-set" handler="setNewDirectory" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkIconView" id="Desktop">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin">6</property>
<property name="selection_mode">multiple</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="width_request">180</property>
<property name="height_request">64</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<property name="max_content_width">225</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="taskBarButtonsVer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="height_request">64</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="vscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="taskBarButtonsHor">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="taskBarWorkspacesHor">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">start</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="timeLabelEveBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="above_child">True</property>
<signal name="button-release-event" handler="toggleCalPopover" swapped="no"/>
<child>
<object class="GtkLabel" id="timeLabel">
<property name="width_request">126</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="justify">center</property>
<property name="track_visited_links">False</property>
<attributes>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkPopover" id="calendarPopup">
<property name="width_request">420</property>
<property name="height_request">225</property>
<property name="can_focus">False</property>
<property name="relative_to">timeLabelEveBox</property>
<property name="modal">False</property>
<child>
<object class="GtkCalendar" id="calendarWid">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="year">2020</property>
<property name="month">3</property>
<property name="day">22</property>
</object>
</child>
</object>
<object class="GtkPopover" id="systemStats">
<property name="width_request">500</property>
<property name="height_request">0</property>
<property name="can_focus">False</property>
<property name="relative_to">taskBarWorkspacesVer</property>
<property name="position">bottom</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkColorButton" id="brushColorProp">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_alpha">True</property>
<property name="rgba">rgb(138,226,52)</property>
<signal name="color-set" handler="onColorSet" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkDrawingArea" id="drawArea">
<property name="height_request">60</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="configure-event" handler="onConfigure" swapped="no"/>
<signal name="draw" handler="onDraw" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">brushSizeProp</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkApplicationWindow" id="menuWindow">
<property name="width_request">600</property>
<property name="height_request">400</property>
<property name="can_focus">False</property>
<property name="border_width">4</property>
<property name="window_position">mouse</property>
<property name="destroy_with_parent">True</property>
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="decorated">False</property>
<property name="deletable">False</property>
<property name="gravity">center</property>
<property name="has_resize_grip">True</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSearchEntry" id="searchProgramsEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<signal name="search-changed" handler="searchForEntry" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="layout_style">start</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Accessories</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Multimedia</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Graphics</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Game</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Office</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Development</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Internet</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Settings</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">7</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">System</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">8</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Wine</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">9</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Other</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="setListGroup" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid" id="programListBttns">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="row_homogeneous">True</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
<style>
<class name="menu_scroller"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkImage" id="trashImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixbuf">icons/trash.png</property>
</object>
<object class="GtkWindow" id="iconControlsWindow">
<property name="can_focus">False</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="window_position">center</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">splashscreen</property>
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="decorated">False</property>
<property name="deletable">False</property>
<property name="gravity">center</property>
<signal name="focus-out-event" handler="closePopup" swapped="no"/>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="width_request">500</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkEntry" id="filenameInput">
<property name="width_request">500</property>
<property name="height_request">26</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_stock">gtk-edit</property>
<signal name="key-release-event" handler="rename" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="iconsButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="label">gtk-copy</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Copy...</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="copy" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-cut</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Cut...</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="cut" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child type="center">
<object class="GtkButton">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Delete...</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="delete" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Trash</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Move to Trash...</property>
<property name="image">trashImage</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="trash" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="menuButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">15</property>
<property name="label" translatable="yes">Folder</property>
<attributes>
<attribute name="size" value="12000"/>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">15</property>
<property name="label" translatable="yes">File</property>
<attributes>
<attribute name="size" value="12000"/>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="createSwitch">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">File/Folder</property>
<property name="active">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Create</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Create File/Folder...</property>
<property name="image">createImage</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="create" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-paste</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Paste...</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="paste" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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;
}

View File

@ -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()

View File

@ -0,0 +1,4 @@
from .mixins import CPUDrawMixin
from .mixins import TaskbarMixin
from .mixins import GridMixin
from signal_classes.Signals import Signals

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,4 @@
from .MainMenuMixin import MainMenuMixin
from .TaskbarMixin import TaskbarMixin
from .CPUDrawMixin import CPUDrawMixin
from .GridMixin import GridMixin

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
from utils.Dragging import Dragging
from .Logger import Logger
from utils.FileHandler import FileHandler
from utils.Settings import Settings

View File

@ -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

View File

@ -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) )

View File

@ -0,0 +1,2 @@
from widgets.Grid import Grid
from widgets.Icon import Icon

View File

@ -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
<http://www.gnu.org/copyleft/gpl.txt> for the terms of the latest version
of the GNU General Public License.