(original) (raw)

import random import tkinter as tk import math import sys from datetime import datetime import time ''' Simple, single-threaded stand-alone Tkinter GUI program demo. Few checks/validation/options, minimal control. Creates sequence of mosaic-type patterns for testing GUI drawing performance with drawing primitives (circles, lines). Python 3, Tkinter 8.6. (Only tested on Python 3.7.) Tested on Windows 10 and Linux Kubuntu KDE. On Windows:- To start six processes, open a Windows command prompt. Example: If the time is now 13:50, allow 1-2 minutes to set up the processes manually, so that all processes can begin at the same time. start python mosaic.py 13:52 Repeat this command six times. Adjust the window postions to verify visually that iterations do run as expected. Do not change the window sizes or results will not be valid. Results on i7-6700HQ, 4-core (8 thread), 2.60GHz, 16GB, Windows 10:- 1 process running alone: 29k objects/sec 6 processes running concurrently: 4.3k objects/sec each, = 25.8k objects/sec combined. Conclusion: One-core performance, global system-wide lock. ''' ''' Startup dimensions, window can be resized. ''' width = 600 height = 600 blues = [ 'lightskyblue1', 'steelblue2', 'skyblue', 'lightblue1', 'lightblue', 'skyblue1', 'steelblue1', 'skyblue2', 'lightskyblue2', 'lightskyblue', ] ''' Mosaic pattern of lines and circles, circles in shades of blue. ''' class Mosaic(tk.Canvas): def __init__(self, parent, start, iter_num): tk.Canvas.__init__(self, parent, background='snow3', width=width, height=height, bd=0) self.bind("", self.on_resize) self.top = parent self.iter_active = False self.iter_num = iter_num self.iter_done = 0 self.iter_objects_done = 0 self.width = 0 self.height = 0 self.time_start = None print('Mosaic pattern, {:.1f} sec to start, {} iterations'.format(start, self.iter_num)) self.top.after(int(start * 1000), self.draw_iter) def on_resize(self, event): self.width = event.width self.height = event.height if 0: print('size', self.width, self.height) self.draw() def draw(self): self.delete('all') ''' This number of lines and circles each. ''' num = 1000 nw = self.width nh = self.height if nw == 0 or nh == 0: return xmid = nw // 2 ymid = nh // 2 col_line = 'lightskyblue1' rmax = math.sqrt(xmid * xmid + ymid * ymid) r = rmax theta = 0 thinc = .1 rinc = rmax / num fs = rmax / 250 for _ in range(num): n = max([int(fs * (4 + (8 * r / rmax))), 1]) x = r * math.sin(theta) y = r * math.cos(theta) self.create_line(xmid, ymid, x + xmid, y + ymid, fill=col_line) col_circ = blues[random.randrange(0, len(blues))] self.create_oval( x-n + xmid, y-n + ymid, x+n + xmid, y+n + ymid, outline=col_circ, fill=col_circ) r -= rinc theta += thinc if self.iter_active: self.iter_done += 1 self.iter_objects_done += num * 2 if self.iter_done < self.iter_num: self.top.after_idle(self.draw) else: fsec = time.time() - self.time_start print('{} iterations, {} objects, {:.1f} sec, {:.1f}k objects/sec'.format( self.iter_done, self.iter_objects_done, fsec, self.iter_objects_done / fsec / 1000)) self.iter_active = False ''' Start a timed number of pattern iterations. ''' def draw_iter(self): self.iter_active = True self.iter_done = 0 self.iter_objects_done = 0 self.time_start = time.time() ''' Returns after one iteration, draw() takes over. ''' self.draw() if __name__ == "__main__": ''' Optional time to start in 24 hr clock, e.g. enter 13:20 to start today at 13:20. number of iterations. ''' args = sys.argv[1:] num_args = len(args) if num_args > 0: dttime = datetime.strptime(args[0], '%H:%M').time() dtdate = datetime.now().date() dttgt = datetime.combine(dtdate, dttime) sec = (dttgt - datetime.now()).total_seconds() if sec < 0: sec = 0 else: sec = 5.0 iter_num = int(args[1]) if num_args > 1 else 50 root = tk.Tk() wnd = Mosaic(root, start=sec, iter_num=iter_num) wnd.pack(fill=tk.BOTH, expand=1) root.wm_title('Mosaic') root.mainloop()