Skip to content

Commit c9fb1cf

Browse files
authored
Merge pull request #6 from ProjectLighthouseCAU/refactor_core
Refactor core
2 parents e547d81 + 8b339b5 commit c9fb1cf

18 files changed

Lines changed: 1004 additions & 294 deletions

CHANGELOG.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Changelog
2+
3+
## 0.4 - Refactoring, Bugfixes and adding new features
4+
5+
### Features
6+
* **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`
7+
* **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
8+
* **Error handeling:** `PyghthouseCanvas.set_image` will now throw more useful errors upon invalid image object
9+
10+
### Changes
11+
* **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.
12+
* **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`
13+
* **Wait for start:** `Pyghthouse.start` will now wait until the start sequence is completed
14+
15+
### Removed
16+
* **Removed dependency:** numpy has been removed as dependency to simplify the image structure
17+
* **Redundant method:** `Pyghthouse.connect` was only intended for internal use and is now combined in `Pyghthouse.start`
18+
* **Redundant behaviour:** signal handler and corresponding method `Pyghthouse._handle_sigint` is now replaced by the main thread check in `PHThread`
19+
* **Unsupported method:**
20+
+ removed `Pyghthouse.get_image_raw`
21+
+ removed `Pyghthouse.empty_image_raw`
22+
23+
### Bugfixes
24+
* **Keyboard interrupt:** keyboard interrupt should now stop the whole program instead of only the main thread
25+
* **Missing warning:** `VerbosityLevel.ALL` now prints all messages, instead of only messages with number 200
26+
* **Error handeling:** upon error inside the library, the Pyghthouse routine will now close properly
27+
* **Fixed image mutations:** added locks for critical sections in `PyghthouseCanvas` to prevent rare image mutations.
28+
* **Fixed connection deadlock:** added timeout to avoid deadlocks upon unexpected connection behaviour
29+
30+
### Refactored
31+
* **Added documentation**
32+
* **Changed data structure:** Changed data structure of `PyghthouseCanvas` from a 3D numpy array to a 3D python list
33+
* **Better maintainability:** Changed overall code structure to allow easier access to single code pieces
34+
* **Changed internal package structure:**
35+
+ moved `PHThread` to the new script `_thread.py`
36+
+ moved `PHMessageHandler` into the new script `handler.py`
37+
+ moved `REID` into the new script `data.py` and renamed from `REID` to `ReID`
38+
+ moved `VerbosityLevel` into the new script `data.py`

example.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,77 @@
11
'''
2-
This example should give a simple overview on how to use the Pyghthouse.
2+
This example should give a simple overview on how to use Pyghthouse.
33
44
A generel orientation of what you need:
55
- Import of pyghthouse
66
- Creating an instance of Pyghthouse
7-
- start connection
8-
- Sending images with either a given function or set_image(image)
9-
- close connection (not needed but recommend)
7+
- Start Pyghthouse routine
8+
- Sending images with either a given callback function or by set_image
9+
- Stop Pyghthouse routine (not needed but recommended)
1010
1111
More examples can be found in the examples folder.
12-
13-
Note that this skript handles Pyghthouse as a local module compared to
14-
the skripts in examples; These handle Pyghthouse as an installed package.
15-
Check examples/README.md for more informations.
1612
'''
1713

18-
from pyghthouse import Pyghthouse
19-
import pyghthouse.utils as utils
20-
from examples.config import UNAME, TOKEN
21-
22-
# Optional: This condition only executes if run as a skript.
14+
# Optional: This condition only executes if run as a script.
2315
# Importing this program won't execute this block.
2416
if __name__ == '__main__':
2517

26-
# Create instance of Pyghthouse and start connection
18+
from pyghthouse import Pyghthouse
19+
import pyghthouse.utils as utils
20+
from examples.config import UNAME, TOKEN
21+
22+
23+
# Create instance of Pyghthouse and start Pyghthouse routine
2724
username = UNAME
2825
token = TOKEN
2926
p = Pyghthouse(username, token)
3027
p.start()
3128

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

