# Mandelbrot with Tkinter and ShedSkin

Here is Mandelbrot set viewer in Tkinter and modules for shedskining (included compiled modules for windows in the zip file):

``````# mandelsh.py

def mandel(real, imag, max_iterations=20):
'''determines if a point is in the Mandelbrot set based on deciding if,
after a maximum allowed number of iterations, the absolute value of
the resulting number is greater or equal to 2.'''
z_real, z_imag = 0.0, 0.0
for i in range(0, max_iterations):
z_real, z_imag = ( z_real*z_real - z_imag*z_imag + real,
2*z_real*z_imag + imag )
if (z_real*z_real + z_imag*z_imag) >= 4:
return i % max_iterations
return -1

res = 0
res = mandel(1.0, 1.0, 128)``````

my_mandel.py

``````#!/usr/bin/python
from __future__ import print_function
import sys
import time

from py_kohn_bmp import kohn_bmp

from mandelsh import mandel
colors = [[0, 100, 100], [127, 0, 0], [127, 127, 0], [0, 127, 0], [0, 255, 0],
[0, 255, 0], [0, 255, 127], [0, 127, 127], [255, 0, 0],
[0, 127, 255], [0, 0, 255], [127, 0, 255],
[127, 0, 255], [255, 0, 255], [255, 0, 127],
[127, 127, 0], [0, 0, 0]]

# Changing the values below will change the resulting image
def mandel_file(cx=-0.7, cy=0.0, size=3.2, max_iterations=512, width = 640, height = 480):
t0 = time.clock()
try:
increment = min(size / width, size / height)
proportion = 1.0 * width / height
start_real, start_imag = cx - increment * width/2, cy - increment * height/2

mandel_pos = "%f %fi_%f_%i" % (cx, cy, size, max_iterations)
fname = "m%s.bmp" % mandel_pos
my_bmp = kohn_bmp(fname, width, height, 3)

current_y = start_imag
for y in range(height):
if not y % 10:
sys.stdout.write('\rrow %i / %i'  % (y + 1, height))
sys.stdout.flush()
current_x = start_real

for x in range(width):
c = mandel(current_x, current_y, max_iterations)
c %= len(colors)
current_x += increment
my_bmp.write_pixel(colors[c][0], colors[c][1], colors[c][2])
current_y += increment

print("\r%.3f s             " % (time.clock() - t0))
my_bmp.close()
return fname
except IOError as e:
print(e,'x %i, y %i, current_x %f, colors %s' %(x, y, current_x, colors))

if __name__ == '__main__':
mf = mandel_file(max_iterations=256)# -0.743643, 0.131826, 0.00001,
#width=600, height = 600,
#max_iterations=1024))
#{ not in shedskin
import webbrowser
webbrowser.open(mf)
#}``````
``````# py_kohn_bmp - Copyright 2007 by Michael Kohn
# http://www.mikekohn.net/
# mike@mikekohn.net
#
# Feel free to use this class in your own programs (commerical or not)
# as long as you don't remove my name, email address, webpage, or copyright.
#
# Example how to use:
#
# my_bmp.kohn_bmp("out.bmp",image_width,image_height,3) <-- depth of 3 is color
# my_bmp.write_pixel(red,green,blue)    <-- do this width*height times
# my_bmp.close()
#
# if depth is set to 1 (black and white image) call write_pixel_bw(y) where
# y is between 0 and 255
from __future__ import print_function
class kohn_bmp:
def __init__(self, filename, width, height, depth):
self.width = width
self.height = height
self.depth = depth
self.xpos = 0

self.width_bytes = width * depth
if (self.width_bytes % 4) != 0:
self.width_bytes = self.width_bytes + (4 - (self.width_bytes % 4))

self.out=open(filename,"wb")
self.out.write("BM")                    # magic number

self.write_int(self.width_bytes * height + 54 + (1024 if depth==1 else 0))

