Python Forum
Tkinter Image Display Weird Tearing Upon Transforming
Thread Rating:
  • 2 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tkinter Image Display Weird Tearing Upon Transforming
#1
I am using Tkinter and the PIL to make a basic photo viewer (mostly for learning purposes). I have made buttons to zoom in, zoom out, rotate (left and right), and move(up, down, left right). I am using a label to display the images. The label has to be rescaled and moved to fit the images when they are scaled up. My current problem is that for some reason when zooming in or out I get for a split second this flash of the image in the wrong place. Here are some pictures of it:

[Image: ZU42P.png]
This is the image at default size (No tearing).


[Image: E9D8a.png]
Zooming in once from default.


[Image: czr3s.png]
Zooming out back to default from zoomed in.


[Image: T8p70.png]
Zooming out to small zoom from default.


Here is my script so you can test if for yourself:

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import PIL
from PIL import Image, ImageTk, ImageChops
import os

class PhotoApp:

    def __init__(self):
        #Make window
        self.mainwindow = tk.Tk()

        #Centers Window To Screen
        self.screenwidth = self.mainwindow.winfo_screenwidth()
        self.screenheight = self.mainwindow.winfo_screenheight()
        self.place_width = str(int(self.screenwidth / 2 - 400))
        self.place_height = str(int(self.screenheight / 2 - 329))
        self.mainwindow.geometry("+" + self.place_width + "+" + self.place_height)

        #Initial Window Setup
        self.mainwindow.title("Photo Viewer")
        self.mainwindow.iconbitmap("unitato.ico")
        self.mainwindow.resizable(width=False, height=False)

        #Variable Declaration
        self.avariable = 1
        self.photoindex = 0
        self.currentphoto = ""
        self.photofactor = 0
        self.currentphotopath = ""
        self.currentphotoready = ""
        self.folderpath = ""
        self.lastfolderpath = ""
        self.photopath = ""
        self.photolist = []
        self.listspot = 0
        self.cwd = "C:/Users/Aaron/Pictures"
        self.rememberme = False
        self.displayatx = 0
        self.displayaty = 0
        self.mouseup = True
        self.currentrotation = 0
        self.thecolor = self.colorcheck()
        self.zoomlevel = 0

        #Widget Layout
        self.setup()

        #Key Bindings
        self.mainwindow.bind("<Up>", self.moveup)
       
        #Initiates Main Loop
        self.mainwindow.mainloop()

    def setup(self):
        print("Setup Begin")
        b_border = 10

        self.frame1 = tk.Frame(self.mainwindow)
        self.frame1.pack(side=tk.TOP)

        self.frame2 = tk.Frame(self.mainwindow)
        self.frame2.pack(side=tk.TOP)

        self.leftframe = tk.Frame(self.frame1, height=600, width=100,
                                  bd=5, relief=tk.GROOVE)
        self.leftframe.pack(side=tk.LEFT)
        self.leftframe.pack_propagate(0)

        self.rotaterightbutton = tk.Button(self.leftframe, text="Rotate ->",
                                   font=("Arial", 12), command=self.rotateright)
        self.rotaterightbutton.pack(side=tk.TOP, pady=10, fill=tk.X)

        self.rotateleftbutton = tk.Button(self.leftframe, text="<- Rotate",
                                   font=("Arial", 12), command=self.rotateleft)
        self.rotateleftbutton.pack(side=tk.TOP, pady=10, fill=tk.X)

        
        self.photoframe = tk.Frame(self.frame1, width=600, height=600)
        self.photoframe.pack(side=tk.LEFT)
        self.photoframe.pack_propagate(0)


        self.rightframe = tk.Frame(self.frame1, height=600, width=100,
                                  bd=5, relief=tk.GROOVE)
        self.rightframe.pack(side=tk.LEFT)
        self.rightframe.pack_propagate(0)

        self.wipbutton = tk.Button(self.rightframe, text="WIP",
                                   font=("Arial", 14), command=self.whatwip)
        self.wipbutton.pack(side=tk.TOP, pady=10, fill=tk.X)

        self.leftbutton = tk.Button(self.rightframe, text="[Left]",
                                   font=("Arial", 14), command=self.moveleft)
        self.leftbutton.pack(side=tk.TOP, pady=10, fill=tk.X)

        self.rightbutton = tk.Button(self.rightframe, text="[Right]",
                                   font=("Arial", 14), command=self.moveright)
        self.rightbutton.pack(side=tk.TOP, pady=10, fill=tk.X)

        self.upbutton = tk.Button(self.rightframe, text="[Up]",
                                   font=("Arial", 14))#, command=self.moveup)
        self.upbutton.pack(side=tk.TOP, pady=10, fill=tk.X)
        self.upbutton.bind("<ButtonPress-1>", self.moveup)

        self.downbutton = tk.Button(self.rightframe, text="[Down]",
                                   font=("Arial", 14), command=self.movedown)
        self.downbutton.pack(side=tk.TOP, pady=10, fill=tk.X)

        self.photodisplay = tk.Label(self.photoframe, text="No Photo To Display",
                                font=("Arial", 14))
        self.photodisplay.place(x=self.zoomsizing_frame()[2],
                                y=self.zoomsizing_frame()[3],
                                width=1000, height=1000)

        self.bottomframe = tk.Frame(self.frame2, width=600, height=100, bd=5,
                                    relief=tk.GROOVE)
        self.bottomframe.pack(side=tk.TOP)
        self.photoframe.pack_propagate(0)
        

        self.previousbutton = tk.Button(self.bottomframe, text="Previous",
                                        font=("Arial", 14), width=10,
                                        command=self.previous)
        self.previousbutton.pack(side=tk.LEFT, anchor=tk.W,
                                 padx=b_border)
        

        self.zoomout = tk.Button(self.bottomframe, text="(-)",
                                 font=("Arial", 18), command=self.zoomout)
        self.zoomout.pack(side=tk.LEFT, padx=b_border)
        

        self.folderbutton = tk.Button(self.bottomframe, text="Folder Select",
                                      font=("Arial", 14), width=15, command=self.folderselect)
        self.folderbutton.pack(side=tk.LEFT, padx=b_border)


        self.zoomin = tk.Button(self.bottomframe, text="(+)",
                                font=("Arial", 18), command=self.zoomin)
        self.zoomin.pack(side=tk.LEFT, padx=b_border)
        

        self.nextbutton = tk.Button(self.bottomframe, text="Next", font=(
            "Arial", 14), width=10, command=self.next)
        self.nextbutton.pack(side=tk.LEFT, padx=b_border)

        print("Setup End")

    def colorcheck(self):
        color16 = self.mainwindow.winfo_rgb("systembuttonface")
        color8List = []

        for value in color16:
            newvalue = value / 256
            newvalue = int(newvalue)
            color8List.append(newvalue)
            print(value, newvalue, "\n", color8List)

        rgbtuple = tuple(color8List)
        return(rgbtuple)

    def zoomsizing_photo(self):
        resolution = [600, 600]
        if self.zoomlevel == 0:
            resolution = [600, 600]
        elif self.zoomlevel == 1:
            resolution = [1200, 1200]
        elif self.zoomlevel == -1:
            resolution = [300, 300]
        else:
            resolution = [600, 600]
        return resolution    

    def zoomsizing_frame(self):
        resandplace = [1000, 1000, -200, -200]
        if self.zoomlevel == 0:
            resolution = [1000, 1000, -200, -200]
        elif self.zoomlevel == 1:
            resolution = [2000, 2000, -700, -700]
        elif self.zoomlevel == -1:
            resolution = [500, 500, 50, 50]
        else:
            resolution = [1000, 1000, -200, -200]
        return resolution

    def zoomin(self):
        self.zoomlevel = self.zoomlevel + 1
        self.reloadphoto()
        print(self.zoomlevel)

    def zoomout(self):
        self.zoomlevel = self.zoomlevel - 1
        self.reloadphoto()
        print(self.zoomlevel)

    def next(self):
        print("Next Begin")
        self.displayatx = 0
        self.displayaty = 0
        self.zoomlevel = 0
        self.currentrotation = 0
        if self.currentphoto != None:
            self.currentphoto.close()
            self.currentphoto = None
        if self.photoindex < len(self.photolist) - 1:
            self.photoindex = self.photoindex + 1
        else:
            self.photoindex = 0

        self.reloadphoto()

        print("Next End")

    def previous(self):
        print("Previous Begin")
        self.displayatx = 0
        self.displayaty = 0
        self.zoomlevel = 0
        self.currentrotation = 0
        if self.currentphoto != None:
            self.currentphoto.close()
            self.currentphoto = None
        if self.photoindex == 0:
            self.photoindex = len(self.photolist) - 1
        else:
            self.photoindex = self.photoindex - 1

        self.reloadphoto()
        print("Previous End")

    def folderselect(self):
        print("Folder Select Begin")
        self.folderpath = filedialog.askdirectory(parent=self.mainwindow,
                                                     initialdir=self.cwd,
                                                     title="Choose A Folder To View Photos In.")
        if self.folderpath != "":
            self.lastfolderpath = self.folderpath
            self.rememberme = False
        else:
            self.lastfolderpath = self.lastfolderpath
            self.folderpath = self.lastfolderpath
            self.rememberme = True
        
        print(self.folderpath)
        if self.folderpath != "" and self.rememberme == False:
            print("Mark 1")
            self.photolist = []
            self.photoindex = 0
            self.displayatx = 0
            self.displayaty = 0
            self.currentphotoready = None
            self.cwd = self.folderpath
            for name in os.listdir(self.folderpath):
                if name.lower().endswith(".jpg") or name.lower().endswith(".png") or name.lower().endswith("tiff") or name.lower().endswith(".gif"):
                    self.photolist.append(name)
            #print(self.photolist)

            if self.photolist != []:
                self.reloadphoto()
            else:
                self.photodisplay["image"]=""
                self.photodisplay["text"]="No photos in this folder."
                print(self.photodisplay["text"], self.photodisplay["image"])

        else:
            self.folderpath = self.lastfolderpath
            if self.photolist != []:
                self.reloadphoto()
            else:
                self.photodisplay["image"]=""
                self.photodisplay["text"]="No photos in this folder."
                print(self.photodisplay["text"], self.photodisplay["image"])

        print("Folder Select End")

    def loadphoto(self):
        print("Load Photo Begin")
        print(self.photoindex)
        self.photodisplay["image"]=""
        self.currentphotoready = ""
        name = self.photolist[self.photoindex]
        if name.lower().endswith(".gif"):
            self.gifplayback()
        else:    
            if self.folderpath != "":
                self.currentphotopath = self.folderpath + "/" + str(self.photolist[self.photoindex])
                self.currentphoto = Image.open(self.currentphotopath)

                
        print("Load Photo End")

    def reloadphoto(self):
        self.loadphoto()
        self.photoscale()
        self.photorotate()
        self.displayphoto()
    
    def displayphoto(self):
        print("Display Photo Begin")
        self.photodisplay.place(width=self.zoomsizing_frame()[0], height=self.zoomsizing_frame()[1],
                                x=self.zoomsizing_frame()[2] + self.displayatx,
                                y=self.zoomsizing_frame()[3] + self.displayaty)
        self.currentphotoready = ImageTk.PhotoImage(self.currentphoto)
        self.photodisplay.config(image=self.currentphotoready)
        print("Display Photo End")
        
    def photoscale(self):
        print("Photo Scale Begin")
        print(self.currentphoto.width, self.currentphoto.height)
        if self.currentphoto.width > self.currentphoto.height:
            self.photofactor = self.currentphoto.width / self.zoomsizing_photo()[0]
        else:
            self.photofactor = self.currentphoto.height / self.zoomsizing_photo()[1]

        #self.currentphoto = self.currentphoto.resize((int(self.currentphoto.width / self.photofactor),
                                                      #int(self.currentphoto.height / self.photofactor)), resample=0)

        self.currentphoto.thumbnail((int(self.currentphoto.width / self.photofactor),
                                     int(self.currentphoto.height / self.photofactor)), Image.ANTIALIAS)

        print(self.currentphoto.width, self.currentphoto.height)
        print("Photo Scale End")

    def photorotate(self):
        self.currentphoto = self.currentphoto.rotate(self.currentrotation, expand=True,
                                                         fillcolor=self.thecolor)

    def gifplayback(self):
        if self.currentphoto != "":
            self.currentphoto.close()
        self.photodisplay["image"]=""
        self.photodisplay["text"]="Gif playback is not yet implemented. \n Please come back later.\n =)"

    def whatwip(self):
        if self.currentphoto != "":
            self.currentphoto.close()
        self.photodisplay["image"]=""
        self.photodisplay["text"]=("""Gif playback \n \n Image rotation \n \n Slideshow mode \n \n Image Too Large Error (or ignore)
                                   \n Zoom In \n \n Zoom Out \n \n Move Image With Mouse
                                   \n Hold Down Mouse For Move""")

    def moveleft(self):
        print("moveleft")
        if self.currentphoto != "":
            self.displayatx = self.displayatx + 10
            self.reloadphoto()

    def moveright(self):
        print("moveright")
        if self.currentphoto != "":
            self.displayatx = self.displayatx - 10
            self.reloadphoto()
            
    def moveup(self, key):
        print("moveup")
        if self.currentphoto != "":
            self.displayaty = self.displayaty + 10
            self.reloadphoto()

    def movedown(self):
        print("movedown")
        if self.currentphoto != "":
            self.displayaty = self.displayaty - 10
            self.reloadphoto()

    def rotateleft(self):
        print("Rotate left")
        if self.currentphoto != "":
            self.currentrotation = self.currentrotation + 10
            self.reloadphoto()

    def rotateright(self):
        print("Rotate right")
        if self.currentphoto != "":
            self.currentrotation = self.currentrotation - 10
            self.reloadphoto()

