Skip to content

Commit 592b852

Browse files
authored
Merge pull request #20 from uniwix/13-manager-gui
Changing the window and setting management
2 parents 42afc4b + f52e436 commit 592b852

17 files changed

+442
-452
lines changed

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ For example, the following codes draw a black square of 100 by 100 pixels.
5959
.. code-block:: python3
6060
6161
for x in range(100):
62-
for y in raange(100):
62+
for y in range(100):
6363
set_pixel(x, y)
6464
show_screen()
6565
@@ -68,7 +68,7 @@ Is faster on the computer, but
6868
.. code-block:: python3
6969
7070
for x in range(100):
71-
for y in raange(100):
71+
for y in range(100):
7272
set_pixel(x, y)
7373
show_screen()
7474
File renamed without changes.
File renamed without changes.

casioplot/casioplot.py

Lines changed: 82 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -6,73 +6,49 @@
66
- :py:func:`set_pixel`
77
- :py:func:`get_pixel`
88
- :py:func:`draw_string`
9+
10+
Contains the original functions from the `casioplot` calculator module and the code needed to emulate the screen.
911
"""
1012

1113
import tkinter as tk
1214
from typing import Literal
13-
from PIL import Image, ImageTk
1415

1516
from casioplot.characters import _get_char
16-
from casioplot.configuration_type import configuration
17-
from casioplot.get_config import _get_settings, _get_image_path
17+
from casioplot.settings import _settings
18+
from casioplot.types import Color
1819

19-
# color type
20-
COLOR = tuple[int, int, int]
2120
# some frequently used colors
22-
_WHITE: COLOR = (255, 255, 255) # RGB white
23-
_BLACK: COLOR = (0, 0, 0) # RGBA black
24-
25-
# create virtual screen, a proper image will be attributed latter
26-
_screen: Image.Image = Image.new("RGB", (1, 1))
27-
28-
# creates a tkinter window
29-
_window = tk.Tk()
30-
_window.grab_release()
31-
_window.title("casioplot")
32-
_window.attributes("-topmost", True)
33-
34-
# needed to display the screen in the tkinter window
35-
_photo_image = ImageTk.PhotoImage(_screen)
36-
_screen_display = tk.Label(_window, image=_photo_image)
37-
_screen_display.pack()
21+
_WHITE: Color = (255, 255, 255) # RGB white
22+
_BLACK: Color = (0, 0, 0) # RGBA black
3823

3924
# these two are only used if the setting save_multiple is set to True
4025
save_screen_counter = 0
4126
current_image_number = 1
4227

4328

44-
# auxiliar functions
45-
46-
47-
def _screen_dimensions() -> tuple[int, int]:
48-
"""Calculates the dimensions of the screen"""
49-
return (
50-
settings["left_margin"] + settings["width"] + settings["right_margin"],
51-
settings["top_margin"] + settings["height"] + settings["bottom_margin"]
52-
)
29+
# functions used by the package
5330

31+
def _canvas_to_screen(x: int, y: int, start_x: int, start_y: int) -> tuple[int, int]:
32+
"""Converts coordinates to canvas coordinates
5433
55-
# auxiliar functions for the functions used by the user
34+
:param x: x coordinate (from the left to the right)
35+
:param y: y coordinate (from the top to the bottom)
36+
:param start_x: the x coordinate of the top left corner of the canvas
37+
:param start_y: the y coordinate of the top left corner of the canvas
38+
:return: a tuple with the canvas coordinates
39+
"""
40+
return x + start_x, y + start_y
5641

5742

43+
# TODO: Takes too much time so needs improvements
5844
def _coordinates_in_bounds(x: int, y: int) -> bool:
5945
"""Checks if the given coordinates are in bounds of the canvas
6046
6147
:param x: x coordinate (from the left to the right)
6248
:param y: y coordinate (from the top to the bottom)
6349
:return: a bool that says if the given coordinates are in bounds of the canvas
6450
"""
65-
return 0 <= x < settings["width"] and 0 <= y < settings["height"]
66-
67-
68-
def _canvas_to_screen(x: int, y: int) -> tuple[int, int]:
69-
"""Translates coordinates of the canvas to coordinates in the virtual screen
70-
71-
:param x: x coordinate (from the left)
72-
:param y: y coordinate (from the top)
73-
:return: The corresponding coordinates in the virtual screen
74-
"""
75-
return x + settings["left_margin"], y + settings["top_margin"]
51+
return 0 <= x < _settings["width"] and 0 <= y < _settings["height"]
7652

7753

7854
def _save_screen(image_suffix: str = ""):
@@ -82,15 +58,14 @@ def _save_screen(image_suffix: str = ""):
8258
:param image_suffix: If the setting save_multiple is True existes a need to
8359
create images with the name `casioplot2.png` for example.
8460
"""
85-
_screen.save(
86-
settings["filename"] + image_suffix + '.' + settings["image_format"],
87-
format=settings["image_format"],
88-
)
8961

