I’m making a simple image viewer for study Tkinter. The code is shown in the appendix A. I noticed that disp_img()
becomes fast after asksaveasfilename()
runed. Default disp_img()
process time is about 0.04s. The disp_img() after asksaveasfilename()
is about 0.002s. I checked detail about disp_img()
's process by cProfile. I noticed create_image()
becomes fast. But why?
I tracked the source code (cpython/filedialog.py at main · python/cpython · GitHub). The asksaveasfilename()
create SaveAs class. To find why create_image()
becomes fast I tried to compare the case used SaveAs with the case used _Dialog (SaveAs parent class). The case used SaveAs (appendix B) is fast, but the case used _Dialog (appendix C) is not. SaveAs class is setted Tk build-in command “tk_getSaveFile”, _Dialog is not. So I think Tk build-in command is the reason why create_image()
becomes fast.
I want to know why Tk build-in command makes create_image()
fast because I want to make create_image()
fast without the dialog.
Why Tk build-in command makes create_image()
fast?
Appendix A: First code
When the code is uncommented, disp_img()
becomes fast.
import tkinter as tk
from tkinter import filedialog
import numpy as np
import time
import cProfile as profile
from PIL import Image, ImageTk, ImageOps
class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.pack()
self.master.title("Image Viewer")
self.master.geometry("400x300")
#filename = filedialog.asksaveasfilename(
# title = "Save As",
# filetypes = [("Image file", ".bmp .png .jpg .tif"),
# ("Bitmap", ".bmp"), ("PNG", ".png"),
# ("JPEG", ".jpg"), ("Tiff", ".tif") ],
# initialdir = "./"
# )
self.back_color = "#008B8B"
self.canvas = tk.Canvas(self.master, bg = self.back_color)
self.canvas.pack(expand = True, fill = tk.BOTH)
self.update_idletasks()
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.pil_img = None
self.after(10, self.timeEvent)
def disp_image(self):
disp_start_time = time.time()
img_array = np.zeros((240,320,3), np.uint8)
self.pil_img = Image.fromarray(img_array)
self.photo_img = ImageTk.PhotoImage(image=self.pil_img)
self.canvas.create_image(
self.canvas_width / 2,
self.canvas_height / 2,
image=self.photo_img
)
print("disp time = "+str(time.time() - disp_start_time))
def timeEvent(self):
profile.runctx('self.disp_image()', globals(), locals())
print('')
self.after(10, self.timeEvent)
def main():
root = tk.Tk()
app = Application(master = root)
app.mainloop()
Part of the output is below.
- Default
disp time 0.04877352714538574
84 function calls in 0.049 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.049 0.049 <string>:1(<module>)
3 0.000 0.000 0.000 0.000 Image.py:2644(_check_size)
1 0.000 0.000 0.000 0.000 Image.py:2662(new)
1 0.000 0.000 0.000 0.000 Image.py:2701(frombytes)
1 0.000 0.000 0.000 0.000 Image.py:2739(frombuffer)
1 0.000 0.000 0.000 0.000 Image.py:2792(fromarray)
1 0.000 0.000 0.000 0.000 Image.py:413(_getdecoder)
2 0.000 0.000 0.000 0.000 Image.py:524(__init__)
3 0.000 0.000 0.000 0.000 Image.py:556(size)
1 0.000 0.000 0.000 0.000 Image.py:560(_new)
1 0.000 0.000 0.000 0.000 Image.py:788(frombytes)
1 0.000 0.000 0.000 0.000 Image.py:814(load)
1 0.000 0.000 0.000 0.000 ImageTk.py:117(__del__)
1 0.000 0.000 0.000 0.000 ImageTk.py:125(__str__)
1 0.000 0.000 0.000 0.000 ImageTk.py:151(paste)
1 0.000 0.000 0.000 0.000 ImageTk.py:85(__init__)
1 0.000 0.000 0.000 0.000 __init__.py:101(_cnfmerge)
1 0.000 0.000 0.000 0.000 __init__.py:1468(_options)
1 0.000 0.000 0.048 0.048 __init__.py:2768(_create)
1 0.000 0.000 0.048 0.048 __init__.py:2788(create_image)
1 0.000 0.000 0.000 0.000 __init__.py:291(_get_default_root)
1 0.000 0.000 0.000 0.000 __init__.py:3994(__init__)
2 0.000 0.000 0.000 0.000 __init__.py:4012(__str__)
1 0.000 0.000 0.000 0.000 __init__.py:4014(__del__)
1 0.000 0.000 0.000 0.000 __init__.py:4059(__init__)
1 0.000 0.000 0.049 0.049 igl_display_py.py:33(disp_image)
1 0.000 0.000 0.000 0.000 {built-in method PIL._imaging.fill}
1 0.000 0.000 0.000 0.000 {built-in method PIL._imaging.raw_decoder}
2 0.000 0.000 0.000 0.000 {built-in method _tkinter._flatten}
3 0.000 0.000 0.000 0.000 {built-in method builtins.callable}
1 0.000 0.000 0.049 0.049 {built-in method builtins.exec}
2 0.000 0.000 0.000 0.000 {built-in method builtins.getattr}
2 0.000 0.000 0.000 0.000 {built-in method builtins.hasattr}
11 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}
7 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 0.000 0.000 0.000 0.000 {built-in method builtins.print}
1 0.000 0.000 0.000 0.000 {built-in method numpy.zeros}
2 0.000 0.000 0.000 0.000 {built-in method time.time}
4 0.048 0.012 0.048 0.012 {method 'call' of '_tkinter.tkapp' objects}
1 0.000 0.000 0.000 0.000 {method 'convert2' of 'ImagingCore' objects}
1 0.000 0.000 0.000 0.000 {method 'copy' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'decode' of 'ImagingDecoder' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'getint' of '_tkinter.tkapp' objects}
1 0.000 0.000 0.000 0.000 {method 'isblock' of 'ImagingCore' objects}
2 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'new_block' of 'ImagingCore' objects}
1 0.000 0.000 0.000 0.000 {method 'pixel_access' of 'ImagingCore' objects}
1 0.000 0.000 0.000 0.000 {method 'setimage' of 'ImagingDecoder' objects}
2 0.000 0.000 0.000 0.000 {method 'update' of 'dict' objects}
- Uncommented
disp time = 0.0014710426330566406
84 function calls in 0.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
3 0.000 0.000 0.000 0.000 Image.py:2644(_check_size)
1 0.000 0.000 0.000 0.000 Image.py:2662(new)
1 0.000 0.000 0.000 0.000 Image.py:2701(frombytes)
1 0.000 0.000 0.000 0.000 Image.py:2739(frombuffer)
1 0.000 0.000 0.000 0.000 Image.py:2792(fromarray)
1 0.000 0.000 0.000 0.000 Image.py:413(_getdecoder)
2 0.000 0.000 0.000 0.000 Image.py:524(__init__)
3 0.000 0.000 0.000 0.000 Image.py:556(size)
1 0.000 0.000 0.000 0.000 Image.py:560(_new)
1 0.000 0.000 0.000 0.000 Image.py:788(frombytes)
1 0.000 0.000 0.000 0.000 Image.py:814(load)
1 0.000 0.000 0.000 0.000 ImageTk.py:117(__del__)
1 0.000 0.000 0.000 0.000 ImageTk.py:125(__str__)
1 0.000 0.000 0.000 0.000 ImageTk.py:151(paste)
1 0.000 0.000 0.000 0.000 ImageTk.py:85(__init__)
1 0.000 0.000 0.000 0.000 __init__.py:101(_cnfmerge)
1 0.000 0.000 0.000 0.000 __init__.py:1468(_options)
1 0.000 0.000 0.001 0.001 __init__.py:2768(_create)
1 0.000 0.000 0.001 0.001 __init__.py:2788(create_image)
1 0.000 0.000 0.000 0.000 __init__.py:291(_get_default_root)
1 0.000 0.000 0.000 0.000 __init__.py:3994(__init__)
2 0.000 0.000 0.000 0.000 __init__.py:4012(__str__)
1 0.000 0.000 0.000 0.000 __init__.py:4014(__del__)
1 0.000 0.000 0.000 0.000 __init__.py:4059(__init__)
1 0.000 0.000 0.001 0.001 igl_display_py.py:33(disp_image)
1 0.000 0.000 0.000 0.000 {built-in method PIL._imaging.fill}
1 0.000 0.000 0.000 0.000 {built-in method PIL._imaging.raw_decoder}
2 0.000 0.000 0.000 0.000 {built-in method _tkinter._flatten}
3 0.000 0.000 0.000 0.000 {built-in method builtins.callable}
1 0.000 0.000 0.002 0.002 {built-in method builtins.exec}
2 0.000 0.000 0.000 0.000 {built-in method builtins.getattr}
2 0.000 0.000 0.000 0.000 {built-in method builtins.hasattr}
11 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}
7 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 0.000 0.000 0.000 0.000 {built-in method builtins.print}
1 0.000 0.000 0.000 0.000 {built-in method numpy.zeros}
2 0.000 0.000 0.000 0.000 {built-in method time.time}
4 0.001 0.000 0.001 0.000 {method 'call' of '_tkinter.tkapp' objects}
1 0.000 0.000 0.000 0.000 {method 'convert2' of 'ImagingCore' objects}
1 0.000 0.000 0.000 0.000 {method 'copy' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'decode' of 'ImagingDecoder' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'getint' of '_tkinter.tkapp' objects}
1 0.000 0.000 0.000 0.000 {method 'isblock' of 'ImagingCore' objects}
2 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'new_block' of 'ImagingCore' objects}
1 0.000 0.000 0.000 0.000 {method 'pixel_access' of 'ImagingCore' objects}
1 0.000 0.000 0.000 0.000 {method 'setimage' of 'ImagingDecoder' objects}
2 0.000 0.000 0.000 0.000 {method 'update' of 'dict' objects}
Appendix B: Use filedialog.SaveAs
Code
import tkinter as tk
from tkinter import filedialog
import numpy as np
import time
import cProfile as profile
from PIL import Image, ImageTk, ImageOps
class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.pack()
self.master.title("Image Viewer")
self.master.geometry("400x300")
filename = filedialog.SaveAs(
title = "Save As",
filetypes = [("Image file", ".bmp .png .jpg .tif"), ("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ],
initialdir = "./"
).show()
self.back_color = "#008B8B"
self.canvas = tk.Canvas(self.master, bg = self.back_color)
self.canvas.pack(expand = True, fill = tk.BOTH)
self.update_idletasks()
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.pil_img = None
self.after(200, self.timeEvent)
def disp_image(self):
disp_start_time = time.time()
img_array = np.zeros((240,320,3), np.uint8)
self.pil_img = Image.fromarray(img_array)
self.photo_img = ImageTk.PhotoImage(image=self.pil_img)
self.canvas.create_image(
self.canvas_width / 2,
self.canvas_height / 2,
image=self.photo_img
)
print("disp time = "+str(time.time() - disp_start_time))
def timeEvent(self):
self.disp_image()
self.after(200, self.timeEvent)
def main():
root = tk.Tk()
app = Application(master = root)
app.mainloop()
Result
disp time = 0.00701141357421875
disp time = 0.005995988845825195
disp time = 0.005327463150024414
disp time = 0.004936695098876953
disp time = 0.0044977664947509766
disp time = 0.004569053649902344
disp time = 0.00503087043762207
disp time = 0.004759550094604492
disp time = 0.004475593566894531
disp time = 0.004952907562255859
disp time = 0.0070192813873291016
disp time = 0.004935503005981445
disp time = 0.003088235855102539
disp time = 0.004522085189819336
disp time = 0.004429817199707031
disp time = 0.004549503326416016
disp time = 0.004712581634521484
disp time = 0.0046079158782958984
disp time = 0.004618406295776367
disp time = 0.004693508148193359
Appendix C: Use filedialog._Dialog (SaveAs parent)
Code
import tkinter as tk
from tkinter import filedialog
import numpy as np
import time
import cProfile as profile
from PIL import Image, ImageTk, ImageOps
class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.pack()
self.master.title("Image Viewer")
self.master.geometry("400x300")
filename = filedialog._Dialog(
title = "Save As",
filetypes = [("Image file", ".bmp .png .jpg .tif"), ("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ],
initialdir = "./"
).show()
self.back_color = "#008B8B"
self.canvas = tk.Canvas(self.master, bg = self.back_color)
self.canvas.pack(expand = True, fill = tk.BOTH)
self.update_idletasks()
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.pil_img = None
self.after(200, self.timeEvent)
def disp_image(self):
disp_start_time = time.time()
img_array = np.zeros((240,320,3), np.uint8)
self.pil_img = Image.fromarray(img_array)
self.photo_img = ImageTk.PhotoImage(image=self.pil_img)
self.canvas.create_image(
self.canvas_width / 2,
self.canvas_height / 2,
image=self.photo_img
)
print("disp time = "+str(time.time() - disp_start_time))
def timeEvent(self):
self.disp_image()
self.after(200, self.timeEvent)
def main():
root = tk.Tk()
app = Application(master = root)
app.mainloop()
Result
disp time = 0.11635494232177734
disp time = 0.1086418628692627
disp time = 0.09522628784179688
disp time = 0.09302139282226562
disp time = 0.09846663475036621
disp time = 0.09961771965026855
disp time = 0.10175251960754395
disp time = 0.09738302230834961
disp time = 0.09523272514343262
disp time = 0.15919780731201172
disp time = 0.15273475646972656
disp time = 0.1178138256072998
disp time = 0.09615159034729004
disp time = 0.08730411529541016
disp time = 0.10056090354919434
disp time = 0.14258956909179688
disp time = 0.1004493236541748
disp time = 0.09966635704040527
disp time = 0.09452080726623535
disp time = 0.1370866298675537