Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8414392
Removed dependencies like numpy from canvas.py
Gedusim Aug 11, 2025
13dc34b
Removed numpy dependency from ph.py
Gedusim Aug 12, 2025
96b5937
Added and adjusted error handeling
Gedusim Aug 12, 2025
8a9f769
Removed bytes as accepted type due to errors in range check
Gedusim Aug 14, 2025
7672085
Fix double output of error type
Gedusim Aug 14, 2025
27d979d
Fix multiple lists with same reference and removed legacy numpy code
Gedusim Aug 14, 2025
99d0942
Fix typo
Gedusim Aug 14, 2025
490fc34
Finished testing and fixed bugs in canvas init method
Gedusim Aug 14, 2025
c55d75d
Finished testing 'canvas.py'. TODOs remaining
Gedusim Aug 14, 2025
bc6a792
Improved structure for better readability
Gedusim Sep 15, 2025
affe8c3
Made canvas.py methods thread safe
Gedusim Sep 15, 2025
1578946
Moved PHThread to a new file
Gedusim Sep 15, 2025
bb6f21c
Reduced default timeout to 10 seconds
Gedusim Sep 15, 2025
820f802
Moved PHMessageHandler to wsconnector.py
Gedusim Sep 15, 2025
151858a
Refactor PHThread and add check for main_thread
Gedusim Sep 17, 2025
f0e7126
Refactor ph.py, moved VerbosityLevel to wsconnector.py
Gedusim Sep 17, 2025
dac4856
Added thread safe copy, added depricated warning to old methods
Gedusim Sep 17, 2025
d305272
removed deprecated tag to support python versions below 3.13
Gedusim Sep 23, 2025
6b4c747
Improved import structure
Gedusim Sep 25, 2025
79c2a56
Renamed _thread.py
Gedusim Sep 25, 2025
31c4a6c
Documentation of _thread.py
Gedusim Oct 31, 2025
fbdfeb9
Adjusted documentation text
Gedusim Oct 31, 2025
ceb7322
Refactored Websocket connection, adjusted _thread.py and ph.py. Added…
Gedusim Nov 11, 2025
8d06f79
Fix import
Gedusim Nov 11, 2025
3b39748
Added wait function. Adjusted timeout. Edited comments
Gedusim Feb 14, 2026
82377cd
Fixed routine check. Edited comments
Gedusim Feb 14, 2026
ea99edb
Removed deprecated features
Gedusim Feb 14, 2026
fc5eaa7
Fixed wait. Adjusted comments and error messages. Added warnings to d…
Gedusim Feb 17, 2026
a4752c3
Added error handeling to callback function. Edited documentation
Gedusim Feb 18, 2026
9dcd608
Removed redundant signal handler
Gedusim Feb 18, 2026
6a94e7a
Changed structure of handler to improve maintainability
Gedusim Feb 18, 2026
ddc0ede
Adjusted example and edited comments
Gedusim Feb 18, 2026
dfccca7
Updated setup.py
Gedusim Feb 18, 2026
c70c9bd
Changed allowed values in canvas to numbers. Updated examples
Gedusim Feb 18, 2026
793c891
Fixed timeout in wait when error occurs
Gedusim Feb 18, 2026
3bb6286
Added method keep_running
Gedusim Feb 19, 2026
b17ab31
Removed redundant try ... except statement
Gedusim Feb 19, 2026
d4f4ead
Added changelog
Gedusim Feb 19, 2026
a5a4d43
Adjusted comment structure for consistency
Gedusim Feb 20, 2026
c7ce28c
Cleanup and adjusted comments
Gedusim May 25, 2026
c38d42e
Added example for set_image usage
Gedusim May 25, 2026
27592df
Fixed typos
Gedusim May 25, 2026
8b339b5
Adjusted examples README for easier project overview
Gedusim May 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Changelog

## 0.4 - Refactoring, Bugfixes and adding new features

### Features
* **Added method:** Added `Pyghthouse.wait` as a new feature to synchronize with the frame building. Commenly used to ensure building a frame with the previous call of `Pyghthouse.set_image`
* **Added method:** Added `Pyghthouse.keep_running` as a new feature to keep the main thread alive. Useable to let callback functions run until keyboard interrupt
* **Error handeling:** `PyghthouseCanvas.set_image` will now throw more useful errors upon invalid image object