62+
_canvas.write(
63+
_settings["filename"] + image_suffix + '.' + _settings["image_format"],
64+
format=_settings["image_format"],
65+
)
9066

9167
# functions for the user
9268

93-
9469
def show_screen() -> None:
9570
"""Show or saves the virtual screen
9671
@@ -100,17 +75,14 @@ def show_screen() -> None:
10075
The image is saved with the filename found in `filename`
10176
"""
10277

103-
if settings["show_screen"] is True:
104-
global _photo_image, _screen_display, _window
78+
if _settings["show_screen"] is True:
10579
# show the screen
106-
_photo_image = ImageTk.PhotoImage(_screen)
107-
_screen_display["image"] = _photo_image
10880
_window.update()
10981

110-
if settings["save_screen"] is True:
111-
if settings["save_multiple"] is True:
82+
if _settings["save_screen"] is True:
83+
if _settings["save_multiple"] is True:
11284
global save_screen_counter, current_image_number
113-
if save_screen_counter == settings["save_rate"]:
85+
if save_screen_counter == _settings["save_rate"]:
11486
_save_screen(str(current_image_number))
11587
current_image_number += 1
11688
save_screen_counter = 0
@@ -123,41 +95,47 @@ def show_screen() -> None:
12395

12496
def clear_screen() -> None:
12597
"""Clear the virtual screen"""
126-
for x in range(settings["width"]):
127-
for y in range(settings["height"]):
128-
set_pixel(*_canvas_to_screen(x, y), _WHITE)
98+
_canvas.put(
99+
"white",
100+
to=(0, 0, _settings["width"], _settings["height"])
101+
)
129102

130103

131-
def get_pixel(x: int, y: int) -> COLOR | None:
104+
def get_pixel(x: int, y: int) -> Color | None:
132105
"""Get the RGB color of the pixel at the given position.
133106
134107
:param x: x coordinate (from the left)
135108
:param y: y coordinate (from the top)
136109
:return: The pixel color. A tuple that contain 3 integers from 0 to 255 or None if the pixel is out of the canvas.
137110
"""
138-
if _coordinates_in_bounds(x, y):
139-
return _screen.getpixel(_canvas_to_screen(x, y))
140-
else:
111+
try:
112+
return _canvas.get(x, y)
113+
except tk.TclError:
141114
return None
142115

143116

144-
def set_pixel(x: int, y: int, color: COLOR = _BLACK) -> None:
117+
def set_pixel(x: int, y: int, color: Color = _BLACK) -> None:
145118
"""Set the RGB color of the pixel at the given position (from top left)
146119
147120
:param x: x coordinate (from the left)
148121
:param y: y coordinate (from the top)
149122
:param color: The pixel color. A tuple that contain 3 integers from 0 to 255.
150123
"""
151-
global _screen
152-
if _coordinates_in_bounds(x, y):
153-
_screen.putpixel(_canvas_to_screen(x, y), color)
124+
try:
125+
_canvas.put(
126+
"#%02x%02x%02x" % color, # convert the color (RGB tuple) to a hexadecimal string '#RRGGBB'
127+
to=(x, y)
128+
)
129+
except tk.TclError:
130+
# the pixel is out of the canvas
131+
pass
154132

155133