self.write_word(0)
self.write_word(0)
self.write_int(54 + (1024 if depth==1 else 0))
self.write_int(width)                        # width
self.write_int(height)                       # height
self.write_word(1)                               # planes
self.write_word(depth * 8)                   # bits per pixel
self.write_int(0)                                # compression
self.write_int(self.width_bytes * height * depth) # image_size
self.write_int(0)                                # biXPelsperMetre
self.write_int(0)                                # biYPelsperMetre

if depth == 1:
self.write_int(256)                          # colors used
self.write_int(256)                          # colors important
self.out.write(''.join(chr(c) * 3 + chr(0) for c in range(256)))
else:
self.write_int(0)                            # colors used - 0 since 24 bit
self.write_int(0)                            # colors important - 0 since 24 bit

def write_int(self, n):
self.out.write('%c%c%c%c' % ((n&255),(n>>8)&255,(n>>16)&255,(n>>24)&255))

def write_word(self, n):
self.out.write('%c%c' % ((n&255),(n>>8)&255))

def write_pixel_bw(self, y):
self.out.write(chr(y))
self.xpos = self.xpos + 1
if self.xpos == self.width:
while self.xpos < self.width_bytes:
self.out.write(chr(0))
self.xpos = self.xpos + 1
self.xpos = 0

def write_pixel(self, red, green, blue):
self.out.write(chr((blue&255)))
self.out.write(chr((green&255)))
self.out.write(chr((red&255)))
self.xpos = self.xpos+1
if self.xpos == self.width:
self.xpos = self.xpos * 3
while self.xpos < self.width_bytes:
self.out.write(chr(0))
self.xpos = self.xpos + 1
self.xpos = 0

def close(self):
self.out.close()

if __name__ == "__main__":
k = kohn_bmp('test.bmp', 640, 512, 3)
k.write_int(1234)
k.write_word(1234)
k.write_pixel_bw(123)
k.write_pixel(123,123,123)
k.close()``````
``````""" class for Python Tkinter for Mandelbrot set
compile modules with shedskin for best performance
debug prints left, not much commenting"""
try:
import tkinter as tk
except ImportError:
import Tkinter as tk

import os
from PIL import Image, ImageTk

from my_mandel import mandel_file

class MandelbrotTk(tk.Tk):
def __init__(self, width=640, height=480, image_file='m-1.000000 0.000000i_3.500000_240.bmp'):
tk.Tk.__init__(self)
self.canvas = tk.Canvas(width=width, height=height)
self.geometry("%sx%s+100+100" % (width,height+40))
self.canvas.pack()
self.item = None
self.label = tk.Label(self.canvas, font=('courier', 16, 'bold'),
width=10)
self.label.pack(side=tk.BOTTOM)
self.cx = None
self.image_file = image_file
self.x, self.y, self.size = None, None, (0,)
# Linux
self.bind("<Button-1>", self.zoom_in)
self.bind("<Button-3>", self.zoom_out)
# Windows
self.bind("<MouseWheel>", self.mouse_wheel)

self.canvas.delete('m')
photo = Image.open(self.image_file)
im = ImageTk.PhotoImage(photo)
self.canvas.create_image(0, 0, image=im, anchor='nw', tag='m')
self.canvas.pack(fill=tk.BOTH, expand=tk.YES)

if self.cx is None:
# extract parameters from name
ipos = self.image_file.find('i')
self.cx, self.cy = map(float, self.image_file[1:ipos].split())
self.fsize, rest = self.image_file[ipos+2:].split('_', 1)
self.fsize = float(self.fsize)
self.max_iterations = int(rest.split('.',1)[0])

self.max_iterations = min(max(self.max_iterations, 256), 10000)
self.label['text'] = self.max_iterations
self.step = self.fsize / max(im.width(), im.height())
print('cx: %f, cy: %f, fsize: %f, max_iterations: %i, step: %f'  %
(self.cx, self.cy, self.fsize, self.max_iterations, self.step))
return im

def zoom_in(self, event):
print('ZOOM in')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
print('Click at %i, %i, xshift %i, yshift %i' % (event.x, event.y, xshift, yshift))