3633
pos_x = 10
3734
pos_y = 5
38-
# color entries are in rgb
35+
36+
# The used color is in the RGB format. We use a list where each index represents a color channel.
37+
# Index 0 for red, 1 for green, 2 for blue.
38+
# Each color channel has a size of 1 byte, so values from 0 to 255 can be used.
3939
color = [100, 124, 24]
40-
41-
# sends the given image
40+
41+
# Set the image with one colored pixel
4242
img[pos_y][pos_x] = color
4343
p.set_image(img)
4444

4545
key = input("Enter 'n' for the next image, enter any other key to skip\n")
4646
if key.upper() == "N":
47-
# set rgb color with a converted hsv color
47+
48+
# Our library also have convertors for other color formats.
49+
# Now we convert a hsv color to an rgb color
4850
color = utils.from_hsv(0.5, 1.0, 0.7)
4951

50-
# set all pixels to the current color
51-
for x in range(28):
52-
for y in range(14):
52+
# Set the color of all pixels
53+
for y in range(14):
54+
for x in range(28):
5355
img[y][x] = color
56+
5457
p.set_image(img)
5558

56-
key = input("Enter 'n' for the next image, enter any other key to skip\n")
59+
60+
key = input("Enter 'n' for the next animation, enter any other key to skip\n")
5761
if key.upper() == "N":
62+
5863
color = [255, 255, 255]
5964

60-
# create 3 white lines
65+
# Let 3 white lines appear
6166
for x in range(28):
6267
for y in range(3,10,3):
68+
6369
img[y][x] = color
64-
p.set_image(img)
70+
71+
p.set_image(img)
72+
# set_image overwrites the old image. So to prevent the loss of an image,
73+
# we wait until the frame has been build
74+
p.wait()
6575

66-
p.close()
76+
77+
p.stop()

examples/README.md

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,94 @@ Some scripts have additional dependencies; Check `README.md` in the repository r
99
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.
1010
Be aware that API tokens are only valid for a few days.
1111

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

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

20-
### Available functions
21-
Here you can find a list of functions containing in this package.
14+
## Ways of programming a pyghthouse script
2215

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

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

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

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

38-
`<instance name>.set_image(image)`
39-
Sends the given `image`.
4023

41-
`<instance name>.close()`
42-
Closes the connection.
24+
### Programming with callback
4325

44-
*More functions can be found in `pyghthouse\ph.py`*
26+
The following examples uses callback functions:
27+
28+
- `huecircle.py`
29+
- `noisefill.py`
30+
- `rainbow.py`
31+
- `rgbfill.py`
32+
- `rgbscan.py`
33+
- `twopoints.py`
34+
35+
In summary, we first need a callback function to create images when called. After that, we initialize the pyghthouse routine.
36+
37+
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.
38+
39+
The pyghthouse routine is now running asynchronous to the main script.
40+
41+
**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.
42+
43+
44+
### Programming with set_image
45+
46+
The following examples use `set_image`:
47+
48+
- `movingdot.py`
49+
- `whitefill.py`
50+
51+
Compared to the callback function, `set_image` is a lot simpler.
52+
53+
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.
54+
55+
**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()`.
56+
57+
58+
59+
## Pyghthouse package content
60+
61+
62+
### The class `pyghthouse.Pyghthouse` with
63+
64+
- `Pyghthouse(username: str, token: str, ...)`:
65+
66+
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`)
67+
68+
69+
- `Pyghthouse.start()`
70+
71+
Starts the pyghthouse routine and opens the websocket connection.
72+
73+
74+
- `Pyghthouse.stop()`
75+
76+
Stops the pyghthouse routine and closes the websocket connection.
77+
78+
79+
- `Pyghthouse.empty_image()`
80+
81+
A static method which creates a black image.
82+
This function can also be called with an instance of the `Pyghthouse` object (*`<instance name>.empty_image()`*)
83+
84+
85+
- `Pyghthouse.set_image(image)`
86+
87+
Set the Pyghthouse canvas to `image`. Every `1/frame_rate` seconds, the last image set is send by the pyghthouse routine.
88+
89+
90+
*More functions can be found in `pyghthouse\ph.py`*
91+
92+
93+
### Utils functions
94+
Here are additional functions in the pyghthouse package.
95+
96+
- `pyghthouse.utils.from_html(html_color)`:
97+
98+
Converts an HTML color string like FF7F00 or #c0ffee to RGB
99+
100+
- `pyghthouse.utils.from_hsv(h: float, s: float, v: float)`:
101+
102+
Converts HSV (float values between 0 and 1) colors to RGB.

