User documentation
==================
What is Permon?
---------------
Permon is a tool to display live line charts in a clear, uncluttered way. Permon comes prepackaged
with a lot of useful stats for monitoring the performance of your PC. It is developed with a focus
on only showing you things you care about, not everything you can monitor in your system.
Permon is developed in Python 3. There is a good chance you already have Python on your system. If
not, install it (recommended: `Miniconda `_. Run ``pip
install permon`` in your preferred command line to install permon. Permon can then be started from the command line.
Synopsis
""""""""
.. argparse::
:module: permon
:func: get_parser
:prog: permon
:nodescription:
Stats
-----
Permon comes prepackaged with some useful stats to monitor the performance of your PC. You can also add custom stats to Permon.
See `Extending permon with custom stats`_ to see how to do that. The following stats are always part of Permon:
.. autoclass:: permon.backend.stats.core.CPUStat()
.. autoclass:: permon.backend.stats.core.RAMStat()
.. autoclass:: permon.backend.stats.core.GPUStat()
.. autoclass:: permon.backend.stats.core.ReadStat()
.. autoclass:: permon.backend.stats.core.WriteStat()
.. autoclass:: permon.backend.stats.core.CPUTempStat()
.. autoclass:: permon.backend.stats.jupyter.JupyterRAMUsage()
Extending permon with custom stats
----------------------------------
Permon can be extended by adding custom stats. Custom stats have to be put in the ``stats`` subdirectory of the user
config directory. You can find your user config directory by executing ``permon config show`` and looking for *Config directory*.
.. hint::
On Linux, execute ``cd `permon config show | grep -o "\S*/permon$"``` to directly change your directory to permon's config directory.
In the config directory, make a subdirectory ``stats`` and create a file called ``.permon.py`` in the subdirectory where ```` is the name for
your suite of stats e. g. ``custom.permon.py``.
The tag of your stat will then be ``.`` where ```` is the static ``base_tag`` attribute
of your stat class.
You can now start creating stats in this file. They will automatically be discovered by permon. A simple custom stat could look like this:
.. code-block:: python
import math
from permon.backend import Stat
class SineStat(Stat):
name = 'Sine'
base_tag = 'sine'
def __init__(self, fps):
self.t = 0
super(SineStat, self).__init__(fps)
def get_stat(self):
# get_stat is called once per frame to fetch the latest value for the stat
self.t += 1 / self.fps
return math.sin(self.t)
@property
def maximum(self):
# sets the maximum value for the stat
# if the stat has no fixed maximum, make it return None
return 1
@property
def minimum(self):
# sets the minimum value for the stat
# if the stat has no fixed minimum, make it return None
return -1
All stats must inherit from the ``permon.backend.Stat`` base class. Also, the parent constructor should be called at the end of ``__init__``
to make sure ``get_stat`` can run successfully (the parent constructor needs to call it once to check whether the stat has a :ref:`contributor breakdown `).
View your first custom stat by running ``permon native custom.sine`` (or, of course, use another frontend).
Adding settings to a stat
"""""""""""""""""""""""""
Stats can have settings to conventienly change some aspects of the stat. Extending the example from above:
.. code-block:: python
import math
from permon.backend import Stat
class SineStat(Stat):
name = 'Sine'
base_tag = 'sine'
default_settings = {
'speed': 1.
}
def __init__(self, fps):
self.t = 0
super(SineStat, self).__init__(fps)
def get_stat(self):
self.t += 1 / self.fps * self.settings['speed']
return math.sin(self.t)
@property
def maximum(self):
return 1
@property
def minimum(self):
return -1
Note that ``default_settings`` only stores the default settings. In the calculation, ``self.settings`` is used because it stores the settings that
are entered in the UI.
Every setting must have a default value. Every value should be a basic python data type (string, float, integer, ...) because
permon will automatically cast the user-entered string to the type specified in the ``default_settings``.
If you need more advanced data types like JSON, make the default value a string and manually convert it to the needed data type afterwards.
Making a stat conditionally available
"""""""""""""""""""""""""""""""""""""
Some stats are not always available, like :meth:`permon.backend.stats.JupyterRAMUsage` which needs ``nvidia-smi`` installed.
To add some logic to check whether your stat is available or not, implement the :meth:`Stat.check_availability` method.
This method must raise a :meth:`permon.exceptions.StatNotAvailableError` if the stat is not available. ``cls.settings`` can also be used
in :meth:`Stat.check_availability` to e. g. disallow some settings. Further expanding the example from above:
.. code-block:: python
import math
from permon.backend import Stat
from permon import exceptions
class SineStat(Stat):
name = 'Sine'
base_tag = 'sine'
default_settings = {
'speed': 1.
}
@classmethod
def check_availability(cls):
if cls.settings['speed'] <= 0:
raise exceptions.StatNotAvailableError(
'Speed must be greater than zero.')
def __init__(self, fps):
self.t = 0
super(SineStat, self).__init__(fps)
def get_stat(self):
self.t += 1 / self.fps * self.settings['speed']
return math.sin(self.t)
@property
def maximum(self):
return 1
@property
def minimum(self):
return -1
.. _contributor-breakdown:
Adding a contributor breakdown to a stat
""""""""""""""""""""""""""""""""""""""""
Stats can have a contributor breakdown. A contributor breakdown shows what contributes to the stat, e. g. how much of the CPU different
processes need for :meth:`permon.backend.stats.CPUUsage`.
.. code-block:: python
import math
from permon.backend import Stat
class SineModulationStat(Stat):
name = 'Sine Modulation'
base_tag = 'sine_modulation'
def __init__(self, fps):
self.t = 0
super(SineModulationStat, self).__init__(fps)
def get_stat(self):
self.t += 1 / self.fps
sine1 = 1 + math.sin(self.t)
sine2 = 1 + math.sin(self.t * 10)
return sine1 + sine2, [
('sine1', sine1),
('sine2', sine2)
]
@property
def maximum(self):
# both sines have a maximum of 2 because they are offset by +1
return 4
@property
def minimum(self):
return 0
Stats with a contributor breakdown must return the stat value and a list containing tuples of ``(name, value)`` for every contributor (`sine1` and `sine2` in the above example).
Permon will automatically handle the rest.
.. warning::
The contributor breakdown of stats with a minimum of less than zero might have unexpected behaviour because the range of contributors is not well-defined
in that case.
Negative contributors are also not handled well at the moment.
That already covers the full functionality of any stat.
To see how the prepackaged stats are implemented, see the `source on github `_.