#
# 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 3 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, see .
#
import json
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GObject, GLib, Gst
#
# DBUS interface
#
class GenericMonitor:
"""
Class that manage DBUS communication with GNOME generic monitor addon.
You have to subclass it
"""
def setupMonitor(self):
""" Setup DBUS stuff (equivalent to constructor) """
self._activated = True
self._encoder = json.JSONEncoder()
self._dbus_interface = 'com.soutade.GenericMonitor'
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
self._dbus = dbus.SessionBus()
self.systray_proxy = self._dbus.get_object('org.gnome.Shell', '/com/soutade/GenericMonitor')
self.add_signal_receiver(self.onClick, 'onClick')
self.add_signal_receiver(self.onDblClick, 'onDblClick')
self.add_signal_receiver(self.onRightClick, 'onRightClick')
self.add_signal_receiver(self.onRightDblClick, 'onRightDblClick')
self.add_signal_receiver(self.onScrollUp, 'onScrollUp')
self.add_signal_receiver(self.onScrollDown, 'onScrollDown')
self.add_signal_receiver(self.onEnter, 'onEnter')
self.add_signal_receiver(self.onLeave, 'onLeave')
self.add_signal_receiver(self.onActivate, 'onActivate')
self.add_signal_receiver(self.onDeactivate, 'onDeactivate')
def runMainLoop(self):
""" Start infinite loop that allows to send and receive events and functions """
self._mainLoop = GLib.MainLoop()
self._mainLoop.run()
def stopMainLoop(self):
""" Stop infinite main loop """
self._mainLoop.quit()
# Generic Monitor functions
def notify(self, group):
""" Send notify() function
Parameters
----------
group : GenericMonitorGroup
group to notify
"""
if self._activated:
if type(group) == GenericMonitorGroup:
group = group.getValues()
self.systray_proxy.notify(self._encoder.encode(group), dbus_interface=self._dbus_interface)
def deleteItems(self, items):
""" Send deleteItems() function
Parameters
----------
items : list of str (@)
items to delete
"""
if self._activated:
items = {'items':items}
self.systray_proxy.deleteItems(self._encoder.encode(items), dbus_interface=self._dbus_interface)
def deleteGroups(self, groups):
""" Send deleteGroups() function
Parameters
----------
groups : list of str ()
groups to delete
"""
if self._activated:
groups = {'groups':groups}
self.systray_proxy.deleteGroups(self._encoder.encode(groups), dbus_interface=self._dbus_interface)
def openPopup(self, item):
""" Send openPopup() function
Parameters
----------
item : str (@)
Open popup (if there is one) of item
"""
if self._activated:
item = {'item':item}
self.systray_proxy.openPopup(self._encoder.encode(item), dbus_interface=self._dbus_interface)
def closePopup(self, item):
""" Send closePopup() function
Parameters
----------
item : str (@)
Close popup (if there is one) of item
"""
if self._activated:
item = {'item':item}
self.systray_proxy.closePopup(self._encoder.encode(item), dbus_interface=self._dbus_interface)
def togglePopup(self, item):
""" Send togglePopup() function
Parameters
----------
item : str (@)
Toggle popup (if there is one) of item
"""
if self._activated:
item = {'item':item}
self.systray_proxy.togglePopup(self._encoder.encode(item), dbus_interface=self._dbus_interface)
# Generic Monitor signals
def onClick(self, sender):
""" onClick event
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onRightClick(self, sender):
""" onRightClick event
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onDblClick(self, sender):
""" onDblClick event
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onRightDblClick(self, sender):
""" onRightDblClick event
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onEnter(self, sender):
""" onEnter event (mouse enter in item)
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onLeave(self, sender):
""" onLeave event (mouse leave item)
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onScrollUp(self, sender):
""" onScrollUp event
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onScrollDown(self, sender):
""" onScrollDown event
Parameters
----------
sender : str (@)
Sender which event has been raised
"""
pass
def onActivate(self):
""" onActivate event (addon activated)
"""
self._activated = True
def onDeactivate(self):
""" onDeactivate event (addon deactivated)
"""
self._activated = False
# DBUS method
""" Add callback when DBUS signal is raised
Parameters
----------
callback : function
Callback raised on DBUS signal
signalName : str
Name of DBUS signal
interface : str
Name of DBUS interface
"""
def add_signal_receiver(self, callback, signalName, interface=None):
if not interface:
interface = self._dbus_interface
self._dbus.add_signal_receiver(callback, signalName, interface)
#
# Item stuff
#
class GenericMonitorGenericWidget:
""" Generic widget class, parent of all widgets
"""
def __init__(self, style='', name='', signals={}):
"""
Parameters
----------
name : str, optional
Widget name
signals : dictionary, optional
Dictionary of signals and their action
"""
self.valuesToMap = ['name', 'style']
self.mapValues = {}
self.mapName = ''
self.style = style
self.name = name
self.signals = signals
def setStyle(self, style):
self.style = style
def _toMap(self):
""" Return dictionary of class elements to send to addon
"""
self.mapValues = {}
for p in self.valuesToMap:
if self.__dict__[p]:
self.mapValues[p] = self.__dict__[p]
for (name, value) in self.signals.items():
self.mapValues[name] = value
return {self.mapName:self.mapValues}
class GenericMonitorTextWidget(GenericMonitorGenericWidget):
""" Text widget
"""
def __init__(self, text, style='', name='', signals={}):
"""
Parameters
----------
text : str
Text to display
style : str, optional
CSS style
name : str, optional
Widget name
signals : dictionary, optional
Dictionary of signals and their action
"""
super().__init__(style, name, signals)
self.valuesToMap += ['text']
self.mapName = 'text'
self.text = text
def setText(self, text):
self.text = text
class GenericMonitorIconWidget(GenericMonitorGenericWidget):
""" Icon widget
"""
def __init__(self, path, style=''):
"""
Parameters
----------
path : str
Icon path
style : str, optional
CSS style
"""
super().__init__(style=style)
self.valuesToMap += ['path']
self.mapName = 'icon'
self.path = path
self.style = style
def setPath(self, path):
self.path = path
class GenericMonitorPictureWidget(GenericMonitorGenericWidget):
""" Picture widget
"""
def __init__(self, path, style='', width=-1, height=-1, name='', signals={}):
"""
Parameters
----------
path : str
Picture path
style : str, optional
CSS style
width : int, optional
Width of displayed picture (-1 for default width)
height : int, optional
Width of displayed picture (-1 for default width)
name : str, optional
Widget name
signals : dictionary, optional
Dictionary of signals and their action
"""
super().__init__(style, name, signals)
self.valuesToMap += ['path', 'width', 'height']
self.mapName = 'picture'
self.path = path
self.width = width
self.height = height
def setPath(self, path):
self.path = path
def setWidth(self, width):
self.width = width
def setHeight(self, height):
self.height = height
class GenericMonitorPopup(GenericMonitorGenericWidget):
""" Popup of current item
"""
def __init__(self, items):
"""
Parameters
----------
items : list of GenericMonitorTextWidget and GenericMonitorPictureWidget
List of items (text or picture)
"""
self.valuesToMap = ('items',)
self.mapName = 'popup'
self.items = items
def _toMap(self):
self.mapValues = {}
self.mapValues['items'] = []
for item in self.items:
self.mapValues['items'] += [item._toMap()]
return {self.mapName:self.mapValues}
def clear(self):
""" Clear items list
"""
self.items = []
def setItems(self, items):
self.items = items
class GenericMonitorItem:
""" Addon item that will be displayed in status bar
"""
def __init__(self, name, items=[], signals={}, popup=None, box='center'):
"""
Parameters
----------
name : str
Item name
items : list of GenericMonitorTextWidget and GenericMonitorIconWidget, optional
List of items (text or icon)
signals : dictionary, optional
Dictionary of signals and their action
"on-click" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-dblclick" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-rightclick" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-rightdblclick" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-click" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-enter" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-leave" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
"on-scroll" : ["signal"|"delete"|"open-popup"|"close-popup"|"toggle-popup"]
popup : GenericMonitorPopup, optional
Popup to be displayed
box : str, optional
Box were to put items : left, center (default), or right
"""
self.name = name
self.items = items
self.signals = signals
self.popup = popup
self.box = box
self.group = ''
self._checkValues()
def _checkValues(self):
if not self.name:
raise ValueError('Need a name')
if len(self.items) > 2:
raise ValueError('Maximum 2 items can be displayed')
for (name, value) in self.signals.items():
if not name in ('on-click', 'on-dblclick', 'on-rightclick', 'on-rightdblclick',
'on-enter', 'on-leave', 'on-scroll'):
raise ValueError('Invalid signal name ' + name)
if not value in ('signal', 'delete', 'open-popup', 'close-popup', 'toggle-popup'):
raise ValueError('Invalid signal value ' + value)
for item in self.items:
if not isinstance(item, GenericMonitorGenericWidget):
raise ValueError('Invalid item ' + item)
if self.popup and not isinstance(self.popup, GenericMonitorPopup):
raise ValueError('Invalid popup object')
if self.box and not self.box in ('left', 'center', 'right'):
raise ValueError('Invalid box value')
def setGroup(self, group):
""" Set current group (automatically done when added in a group)
Parameters
----------
group : str
Group name
"""
self.group = group
def getName(self):
return self.name
def getFullName(self):
""" return full name used by addon
"""
return '%s@%s' % (self.name, self.group)
def _toMap(self):
myMap = {}
for p in ('name', 'box'):
if self.__dict__[p]:
myMap[p] = self.__dict__[p]
for item in self.items:
item._toMap()
myMap[item.mapName] = item.mapValues
if self.popup:
self.popup._toMap()
myMap['popup'] = self.popup.mapValues
for (name, value) in self.signals.items():
myMap[name] = value
return [myMap]
class GenericMonitorGroup:
""" Group of items
"""
def __init__(self, name, items=[]):
"""
Parameters
----------
name : str
Group name
items : list of GenericMonitorItem, optional
List of items
"""
self.name = name
self.items = []
if type(items) != list:
self.addItem(items)
else:
self.addItems(items)
def addItem(self, item):
""" Add item into the groupw
Parameters
----------
item : GenericMonitorItem
Item to add
"""
item.setGroup(self.name)
self.items.append(item)
def addItems(self, items):
""" Add items into the group
Parameters
----------
items : list of GenericMonitorItem
Items to add
"""
for item in items:
self.addItem(item)
def clear(self):
""" Clear items list
"""
for item in items:
item.setGroup('')
self.items = []
def getValues(self):
""" Returns group and its items in addon format
"""
res = {'group': self.name, 'items':[]}
for item in self.items:
res['items'] += item._toMap()
return res
def __str__(self):
return str(self.getValues())