6
6
- :py:func:`set_pixel`
7
7
- :py:func:`get_pixel`
8
8
- :py:func:`draw_string`
9
+
10
+ Contains the original functions from the `casioplot` calculator module and the code needed to emulate the screen.
9
11
"""
10
12
11
13
import tkinter as tk
12
14
from typing import Literal
13
- from PIL import Image , ImageTk
14
15
15
16
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
18
19
19
- # color type
20
- COLOR = tuple [int , int , int ]
21
20
# 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
38
23
39
24
# these two are only used if the setting save_multiple is set to True
40
25
save_screen_counter = 0
41
26
current_image_number = 1
42
27
43
28
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
53
30
31
+ def _canvas_to_screen (x : int , y : int , start_x : int , start_y : int ) -> tuple [int , int ]:
32
+ """Converts coordinates to canvas coordinates
54
33
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
56
41
57
42
43
+ # TODO: Takes too much time so needs improvements
58
44
def _coordinates_in_bounds (x : int , y : int ) -> bool :
59
45
"""Checks if the given coordinates are in bounds of the canvas
60
46
61
47
:param x: x coordinate (from the left to the right)
62
48
:param y: y coordinate (from the top to the bottom)
63
49
:return: a bool that says if the given coordinates are in bounds of the canvas
64
50
"""
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" ]
76
52
77
53
78
54
def _save_screen (image_suffix : str = "" ):
@@ -82,15 +58,14 @@ def _save_screen(image_suffix: str = ""):
82
58
:param image_suffix: If the setting save_multiple is True existes a need to
83
59
create images with the name `casioplot2.png` for example.
84
60
"""
85
- _screen .save (
86
- settings ["filename" ] + image_suffix + '.' + settings ["image_format" ],
87
- format = settings ["image_format" ],
88
- )
89
61
62
+ _canvas .write (
63
+ _settings ["filename" ] + image_suffix + '.' + _settings ["image_format" ],
64
+ format = _settings ["image_format" ],
65
+ )
90
66
91
67
# functions for the user
92
68
93
-
94
69
def show_screen () -> None :
95
70
"""Show or saves the virtual screen
96
71
@@ -100,17 +75,14 @@ def show_screen() -> None:
100
75
The image is saved with the filename found in `filename`
101
76
"""
102
77
103
- if settings ["show_screen" ] is True :
104
- global _photo_image , _screen_display , _window
78
+ if _settings ["show_screen" ] is True :
105
79
# show the screen
106
- _photo_image = ImageTk .PhotoImage (_screen )
107
- _screen_display ["image" ] = _photo_image
108
80
_window .update ()
109
81
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 :
112
84
global save_screen_counter , current_image_number
113
- if save_screen_counter == settings ["save_rate" ]:
85
+ if save_screen_counter == _settings ["save_rate" ]:
114
86
_save_screen (str (current_image_number ))
115
87
current_image_number += 1
116
88
save_screen_counter = 0
@@ -123,41 +95,47 @@ def show_screen() -> None:
123
95
124
96
def clear_screen () -> None :
125
97
"""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
+ )
129
102
130
103
131
- def get_pixel (x : int , y : int ) -> COLOR | None :
104
+ def get_pixel (x : int , y : int ) -> Color | None :
132
105
"""Get the RGB color of the pixel at the given position.
133
106
134
107
:param x: x coordinate (from the left)
135
108
:param y: y coordinate (from the top)
136
109
:return: The pixel color. A tuple that contain 3 integers from 0 to 255 or None if the pixel is out of the canvas.
137
110
"""
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 :
141
114
return None
142
115
143
116
144
- def set_pixel (x : int , y : int , color : COLOR = _BLACK ) -> None :
117
+ def set_pixel (x : int , y : int , color : Color = _BLACK ) -> None :
145
118
"""Set the RGB color of the pixel at the given position (from top left)
146
119
147
120
:param x: x coordinate (from the left)
148
121
:param y: y coordinate (from the top)
149
122
:param color: The pixel color. A tuple that contain 3 integers from 0 to 255.
150
123
"""
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
154
132
155
133
156
134
def draw_string (
157
135
x : int ,
158
136
y : int ,
159
137
text : str ,
160
- color : COLOR = _BLACK ,
138
+ color : Color = _BLACK ,
161
139
size : Literal ["small" , "medium" , "large" ] = "medium"
162
140
) -> None :
163
141
"""Draw a string on the virtual screen with the given RGB color and size.
@@ -186,97 +164,41 @@ def draw_char() -> None:
186
164
x += len (char_map [0 ])
187
165
188
166
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
0 commit comments