self.fsize /= 4.0
self.cx, self.cy = xshift * self.step + self.cx, yshift * self.step + self.cy
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def zoom_out(self, event):
print('zoom out')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
print('Click at %i, %i, xshift %i, yshift %i' % (event.x, event.y, xshift, yshift))
self.fsize *= 4.0
self.cx, self.cy =  xshift * self.step + self.cx, yshift * self.step + self.cy
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def mouse_wheel(self, event):
"""respond to Linux or Windows wheel event"""
print('Mousewheel event.num %s, ent.delta %s' % (event.num, event.delta))
if event.num == 5 or event.delta == -120:
self.max_iterations -= 120
if event.num == 4 or event.delta == 120:
self.max_iterations += 120
self.label['text'] = self.max_iterations

if __name__ == '__main__':
if not os.path.isfile('m-1.000000 0.000000i_3.500000_240.bmp'):
mandel_file(-1.000000, 0.000000, 3.500000, 240)
mand = MandelbrotTk()
mand.mainloop()``````
TrustyTony 888

Also included in zip are separate modules compiled. I would like to know why my_mandel.pyd runs only from direct execution of click_sel.py, not inside Idle, Python source instead have no problems running with other modules compiled (bmp and mandelbrot main iteration). Kohn code is quite heavily massaged by me. Lines 15 and 16 in mandelsh are hints for Shedskin (functions need to be called for type analysis).

Mouse wheel controls the number of iterations for bailout and left click zooms in right click zooms out (it would make sense to record the file names and not actually recalculate, but now does recalculation).

TrustyTony 888

Linux part of mousewheel routine was droped from vegaseat's code for mousewheel, so here the init again, and possibility to recalculate current image with click of mousewheel (button-2)

``````def __init__(self, width=640, height=480, image_file='m-1.000000 0.000000i_3.500000_240.bmp'):
tk.Tk.__init__(self)
self.canvas = tk.Canvas(width=width, height=height)
self.geometry("%sx%s+100+100" % (width,height+40))
self.canvas.pack()
self.item = None
self.label = tk.Label(self.canvas, font=('courier', 16, 'bold'),
width=10)
self.label.pack(side=tk.BOTTOM)
self.cx = None
self.image_file = image_file
self.x, self.y, self.size = None, None, (0,)
self.bind("<Button-1>", self.zoom_in)
self.bind("<Button-2>", self.recalculate)
self.bind("<Button-3>", self.zoom_out)
# mousewheel routine by vegaseat:
# Linux
self.bind("<Button-4>", self.mouse_wheel)
self.bind("<Button-5>", self.mouse_wheel)
# Windows
self.bind("<MouseWheel>", self.mouse_wheel)

def recalculate(self, event):
print('recalculating')
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)
TrustyTony 888

I did some search of preparing palette of colors. Had to add prototype callings to colorsys module to get it compile with shedskin, just added these lines to end of it:

``````hls_to_rgb(1.0, 0.5, 0.7)
rgb_to_hls(1.0, 0.5, 0.7)
yiq_to_rgb(1.0, 0.5, 0.7)
rgb_to_yiq(1.0, 0.5, 0.7)
hsv_to_rgb(1.0, 0.5, 0.7)
rgb_to_hsv(1.0, 0.5, 0.7)``````

I added make_colors routine to my_mandel in place of old fixed list:

``````# colors can be writen over to module by user
def make_colors(number_of_colors, saturation=0.8, value=0.9):
number_of_colors -= 1  # first reserved for black
tuples = [hsv_to_rgb(x*1.0/number_of_colors, saturation, value) for x in range(number_of_colors)]
return [(0,0,0)] + [(int(256*r),int(256*g),int(256*b)) for r,g,b in tuples]

colors = make_colors(256)``````

I also changed color picker modulo line in inner loop in my_mandel to:

``c = (c % (len(colors) - 1) + 1) if c != -1 else 0``

mandelsh module has unnecessary modulo itself from old experiments with fixed palette, it can be removed as it does nothing.

Then I check for wrong size palette when generating the image by adding the lines:

``````if len(my_mandel.colors) != self.max_iterations:
my_mandel.colors = my_mandel.make_colors(self.max_iterations)``````
TrustyTony 888

