@@ -115,15 +115,190 @@ def to_string(self):
115115
116116colortable_float = [[i [0 ] / 255.0 , i [1 ] / 255.0 , i [2 ] / 255.0 ] for i in colortable ]
117117
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+
118289from sys import argv
119290if __name__ == "__main__" :
120291 if len (argv ) > 1 :
121292 if argv [1 ] == "debug" :
122293 send_command (9 , view , debug = True )
123294
124- if argv [1 ] == "png" :
295+ elif argv [1 ] == "png" :
125296 tstart = time .perf_counter ()
126297
298+ if len (argv ) == 4 :
299+ view .width = int (argv [2 ])
300+ view .height = int (argv [3 ])
301+
127302 usb_reader = lambda : send_command (9 , view , debug = False )
128303 usb_thread = threading .Thread (target = usb_reader , daemon = True )
129304 usb_thread .start ()
@@ -168,172 +343,8 @@ def unpacker():
168343 print (f"saving image took: { img_save - pix_conv :0.4f} seconds" )
169344 openImage (outfilename )
170345
346+ elif argv [1 ] == "orbits" :
347+ gtk_gui (orbits = True )
348+
171349 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