def main():
    program = PhotoApp()

if __name__ == "__main__":
    main()
I've had this on stack overflow for a couple of days now but no one seems to be answering it. Hopefully someone here can help me. I'm pretty new at this, and any help is much appreciated. Thanks in advance!
Reply
#2
without digging into your code, you can visually see what the problem (most likely) is.
It looks like when you are scaling up, you are increasing the size of your frame, canvas or whatever your background widget is.
When scale down, the widget is not resizing with the image. So look at how the background widget geography is scaling.
Reply
#3
That is not the case. Right now I have only implemented 3 zoom levels: default, zoomed in once, zoomed out once. For each of those levels there is a different image size, a different frame size (it's a label that the image is displayed on), and different position values to make sure the image stays centered. As I said in the introduction of this post it doesn't stay that way, the pictures provided are simply FLASHES that appear on the screen briefly before the image settles into the right place.
Reply
#4
are you refreshing (redrawing) the previous image (one underneath) when you resize?
Reply
#5
The image has to be redrawn and then converted to a PhotoImage so it can be used by Tkinter every time the zoom level is changed. Just take a quick look at my code, especially everything tied to the "reload" function to see how it works.
Reply
#6
looking for an icon, unitato.ico
please attach with any other dependencies, or I can substitute if you give me sizes.
Reply
#7
Sorry, I forgot about that little guy. You can use any ico file size 48x48.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Tkinter: An image and label are not appearing. emont 7 405 Mar-21-2024, 03:00 PM
Last Post: deanhystad
  My Background Image Is Not Appearing (Python Tkinter) HailyMary 2 3,976 Mar-14-2023, 06:13 PM
Last Post: deanhystad
  simple tkinter question function call not opening image gr3yali3n 5 3,303 Aug-02-2022, 09:13 PM
Last Post: woooee
  [Tkinter] Tkinter don't change the image DQT 2 1,559 Jul-22-2022, 10:26 AM
Last Post: menator01
  [PyQt] Cannot Display Image after Selecting Image bintangkecil 4 2,499 Jun-12-2022, 08:18 AM
Last Post: Axel_Erfurt
  looking for scripts that do simple image display Skaperen 10 4,132 Sep-13-2021, 05:35 PM
Last Post: FullOfHelp
  How to redraw an existing image to a different image TkInter zazas321 6 5,750 Jul-08-2021, 07:44 PM
Last Post: deanhystad
  tkinter showing image in button rwahdan 3 5,523 Jun-16-2021, 06:08 AM
Last Post: Yoriz
  tkinter button image Nick_tkinter 4 3,962 Mar-04-2021, 11:33 PM
Last Post: deanhystad
  how to resize image in canvas tkinter samuelmv30 2 17,556 Feb-06-2021, 03:35 PM
Last Post: joe_momma

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020