Added finally stack of parameters and images and zoom before calculate:

``````""" class for Python Tkinter for Mandelbrot set
compile modules with shedskin for best performance
debug prints left, not much commenting"""
try:
import tkinter as tk
except ImportError:
import Tkinter as tk

import os
import colorsys

from PIL import Image, ImageTk

import my_mandel

base = 'm-1 0i_3.5_240.bmp'
class MandelbrotTk(tk.Tk):
def __init__(self, width=640, height=480, image_file=base):
tk.Tk.__init__(self)
self.canvas = tk.Canvas(width=width, height=height)
self.geometry("%sx%s+100+100" % (width,height+40))
self.canvas.pack()
self.item = None
self.label = tk.Label(self.canvas, font=('courier', 16, 'bold'),
width=10)
self.label.pack(side=tk.BOTTOM)
self.cx = None
self.image_file = image_file
self.history =  []

self.bind("<Button-1>", self.zoom_in)
self.bind("<Button-2>", self.recalculate)
self.bind("<Button-3>", self.zoom_out)
# mousewheel routine by vegaseat http://www.daniweb.com/software-development/python/threads/191210/1158775#post1158775
# Linux
self.bind("<Button-4>", self.mouse_wheel)
self.bind("<Button-5>", self.mouse_wheel)
# Windows
self.bind("<MouseWheel>", self.mouse_wheel)

def recalculate(self, event):
print('recalculating')
self.image_file = my_mandel.mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

self.canvas.delete('m')
self.photo = Image.open(self.image_file)
self.image = ImageTk.PhotoImage(self.photo)
self.canvas.create_image(0, 0, image=self.image, anchor='nw', tag='m')
self.canvas.pack(fill=tk.BOTH, expand=tk.YES)

if self.cx is None:
# extract parameters from name
ipos = self.image_file.find('i')
self.cx, self.cy = map(float, self.image_file[1:ipos].split())
self.fsize, rest = self.image_file[ipos+2:].split('_', 1)
self.fsize = float(self.fsize)
self.max_iterations = int(rest.split('.',1)[0])

if len(my_mandel.colors) != self.max_iterations:
my_mandel.colors = my_mandel.make_colors(self.max_iterations)
self.max_iterations = min(max(self.max_iterations, 128), 40000)
self.label['text'] = self.max_iterations
self.step = self.fsize / max(self.image.width(), self.image.height())
print('cx: %g, cy: %g, fsize: %g, max_iterations: %i, step: %g'  %
(self.cx, self.cy, self.fsize, self.max_iterations, self.step))
return self.image

def zoom_in(self, event):
self.history.append((self.cx, self.cy, self.fsize, self.max_iterations, self.canvas.image))
print('ZOOM in')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
print('Click at %i, %i, xshift %i, yshift %i' % (event.x, event.y, xshift, yshift))

self.fsize /= 2.0
quarter = [self.canvas.image.width()/ 4, self.canvas.image.height()/ 4]
self.cx, self.cy = xshift * self.step + self.cx, yshift * self.step + self.cy
if quarter[0] < event.x < 3 * quarter[0]  and quarter[1] < event.y < 3 * quarter[1]:
self.canvas.delete('m')
im = self.photo.crop((event.x - quarter[0], event.y-quarter[1], event.x + quarter[0], event.y + quarter[1]))
self.photo = im.resize(self.photo.size)
self.image = ImageTk.PhotoImage(image=self.photo)
self.canvas.create_image(0, 0, image=self.image, anchor='nw', tag='m')
self.canvas.pack(fill=tk.BOTH, expand=tk.YES)
self.canvas.update()

self.image_file = my_mandel.mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def zoom_out(self, event):
print('zoom out')
if self.history:
self.cx, self.cy, self.fsize, self.max_iterations, self.canvas.image =  self.history.pop()
self.label['text'] = self.max_iterations
self.canvas.delete('m')
self.canvas.create_image(0, 0, image=self.canvas.image, anchor='nw', tag='m')
self.canvas.pack(fill=tk.BOTH, expand=tk.YES)
self.canvas.update()

