Skip to content

Commit 0cdd963

Browse files
committed
mandelbrot-app: factor out gtk gui into own method
add size command line args to png
1 parent 6c1bc34 commit 0cdd963

File tree

1 file changed

+180
-169
lines changed

1 file changed

+180
-169
lines changed

software/mandelbrot-app.py

Lines changed: 180 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,190 @@ def to_string(self):
115115

116116
colortable_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+
118289
from sys import argv
119290
if __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

Comments
 (0)