@@ -115,15 +115,190 @@ def to_string(self):
115
115
116
116
colortable_float = [[i [0 ] / 255.0 , i [1 ] / 255.0 , i [2 ] / 255.0 ] for i in colortable ]
117
117
118
+ def gtk_gui (orbits = False ):
119
+ import gi
120
+ gi .require_version ("Gtk" , "3.0" )
121
+ from gi .repository import GLib , Gtk , Gdk
122
+ from gi .repository .Gtk import DrawingArea
123
+ from gi .repository .GdkPixbuf import Pixbuf , Colorspace
124
+ import cairo
125
+
126
+ class GuiHandler :
127
+ pixbuf = None
128
+ builder = None
129
+ canvas = None
130
+
131
+ width = 0
132
+ weight = 0
133
+
134
+ drawing = False
135
+ view = default_view
136
+
137
+ def __init__ (self , builder ) -> None :
138
+ self .builder = builder
139
+ self .pixels = None
140
+ centerx_widget = builder .get_object ("center_x" )
141
+ centery_widget = builder .get_object ("center_y" )
142
+ radius_widget = builder .get_object ("radius" )
143
+ iterations_widget = builder .get_object ("iterations" )
144
+ zero_y = builder .get_object ("zero_y" )
145
+
146
+ center_x , center_y = view .get_center ()
147
+ radius = view .get_radius ()
148
+ iterations = view .max_iterations
149
+
150
+ centerx_widget .set_text (str (center_x ))
151
+ centery_widget .set_text (str (center_y ))
152
+ radius_widget .set_text (str (radius ))
153
+ iterations_widget .set_text (str (iterations ))
154
+
155
+ self .updateImageBufferIfNeeded ()
156
+
157
+ def painter (self ):
158
+ drawing_start = time .perf_counter ()
159
+ try :
160
+ channels = 3
161
+ rowstride = self .width * channels
162
+ pixel_count = 0
163
+ while True :
164
+ # get() will exit this thread if the
165
+ # queue is empty
166
+ pixel = pixel_queue .get ()
167
+ pixel_count += 1
168
+
169
+ x = pixel [0 ]
170
+ y = self .view .height - pixel [1 ]
171
+
172
+ red , green , blue = colortable [pixel [2 ] & 0xf ]
173
+ maxed = pixel [2 ] >> 7
174
+
175
+ pixel_index = y * rowstride + x * channels
176
+
177
+ if maxed :
178
+ red = green = blue = 0
179
+
180
+ if pixel_index + 2 < len (self .pixels ):
181
+ self .pixels [pixel_index ] = red
182
+ self .pixels [pixel_index + 1 ] = green
183
+ self .pixels [pixel_index + 2 ] = blue
184
+
185
+ if pixel_count % (2 * self .width ) == 0 :
186
+ GLib .idle_add (self .canvas .queue_draw )
187
+
188
+ pixel_queue .task_done ()
189
+
190
+ finally :
191
+ now = time .perf_counter ()
192
+ print (f"drawing took: { now - drawing_start :0.4f} seconds" )
193
+
194
+ def onDestroy (self , * args ):
195
+ Gtk .main_quit ()
196
+
197
+ def updateImageBufferIfNeeded (self ):
198
+ self .canvas = canvas = builder .get_object ("canvas" )
199
+ width = canvas .get_allocated_size ().allocation .width
200
+ height = canvas .get_allocated_size ().allocation .height
201
+ if (self .pixbuf is None or self .width != width or self .height != height ) and width > 0 and height > 0 :
202
+ print (f"w { width } h { height } " )
203
+ self .pixels = bytearray ((height + 1 ) * 3 * width )
204
+ self .width , self .height = width , height
205
+
206
+ def getViewParameterWidgets (self ):
207
+ center_x = builder .get_object ("center_x" )
208
+ center_y = builder .get_object ("center_y" )
209
+ radius = builder .get_object ("radius" )
210
+ return [center_x , center_y , radius ]
211
+
212
+ def getViewParameters (self ):
213
+ getValue = lambda w : float (w .get_text ())
214
+ return map (getValue , self .getViewParameterWidgets ())
215
+
216
+ def onUpdateButtonPress (self , button ):
217
+ self .updateImageBufferIfNeeded ()
218
+
219
+ center_x , center_y , radius = self .getViewParameters ()
220
+ iterations = int (builder .get_object ("iterations" ).get_text ())
221
+
222
+ self .view .update (center_x = center_x , center_y = center_y , radius = radius , width = self .width , height = self .height , max_iterations = iterations )
223
+ print (self .view .to_string ())
224
+
225
+ # clear out image
226
+ for i in range (len (self .pixels )):
227
+ self .pixels [i ] = 0
228
+
229
+ view = self .view
230
+ view .width = self .width
231
+ view .height = self .height
232
+ usb_reader = lambda : send_command (9 , view , view .max_iterations , debug = False )
233
+ usb_thread = threading .Thread (target = usb_reader , daemon = True )
234
+ usb_thread .start ()
235
+
236
+ painter_thread = threading .Thread (target = lambda : self .painter (), daemon = True )
237
+ painter_thread .start ()
238
+
239
+ def onCanvasButtonPress (self , canvas , event ):
240
+ step = fix2float (self .view .step )
241
+ x = fix2float (self .view .corner_x ) + (event .x * step )
242
+ y = fix2float (self .view .corner_y ) + ((self .view .height - event .y ) * step )
243
+ center_x , center_y , _ = self .getViewParameterWidgets ()
244
+ center_x .set_text (str (x ))
245
+ center_y .set_text (str (y ))
246
+
247
+ crosshairs = None
248
+
249
+ def onCanvasMotion (self , canvas , event ):
250
+ step = fix2float (self .view .step )
251
+ x = fix2float (self .view .corner_x ) + (event .x * step )
252
+ y = fix2float (self .view .corner_y ) + ((self .view .height - event .y ) * step )
253
+ self .crosshairs = [(event .x , event .y ), (x ,y )]
254
+ canvas .queue_draw ()
255
+
256
+ def onDraw (self , canvas : DrawingArea , cr : cairo .Context ):
257
+ if not self .pixels is None :
258
+ pixbuf = Pixbuf .new_from_data (bytes (self .pixels ), Colorspace .RGB , False , 8 , self .width , self .height + 1 , self .width * 3 )
259
+ Gdk .cairo_set_source_pixbuf (cr , pixbuf , 0 , 0 )
260
+ cr .paint ()
261
+ if not self .crosshairs is None :
262
+ x , y = self .crosshairs [0 ]
263
+ cr .set_source_rgb (1 , 1 , 1 )
264
+ cr .set_line_width (1 )
265
+ cr .move_to (x , 0 )
266
+ cr .line_to (x , self .view .height )
267
+ cr .move_to (0 , y )
268
+ cr .line_to (self .view .width , y )
269
+ cr .stroke ()
270
+
271
+ cr .set_font_size (20 )
272
+ cr .move_to (20 , 20 )
273
+ cr .show_text (f"x: { str (self .crosshairs [1 ][0 ])} " )
274
+ cr .move_to (20 , 40 )
275
+ cr .show_text (f"y: { str (self .crosshairs [1 ][1 ])} " )
276
+
277
+
278
+ builder = Gtk .Builder ()
279
+ builder .add_from_file ("mandelbrot-client-gui.ui" )
280
+ handler = GuiHandler (builder )
281
+ builder .connect_signals (handler )
282
+
283
+ window = builder .get_object ("window" )
284
+ window .connect ("destroy" , Gtk .main_quit )
285
+ window .show_all ()
286
+
287
+ Gtk .main ()
288
+
118
289
from sys import argv
119
290
if __name__ == "__main__" :
120
291
if len (argv ) > 1 :
121
292
if argv [1 ] == "debug" :
122
293
send_command (9 , view , debug = True )
123
294
124
- if argv [1 ] == "png" :
295
+ elif argv [1 ] == "png" :
125
296
tstart = time .perf_counter ()
126
297
298
+ if len (argv ) == 4 :
299
+ view .width = int (argv [2 ])
300
+ view .height = int (argv [3 ])
301
+
127
302
usb_reader = lambda : send_command (9 , view , debug = False )
128
303
usb_thread = threading .Thread (target = usb_reader , daemon = True )
129
304
usb_thread .start ()
@@ -168,172 +343,8 @@ def unpacker():
168
343
print (f"saving image took: { img_save - pix_conv :0.4f} seconds" )
169
344
openImage (outfilename )
170
345
346
+ elif argv [1 ] == "orbits" :
347
+ gtk_gui (orbits = True )
348
+
171
349
else :
172
- import gi
173
- gi .require_version ("Gtk" , "3.0" )
174
- from gi .repository import GLib , Gtk , Gdk
175
- from gi .repository .Gtk import DrawingArea
176
- from gi .repository .GdkPixbuf import Pixbuf , Colorspace
177
- import cairo
178
-
179
- class GuiHandler :
180
- pixbuf = None
181
- builder = None
182
- canvas = None
183
-
184
- width = 0
185
- weight = 0
186
-
187
- drawing = False
188
- view = default_view
189
-
190
- def __init__ (self , builder ) -> None :
191
- self .builder = builder
192
- self .pixels = None
193
- centerx_widget = builder .get_object ("center_x" )
194
- centery_widget = builder .get_object ("center_y" )
195
- radius_widget = builder .get_object ("radius" )
196
- iterations_widget = builder .get_object ("iterations" )
197
-
198
- center_x , center_y = view .get_center ()
199
- radius = view .get_radius ()
200
- iterations = view .max_iterations
201
-
202
- centerx_widget .set_text (str (center_x ))
203
- centery_widget .set_text (str (center_y ))
204
- radius_widget .set_text (str (radius ))
205
- iterations_widget .set_text (str (iterations ))
206
-
207
- self .updateImageBufferIfNeeded ()
208
-
209
- def painter (self ):
210
- drawing_start = time .perf_counter ()
211
- try :
212
- channels = 3
213
- rowstride = self .width * channels
214
- pixel_count = 0
215
- while True :
216
- # get() will exit this thread if the
217
- # queue is empty
218
- pixel = pixel_queue .get ()
219
- pixel_count += 1
220
-
221
- x = pixel [0 ]
222
- y = self .view .height - pixel [1 ]
223
-
224
- red , green , blue = colortable [pixel [2 ] & 0xf ]
225
- maxed = pixel [2 ] >> 7
226
-
227
- pixel_index = y * rowstride + x * channels
228
-
229
- if maxed :
230
- red = green = blue = 0
231
-
232
- if pixel_index + 2 < len (self .pixels ):
233
- self .pixels [pixel_index ] = red
234
- self .pixels [pixel_index + 1 ] = green
235
- self .pixels [pixel_index + 2 ] = blue
236
-
237
- if pixel_count % (2 * self .width ) == 0 :
238
- GLib .idle_add (self .canvas .queue_draw )
239
-
240
- pixel_queue .task_done ()
241
-
242
- finally :
243
- now = time .perf_counter ()
244
- print (f"drawing took: { now - drawing_start :0.4f} seconds" )
245
-
246
- def onDestroy (self , * args ):
247
- Gtk .main_quit ()
248
-
249
- def updateImageBufferIfNeeded (self ):
250
- self .canvas = canvas = builder .get_object ("canvas" )
251
- width = canvas .get_allocated_size ().allocation .width
252
- height = canvas .get_allocated_size ().allocation .height
253
- if (self .pixbuf is None or self .width != width or self .height != height ) and width > 0 and height > 0 :
254
- print (f"w { width } h { height } " )
255
- self .pixels = bytearray ((height + 1 ) * 3 * width )
256
- self .width , self .height = width , height
257
-
258
- def getViewParameterWidgets (self ):
259
- center_x = builder .get_object ("center_x" )
260
- center_y = builder .get_object ("center_y" )
261
- radius = builder .get_object ("radius" )
262
- return [center_x , center_y , radius ]
263
-
264
- def getViewParameters (self ):
265
- getValue = lambda w : float (w .get_text ())
266
- return map (getValue , self .getViewParameterWidgets ())
267
-
268
- def onUpdateButtonPress (self , button ):
269
- self .updateImageBufferIfNeeded ()
270
-
271
- center_x , center_y , radius = self .getViewParameters ()
272
- iterations = int (builder .get_object ("iterations" ).get_text ())
273
-
274
- self .view .update (center_x = center_x , center_y = center_y , radius = radius , width = self .width , height = self .height , max_iterations = iterations )
275
- print (self .view .to_string ())
276
-
277
- # clear out image
278
- for i in range (len (self .pixels )):
279
- self .pixels [i ] = 0
280
-
281
- view = self .view
282
- view .width = self .width
283
- view .height = self .height
284
- usb_reader = lambda : send_command (9 , view , view .max_iterations , debug = False )
285
- usb_thread = threading .Thread (target = usb_reader , daemon = True )
286
- usb_thread .start ()
287
-
288
- painter_thread = threading .Thread (target = lambda : self .painter (), daemon = True )
289
- painter_thread .start ()
290
-
291
- def onCanvasButtonPress (self , canvas , event ):
292
- step = fix2float (self .view .step )
293
- x = fix2float (self .view .corner_x ) + (event .x * step )
294
- y = fix2float (self .view .corner_y ) + ((self .view .height - event .y ) * step )
295
- center_x , center_y , _ = self .getViewParameterWidgets ()
296
- center_x .set_text (str (x ))
297
- center_y .set_text (str (y ))
298
-
299
- crosshairs = None
300
-
301
- def onCanvasMotion (self , canvas , event ):
302
- step = fix2float (self .view .step )
303
- x = fix2float (self .view .corner_x ) + (event .x * step )
304
- y = fix2float (self .view .corner_y ) + ((self .view .height - event .y ) * step )
305
- self .crosshairs = [(event .x , event .y ), (x ,y )]
306
- canvas .queue_draw ()
307
-
308
- def onDraw (self , canvas : DrawingArea , cr : cairo .Context ):
309
- if not self .pixels is None :
310
- pixbuf = Pixbuf .new_from_data (bytes (self .pixels ), Colorspace .RGB , False , 8 , self .width , self .height + 1 , self .width * 3 )
311
- Gdk .cairo_set_source_pixbuf (cr , pixbuf , 0 , 0 )
312
- cr .paint ()
313
- if not self .crosshairs is None :
314
- x , y = self .crosshairs [0 ]
315
- cr .set_source_rgb (1 , 1 , 1 )
316
- cr .set_line_width (1 )
317
- cr .move_to (x , 0 )
318
- cr .line_to (x , self .view .height )
319
- cr .move_to (0 , y )
320
- cr .line_to (self .view .width , y )
321
- cr .stroke ()
322
-
323
- cr .set_font_size (20 )
324
- cr .move_to (20 , 20 )
325
- cr .show_text (f"x: { str (self .crosshairs [1 ][0 ])} " )
326
- cr .move_to (20 , 40 )
327
- cr .show_text (f"y: { str (self .crosshairs [1 ][1 ])} " )
328
-
329
-
330
- builder = Gtk .Builder ()
331
- builder .add_from_file ("mandelbrot-client-gui.ui" )
332
- handler = GuiHandler (builder )
333
- builder .connect_signals (handler )
334
-
335
- window = builder .get_object ("window" )
336
- window .connect ("destroy" , Gtk .main_quit )
337
- window .show_all ()
338
-
339
- Gtk .main ()
350
+ gtk_gui ()
0 commit comments