def mouse_wheel(self, event):
"""respond to Linux or Windows wheel event"""
print('Mousewheel event.num %s, ent.delta %s' % (event.num, event.delta))
if event.num == 5 or event.delta == -120:
self.max_iterations -= 120
if event.num == 4 or event.delta == 120:
self.max_iterations += 120
self.label['text'] = self.max_iterations
my_mandel.colors =  my_mandel.make_colors(self.max_iterations)

if __name__ == '__main__':
if not os.path.isfile(base):
my_mandel.mandel_file(-1.000000, 0.000000, 3.500000, 240)
mand = MandelbrotTk()
mand.mainloop()``````
TrustyTony 888

Shedskin author asked to include my code as example code and will include the colorsys module with next shedskin (0.9). That version will also include faster complex number support, so the calculation routine may also later be updated to use straight complex arithmetic.

Mark Dufour took basically the original version of the code and transformed it to use bmp from class and not put the core math calculation in separate module, but inside the my_mandel module renamed mandel2 (the current example includes one more primitive version of Mandelbrot allready).

Here I have back ported to this code of Mark my recalculate on mousewheel click and %g format for parameters in filename and new colorscheme with the colorsys.

``````# interactive mandelbrot program

from __future__ import print_function
import sys
import time
import colorsys

class kohn_bmp:
'''py_kohn_bmp - Copyright 2007 by Michael Kohn
http://www.mikekohn.net/
mike@mikekohn.net'''

def __init__(self, filename, width, height, depth):
self.width = width
self.height = height
self.depth = depth
self.xpos = 0

self.width_bytes = width * depth
if (self.width_bytes % 4) != 0:
self.width_bytes = self.width_bytes + (4 - (self.width_bytes % 4))

self.out=open(filename,"wb")
self.out.write("BM")                    # magic number

self.write_int(self.width_bytes * height + 54 + (1024 if depth==1 else 0))

self.write_word(0)
self.write_word(0)
self.write_int(54 + (1024 if depth==1 else 0))
self.write_int(width)                        # width
self.write_int(height)                       # height
self.write_word(1)                               # planes
self.write_word(depth * 8)                   # bits per pixel
self.write_int(0)                                # compression
self.write_int(self.width_bytes * height * depth) # image_size
self.write_int(0)                                # biXPelsperMetre
self.write_int(0)                                # biYPelsperMetre

if depth == 1:
self.write_int(256)                          # colors used
self.write_int(256)                          # colors important
self.out.write(''.join(chr(c) * 3 + chr(0) for c in range(256)))
else:
self.write_int(0)                            # colors used - 0 since 24 bit
self.write_int(0)                            # colors important - 0 since 24 bit

def write_int(self, n):
self.out.write('%c%c%c%c' % ((n&255),(n>>8)&255,(n>>16)&255,(n>>24)&255))

def write_word(self, n):
self.out.write('%c%c' % ((n&255),(n>>8)&255))

def write_pixel_bw(self, y):
self.out.write(chr(y))
self.xpos = self.xpos + 1
if self.xpos == self.width:
while self.xpos < self.width_bytes:
self.out.write(chr(0))
self.xpos = self.xpos + 1
self.xpos = 0

def write_pixel(self, red, green, blue):
self.out.write(chr((blue&255)))
self.out.write(chr((green&255)))
self.out.write(chr((red&255)))
self.xpos = self.xpos+1
if self.xpos == self.width:
self.xpos = self.xpos * 3
while self.xpos < self.width_bytes:
self.out.write(chr(0))
self.xpos = self.xpos + 1
self.xpos = 0

def close(self):
self.out.close()

def mandel(real, imag, max_iterations=20):
'''determines if a point is in the Mandelbrot set based on deciding if,
after a maximum allowed number of iterations, the absolute value of
the resulting number is greater or equal to 2.'''
z_real, z_imag = 0.0, 0.0
for i in range(0, max_iterations):
z_real, z_imag = ( z_real*z_real - z_imag*z_imag + real,
2*z_real*z_imag + imag )
if (z_real*z_real + z_imag*z_imag) >= 4:
return i % max_iterations
return -1

