added content and structured others
This commit is contained in:
parent
06ca6a91ae
commit
8cfd143c93
|
@ -0,0 +1,15 @@
|
||||||
|
// Open the Orders page in Amazon.
|
||||||
|
// Run the script for each page you wish to calculate total of.
|
||||||
|
|
||||||
|
elms = document.querySelectorAll("#ordersContainer")[0].querySelectorAll(".a-row .a-size-base");
|
||||||
|
total = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < elms.length ; i++) {
|
||||||
|
value = elms[i].innerText;
|
||||||
|
if (value.includes("$")) {
|
||||||
|
total += parseInt(value.replace("$", ""), 10);
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(total);
|
|
@ -0,0 +1,39 @@
|
||||||
|
pos = 7 // Position to delete from. Note: 0 is the 1st video of the list; 1 is the 2nd, etc.
|
||||||
|
end = 5 // How many videos to delete?
|
||||||
|
|
||||||
|
// :: UI Timeouts :: (1000 equals 1 second.)
|
||||||
|
// Note: Your computer is a toaster or potato? Can't render UI changes quickly?
|
||||||
|
// Bump these up till the proper prompts show, then up the Interval Timer.
|
||||||
|
t1 = 800
|
||||||
|
t2 = 800
|
||||||
|
t3 = 800
|
||||||
|
|
||||||
|
// :: Interval Timer :: (1000 equals 1 second.)
|
||||||
|
// Note: Slow internet or bumped up the UI Timeouts?
|
||||||
|
// If slow internet, incrimint this by 1 second till things delete.
|
||||||
|
// If you bumped uo or down the UI Timeouts, add them up and add ~2000.
|
||||||
|
wait = 4000
|
||||||
|
|
||||||
|
|
||||||
|
i = 1 // Index to keep track of deletions. Don't change!
|
||||||
|
interval = setInterval(function () {
|
||||||
|
if (i == end)
|
||||||
|
clearInterval(interval)
|
||||||
|
|
||||||
|
elm = document.getElementsByClassName('style-scope ytcp-video-list-cell-video open-menu-button')[pos];
|
||||||
|
|
||||||
|
elm.click();
|
||||||
|
setTimeout(function () {
|
||||||
|
document.getElementById('text-item-4').click();
|
||||||
|
}, t1);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
document.getElementById('delete-confirm-checkbox').click();
|
||||||
|
}, t2);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
document.getElementById('delete-confirm-button').click();
|
||||||
|
}, t3);
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
}, wait);
|
|
@ -0,0 +1,39 @@
|
||||||
|
Reference Material:
|
||||||
|
https://developers.google.com/apps-script
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ---- Functions ---- #
|
||||||
|
|
||||||
|
function createSheetsFromRow() {
|
||||||
|
// getActiveSpreadsheet actually returns the whole SpreadSheet object whereas getActiveSheet returns a single "Sheet" or null object.
|
||||||
|
let spreadSheet = SpreadsheetApp.getActiveSpreadsheet(); // The "container" that holds all your Sheet objects
|
||||||
|
let activeSheet = SpreadsheetApp.getActiveSheet(); // The Sheet that is active in your SpreadSheet object
|
||||||
|
let sheetData = activeSheet.getDataRange().getValues(); // Get the active Sheet's data... aka Rows and Columns
|
||||||
|
|
||||||
|
for (var i in sheetData) {
|
||||||
|
if (i == 0) { continue; } // Keep from adding first row as sheet
|
||||||
|
let row = sheetData[i]; // Get row's data
|
||||||
|
let newSheetName = row[2]; // New sheet name is derived from 3rd column (2nd index) which is Agency column
|
||||||
|
let newSheet = spreadSheet.getSheetByName(newSheetName);
|
||||||
|
if (newSheet === null) { // If sheet doesn't exists, we create it
|
||||||
|
newSheet = spreadSheet.insertSheet(newSheetName);
|
||||||
|
}
|
||||||
|
newSheet.appendRow(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAllSheetsButForFirst() {
|
||||||
|
let spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
|
||||||
|
let sheets = spreadSheet.getSheets();
|
||||||
|
|
||||||
|
for (var i in sheets) {
|
||||||
|
if (i == 0) { continue; } // Keep from deleting first sheet
|
||||||
|
let sheet = sheets[i];
|
||||||
|
spreadSheet.deleteSheet(sheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Gtk imports
|
||||||
|
import gi, faulthandler, signal
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
gi.require_version('WebKit2', '4.0')
|
||||||
|
|
||||||
|
from gi.repository import Gtk as gtk
|
||||||
|
from gi.repository import Gdk as gdk
|
||||||
|
from gi.repository import WebKit2 as webkit
|
||||||
|
from gi.repository import GLib
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
import inspect, faulthandler
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from utils import Settings
|
||||||
|
from signal_classes import CrossClassSignals, WebviewSignals
|
||||||
|
|
||||||
|
|
||||||
|
class Main:
|
||||||
|
def __init__(self):
|
||||||
|
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit)
|
||||||
|
faulthandler.enable() # For better debug info
|
||||||
|
webkit.WebView() # Need one initialized for webview to work from glade file
|
||||||
|
|
||||||
|
builder = gtk.Builder()
|
||||||
|
settings = Settings()
|
||||||
|
settings.attachBuilder(builder)
|
||||||
|
|
||||||
|
# Gets the methods from the classes and sets to handler.
|
||||||
|
# Then, builder connects to any signals it needs.
|
||||||
|
classes = [CrossClassSignals(settings),
|
||||||
|
WebviewSignals(settings)]
|
||||||
|
|
||||||
|
handlers = {}
|
||||||
|
for c in classes:
|
||||||
|
methods = inspect.getmembers(c, predicate=inspect.ismethod)
|
||||||
|
handlers.update(methods)
|
||||||
|
|
||||||
|
builder.connect_signals(handlers)
|
||||||
|
window = settings.createWindow()
|
||||||
|
window.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main = Main()
|
||||||
|
gtk.main()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
|
@ -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() {
|
||||||
|
# GTK_DEBUG=interactive python3 ./ChronosBrowser.py
|
||||||
|
python3 ./ChronosBrowser.py
|
||||||
|
}
|
||||||
|
main $@;
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Gtk imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('WebKit2', '4.0')
|
||||||
|
|
||||||
|
from gi.repository import WebKit2 as webkit
|
||||||
|
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
class WebviewFactory:
|
||||||
|
def __init__(self):
|
||||||
|
self.index = -1
|
||||||
|
self.view_collection = []
|
||||||
|
|
||||||
|
def createWebview(self):
|
||||||
|
view = webkit.WebView()
|
||||||
|
self.view_collection.append(view)
|
||||||
|
self.index += 1
|
||||||
|
return view
|
||||||
|
|
||||||
|
def deleteWebview(self, index):
|
||||||
|
if (len(self.view_collection) - 1) > 0:
|
||||||
|
self.view_collection.pop(index)
|
||||||
|
self.index -= 1
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def get_index(self, index):
|
||||||
|
val = index
|
||||||
|
if val >= 0 and val < len(self.view_collection):
|
||||||
|
self.index = index
|
||||||
|
return self.view_collection[self.index]
|
||||||
|
|
||||||
|
def returnNotebookSize(self,):
|
||||||
|
return len(self.view_collection)
|
|
@ -0,0 +1 @@
|
||||||
|
from factory.WebviewFactory import WebviewFactory
|
|
@ -0,0 +1 @@
|
||||||
|
sudo apt-get -y install python3 pygobject3-devel webkitgtk3-devel ubuntu-restricted-extras gstreamer1.0-gtk3
|
|
@ -0,0 +1,164 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkImage" id="addImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-add</property>
|
||||||
|
<property name="icon_size">3</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="deleteImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-delete</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="homeImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Home</property>
|
||||||
|
<property name="stock">gtk-home</property>
|
||||||
|
<property name="icon_size">3</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="refreshImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-refresh</property>
|
||||||
|
<property name="icon_size">3</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkWindow" id="Window">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Chronos Browser</property>
|
||||||
|
<property name="window_position">center</property>
|
||||||
|
<property name="default_width">800</property>
|
||||||
|
<property name="default_height">600</property>
|
||||||
|
<property name="gravity">center</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="menuBar1">
|
||||||
|
<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">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="image">homeImage</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="loadHome" 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="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Refresh...</property>
|
||||||
|
<property name="image">refreshImage</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="refreshPage" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSearchEntry" id="webview_search">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Search...</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="key-release-event" handler="runSearchWebview" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</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">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkNotebook" id="notebook">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="show_border">False</property>
|
||||||
|
<property name="scrollable">True</property>
|
||||||
|
<property name="enable_popup">True</property>
|
||||||
|
<signal name="switch-page" handler="selecte_view" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child type="tab">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child type="tab">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child type="tab">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child type="action-start">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="image">deleteImage</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="delTab" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="tab_fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child type="action-end">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="image">addImage</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="addTab" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="tab_fill">False</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>
|
||||||
|
</interface>
|
|
@ -0,0 +1,3 @@
|
||||||
|
window {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Gtk imports
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
class CrossClassSignals:
|
||||||
|
def __init__(self, settings):
|
||||||
|
self.settings = settings
|
||||||
|
self.builder = self.settings.returnBuilder()
|
||||||
|
|
||||||
|
|
||||||
|
def closePopup(self, widget, data=None):
|
||||||
|
widget.hide()
|
|
@ -0,0 +1,98 @@
|
||||||
|
# Gtk imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
|
||||||
|
from gi.repository import Gtk as gtk
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from factory import WebviewFactory
|
||||||
|
|
||||||
|
|
||||||
|
class WebviewSignals:
|
||||||
|
def __init__(self, settings):
|
||||||
|
self.webviewFactory = WebviewFactory()
|
||||||
|
self.settings = settings
|
||||||
|
builder = settings.returnBuilder()
|
||||||
|
|
||||||
|
self.home_page = settings.returnWebHome()
|
||||||
|
self.webview_search = builder.get_object("webview_search")
|
||||||
|
self.notebook = builder.get_object("notebook")
|
||||||
|
self.page = None
|
||||||
|
self.index = 0
|
||||||
|
self.labelLen = settings.returnLabelLen()
|
||||||
|
|
||||||
|
self.addTab()
|
||||||
|
self.page = self.notebook.get_nth_page(0)
|
||||||
|
|
||||||
|
|
||||||
|
def addTab(self, widget=None, uri=None):
|
||||||
|
self.current_webview = self.createWebview()
|
||||||
|
self.addToNotebook(self.current_webview, uri)
|
||||||
|
|
||||||
|
def delTab(self, widget):
|
||||||
|
state = self.webviewFactory.deleteWebview(self.index)
|
||||||
|
if state == 0:
|
||||||
|
self.notebook.remove_page(self.index)
|
||||||
|
|
||||||
|
def selecte_view(self, widget, page, index):
|
||||||
|
self.current_webview = self.webviewFactory.get_index(index)
|
||||||
|
self.webview_search.set_text(self.current_webview.get_uri())
|
||||||
|
self.page = page
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
def createWebview(self):
|
||||||
|
webview = self.webviewFactory.createWebview()
|
||||||
|
webview.connect("create", self.webviewCreateSignal)
|
||||||
|
self.set_webview_settings(webview)
|
||||||
|
webview.load_uri(self.home_page)
|
||||||
|
webview.connect("load-changed", self.setUrlBar)
|
||||||
|
return webview
|
||||||
|
|
||||||
|
def runSearchWebview(self, widget, data=None):
|
||||||
|
query = widget.get_text().strip()
|
||||||
|
if data.keyval == 65293: # If enter key pressed
|
||||||
|
if "http://" in query or "https://" in query or "file://" in query:
|
||||||
|
self.current_webview.load_uri(query)
|
||||||
|
else:
|
||||||
|
query = '+'.join(query.split())
|
||||||
|
query = "http://www.google.com/search?q=" + query
|
||||||
|
self.current_webview.load_uri(query)
|
||||||
|
|
||||||
|
self.notebook.get_tab_label(self.page).set_text(query)
|
||||||
|
|
||||||
|
def refreshPage(self, widget, data=None):
|
||||||
|
self.current_webview.load_uri(self.current_webview.get_uri())
|
||||||
|
|
||||||
|
def loadHome(self, widget):
|
||||||
|
self.current_webview.load_uri(self.home_page)
|
||||||
|
|
||||||
|
def setUrlBar(self, widget, data=None):
|
||||||
|
uri = widget.get_uri()
|
||||||
|
self.webview_search.set_text(uri)
|
||||||
|
label = uri[0: self.labelLen] + "..."
|
||||||
|
self.notebook.get_tab_label(self.page).set_text(label)
|
||||||
|
|
||||||
|
def webviewCreateSignal(self, widget, eve):
|
||||||
|
uri = eve.get_request()
|
||||||
|
type = eve.get_navigation_type()
|
||||||
|
isRedirect = eve.is_redirect()
|
||||||
|
|
||||||
|
if isRedirect == False and type == 5: # Open in new tab
|
||||||
|
self.addTab(None, uri.get_uri())
|
||||||
|
|
||||||
|
|
||||||
|
def addToNotebook(self, view, uri=None):
|
||||||
|
webview_box = gtk.Box()
|
||||||
|
webview_box.pack_start(view, expand = True, fill = True, padding = 0)
|
||||||
|
webview_box.show_all()
|
||||||
|
|
||||||
|
if uri:
|
||||||
|
self.notebook.append_page(webview_box, gtk.Label(uri))
|
||||||
|
view.load_uri(uri)
|
||||||
|
else:
|
||||||
|
self.notebook.append_page(webview_box, gtk.Label(view.get_uri()))
|
||||||
|
|
||||||
|
def set_webview_settings(self, view):
|
||||||
|
self.settings.setDefaultWebviewSettings(view, view.get_settings())
|
|
@ -0,0 +1,2 @@
|
||||||
|
from signal_classes.CrossClassSignals import CrossClassSignals
|
||||||
|
from signal_classes.WebviewSignals import WebviewSignals
|
|
@ -0,0 +1,125 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self):
|
||||||
|
self.builder = None
|
||||||
|
|
||||||
|
self.THUMB_GENERATOR = "ffmpegthumbnailer"
|
||||||
|
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.webHome = 'http://webfm.com/'
|
||||||
|
self.labelLength = 25 - 3
|
||||||
|
self.vidsExtensionList = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv', '.mpeg', '.mp4', '.webm')
|
||||||
|
self.imagesExtensionList = ('.png', '.jpg', '.jpeg', '.gif', '.ico', '.tga')
|
||||||
|
|
||||||
|
|
||||||
|
def attachBuilder(self, builder):
|
||||||
|
self.builder = builder
|
||||||
|
self.builder.add_from_file("resources/ChronosBrowser.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('resources/stylesheet.css')
|
||||||
|
screen = gdk.Screen.get_default()
|
||||||
|
styleContext = gtk.StyleContext()
|
||||||
|
styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||||
|
|
||||||
|
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) + "x" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
|
||||||
|
|
||||||
|
return monitors
|
||||||
|
|
||||||
|
|
||||||
|
def returnBuilder(self): return self.builder
|
||||||
|
def returnWebHome(self): return self.webHome
|
||||||
|
def returnVidsExtensionList(self): return self.vidsExtensionList
|
||||||
|
def returnImagesExtensionList(self): return self.imagesExtensionList
|
||||||
|
def returnLabelLen(self): return self.labelLength
|
||||||
|
|
||||||
|
def setDefaultWebviewSettings(self, widget, settings=None):
|
||||||
|
# Usability
|
||||||
|
settings.set_property('enable-fullscreen', True)
|
||||||
|
settings.set_property('print-backgrounds', True)
|
||||||
|
settings.set_property('enable-frame-flattening', False)
|
||||||
|
settings.set_property('enable-plugins', True)
|
||||||
|
settings.set_property('enable-java', False)
|
||||||
|
settings.set_property('enable-resizable-text-areas', True)
|
||||||
|
settings.set_property('zoom-text-only', False)
|
||||||
|
settings.set_property('enable-smooth-scrolling', True)
|
||||||
|
settings.set_property('enable-back-forward-navigation-gestures', False)
|
||||||
|
settings.set_property('media-playback-requires-user-gesture', False)
|
||||||
|
settings.set_property('enable-tabs-to-links', True)
|
||||||
|
settings.set_property('enable-caret-browsing', False)
|
||||||
|
|
||||||
|
# Security
|
||||||
|
settings.set_property('user-agent','Mozilla/5.0 (X11; Generic; Linux x86-64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Safari/605.1.15')
|
||||||
|
settings.set_property('enable-private-browsing', False)
|
||||||
|
settings.set_property('enable-xss-auditor', True)
|
||||||
|
settings.set_property('enable-hyperlink-auditing', False)
|
||||||
|
settings.set_property('enable-site-specific-quirks', True)
|
||||||
|
settings.set_property('enable-offline-web-application-cache', True)
|
||||||
|
settings.set_property('enable-page-cache', True)
|
||||||
|
settings.set_property('allow-modal-dialogs', True)
|
||||||
|
settings.set_property('enable-html5-local-storage', True)
|
||||||
|
settings.set_property('enable-html5-database', True)
|
||||||
|
settings.set_property('allow-file-access-from-file-urls', True)
|
||||||
|
settings.set_property('allow-universal-access-from-file-urls', False)
|
||||||
|
settings.set_property('enable-dns-prefetching', False)
|
||||||
|
|
||||||
|
# Media stuff
|
||||||
|
settings.set_hardware_acceleration_policy(0)
|
||||||
|
# settings.set_property('hardware-acceleration-policy', 'on-demand')
|
||||||
|
settings.set_property('enable-webgl', True)
|
||||||
|
settings.set_property('enable-webaudio', True)
|
||||||
|
settings.set_property('enable-accelerated-2d-canvas', True)
|
||||||
|
settings.set_property('auto-load-images', True)
|
||||||
|
settings.set_property('enable-media-capabilities', True)
|
||||||
|
settings.set_property('enable-media-stream', True)
|
||||||
|
settings.set_property('enable-mediasource', True)
|
||||||
|
settings.set_property('enable-encrypted-media', True)
|
||||||
|
settings.set_property('media-playback-allows-inline', True)
|
||||||
|
|
||||||
|
# JS
|
||||||
|
settings.set_property('enable-javascript', True)
|
||||||
|
settings.set_property('enable-javascript-markup', True)
|
||||||
|
settings.set_property('javascript-can-access-clipboard', False)
|
||||||
|
settings.set_property('javascript-can-open-windows-automatically', False)
|
||||||
|
|
||||||
|
# Debugging
|
||||||
|
settings.set_property('enable-developer-extras', False)
|
||||||
|
settings.set_property('enable-write-console-messages-to-stdout', False)
|
||||||
|
settings.set_property('draw-compositing-indicators', False)
|
||||||
|
settings.set_property('enable-mock-capture-devices', False)
|
||||||
|
settings.set_property('enable-spatial-navigation', False)
|
|
@ -0,0 +1 @@
|
||||||
|
from utils.Settings import Settings
|
|
@ -0,0 +1,11 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=pyDarkness
|
||||||
|
GenericName=Darken a region or add highlighting over an area.
|
||||||
|
Comment=Darken a region or add highlighting over an area.
|
||||||
|
Exec=/home/abaddon/Portable_Apps/local_bin/DarkOverlay/src/darkness
|
||||||
|
Icon=/home/abaddon/Portable_Apps/local_bin/DarkOverlay/src/resources/darkness.png
|
||||||
|
Type=Application
|
||||||
|
StartupNotify=true
|
||||||
|
Categories=Utility;
|
||||||
|
MimeType=text/plain;
|
||||||
|
Terminal=false
|
|
@ -0,0 +1,6 @@
|
||||||
|
EasyProcess==0.2.7
|
||||||
|
Pillow==6.1.0
|
||||||
|
pycairo==1.18.1
|
||||||
|
PyGObject==3.34.0
|
||||||
|
pyscreenshot==0.5.1
|
||||||
|
setproctitle==1.1.10
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Python imports
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
|
# Gtk imports
|
||||||
|
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from utils import Settings
|
||||||
|
from signal_classes import CrossClassSignals
|
||||||
|
|
||||||
|
|
||||||
|
class Main:
|
||||||
|
def __init__(self, args):
|
||||||
|
settings = Settings()
|
||||||
|
builder = settings.returnBuilder()
|
||||||
|
|
||||||
|
# Gets the methods from the classes and sets to handler.
|
||||||
|
# Then, builder connects to any signals it needs.
|
||||||
|
classes = [CrossClassSignals(settings)]
|
||||||
|
|
||||||
|
handlers = {}
|
||||||
|
for c in classes:
|
||||||
|
methods = inspect.getmembers(c, predicate=inspect.ismethod)
|
||||||
|
handlers.update(methods)
|
||||||
|
|
||||||
|
builder.connect_signals(handlers)
|
||||||
|
window = settings.createWindow()
|
||||||
|
window.show()
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
import argparse
|
||||||
|
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:
|
||||||
|
setproctitle('DarkOverlay')
|
||||||
|
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="firefox", 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) )
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/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() {
|
||||||
|
SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||||
|
source "/home/abaddon/Portable_Apps/py-venvs/pycornia-venv/bin/activate"
|
||||||
|
python "${SCRIPTPATH}"
|
||||||
|
}
|
||||||
|
main $@;
|
|
@ -0,0 +1,172 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkApplicationWindow" id="Main_Window">
|
||||||
|
<property name="width_request">320</property>
|
||||||
|
<property name="height_request">220</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="window_position">center</property>
|
||||||
|
<property name="default_width">600</property>
|
||||||
|
<property name="default_height">480</property>
|
||||||
|
<property name="icon">darkness.png</property>
|
||||||
|
<property name="skip_pager_hint">True</property>
|
||||||
|
<property name="decorated">False</property>
|
||||||
|
<property name="gravity">center</property>
|
||||||
|
<property name="has_resize_grip">True</property>
|
||||||
|
<property name="show_menubar">False</property>
|
||||||
|
<child type="titlebar">
|
||||||
|
<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="GtkSeparator" id="separator1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEventBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
|
||||||
|
<property name="above_child">True</property>
|
||||||
|
<signal name="button-press-event" handler="getStartCoords" swapped="no"/>
|
||||||
|
<signal name="button-release-event" handler="popupMenu" swapped="no"/>
|
||||||
|
<signal name="key-press-event" handler="keyActionToggle" swapped="no"/>
|
||||||
|
<signal name="key-release-event" handler="endKeyActionToggle" swapped="no"/>
|
||||||
|
<signal name="motion-notify-event" handler="onMotion" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkDrawingArea" id="drawArea">
|
||||||
|
<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>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="regionWindow"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<object class="GtkListStore" id="monitorStore">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name Monitors -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
</columns>
|
||||||
|
</object>
|
||||||
|
<object class="GtkAdjustment" id="opacityAdjustment">
|
||||||
|
<property name="lower">0.01</property>
|
||||||
|
<property name="upper">1</property>
|
||||||
|
<property name="value">0.75</property>
|
||||||
|
<property name="step_increment">0.01</property>
|
||||||
|
<property name="page_increment">10</property>
|
||||||
|
<signal name="value-changed" handler="onOpacityChange" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<object class="GtkPopover" id="rClickMenu">
|
||||||
|
<property name="width_request">320</property>
|
||||||
|
<property name="height_request">220</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="relative_to">separator1</property>
|
||||||
|
<property name="constrain_to">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="homogeneous">True</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(0,0,0)</property>
|
||||||
|
<signal name="color-set" handler="onColorSet" 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>
|
||||||
|
<property name="margin_top">10</property>
|
||||||
|
<property name="margin_bottom">10</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<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="label" translatable="yes">Opacity</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinButton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="adjustment">opacityAdjustment</property>
|
||||||
|
<property name="climb_rate">0.01</property>
|
||||||
|
<property name="digits">2</property>
|
||||||
|
<property name="value">0.75</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</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">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label">gtk-quit</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="close_app" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
.regionWindow {
|
||||||
|
background-color: rgba(0, 0, 0, 0.0);
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
# Python imports
|
||||||
|
import threading, subprocess, os, cairo
|
||||||
|
|
||||||
|
|
||||||
|
# Gtk imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk as gtk
|
||||||
|
from gi.repository import Gdk as gdk
|
||||||
|
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
def threaded(fn):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class MouseButton:
|
||||||
|
LEFT_BUTTON = 1
|
||||||
|
MIDDLE_BUTTON = 2
|
||||||
|
RIGHT_BUTTON = 3
|
||||||
|
|
||||||
|
class StateController:
|
||||||
|
isDragging = False
|
||||||
|
isCtrlDown = False
|
||||||
|
|
||||||
|
|
||||||
|
class CrossClassSignals:
|
||||||
|
def __init__(self, settings):
|
||||||
|
self.settings = settings
|
||||||
|
self.builder = self.settings.returnBuilder()
|
||||||
|
|
||||||
|
self.window = self.builder.get_object("Main_Window")
|
||||||
|
self.drawArea = self.builder.get_object("drawArea")
|
||||||
|
self.brushColorProp = self.builder.get_object("brushColorProp")
|
||||||
|
self.wh = self.builder.get_object("wh")
|
||||||
|
self.xy = self.builder.get_object("xy")
|
||||||
|
|
||||||
|
self.opacityVal = 0.75
|
||||||
|
self.states = StateController()
|
||||||
|
self.area = None
|
||||||
|
self.states.isCtrlDown = False
|
||||||
|
self.states.isMouseHeld = False
|
||||||
|
self.doDrawBackground = True
|
||||||
|
self.surface = None
|
||||||
|
self.brush = None
|
||||||
|
self.aw = None # Draw area width
|
||||||
|
self.ah = None # Draw area height
|
||||||
|
rgba = self.brushColorProp.get_rgba()
|
||||||
|
self.brushColorVal = [rgba.red, rgba.green, rgba.blue, self.opacityVal]
|
||||||
|
self.startCoords = [0.0, 0.0]
|
||||||
|
self.w1 = 0.0
|
||||||
|
self.h1 = 0.0
|
||||||
|
|
||||||
|
self.window.set_keep_above(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def onConfigure(self, area, eve, data = None):
|
||||||
|
self.area = area
|
||||||
|
aw = area.get_allocated_width()
|
||||||
|
ah = area.get_allocated_height()
|
||||||
|
self.aw = aw
|
||||||
|
self.ah = ah
|
||||||
|
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, aw, ah)
|
||||||
|
self.brush = cairo.Context(self.surface)
|
||||||
|
|
||||||
|
self.drawBackground(self.brush, aw, ah)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
# Draw background white
|
||||||
|
def drawBackground(self, brush, aw, ah):
|
||||||
|
rgba = self.brushColorVal
|
||||||
|
brush.rectangle(0, 0, aw, ah) # x, y, width, height
|
||||||
|
brush.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3]) # 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...
|
||||||
|
|
||||||
|
|
||||||
|
def onColorSet(self, widget):
|
||||||
|
rgba = widget.get_rgba()
|
||||||
|
self.brushColorVal = [rgba.red, rgba.green, rgba.blue, self.opacityVal]
|
||||||
|
self.drawArea.queue_draw()
|
||||||
|
self.drawArea.emit("configure_event", gdk.Event())
|
||||||
|
|
||||||
|
|
||||||
|
def onOpacityChange(self, widget):
|
||||||
|
self.opacityVal = widget.get_value()
|
||||||
|
self.brushColorVal[3] = self.opacityVal
|
||||||
|
self.drawArea.queue_draw()
|
||||||
|
self.drawArea.emit("configure_event", gdk.Event())
|
||||||
|
|
||||||
|
|
||||||
|
def popupMenu(self, widget, eve):
|
||||||
|
self.states.isMouseHeld = False
|
||||||
|
if eve.type == gdk.EventType.BUTTON_RELEASE and eve.button == MouseButton.RIGHT_BUTTON:
|
||||||
|
self.builder.get_object("rClickMenu").popup()
|
||||||
|
|
||||||
|
|
||||||
|
def getStartCoords(self, widget, eve):
|
||||||
|
if eve.type == gdk.EventType.BUTTON_PRESS and eve.button == MouseButton.LEFT_BUTTON:
|
||||||
|
self.startCoords = [eve.x, eve.y] # Used for delta calculations
|
||||||
|
self.w1 = self.window.get_size()[0] # Ref window width
|
||||||
|
self.h1 = self.window.get_size()[1] # Ref window height
|
||||||
|
self.states.isMouseHeld = True # State check for when updating width 'n height
|
||||||
|
|
||||||
|
|
||||||
|
def keyActionToggle(self, widget, eve):
|
||||||
|
key_id = gdk.keyval_name(eve.keyval).upper()
|
||||||
|
if key_id in ["CONTROL_R", "CONTROL_L"]:
|
||||||
|
self.states.isCtrlDown = True
|
||||||
|
|
||||||
|
def endKeyActionToggle(self, widget, eve):
|
||||||
|
key_id = gdk.keyval_name(eve.keyval).upper()
|
||||||
|
if key_id in ["CONTROL_R", "CONTROL_L"]:
|
||||||
|
self.states.isCtrlDown = False
|
||||||
|
|
||||||
|
|
||||||
|
def onMotion(self, widget, eve):
|
||||||
|
if self.states.isMouseHeld:
|
||||||
|
if self.states.isCtrlDown is False:
|
||||||
|
px1 = self.window.get_position()[0] # Ref window x
|
||||||
|
py1 = self.window.get_position()[1] # Ref window y
|
||||||
|
|
||||||
|
# Getting deltas of movement inner to draw event box
|
||||||
|
x1 = self.startCoords[0]
|
||||||
|
y1 = self.startCoords[1]
|
||||||
|
x2 = eve.x
|
||||||
|
y2 = eve.y
|
||||||
|
px = 0
|
||||||
|
py = 0
|
||||||
|
|
||||||
|
# Calculate that to actual posion change
|
||||||
|
if x2 > x1: # Is growing
|
||||||
|
px = px1 + (x2 - x1)
|
||||||
|
else: # Is shrinking
|
||||||
|
px = px1 - (x1 - x2)
|
||||||
|
|
||||||
|
if y2 > y1: # Is growing
|
||||||
|
py = py1 + (y2 - y1)
|
||||||
|
else: # Is shrinking
|
||||||
|
py = py1 - (y1 - y2)
|
||||||
|
|
||||||
|
self.window.move(px, py)
|
||||||
|
|
||||||
|
if self.states.isCtrlDown:
|
||||||
|
x1 = self.startCoords[0]
|
||||||
|
y1 = self.startCoords[1]
|
||||||
|
x2 = eve.x
|
||||||
|
y2 = eve.y
|
||||||
|
w = 0
|
||||||
|
h = 0
|
||||||
|
|
||||||
|
if x2 > x1: # Is growing
|
||||||
|
w = self.w1 + (x2 - x1)
|
||||||
|
else: # Is shrinking
|
||||||
|
w = self.w1 - (x1 - x2)
|
||||||
|
|
||||||
|
if y2 > y1: # Is growing
|
||||||
|
h = self.h1 + (y2 - y1)
|
||||||
|
else: # Is shrinking
|
||||||
|
h = self.h1 - (y1 - y2)
|
||||||
|
|
||||||
|
self.window.resize(w, h)
|
||||||
|
|
||||||
|
|
||||||
|
def close_app(self, widget):
|
||||||
|
gtk.main_quit()
|
|
@ -0,0 +1 @@
|
||||||
|
from .CrossClassSignals import CrossClassSignals
|
|
@ -0,0 +1,71 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self):
|
||||||
|
self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
|
||||||
|
self.builder = gtk.Builder()
|
||||||
|
self.builder.add_from_file(self.SCRIPT_PTH + "../resources/Main_Window.glade")
|
||||||
|
|
||||||
|
# 'Filters'
|
||||||
|
self.images = ('.png', '.jpg', '.jpeg', '.gif')
|
||||||
|
HOME_PATH = os.path.expanduser('~')
|
||||||
|
self.SCREENSHOTS_DIR = HOME_PATH + "/" + ".screenshots"
|
||||||
|
|
||||||
|
if not os.path.isdir(self.SCREENSHOTS_DIR):
|
||||||
|
os.mkdir(self.SCREENSHOTS_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
def createWindow(self):
|
||||||
|
# Get window and connect signals
|
||||||
|
window = self.builder.get_object("Main_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)
|
||||||
|
|
||||||
|
def getMonitorData(self):
|
||||||
|
screen = self.builder.get_object("Main_Window").get_screen()
|
||||||
|
wdth = screen.get_width()
|
||||||
|
hght = screen.get_height()
|
||||||
|
mon0 = gdk.Rectangle()
|
||||||
|
mon0.width = wdth
|
||||||
|
mon0.height = hght
|
||||||
|
monitors = []
|
||||||
|
|
||||||
|
monitors.append(mon0)
|
||||||
|
for m in range(screen.get_n_monitors()):
|
||||||
|
monitors.append(screen.get_monitor_geometry(m))
|
||||||
|
|
||||||
|
return monitors
|
||||||
|
|
||||||
|
|
||||||
|
def returnBuilder(self): return self.builder
|
||||||
|
def returnScreenshotsDir(self): return self.SCREENSHOTS_DIR
|
||||||
|
|
||||||
|
# Filter returns
|
||||||
|
def returnImagesFilter(self): return self.images
|
|
@ -0,0 +1 @@
|
||||||
|
from .Settings import Settings
|
|
@ -0,0 +1,151 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# GTK Imports
|
||||||
|
import gi, faulthandler, signal
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
|
||||||
|
from gi.repository import Gtk as gtk
|
||||||
|
from gi.repository import GLib
|
||||||
|
|
||||||
|
|
||||||
|
# Python Imports
|
||||||
|
import os, threading, time
|
||||||
|
|
||||||
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
|
|
||||||
|
def threaded(fn):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Main:
|
||||||
|
def __init__(self):
|
||||||
|
setproctitle('PATH Edit Tool')
|
||||||
|
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, gtk.main_quit)
|
||||||
|
faulthandler.enable() # For better debug info
|
||||||
|
|
||||||
|
self.HOME = os.path.expanduser('~')
|
||||||
|
PREFERED_BASH_PATH = self.HOME + "/" + ".bash_paths"
|
||||||
|
SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) + "/"
|
||||||
|
GLADE_FILE = "main.glade"
|
||||||
|
|
||||||
|
self.builder = gtk.Builder()
|
||||||
|
self.builder.add_from_file(SCRIPT_PTH + GLADE_FILE)
|
||||||
|
self.builder.connect_signals(self)
|
||||||
|
|
||||||
|
self.pathTreeView = self.builder.get_object("pathTreeView")
|
||||||
|
self.messageWidget = self.builder.get_object("messageWidget")
|
||||||
|
self.messageLabel = self.builder.get_object("messageLabel")
|
||||||
|
|
||||||
|
self.pathListStore = gtk.ListStore(str)
|
||||||
|
self.success = "#88cc27"
|
||||||
|
self.warning = "#ffa800"
|
||||||
|
self.error = "#ff0000"
|
||||||
|
self.selected = None
|
||||||
|
self.bashrcPath = ""
|
||||||
|
|
||||||
|
if os.path.isfile(PREFERED_BASH_PATH):
|
||||||
|
self.bashrcPath = PREFERED_BASH_PATH
|
||||||
|
else:
|
||||||
|
self.bashrcPath = self.HOME + "/" + ".bashrc"
|
||||||
|
|
||||||
|
self.setupTreeview()
|
||||||
|
self.loadPaths()
|
||||||
|
|
||||||
|
window = self.builder.get_object("Main_Window")
|
||||||
|
window.connect("delete-event", gtk.main_quit)
|
||||||
|
window.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def addEntry(self, widget):
|
||||||
|
toAddPathTxt = self.builder.get_object("toAddPathEntry").get_text().strip()
|
||||||
|
|
||||||
|
if os.path.isdir(toAddPathTxt):
|
||||||
|
self.pathListStore.append([toAddPathTxt])
|
||||||
|
else:
|
||||||
|
self.displayMessage(self.warning, "Not a directory!")
|
||||||
|
|
||||||
|
def deleteEntry(self, widget):
|
||||||
|
self.pathListStore.remove(self.selected)
|
||||||
|
|
||||||
|
def saveToBashrc(self, widget):
|
||||||
|
try:
|
||||||
|
paths = list()
|
||||||
|
iter = self.pathListStore.get_iter_first()
|
||||||
|
while iter != None:
|
||||||
|
pth = self.pathListStore.get_value(iter, 0)
|
||||||
|
pth = pth.replace(self.HOME, "$HOME")
|
||||||
|
paths.append(pth)
|
||||||
|
# paths.append(self.pathListStore.get_value(iter, 0))
|
||||||
|
iter = self.pathListStore.iter_next(iter)
|
||||||
|
|
||||||
|
|
||||||
|
toExport = "export PATH=\"" + ':'.join(paths) + "\"\n\n"
|
||||||
|
file = open(self.bashrcPath, mode='r')
|
||||||
|
for line in file:
|
||||||
|
if "export PATH=" in line:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
toExport += line
|
||||||
|
|
||||||
|
file.close()
|
||||||
|
file = open(self.bashrcPath, mode='w')
|
||||||
|
file.write(toExport)
|
||||||
|
file.close()
|
||||||
|
self.displayMessage(self.success, "Successfully saved file!")
|
||||||
|
except Exception as e:
|
||||||
|
self.displayMessage(self.error, "Opening/Writing to file failed!")
|
||||||
|
print("Opening/Writing to file failed with the following:\n\n")
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
def setSelected(self, user_data):
|
||||||
|
selected = user_data.get_selected()[1]
|
||||||
|
if selected:
|
||||||
|
self.selected = selected
|
||||||
|
|
||||||
|
def loadPaths(self):
|
||||||
|
pathsStr = os.getenv("PATH")
|
||||||
|
|
||||||
|
# If path exists in bashrc replace default selection...
|
||||||
|
file = open(self.bashrcPath, mode='r')
|
||||||
|
for line in file:
|
||||||
|
if "export PATH=" in line:
|
||||||
|
part = line.replace("export PATH=", "")
|
||||||
|
cleaned = part.replace("\"", "")
|
||||||
|
pathsStr = cleaned.strip()
|
||||||
|
|
||||||
|
# Split string into list/tuple and add parts to store
|
||||||
|
paths = pathsStr.split(":")
|
||||||
|
for path in paths:
|
||||||
|
self.pathListStore.append([path])
|
||||||
|
|
||||||
|
|
||||||
|
def setupTreeview(self):
|
||||||
|
renderer = gtk.CellRendererText()
|
||||||
|
pathColumn = gtk.TreeViewColumn(title="Paths", cell_renderer=renderer, text=0)
|
||||||
|
self.pathTreeView.append_column(pathColumn)
|
||||||
|
self.pathTreeView.set_model(self.pathListStore)
|
||||||
|
|
||||||
|
|
||||||
|
def displayMessage(self, type, text):
|
||||||
|
markup = "<span foreground='" + type + "'>" + text + "</span>"
|
||||||
|
self.messageLabel.set_markup(markup)
|
||||||
|
self.messageWidget.popup()
|
||||||
|
self.hideMessageTimed()
|
||||||
|
|
||||||
|
@threaded
|
||||||
|
def hideMessageTimed(self):
|
||||||
|
time.sleep(3)
|
||||||
|
GLib.idle_add(self.messageWidget.popdown)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main = Main()
|
||||||
|
gtk.main()
|
|
@ -0,0 +1,162 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkImage" id="delImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-delete</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="plusImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-add</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkWindow" id="Main_Window">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Edit PATH App</property>
|
||||||
|
<property name="window_position">center</property>
|
||||||
|
<property name="default_width">480</property>
|
||||||
|
<property name="default_height">560</property>
|
||||||
|
<property name="icon_name">applications-accessories</property>
|
||||||
|
<property name="gravity">center</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="GtkSeparator" id="separator1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</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="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="pathTreeView">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<child internal-child="selection">
|
||||||
|
<object class="GtkTreeSelection">
|
||||||
|
<signal name="changed" handler="setSelected" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</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="GtkEntry" id="toAddPathEntry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="placeholder_text" translatable="yes">Path...</property>
|
||||||
|
</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="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Add path...</property>
|
||||||
|
<property name="image">plusImage</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="addEntry" swapped="no"/>
|
||||||
|
</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="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="image">delImage</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="deleteEntry" swapped="no"/>
|
||||||
|
</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">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label">gtk-save</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Save...</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="saveToBashrc" swapped="no"/>
|
||||||
|
</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="messageWidget">
|
||||||
|
<property name="width_request">320</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="relative_to">separator1</property>
|
||||||
|
<property name="position">bottom</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="messageLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="justify">center</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="scale" value="2"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Python Imports
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Lib Imports
|
||||||
|
import typer
|
||||||
|
|
||||||
|
|
||||||
|
# Application Imports
|
||||||
|
|
||||||
|
|
||||||
|
app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def makeTitleCase(file: str):
|
||||||
|
if os.path.isdir(file) :
|
||||||
|
for f in os.listdir(file):
|
||||||
|
os.rename(f, f.title())
|
||||||
|
else:
|
||||||
|
os.rename(file, file.title())
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def removeFromName(fsub: str, file: str):
|
||||||
|
if os.path.isdir(file) :
|
||||||
|
for f in os.listdir(file):
|
||||||
|
os.rename(f, f.replace(fsub, ''))
|
||||||
|
else:
|
||||||
|
os.rename(file, file.replace(fsub, ''))
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def removeFromToName(fsub: str, tsub: str, file: str):
|
||||||
|
if os.path.isdir(file) :
|
||||||
|
for f in os.listdir(file):
|
||||||
|
startIndex = f.index(fsub) + 1
|
||||||
|
endIndex = f.index(tsub)
|
||||||
|
toRemove = f[startIndex:endIndex]
|
||||||
|
os.rename(f, f.replace(toRemove, ''))
|
||||||
|
else:
|
||||||
|
startIndex = file.index(fsub) + 1
|
||||||
|
endIndex = file.index(tsub)
|
||||||
|
toRemove = file[startIndex:endIndex]
|
||||||
|
os.rename(file, file.replace(toRemove, ''))
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def replaceInName(fsub: str, tsub: str, file: str):
|
||||||
|
if os.path.isdir(file) :
|
||||||
|
for f in os.listdir(file):
|
||||||
|
os.rename(f, f.replace(fsub, tsub))
|
||||||
|
else:
|
||||||
|
os.rename(file, file.replace(fsub, tsub))
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/sbin/python
|
||||||
|
|
||||||
|
from __init__ import app
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Python imports
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
from kasa import Discover
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
class SmartDeviceManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.BASE_IP = "192.168.0."
|
||||||
|
self.IPs = ["12", "13", "14", "15", "16", "17"]
|
||||||
|
self.devices = self.retrieve_devices()
|
||||||
|
|
||||||
|
|
||||||
|
def retrieve_devices(self):
|
||||||
|
devices = []
|
||||||
|
for ip in self.IPs:
|
||||||
|
device = self.get_device(self.BASE_IP + ip)
|
||||||
|
if device:
|
||||||
|
print("Device: {} is available...".format(device.alias))
|
||||||
|
print("{}".format(device.hw_info))
|
||||||
|
devices.append(device)
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
|
def set_device_on(self, device):
|
||||||
|
asyncio.run(device.turn_on())
|
||||||
|
|
||||||
|
def set_device_off(self, device):
|
||||||
|
asyncio.run(device.turn_off())
|
||||||
|
|
||||||
|
def set_led_on(self, device):
|
||||||
|
asyncio.run(device.set_led(True))
|
||||||
|
|
||||||
|
def set_led_off(self, device):
|
||||||
|
asyncio.run(device.set_led(False))
|
||||||
|
|
||||||
|
def set_device_alias(self, device, alias):
|
||||||
|
asyncio.run(device.set_alias(alias))
|
||||||
|
|
||||||
|
def update_info(self, device):
|
||||||
|
try:
|
||||||
|
asyncio.run(device.update())
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_device(self, addr):
|
||||||
|
try:
|
||||||
|
device = asyncio.run( Discover.discover_single(addr) )
|
||||||
|
self.update_info(device)
|
||||||
|
return device
|
||||||
|
except Exception as e:
|
||||||
|
print(repr(e))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def pulsate(self, device, rate = 1):
|
||||||
|
state = "on"
|
||||||
|
while True:
|
||||||
|
if state == "on":
|
||||||
|
state = "off"
|
||||||
|
self.set_device_on(device)
|
||||||
|
else:
|
||||||
|
state = "on"
|
||||||
|
self.set_device_off(device)
|
||||||
|
|
||||||
|
time.sleep(rate)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
SmartDeviceManager()
|
|
@ -0,0 +1,288 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Python Imports
|
||||||
|
import sys
|
||||||
|
import enum
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Lib Imports
|
||||||
|
from Xlib import display
|
||||||
|
from Xlib.ext import randr, xinerama
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from . import __version__
|
||||||
|
except ImportError:
|
||||||
|
__version__ = "N/A"
|
||||||
|
|
||||||
|
|
||||||
|
class Position(enum.Enum):
|
||||||
|
TOP = enum.auto()
|
||||||
|
BOTTOM = enum.auto()
|
||||||
|
LEFT = enum.auto()
|
||||||
|
RIGHT = enum.auto()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name.lower()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_str(pos):
|
||||||
|
try:
|
||||||
|
return Position[pos.upper()]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError(f"value '{pos}' not part of the Pos enum")
|
||||||
|
|
||||||
|
|
||||||
|
class Shape:
|
||||||
|
def __init__(self, major, minor):
|
||||||
|
if max(major, minor) > 1.0 or min(major, minor) < 0.0:
|
||||||
|
raise ValueError(f"Shape out of range [0,1]: major={major}, minor={minor}")
|
||||||
|
self.major = major
|
||||||
|
self.minor = minor
|
||||||
|
|
||||||
|
|
||||||
|
class Monitor:
|
||||||
|
def __init__(self, name, x, y, width, height):
|
||||||
|
self.name = name
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULTS = {
|
||||||
|
"name": "Smart Position",
|
||||||
|
"shape": (1.0, 0.4),
|
||||||
|
"position": str(Position.LEFT),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SmartPositioner:
|
||||||
|
def __init__(self, name: str, shape: Shape, pos: Position, positioner_argv: list = None):
|
||||||
|
self.positioner_argv = positioner_argv
|
||||||
|
self.display = display.Display()
|
||||||
|
self.screen = self.display.screen()
|
||||||
|
self.root = self.screen.root
|
||||||
|
|
||||||
|
# self.fakeWindow = self.screen.root.create_window(0, 0, 1, 1, 1, self.screen.root_depth)
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.shape = shape
|
||||||
|
self.pos = pos
|
||||||
|
self.id = None
|
||||||
|
self.monitors = []
|
||||||
|
|
||||||
|
self.printXineramaVersion()
|
||||||
|
self.generateMonitorList()
|
||||||
|
self.getActiveMonitor()
|
||||||
|
|
||||||
|
|
||||||
|
def on_keybind(self, _, be):
|
||||||
|
pass
|
||||||
|
# if be.binding.command == f"nop {self.name}":
|
||||||
|
# self.toggle()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_instance(self):
|
||||||
|
pass
|
||||||
|
# tree = self.i3.get_tree()
|
||||||
|
# if self.id is None:
|
||||||
|
# matches = tree.find_instanced(self.name)
|
||||||
|
# instance = {m.window_instance: m for m in matches}.get(self.name, None)
|
||||||
|
# if instance is not None:
|
||||||
|
# self.id = instance.id
|
||||||
|
# else:
|
||||||
|
# instance = tree.find_by_id(self.id)
|
||||||
|
# return instance
|
||||||
|
|
||||||
|
def toggle(self):
|
||||||
|
scrSize = self.getScreenSize()
|
||||||
|
mPos = self.getMousePosition()
|
||||||
|
self.display.flush() # Needed in some instances to prompt XWindows
|
||||||
|
|
||||||
|
# kitty = self._get_instance()
|
||||||
|
# if kitty is None:
|
||||||
|
# self.spawn()
|
||||||
|
# else:
|
||||||
|
# focused_ws = self._get_focused_workspace()
|
||||||
|
# if focused_ws is None:
|
||||||
|
# print("no focused workspaces; ignoring toggle request")
|
||||||
|
# return
|
||||||
|
# if kitty.workspace().name == focused_ws.name: # kitty present on current WS; hide
|
||||||
|
# self.i3.command(f"[con_id={self.id}] floating enable, move scratchpad")
|
||||||
|
# else:
|
||||||
|
# self.fetch(focused_ws)
|
||||||
|
|
||||||
|
|
||||||
|
def getMousePosition(self):
|
||||||
|
data = self.screen.root.query_pointer()._data
|
||||||
|
return data["root_x"], data["root_y"]
|
||||||
|
|
||||||
|
def getActiveMonitor(self):
|
||||||
|
pos = self.getMousePosition()
|
||||||
|
for mon in self.monitors:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getScreenSize(self):
|
||||||
|
w = self.display.screen().width_in_pixels
|
||||||
|
h = self.display.screen().height_in_pixels
|
||||||
|
return w, h
|
||||||
|
|
||||||
|
|
||||||
|
def printXineramaVersion(self):
|
||||||
|
xinerama_version = self.display.xinerama_query_version()
|
||||||
|
print('Found XINERAMA version %s.%s' % (
|
||||||
|
xinerama_version.major_version,
|
||||||
|
xinerama_version.minor_version,
|
||||||
|
), file=sys.stderr)
|
||||||
|
|
||||||
|
def generateMonitorList(self):
|
||||||
|
print('Available screens:')
|
||||||
|
screens = xinerama.query_screens(self.root).screens
|
||||||
|
i = 1
|
||||||
|
for (idx, screen) in enumerate(screens):
|
||||||
|
x, y, w, h = screen.x, screen.y, screen.width, screen.height
|
||||||
|
print('screen %d: %s' % (idx, screen))
|
||||||
|
self.monitors.append( Monitor( "Monitor " + str(i), x, y, w, x ) )
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# resources = randr.get_screen_resources(self.root)
|
||||||
|
# for output in resources.outputs:
|
||||||
|
# params = self.display.xrandr_get_output_info(output, resources.config_timestamp)
|
||||||
|
# if not params.crtc:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# crtc = self.display.xrandr_get_crtc_info(params.crtc, resources.config_timestamp)
|
||||||
|
# self.monitors.append( Monitor(params.name, crtc.width, crtc.height) )
|
||||||
|
|
||||||
|
|
||||||
|
def spawn(self):
|
||||||
|
pass
|
||||||
|
# cmd_base = f"exec --no-startup-id kitty --name {self.name}"
|
||||||
|
# if self.positioner_argv is None:
|
||||||
|
# cmd = cmd_base
|
||||||
|
# else:
|
||||||
|
# argv = " ".join(self.positioner_argv)
|
||||||
|
# cmd = f"{cmd_base} {argv}"
|
||||||
|
# self.i3.command(cmd)
|
||||||
|
|
||||||
|
def on_spawned(self, _, we):
|
||||||
|
pass
|
||||||
|
# if we.container.window_instance == self.name:
|
||||||
|
# self.id = we.container.id
|
||||||
|
# self.i3.command(f"[con_id={we.container.id}] "
|
||||||
|
# "floating enable, "
|
||||||
|
# "border none, "
|
||||||
|
# "move scratchpad")
|
||||||
|
# self.fetch(self._get_focused_workspace())
|
||||||
|
|
||||||
|
def on_moved(self, _, we):
|
||||||
|
pass
|
||||||
|
# # Con is floating wrapper; the Kitty window/container is a child
|
||||||
|
# is_kitty = we.container.find_by_id(self.id)
|
||||||
|
# if not is_kitty:
|
||||||
|
# return
|
||||||
|
# focused_ws = self._get_focused_workspace()
|
||||||
|
# if focused_ws is None:
|
||||||
|
# return
|
||||||
|
# kitty = self._get_instance() # need "fresh" instance to capture destination WS
|
||||||
|
# kitty_ws = kitty.workspace()
|
||||||
|
# if (kitty_ws is None or kitty_ws.name == focused_ws.name or
|
||||||
|
# kitty_ws.name == "__i3_scratch"): # FIXME: fragile way to check if hidden?
|
||||||
|
# return
|
||||||
|
# self.fetch(kitty_ws, retrieve=False)
|
||||||
|
|
||||||
|
def _get_focused_workspace(self):
|
||||||
|
pass
|
||||||
|
# focused_workspaces = [w for w in self.i3.get_workspaces() if w.focused]
|
||||||
|
# if not len(focused_workspaces):
|
||||||
|
# return None
|
||||||
|
# return focused_workspaces[0]
|
||||||
|
|
||||||
|
def fetch(self, ws, retrieve=True):
|
||||||
|
pass
|
||||||
|
# if self.id is None:
|
||||||
|
# raise RuntimeError("Kitty instance ID not yet assigned")
|
||||||
|
#
|
||||||
|
# if self.pos in (Position.TOP, Position.BOTTOM):
|
||||||
|
# width = round(ws.rect.width * self.shape.major)
|
||||||
|
# height = round(ws.rect.height * self.shape.minor)
|
||||||
|
# x = ws.rect.x
|
||||||
|
# y = ws.rect.y if self.pos is Position.TOP else ws.rect.y + ws.rect.height - height
|
||||||
|
# else: # LEFT || RIGHT
|
||||||
|
# width = round(ws.rect.width * self.shape.minor)
|
||||||
|
# height = round(ws.rect.height * self.shape.major)
|
||||||
|
# x = ws.rect.x if self.pos is Position.LEFT else ws.rect.x + ws.rect.width - width
|
||||||
|
# y = ws.rect.y
|
||||||
|
#
|
||||||
|
# self.i3.command(f"[con_id={self.id}] "
|
||||||
|
# f"resize set {width}px {height}px, "
|
||||||
|
# f"{', move scratchpad, scratchpad show' if retrieve else ''}"
|
||||||
|
# f"move absolute position {x}px {y}px")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def on_shutdown(_, se):
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def _split_args(args):
|
||||||
|
try:
|
||||||
|
split = args.index("--")
|
||||||
|
return args[:split], args[split + 1:]
|
||||||
|
except ValueError:
|
||||||
|
return args, None
|
||||||
|
|
||||||
|
|
||||||
|
def _simple_fraction(arg):
|
||||||
|
arg = float(arg)
|
||||||
|
if not 0 <= arg <= 1:
|
||||||
|
raise argparse.ArgumentError("Argument needs to be a simple fraction, within"
|
||||||
|
"[0, 1]")
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_args(argv, defaults):
|
||||||
|
ap = argparse.ArgumentParser(
|
||||||
|
description="SmartPositioner: Window position wrapper for programs. "
|
||||||
|
"Arguments following '--' are forwarded to the program instance")
|
||||||
|
ap.set_defaults(**defaults)
|
||||||
|
ap.add_argument("-v", "--version",
|
||||||
|
action="version",
|
||||||
|
version=f"%(prog)s {__version__}",
|
||||||
|
help="show %(prog)s's version number and exit")
|
||||||
|
ap.add_argument("-n", "--name",
|
||||||
|
help="name/tag connecting a Kitti3 bindsym with a Kitty instance. "
|
||||||
|
"Forwarded to Kitty on spawn and scanned for on i3 binding "
|
||||||
|
"events")
|
||||||
|
ap.add_argument("-p", "--position",
|
||||||
|
type=Position.from_str,
|
||||||
|
choices=list(Position),
|
||||||
|
help="Along which edge of the screen to align the Kitty window")
|
||||||
|
ap.add_argument("-s", "--shape",
|
||||||
|
type=_simple_fraction,
|
||||||
|
nargs=2,
|
||||||
|
help="shape of the terminal window major and minor dimensions as a "
|
||||||
|
"fraction [0, 1] of the screen size (note: i3bar is accounted "
|
||||||
|
"for such that a 1.0 1.0 shaped terminal would not overlap it)")
|
||||||
|
|
||||||
|
args = ap.parse_args(argv)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def cli():
|
||||||
|
argv_positioner3, argv_positioner = _split_args(sys.argv[1:])
|
||||||
|
args = _parse_args(argv_positioner3, DEFAULTS)
|
||||||
|
|
||||||
|
positioner = SmartPositioner(
|
||||||
|
name=args.name,
|
||||||
|
shape=Shape(*args.shape),
|
||||||
|
pos=args.position,
|
||||||
|
positioner_argv=argv_positioner,
|
||||||
|
)
|
||||||
|
# positioner.loop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cli()
|
Loading…
Reference in New Issue