156134
def draw_string(
157135
x: int,
158136
y: int,
159137
text: str,
160-
color: COLOR = _BLACK,
138+
color: Color = _BLACK,
161139
size: Literal["small", "medium", "large"] = "medium"
162140
) -> None:
163141
"""Draw a string on the virtual screen with the given RGB color and size.
@@ -186,97 +164,41 @@ def draw_char() -> None:
186164
x += len(char_map[0])
187165

188166

189-
# functinos used only by the package
190-
191-
192-
# stores checks for specific settings
193-
_settings_checks = {
194-
"width": lambda width: width > 0,
195-
"height": lambda height: height > 0,
196-
"left_margin": lambda left_margin: left_margin >= 0,
197-
"right_margin": lambda right_margin: right_margin >= 0,
198-
"top_margin": lambda top_margin: top_margin >= 0,
199-
"bottom_margin": lambda bottom_margin: bottom_margin >= 0,
200-
"image_format": lambda image_format: image_format in ("jpeg", "jpg", "png", "gif", "bmp", "tiff", "tif"),
201-
"save_rate": lambda save_rate: save_rate > 0
202-
}
203-
204-
# stores the error messages if a check of `_settings_cheks` fails
205-
_settings_errors = {
206-
"width": "be greater than zero",
207-
"height": "be greater than zero",
208-
"left_margin": "be greater or equal to zero",
209-
"right_margin": "be greater or equal to zero",
210-
"top_margin": "be greater or equal to zero",
211-
"bottom_margin": "be greater or equal to zero",
212-
"image_format": "be on of the following values, jpeg, jpg, png, gif, bmp, tiff or tif",
213-
"save_rate": "be greater than zero"
214-
}
215-
216-
217-
def _check_settings() -> None:
218-
"""Checks if all settings have a value, have the correct type of data and have a proper value"""
219-
for setting, correct_type in configuration.__annotations__.items():
220-
value = settings[setting]
221-
# does it exist?
222-
if setting not in settings:
223-
raise ValueError(f"The setting {setting} must have a value attributed")
224-
# does it have the correct type?
225-
if not isinstance(value, correct_type):
226-
raise ValueError(f"The setting {setting} must be of type {correct_type} \
227-
but the value given is of the type {type(value)}")
228-
# does it have a proper value?
229-
if setting in _settings_checks and not _settings_checks[setting](value):
230-
raise ValueError(f"The settings {setting} must {_settings_errors[setting]}")
231-
232-
# some additional checks in case there is a background image
233-
if settings["bg_image_is_set"] is True:
234-
bg_width, bg_height = settings["background_image"].size
235-
236-
if settings["left_margin"] + settings["right_margin"] >= bg_width:
237-
raise ValueError("Invalid settings, the combained values of \
238-
left_margin and right_margin must be smaller than the \
239-
width of the background image")
240-
if settings["top_margin"] + settings["bottom_margin"] >= bg_height:
241-
raise ValueError("Invalid settings, the combained values of \
242-
top_margin and bottom_margin must be smaller than the \
243-
height of the background image")
244-
245-
246-
def _setup_screen() -> None:
247-
"""Calculates some screen attributesn and redraw the screen if necessary"""
248-
if settings["bg_image_is_set"] is True:
249-
bg_width, bg_height = settings["background_image"].size
250-
251-
settings["width"] = bg_width - (settings["left_margin"] + settings["right_margin"])
252-
settings["height"] = bg_height - (settings["top_margin"] + settings["bottom_margin"])
253-
254-
bg_image_path = _get_image_path(settings["background_image"])
255-
global _screen
256-
_screen = Image.open(bg_image_path)
257-
258-
else:
259-
global _screen, _window
260-
261-
# Create a new white image
262-
_screen = Image.new("RGB", (screen_width, screen_height), _WHITE)
263-
# updates the window dimensions
264-
_window.geometry(f"{screen_width}x{screen_height}")
265-
266-
267-
def _setup_window() -> None:
268-
"""Configures properly _window"""
269-
# hides the screen in case it isn't needed
270-
if settings["show_screen"] is False:
271-
_window.withdraw()
272-
273-
# makes _window the same size as _screen, so it fits
274-
screen_width, screen_height = _screen_dimensions()
275-
_window.geometry(f"{screen_width}x{screen_height}")
276-
277-
278-
settings: configuration = _get_settings()
279-
280-
_check_settings() # avoids runing the package with wrong settings
281-
_setup_screen()
282-
_setup_window()
167+
# functions used only by the package
168+
169+
def _screen_dimensions() -> tuple[int, int]:
170+
"""Calculates the dimensions of the screen"""
171+
return (
172+
_settings["left_margin"] + _settings["width"] + _settings["right_margin"],
173+
_settings["top_margin"] + _settings["height"] + _settings["bottom_margin"]
174+
)
175+
176+
177+
# window
178+
179+
_window = tk.Tk()
180+
if _settings["show_screen"] is True:
181+
_window.geometry("{}x{}".format(*_screen_dimensions()))
182+
183+
_window.grab_release()
184+
_window.title("casioplot")
185+
_window.attributes("-topmost", True)
186+
_window.resizable(False, False)
187+
else:
188+
_window.withdraw()
189+
190+
# screen
191+
192+
_canvas = tk.PhotoImage(width=_settings["width"], height=_settings["height"])
193+
194+
if _settings["bg_image_is_set"] is True:
195+
_background = tk.PhotoImage(file=_settings["background_image"])
196+
else:
197+
_background = _canvas.copy()
198+
199+
_background_display = tk.Label(master=_window, image=_background, border=0)
200+
_background_display.place(x=0, y=0)
201+
_canvas_display = tk.Label(master=_window, image=_canvas, border=0)
202+
_canvas_display.place(x=_settings["left_margin"], y=_settings["top_margin"])
203+
204+
clear_screen() # ensures the pixels are set to white and not transparent

casioplot/characters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
:py:func:`_get_char` gets the correct character from the dictionarys,
88
serves as an interface for casioplot.py to get the characters it needs.
99
"""
10+
# TODO: change how characters are drawn to be more efficient and faster
1011

1112

1213
large = {

0 commit comments

Comments
 (0)