def make_colors(number_of_colors, saturation=0.8, value=0.9):
number_of_colors -= 1  # first reserved for black
tuples = [colorsys.hsv_to_rgb(x*1.0/number_of_colors, saturation, value) for x in range(number_of_colors)]
return [(0,0,0)] + [(int(256*r),int(256*g),int(256*b)) for r,g,b in tuples]

# colors can be writen over to module by user
colors = make_colors(1024)

# Changing the values below will change the resulting image
def mandel_file(cx=-0.7, cy=0.0, size=3.2, max_iterations=512, width = 640, height = 480):
t0 = time.clock()
increment = min(size / width, size / height)
proportion = 1.0 * width / height
start_real, start_imag = cx - increment * width/2, cy - increment * height/2

mandel_pos = "%g %gi_%g_%i" % (cx, cy, size, max_iterations)
fname = "m%s.bmp" % mandel_pos
my_bmp = kohn_bmp(fname, width, height, 3)

current_y = start_imag
for y in range(height):
if not y % 10:
sys.stdout.write('\rrow %i / %i'  % (y + 1, height))
sys.stdout.flush()
current_x = start_real

for x in range(width):
c = mandel(current_x, current_y, max_iterations)
c = (c % (len(colors) - 1) + 1) if c != -1 else 0
current_x += increment
my_bmp.write_pixel(colors[c][0], colors[c][1], colors[c][2])
current_y += increment

print("\r%.3f s             " % (time.clock() - t0))
my_bmp.close()
return fname

if __name__ == '__main__':
res = 0
res = mandel(1.0, 1.0, 128)
mandel_file(max_iterations=256)``````

mandelbrot2_main.py (crashes from WindowsXP IDLE run with F5, for me at least. Run from command line or double-click)

``````# interactive mandelbrot program
# copyright Tony Veijalainen, tony.veijalainen A T gmail.com

try:
import tkinter as tk
except ImportError:
import Tkinter as tk

import os
from PIL import Image, ImageTk

from mandelbrot2 import mandel_file

main_file = 'm-1 0i_3.5_240.bmp'
class MandelbrotTk(tk.Tk):
def __init__(self, width=640, height=480, image_file=main_file):
tk.Tk.__init__(self)
self.canvas = tk.Canvas(width=width, height=height)
self.geometry("%sx%s+100+100" % (width,height+40))
self.canvas.pack()
self.item = None
self.label = tk.Label(self.canvas, font=('courier', 16, 'bold'),
width=10)
self.label.pack(side=tk.BOTTOM)

if not os.path.isfile(image_file):
self.parameters_from_fn(image_file)
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)
print('Mainfile %s generated' % self.image_file)
else:
self.image_file = image_file

self.x, self.y, self.size = None, None, (0,)
self.bind("<Button-2>", self.recalculate)
# Linux
self.bind("<Button-1>", self.zoom_in)
self.bind("<Button-3>", self.zoom_out)
# Windows
self.bind("<MouseWheel>", self.mouse_wheel)

def recalculate(self, event):
print('recalculating')
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def parameters_from_fn(self, fn):
# extract parameters from name
ipos = fn.find('i')
self.cx, self.cy = map(float, fn[1:ipos].split())
self.fsize, rest = fn[ipos+2:].split('_', 1)
self.fsize = float(self.fsize)
self.max_iterations = int(rest.split('.',1)[0])

self.canvas.delete('m')
photo = Image.open(self.image_file)
im = ImageTk.PhotoImage(photo)
self.canvas.create_image(0, 0, image=im, anchor='nw', tag='m')
self.canvas.pack(fill=tk.BOTH, expand=tk.YES)
self.max_iterations = min(max(self.max_iterations, 256), 10000)
self.label['text'] = self.max_iterations
self.step = self.fsize / max(im.width(), im.height())
print('cx: %f, cy: %f, fsize: %f, max_iterations: %i, step: %f'  %
(self.cx, self.cy, self.fsize, self.max_iterations, self.step))
return im

