Components Guide

Hyprpy components represent all the moving parts of a running Hyprland instance. They allow us to retrieve information about the compositor in real time.

Hyprpy handles four component types:

Let’s get started, and go through some examples.

The Instance

To retrieve a component, we first have to get the current Hyprland instance:

from hyprpy.components.instances import Instance

# Get the current instance
instance = Instance()

We can also use the shorthand alias Hyprland, which looks slightly nicer:

from hyprpy import Hyprland

instance = Hyprland()

By default, Hyprpy reads the $HYPRLAND_INSTANCE_SIGNATURE environment variable to find the Hyprland Instance Signature. If the environment variable is unset because we are in an SSH session, or if we want to access a second Hyprland instance running on our system, we can specify its value manually:

from hyprpy import Hyprland

instance = Hyprland("v0.25.0_1691941479")

Reading component data

Now that we have our instance, we can query it for components. Components have data attributes, which allow us to access information about them. Let’s grab the currently active window and print some information about it:

from hyprpy import Hyprland

instance = Hyprland()

window = instance.get_active_window()
print(window)
# Output: "<Window(address='0x1981f40', wm_class='kitty', title='python ~/d/p/src')>"
print(window.width)
# Output: 1258
print(window.wm_class)
# Output: 'kitty'

Here, we queried the instance for the active window, and printed the window’s width and wm_class data attributes, which tell us the current width and display class of the window.

Windows, workspaces and monitors have a wide range of useful data attributes. For a complete list of data attributes for each type of component, refer to the Component API.

Note

Component data attributes are read-only. Writing new values into them will not raise an exception, but will also have no effect on Hyprland’s actual state.

Reacting to events

Besides access to other components, the Instance class provides the watch() method, which monitors Hyprland for events and emits a specific Signal whenever an event occurs. By connecting our own callback functions to these signals, we can execute python code dynamically, in response to changes in Hyprland’s state:

from hyprpy import Hyprland
from hyprpy.utils.shell import run_or_fail

instance = Hyprland()

# Define a callback function
def workspace_changed(sender, **kwargs):
   run_or_fail(["notify-send", "Workspace Changed"])

# Connect the callback function to the signal
instance.signal_active_workspace_changed.connect(workspace_changed)

# Start watching for hyprland events
instance.watch()

In this example, we defined our own callback function called workspace_changed. The function executes a shell command, notify-send, with "Workspace Changed" as an argument. We used a helper function called run_or_fail() here to run the shell command, but the body of our callback function can be any valid python code.

Then, we connected our callback function to the Instance’s signal_active_workspace_changed signal and, finally, we called the Instance’s watch() method.

The watch() method runs indefinitely, but executes our callback function whenever the underlying signal is emitted. In this case, we get a desktop notification whenever we switch to another workspace.

Attention

The callback’s function signature must be (sender, **kwargs).

Dispatched signals include some data about the event which triggered them. The data can be retrieved from the **kwargs in our callback function:

from hyprpy import Hyprland
from hyprpy.utils.shell import run_or_fail

instance = Hyprland()

def workspace_changed(sender, **kwargs):
   # Retrieve the newly active workspace from the signal's data
   active_workspace_id = kwargs.get('active_workspace_id')
   run_or_fail(["notify-send", "Workspace Changed", f"Workspace is now {active_workspace_id}"])

instance.signal_active_workspace_changed.connect(workspace_changed)
instance.watch()

Building on the previous example, our desktop notification now also includes the ID of the workspace we switched to.

We can disconnect signals as well:

instance.signal_active_workspace_changed.disconnect(workspace_changed)

Aside from saving resources, disconnecting signals is useful if we only want our callback to get triggered a few times.

The following table shows a list of available signals, and the data they send to the callback function:

Instance Signals

Event

Signal

Signal Data

A workspace was created

signal_workspace_created

created_workspace_id: the id of the created workspace

A workspace was destroyed

signal_workspace_destroyed

destroyed_workspace_id: the id of the destroyed workspace

The active workspace changed

signal_active_workspace_changed

active_workspace_id: the id of the now active workspace

A window was created

signal_window_created

created_window_address: the address of the newly created window

A window was destroyed

signal_window_destroyed

destroyed_window_address: the address of the destroyed window

The active window changed

signal_active_window_changed

active_window_address: the address of the now active window

Note

The watch() method is a blocking operation that runs indefinitely.

Using signals in conjunction with watch() is much more efficient than polling, because Hyprpy watches Hyprland’s event socket directly, saving on CPU time and I/O operations.

Component state

When we instantiate a component object (for example a Workspace) in Hyprpy, its data attributes reflect its current state in Hyprland. As time passes and things happen in Hyprland, the object’s attributes may no longer reflect its actual state in the compositor. There is no synchronization of state between a Hyprpy component its real-world counterpart.

Attention

With the exception of the Instance object, do not re-use component objects after their state may have changed.

Instead, we should use instantiated component objects immediately, and discard them once we have the information we need:

from hyprpy import Hyprland

instance = Hyprland()

workspace_3 = instance.get_workspace_by_id(3)
if workspace_3 and workspace_3.window_count > 2:
    ... # do some stuff

... # time passes, things happen

# Don't do this:
for window in workspace_3:
    ...
    # The windows on workspace_3, and the workspace itself may have changed

# Instead, reinstantiate the workspace using the instance object:
workspace_3 = instance.get_workspace_by_id(3)
if workspace_3:
    for window in workspace_3.windows:
        ... # do some other stuff

The Instance object’s data attributes won’t change unless you restart Hyprland, so it is generally safe to re-use. For other components, if you suspect that the component’s state has changed since it has been instantiated, it is better to overwrite it with a fresh copy.

Further reading

And that’s all you need to know to get started!

For more details, check out the Components API and Utilities.