### Changes
* **start ... stop:** `Pyghthouse.stop` now does the same as `Pyghthouse.close`. For consistency, we recommend to use the start ... stop pattern for the Pyghthouse routine.
* **main thread check:** The pyghthouse routine will now stop when the main thread has died. To keep the pyghthouse routine running, use `Pyghthouse.keep_running`
* **Wait for start:** `Pyghthouse.start` will now wait until the start sequence is completed

### Removed
* **Removed dependency:** numpy has been removed as dependency to simplify the image structure
* **Redundant method:** `Pyghthouse.connect` was only intended for internal use and is now combined in `Pyghthouse.start`
* **Redundant behaviour:** signal handler and corresponding method `Pyghthouse._handle_sigint` is now replaced by the main thread check in `PHThread`
* **Unsupported method:**
+ removed `Pyghthouse.get_image_raw`
+ removed `Pyghthouse.empty_image_raw`

### Bugfixes
* **Keyboard interrupt:** keyboard interrupt should now stop the whole program instead of only the main thread
* **Missing warning:** `VerbosityLevel.ALL` now prints all messages, instead of only messages with number 200
* **Error handeling:** upon error inside the library, the Pyghthouse routine will now close properly
* **Fixed image mutations:** added locks for critical sections in `PyghthouseCanvas` to prevent rare image mutations.
* **Fixed connection deadlock:** added timeout to avoid deadlocks upon unexpected connection behaviour

### Refactored
* **Added documentation**
* **Changed data structure:** Changed data structure of `PyghthouseCanvas` from a 3D numpy array to a 3D python list
* **Better maintainability:** Changed overall code structure to allow easier access to single code pieces
* **Changed internal package structure:**
+ moved `PHThread` to the new script `_thread.py`
+ moved `PHMessageHandler` into the new script `handler.py`
+ moved `REID` into the new script `data.py` and renamed from `REID` to `ReID`
+ moved `VerbosityLevel` into the new script `data.py`
65 changes: 38 additions & 27 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,77 @@
'''
This example should give a simple overview on how to use the Pyghthouse.
This example should give a simple overview on how to use Pyghthouse.

A generel orientation of what you need:
- Import of pyghthouse
- Creating an instance of Pyghthouse
- start connection
- Sending images with either a given function or set_image(image)
- close connection (not needed but recommend)
- Start Pyghthouse routine
- Sending images with either a given callback function or by set_image
- Stop Pyghthouse routine (not needed but recommended)

More examples can be found in the examples folder.

Note that this skript handles Pyghthouse as a local module compared to
the skripts in examples; These handle Pyghthouse as an installed package.
Check examples/README.md for more informations.
'''

from pyghthouse import Pyghthouse
import pyghthouse.utils as utils
from examples.config import UNAME, TOKEN

# Optional: This condition only executes if run as a skript.
# Optional: This condition only executes if run as a script.
# Importing this program won't execute this block.
if __name__ == '__main__':

# Create instance of Pyghthouse and start connection
from pyghthouse import Pyghthouse
import pyghthouse.utils as utils
from examples.config import UNAME, TOKEN


# Create instance of Pyghthouse and start Pyghthouse routine
username = UNAME
token = TOKEN
p = Pyghthouse(username, token)
p.start()

# the image is a 3 dimensional list, meaning a list with [[[r,g,b]]] entries
# create a black image
# The image object is a 3 dimensional list. Each index is accessed via [row][collum][rgb]
# Create a black image
img = p.empty_image()

pos_x = 10
pos_y = 5
# color entries are in rgb

# The used color is in the RGB format. We use a list where each index represents a color channel.
# Index 0 for red, 1 for green, 2 for blue.
# Each color channel has a size of 1 byte, so values from 0 to 255 can be used.
color = [100, 124, 24]
# sends the given image

# Set the image with one colored pixel
img[pos_y][pos_x] = color
p.set_image(img)

key = input("Enter 'n' for the next image, enter any other key to skip\n")
if key.upper() == "N":
# set rgb color with a converted hsv color

# Our library also have convertors for other color formats.
# Now we convert a hsv color to an rgb color
color = utils.from_hsv(0.5, 1.0, 0.7)

# set all pixels to the current color
for x in range(28):
for y in range(14):
# Set the color of all pixels
for y in range(14):
for x in range(28):
img[y][x] = color