def zoom_in(self, event):
print('ZOOM in')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
self.fsize /= 4.0
self.cx, self.cy = xshift * self.step + self.cx, yshift * self.step + self.cy
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def zoom_out(self, event):
print('zoom out')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
self.fsize *= 4.0
self.cx, self.cy =  xshift * self.step + self.cx, yshift * self.step + self.cy
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def mouse_wheel(self, event):
"""respond to Linux or Windows wheel event"""
if event.num == 5 or event.delta == -120:
self.max_iterations -= 120
if event.num == 4 or event.delta == 120:
self.max_iterations += 120
self.label['text'] = self.max_iterations

if __name__ == '__main__':
mand = MandelbrotTk()
mand.mainloop()``````
TrustyTony 888

The code had bug, when base image was ready previously, here the fixed version (after taking out the IOError exception from mandelbrot2, this code does not crash IDLE, but only causes IOError from IDLE):

``````# interactive mandelbrot program

try:
import tkinter as tk
except ImportError:
import Tkinter as tk

import os
from PIL import Image, ImageTk

from mandelbrot2 import mandel_file

main_file = 'm-1 0i_3.5_240.bmp'
class MandelbrotTk(tk.Tk):
def __init__(self, width=640, height=480, image_file=main_file):
tk.Tk.__init__(self)
self.canvas = tk.Canvas(width=width, height=height)
self.geometry("%sx%s+100+100" % (width,height+40))
self.canvas.pack()
self.item = None

self.label = tk.Label(self.canvas, font=('courier', 10))
self.parameters_from_fn(image_file)
if not os.path.isfile(image_file):
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)
print('Mainfile %s generated' % self.image_file)
else:
self.image_file = image_file
self.label.pack(side=tk.BOTTOM)

self.x, self.y, self.size = None, None, (0,)
self.bind("<Button-2>", self.recalculate)
# Linux
self.bind("<Button-1>", self.zoom_in)
self.bind("<Button-3>", self.zoom_out)
# Windows
self.bind("<MouseWheel>", self.mouse_wheel)

def recalculate(self, event):
print('recalculating')
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)
self.update_label()

def parameters_from_fn(self, fn):
# extract parameters from name
ipos = fn.find('i')
self.cx, self.cy = map(float, fn[1:ipos].split())
self.fsize, rest = fn[ipos+2:].split('_', 1)
self.fsize = float(self.fsize)
self.max_iterations = int(rest.split('.',1)[0])
self.update_label()

self.canvas.delete('m')
photo = Image.open(self.image_file)
im = ImageTk.PhotoImage(photo)
self.canvas.create_image(0, 0, image=im, anchor='nw', tag='m')
self.canvas.pack(fill=tk.BOTH, expand=tk.YES)
self.max_iterations = min(max(self.max_iterations, 256), 10000)
self.update_label()
self.step = self.fsize / max(im.width(), im.height())
return im

def update_label(self):
self.label['text'] = ('cx: %f, cy: %f, fsize: %f, max_iterations: %i'  %
(self.cx, self.cy, self.fsize, self.max_iterations))

def zoom_in(self, event):
print('ZOOM in')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
self.fsize /= 4.0
self.cx, self.cy = xshift * self.step + self.cx, yshift * self.step + self.cy
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def zoom_out(self, event):
print('zoom out')
xshift, yshift = event.x - self.canvas.image.width() // 2, self.canvas.image.height() // 2 - event.y
self.fsize *= 4.0
self.cx, self.cy =  xshift * self.step + self.cx, yshift * self.step + self.cy
self.image_file = mandel_file(self.cx, self.cy, self.fsize, self.max_iterations)

def mouse_wheel(self, event):
"""respond to Linux or Windows wheel event"""
if event.num == 5 or event.delta == -120:
self.max_iterations -= 120
if event.num == 4 or event.delta == 120:
self.max_iterations += 120
self.update_label()

if __name__ == '__main__':
mand = MandelbrotTk()
mand.mainloop()``````
TrustyTony 888

Sorry about bumping, but finally from last posts' code line 68, %f's should be replaced with %g format (otherwice smaller size the size shown become zero).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.