examples/huecircle.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ def callback():
3232
p = Pyghthouse(UNAME, TOKEN, image_callback=callback)
3333
print("Starting... use CTRL+C to stop.")
3434
p.start()
35+
p.keep_running()

examples/noisefill.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ def image_gen():
2424
p = Pyghthouse(UNAME, TOKEN, image_callback=g.__next__, frame_rate=60)
2525
print("Starting... use CTRL+C to stop.")
2626
p.start()
27+
p.keep_running()

examples/rainbow.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44

55

66
def rainbow_generator():
7+
image = Pyghthouse.empty_image()
78
while True:
89
for i in range(180):
9-
yield [from_hsv((i / 180 + j / (14 * 28)) % 1.0, 1.0, 1.0) for j in range(14 * 28)]
10+
for x in range(28):
11+
for y in range(14):
12+
j = x + y*28
13+
image[y][x] = from_hsv((i / 180 + j / (14 * 28)) % 1.0, 1.0, 1.0)
14+
yield image
15+
1016

1117

1218
rainbow = rainbow_generator()
@@ -20,4 +26,4 @@ def callback():
2026
p = Pyghthouse(UNAME, TOKEN, image_callback=callback)
2127
print("Starting... use CTRL+C to stop.")
2228
p.start()
23-
29+
p.keep_running()

examples/rgbfill.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ def image_gen():
88
image = np.zeros((14, 28, 3))
99
yield image
1010
while True:
11-
for x in range(14):
12-
for y in range(28):
11+
for y in range(14):
12+
for x in range(28):
1313
for j in range(3):
14-
image[x, y, j] = 255
14+
image[y, x, j] = 255
1515
yield image
16-
for y in range(28):
17-
for x in range(14):
16+
for x in range(28):
17+
for y in range(14):
1818
for j in range(3):
19-
image[x, y, j] = 0
19+
image[y, x, j] = 0
2020
yield image
2121

2222

@@ -26,3 +26,4 @@ def image_gen():
2626
p = Pyghthouse(UNAME, TOKEN, image_callback=g.__next__, frame_rate=60)
2727
print("Starting... use CTRL+C to stop.")
2828
p.start()
29+
p.keep_running()

examples/rgbscan.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ def image_gen():
99
yield image
1010
while True:
1111
for j in range(3):
12-
for x in range(14):
13-
for y in range(28):
14-
image[x, y, j] = 255
12+
for y in range(14):
13+
for x in range(28):
14+
image[y, x, j] = 255
1515
yield image
16-
image[x, y, j] = 0
16+
image[y, x, j] = 0
1717

1818

1919
g = image_gen()
@@ -22,3 +22,4 @@ def image_gen():
2222
p = Pyghthouse(UNAME, TOKEN, image_callback=g.__next__, frame_rate=60)
2323
print("Starting... use CTRL+C to stop.")
2424
p.start()
25+
p.keep_running()

examples/twopoints.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ def callback(self):
6262
i = ImageMaker()
6363
p = Pyghthouse(UNAME, TOKEN, image_callback=i.callback, frame_rate=60)
6464
p.start()
65+
p.keep_running()

examples/whitefill.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from pyghthouse import Pyghthouse, VerbosityLevel
2+
from config import UNAME, TOKEN
3+
from time import sleep
4+
5+
6+
def main_loop():
7+
p = Pyghthouse(UNAME, TOKEN, frame_rate=60)
8+
p.start()
9+
10+
img = p.empty_image()
11+
while True:
12+
for y in range(14):
13+
for x in range(28):
14+
img[y][x] = (255, 255, 255)
15+
p.set_image(img)
16+
p.wait()
17+
18+
sleep(1)
19+
20+
for y in range(13, -1, -1):
21+
for x in range(27, -1, -1):
22+
img[y][x] = [0,0,0]
23+
p.set_image(img)
24+
# We skip frames here, but our frame_rate is high enough to hide it
25+
sleep(0.01)
26+
27+
28+
if __name__ == '__main__':
29+
main_loop()

0 commit comments

Comments
 (0)