p.set_image(img)

key = input("Enter 'n' for the next image, enter any other key to skip\n")

key = input("Enter 'n' for the next animation, enter any other key to skip\n")
if key.upper() == "N":

color = [255, 255, 255]

# create 3 white lines
# Let 3 white lines appear
for x in range(28):
for y in range(3,10,3):

img[y][x] = color
p.set_image(img)

p.set_image(img)
# set_image overwrites the old image. So to prevent the loss of an image,
# we wait until the frame has been build
p.wait()

p.close()

p.stop()
106 changes: 82 additions & 24 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,94 @@ Some scripts have additional dependencies; Check `README.md` in the repository r
If you set up config.py with your username and API token, you won't have to enter them every time you run a script.
Be aware that API tokens are only valid for a few days.

##### Beware:
Python imports search for the modules in the current folder and in installed packages. So, if you haven't installed
pyghthouse as a package, you need to change the imports of the files in this folder to work.

In the repository root directory should be a file called `example.py`. This file shows the usage of imports of
pyghthouse without having it installed as a package.
Note: To use the imports the same way as `example.py`, your skript has to be in the same folder.

### Available functions
Here you can find a list of functions containing in this package.
## Ways of programming a pyghthouse script

`from_html(html_color)`
Converts an HTML color string like FF7F00 or #c0ffee to RGB

`from_hsv(h: float, s: float, v: float)`
Converts HSV (float values between 0 and 1) colors to RGB.
The Pyghthouse library has two intended ways of usage:

###### The class `Pyghthouse` with
`Pyghthouse(username: str, token: str, ...)`
Set up the `Pyghthouse` object. Needed arguments are `username` and `token`. Optional arguments and further explanation
can be found in the class definition (see `pyghthouse\ph.py`)
- Passing a callback function at creation with `Pyghthouse(..., callback=function)` or with the method `Pyghthouse.set_image_callback(callback)`

`Pyghthouse.empty_image()`
Creates a black image.
This function can also be called with an instance of the `Pyghthouse` object (*`<instance name>.empty_image()`*)
- Setting the currently shown canvas with `Pyghthouse.set_image()`

`<instance name>.set_image(image)`
Sends the given `image`.

`<instance name>.close()`
Closes the connection.
### Programming with callback

*More functions can be found in `pyghthouse\ph.py`*
The following examples uses callback functions:

- `huecircle.py`
- `noisefill.py`
- `rainbow.py`
- `rgbfill.py`
- `rgbscan.py`
- `twopoints.py`

In summary, we first need a callback function to create images when called. After that, we initialize the pyghthouse routine.

When we now start the pyghthouse routine, images will be generated by the given callback function. Generated images are then send to the server. This will happen in an interval given by the frame_rate.

The pyghthouse routine is now running asynchronous to the main script.

**Important to notice** is, that the pyghthouse routine will end when the main script ends. To let a pyghthouse routine run forever (until error or keyboard interrupt), the `keep_running()` method can be used.


### Programming with set_image

The following examples use `set_image`:

- `movingdot.py`
- `whitefill.py`

Compared to the callback function, `set_image` is a lot simpler.

We again need to initialize and start the pyghthouse routine. After that, we only need to call `set_image(new_image)` to send images to the server.

**Important to notice** is, that `set_image`does not wait until the image is send. This means that we can overwrite the currently set image by setting a new one without waiting for the send. So to ensure important frames to be send, we recommend to use the method `wait()`.



## Pyghthouse package content


### The class `pyghthouse.Pyghthouse` with

- `Pyghthouse(username: str, token: str, ...)`:

Set up the `Pyghthouse` object. Needed arguments are `username` and `token`. Optional arguments, like `frame_rate` and further explanations can be found in the class definition (see `pyghthouse\ph.py`)


- `Pyghthouse.start()`

Starts the pyghthouse routine and opens the websocket connection.


- `Pyghthouse.stop()`

Stops the pyghthouse routine and closes the websocket connection.


- `Pyghthouse.empty_image()`

A static method which creates a black image.
This function can also be called with an instance of the `Pyghthouse` object (*`<instance name>.empty_image()`*)


- `Pyghthouse.set_image(image)`

