@@ -54,21 +54,26 @@ class LightState(TypedDict):
54
54
55
55
56
56
async def update_bulb (bulb , brightness = None , hue = None , saturation = None ):
57
- if brightness != None :
58
- await bulb .set_brightness (brightness )
59
- if hue != None or saturation != None :
60
- state : LightState = await bulb .get_light_state ()
61
- await bulb .set_hsv (
62
- hue if hue != None else state ["hue" ],
63
- saturation if saturation != None else state ["saturation" ],
64
- brightness if brightness != None else state ["brightness" ],
65
- )
57
+ # note: all arguments need to ints
58
+ assert brightness is None or isinstance (brightness , int )
59
+ assert hue is None or isinstance (hue , int )
60
+ assert saturation is None or isinstance (saturation , int )
61
+
62
+ state : LightState = await bulb .get_light_state ()
63
+ await bulb .set_hsv (
64
+ hue if hue != None else state ["hue" ],
65
+ saturation if saturation != None else state ["saturation" ],
66
+ brightness if brightness != None else state ["brightness" ],
67
+ )
66
68
67
69
68
70
class ScrollableFrame (tkinter .ttk .Frame ):
69
71
def __init__ (self , container , * args , ** kwargs ):
70
72
super ().__init__ (container , * args , ** kwargs )
71
- self .canvas = tkinter .Canvas (self )
73
+ background = tkinter .ttk .Style ().lookup ("TFrame" , "background" )
74
+ self .canvas = tkinter .Canvas (
75
+ self , background = background , bd = 0 , highlightthickness = 0
76
+ )
72
77
self .scrollable_frame = tkinter .ttk .Frame (self .canvas )
73
78
self .scrollable_frame .bind (
74
79
"<Configure>" ,
@@ -126,7 +131,7 @@ def _on_mouse_scroll(self, event):
126
131
self .canvas .yview_scroll (int (- 1 * (event .delta / 120 )), "units" )
127
132
128
133
129
- class EditableText (tkinter .Frame ):
134
+ class EditableText (tkinter .ttk . Frame ):
130
135
def __init__ (self , container , initial_text_value , callback ):
131
136
super (self .__class__ , self ).__init__ (container )
132
137
@@ -149,22 +154,27 @@ def _edit_mode_finish(self, event=None):
149
154
150
155
def _render_static_mode (self ):
151
156
"""render non-editable text"""
152
- label_font = tkinter .font .Font (
153
- family = "Helvetica" , name = "appHighlightFont" , size = 12 , weight = "bold"
157
+ big_label_style_name = "my.TLabel"
158
+ style = tkinter .ttk .Style ()
159
+ default_font_name = style .lookup ("TLabel" , "font" )
160
+ default_font = tkinter .font .nametofont (default_font_name )
161
+ custom_font = {** default_font .actual (), "size" : 12 }
162
+ style .configure (big_label_style_name , font = custom_font )
163
+
164
+ text_label = tkinter .ttk .Label (
165
+ self , text = self .text .get (), style = big_label_style_name
154
166
)
155
-
156
- text_label = tkinter .Label (self , text = self .text .get (), font = label_font )
157
167
text_label .pack (side = tkinter .LEFT )
158
168
159
- edit_label = tkinter .Label (self , image = self .pencil_icon )
169
+ edit_label = tkinter .ttk . Label (self , image = self .pencil_icon )
160
170
edit_label .bind ("<ButtonRelease-1>" , self ._edit_mode_start )
161
171
edit_label .pack (side = tkinter .LEFT )
162
172
163
173
self .child_widgets .append (text_label )
164
174
self .child_widgets .append (edit_label )
165
175
166
176
def _render_edit_mode (self ):
167
- text_entry = tkinter .Entry (self , textvariable = self .text )
177
+ text_entry = tkinter .ttk . Entry (self , textvariable = self .text )
168
178
text_entry .pack (side = tkinter .LEFT )
169
179
text_entry .bind ("<Return>" , self ._edit_mode_finish )
170
180
@@ -177,33 +187,46 @@ def pencil_icon(self):
177
187
tkinter.Label is not sufficient.
178
188
(https://stackoverflow.com/a/31959529/2796349)
179
189
"""
190
+ style = tkinter .ttk .Style ()
191
+ foreground = style .lookup ("TFrame" , "foreground" )
192
+ background = style .lookup ("TFrame" , "background" )
193
+
180
194
if not hasattr (self , "_pencil_icon" ):
181
195
self ._pencil_icon = tkinter .BitmapImage (
182
- data = b"#define image_width 16\n #define image_height 16\n static char image_bits[] = {\n 0x00,0x1c,0x00,0x3e,0x00,0x7f,0x80,0xf7,0xc0,0xf3,0xe0,0x79,0xf0,0x3c,0x78,\n 0x1e,0x3c,0x0f,0x9c,0x07,0xcc,0x03,0xfc,0x01,0xfc,0x00,0x7c,0x00,0xff,0xff,\n 0xff,0xff\n };"
196
+ data = b"#define image_width 16\n #define image_height 16\n static char image_bits[] = {\n 0x00,0x1c,0x00,0x3e,0x00,0x7f,0x80,0xf7,0xc0,0xf3,0xe0,0x79,0xf0,0x3c,0x78,\n 0x1e,0x3c,0x0f,0x9c,0x07,0xcc,0x03,0xfc,0x01,0xfc,0x00,0x7c,0x00,0xff,0xff,\n 0xff,0xff\n };" ,
197
+ background = background ,
198
+ foreground = foreground ,
183
199
)
184
200
return self ._pencil_icon
185
201
186
202
187
- class BulbFrame (tkinter .Frame ):
203
+ class BulbFrame (tkinter .ttk . Frame ):
188
204
async def _hue_callback (self ):
189
- return await update_bulb (self .bulb , None , self .hue_slider .get ())
205
+ return await update_bulb (self .bulb , None , int ( self .hue_slider .get () ))
190
206
191
207
async def _saturation_callback (self ):
192
- return await update_bulb (self .bulb , saturation = self .saturation_slider .get ())
208
+ return await update_bulb (
209
+ self .bulb , int (saturation = self .saturation_slider .get ())
210
+ )
193
211
194
212
async def _brightness_callback (self ):
195
- return await update_bulb (self .bulb , self .brightness_slider .get ())
213
+ logger .info ("Brightness callback: {}" .format (self .brightness_slider .get ()))
214
+ return await update_bulb (self .bulb , int (self .brightness_slider .get ()))
196
215
197
216
async def _power_callback (self ):
198
- self .power_button [ "state" ] = tkinter . DISABLED
217
+ self .power_button . state ([ "disabled" ])
199
218
200
219
try :
201
220
await (self .bulb .turn_off () if self .bulb .is_on else self .bulb .turn_on ())
202
221
await self .bulb .update ()
203
222
finally :
204
- self .power_button ["relief" ] = "sunken" if self .bulb .is_on else "raised"
223
+ self .power_button .state (
224
+ [
225
+ "!disabled" ,
226
+ "pressed" if self .bulb .is_on else "!pressed" ,
227
+ ]
228
+ )
205
229
self .power_button ["text" ] = "Turn Off" if self .bulb .is_on else "Turn On"
206
- self .power_button ["state" ] = tkinter .NORMAL
207
230
208
231
@classmethod
209
232
def for_bulb (cls , loop , bulb : kasa .SmartBulb , config , * args , ** kwargs ):
@@ -215,11 +238,11 @@ def for_bulb(cls, loop, bulb: kasa.SmartBulb, config, *args, **kwargs):
215
238
self = cls (* args , ** kwargs )
216
239
self .bulb = bulb
217
240
218
- self .hue_label = tkinter .Label (self , text = "hue" )
219
- self .saturation_label = tkinter .Label (self , text = "saturation" )
220
- self .brightness_label = tkinter .Label (self , text = "brightness" )
241
+ self .hue_label = tkinter .ttk . Label (self , text = "hue" )
242
+ self .saturation_label = tkinter .ttk . Label (self , text = "saturation" )
243
+ self .brightness_label = tkinter .ttk . Label (self , text = "brightness" )
221
244
222
- self .hue_slider = tkinter .Scale (
245
+ self .hue_slider = tkinter .ttk . Scale (
223
246
self , from_ = 0 , to = 360 , orient = tkinter .HORIZONTAL
224
247
)
225
248
self .hue_slider .bind (
@@ -230,7 +253,7 @@ def for_bulb(cls, loop, bulb: kasa.SmartBulb, config, *args, **kwargs):
230
253
)
231
254
self .hue_slider .set (self .bulb .hsv [0 ])
232
255
233
- self .saturation_slider = tkinter .Scale (
256
+ self .saturation_slider = tkinter .ttk . Scale (
234
257
self , from_ = 0 , to = 100 , orient = tkinter .HORIZONTAL
235
258
)
236
259
self .saturation_slider .bind (
@@ -241,7 +264,7 @@ def for_bulb(cls, loop, bulb: kasa.SmartBulb, config, *args, **kwargs):
241
264
)
242
265
self .saturation_slider .set (self .bulb .hsv [1 ])
243
266
244
- self .brightness_slider = tkinter .Scale (
267
+ self .brightness_slider = tkinter .ttk . Scale (
245
268
self , from_ = 0 , to = 100 , orient = tkinter .HORIZONTAL
246
269
)
247
270
self .brightness_slider .bind (
@@ -260,12 +283,13 @@ def for_bulb(cls, loop, bulb: kasa.SmartBulb, config, *args, **kwargs):
260
283
self , bulb_name , lambda new_device_name : logger .info (new_device_name )
261
284
).grid (column = 0 , row = 0 , columnspan = 4 )
262
285
263
- self .power_button = tkinter .Button (
286
+ self .power_button = tkinter .ttk . Button (
264
287
self ,
265
288
text = "Turn Off" if self .bulb .is_on else "Turn On" ,
266
- width = 12 ,
267
- relief = "sunken" if self .bulb .is_on else "raised" ,
289
+ # relief="sunken" if self.bulb.is_on else "raised" ,
290
+ # pressed= self.bulb.is_on
268
291
)
292
+ self .power_button .state (["pressed" if self .bulb .is_on else "!pressed" ])
269
293
self .power_button .bind (
270
294
"<ButtonRelease-1>" ,
271
295
lambda event , self = self , loop = loop : asyncio .run_coroutine_threadsafe (
@@ -328,7 +352,7 @@ async def process_devices():
328
352
329
353
# TODO make sure that self.device_queue exists before exiting this function
330
354
331
- self .refresh_button = tkinter .Button (
355
+ self .refresh_button = tkinter .ttk . Button (
332
356
self , text = "Refresh" , command = self .start_refresh
333
357
)
334
358
self .refresh_button .pack (fill = tkinter .X )
@@ -359,11 +383,11 @@ async def add_device(self, device):
359
383
360
384
async def _do_refresh (self ):
361
385
logger .info ("KasaDevices._do_refresh() called" )
362
- self .refresh_button [ "state" ] = tkinter . DISABLED
386
+ self .refresh_button . state ([ "disabled" ])
363
387
self .refresh_button ["text" ] = "Refreshing..."
364
388
await self .clear_devices ()
365
389
await kasa .Discover .discover (on_discovered = self .device_queue .put )
366
- self .refresh_button [ "state" ] = tkinter . NORMAL
390
+ self .refresh_button . state ([ "!disabled" ])
367
391
self .refresh_button ["text" ] = "Refresh"
368
392
369
393
async def clear_devices (self ):
0 commit comments