Set the Pyghthouse canvas to `image`. Every `1/frame_rate` seconds, the last image set is send by the pyghthouse routine.


*More functions can be found in `pyghthouse\ph.py`*


### Utils functions
Here are additional functions in the pyghthouse package.

- `pyghthouse.utils.from_html(html_color)`:

Converts an HTML color string like FF7F00 or #c0ffee to RGB

- `pyghthouse.utils.from_hsv(h: float, s: float, v: float)`:

Converts HSV (float values between 0 and 1) colors to RGB.
1 change: 1 addition & 0 deletions examples/huecircle.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ def callback():
p = Pyghthouse(UNAME, TOKEN, image_callback=callback)
print("Starting... use CTRL+C to stop.")
p.start()
p.keep_running()
1 change: 1 addition & 0 deletions examples/noisefill.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ def image_gen():
p = Pyghthouse(UNAME, TOKEN, image_callback=g.__next__, frame_rate=60)
print("Starting... use CTRL+C to stop.")
p.start()
p.keep_running()
10 changes: 8 additions & 2 deletions examples/rainbow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@


def rainbow_generator():
image = Pyghthouse.empty_image()
while True:
for i in range(180):
yield [from_hsv((i / 180 + j / (14 * 28)) % 1.0, 1.0, 1.0) for j in range(14 * 28)]
for x in range(28):
for y in range(14):
j = x + y*28
image[y][x] = from_hsv((i / 180 + j / (14 * 28)) % 1.0, 1.0, 1.0)
yield image



rainbow = rainbow_generator()
Expand All @@ -20,4 +26,4 @@ def callback():
p = Pyghthouse(UNAME, TOKEN, image_callback=callback)
print("Starting... use CTRL+C to stop.")
p.start()

p.keep_running()
13 changes: 7 additions & 6 deletions examples/rgbfill.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ def image_gen():
image = np.zeros((14, 28, 3))
yield image
while True:
for x in range(14):
for y in range(28):
for y in range(14):
for x in range(28):
for j in range(3):
image[x, y, j] = 255
image[y, x, j] = 255
yield image
for y in range(28):
for x in range(14):
for x in range(28):
for y in range(14):
for j in range(3):
image[x, y, j] = 0
image[y, x, j] = 0
yield image


Expand All @@ -26,3 +26,4 @@ def image_gen():
p = Pyghthouse(UNAME, TOKEN, image_callback=g.__next__, frame_rate=60)
print("Starting... use CTRL+C to stop.")
p.start()
p.keep_running()
9 changes: 5 additions & 4 deletions examples/rgbscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ def image_gen():
yield image
while True:
for j in range(3):
for x in range(14):
for y in range(28):
image[x, y, j] = 255
for y in range(14):
for x in range(28):
image[y, x, j] = 255
yield image
image[x, y, j] = 0
image[y, x, j] = 0


g = image_gen()
Expand All @@ -22,3 +22,4 @@ def image_gen():
p = Pyghthouse(UNAME, TOKEN, image_callback=g.__next__, frame_rate=60)
print("Starting... use CTRL+C to stop.")
p.start()
p.keep_running()
1 change: 1 addition & 0 deletions examples/twopoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ def callback(self):
i = ImageMaker()
p = Pyghthouse(UNAME, TOKEN, image_callback=i.callback, frame_rate=60)
p.start()
p.keep_running()
29 changes: 29 additions & 0 deletions examples/whitefill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from pyghthouse import Pyghthouse, VerbosityLevel
from config import UNAME, TOKEN
from time import sleep


def main_loop():
p = Pyghthouse(UNAME, TOKEN, frame_rate=60)
p.start()

img = p.empty_image()
while True:
for y in range(14):
for x in range(28):
img[y][x] = (255, 255, 255)
p.set_image(img)
p.wait()

sleep(1)

for y in range(13, -1, -1):
for x in range(27, -1, -1):
img[y][x] = [0,0,0]
p.set_image(img)
# We skip frames here, but our frame_rate is high enough to hide it
sleep(0.01)


if __name__ == '__main__':
main_loop()
3 changes: 2 additions & 1 deletion pyghthouse/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from pyghthouse.ph import Pyghthouse, VerbosityLevel
from pyghthouse.ph import Pyghthouse
from pyghthouse.connection.data import VerbosityLevel
Loading
Loading