I'm making an image manipulation class to be used in an intro CS course, using PIL for image manipulation and Tkinter to show the picture. In order for the users to be able to see the picture while manipulating it, I have the graphics operations running on a separate thread, using code similar to this question. This seems to be working (i.e., nothing crashes), but I can't get the image to display -- Tk is starting up, but no window comes up. The code looks like this:
self.root = Tk.Toplevel()
self.frame = tk.Frame(self.root, width=self.image.size[0], height=self.image.size[1])
img = ImageTk.PhotoImage(self.image)
self.label = tk.Label(self.frame, image=img)
self.label.image = img
self.label.pack()
self.frame.pack()
tick()
self.root.mainloop()
The tick function is similar to the one in the linked question. I suspect that my problem is due a a misunderstanding of Tkinter, but I really have no idea.
Also, I can't seem to get the program to exit nicely -- even if I set daemon=True when I construct the Thread which this is running on, I still have to hit C-c when I'm finished. That looks kinda ugly, and I'd rather not bother the students with spurious error messages.
EDIT: Here's some more code.
class Picture():
##
# Constructor. Creates Picture either by passing in the path of an image file as param
# or by passing in a tuple in the format of (x, y) to indicate the size of a blank image.
# Example 1: Picture("image.jpg") constructs Picture with image.jpg
# Example 2: Picture((500, 500)) constructs Picture with a blank image of size 500*500
def __init__(self, param):
# Check if parameter is the right type, because we can't
# overload functions
if isinstance(param, tuple) and len(param) == 2:
self.image = Image.new('RGB', (param))
elif isinstance(param, str):
self.image = Image.open(param)
else:
raise TypeError('Parameter to Picture() should be a string or 2-tuple!')
# Default values for pen
self.pen_color = (0, 0, 0)
self.pen_position = (0, 0)
self.pen_width = 1.0
self.pen_rotation = 0
# Pixel data of the image
self.pixel = self.image.load()
# Draw object of the image
self.draw = ImageDraw.Draw(self.image)
# The main window, and associated widgets.
self.root = None
self.label = None
self.frame = None
# Threading support, so that we can show the image while
# continuing to draw on it.
self.request_queue = queue.Queue()
self.result_queue = queue.Queue()
self.thread = threading.Thread(target=self._thread_main)
self.thread.start()
def _thread_main(self):
"""
Runs the main Tkinter loop, as well as setting up all the
necessary GUI widgets and whatnot. By running Tkinter on
a separate thread, we can keep the picture displaying
even after the user's program is finished drawing on it.
"""
def tick():
"""
Called whenever Tk's main loop is idle. This lets us perform
drawing operations on the right thread.
"""
try:
f, args, kwargs = self.request_queue.get_nowait()
except queue.Empty:
pass
else:
value = f(*args, **kwargs)
self.result_queue.put(value)
self.root.after_idle(tick)
self.root = tk.Toplevel()
self.frame = tk.Frame(self.root, width=self.image.size[0], height=self.image.size[1])
img = ImageTk.PhotoImage(self.image)
self.label = tk.Label(self.frame, image=img)
# This line ensures that Python doesn't try to garbage collect
# our photo, due to a bug in Tk.
self.label.image = img
self.label.pack()
self.frame.pack()
tick()
self.root.mainloop()
def _submit_operation(self, f, *args, **kwargs):
"""
Submits an operation to the request queue. The arguments
should consist of a function, any positional arguments
to said function, and any keyword arguments to the function.
If f returns a value, that value will be returned.
Any function that does something with the picture (i.e.,
saving it, drawing to it, reading from it, etc.) should
be called only by submitting it to the queue.
"""
self.request_queue.put((f, args, kwargs))
return self.result_queue.get()
##
# Display the picture.
def display(self):
def display_func():
img = ImageTk.PhotoImage(self.image)
self.label.configure(image=img)
self.label.image = img
self.label.pack()
self.frame.pack()
self._submit_operation(display_func)
Related
I want to make a program that begins as a small window, then when given a path to an image, it maximises the screen and places the image in the centre.
If you run the code below you will see that the window maximises, the image is loaded into memory, the code runs with no errors and self.open_image calls self.draw_image(self.pimg) which runs without error, however the image is not present on the canvas.
If I click the button "Fix" and call self.fix it calls self.draw_image(self.pimg) which runs without error and correctly draws the image.
How can you call the same function twice with the same arguments and get different results. What is different.
I get the feeling this is happening because something has taken place in the main loop that hasn't taken place at the end of self.__init__, so that when i call self.draw_image the second time self.cv.create_image is able to interact with something in the resizable canvas.
In this example I am happy to assume the program will always begin as a small window and become a maximised window untill it is closed, never being resized again, however in my real program I would like to make it more dynamic where the window handles resizing sensibly, this is just a minimum reproducible example. It is for this reason that I would like to use the ResizingCanvas class (or one like it) even though I feel that it is likely the cause of the issue I am experiencing.
I have tried using breakpoints and stepping through the code watching the variables get created but I cant see the difference between the self.cv the first time around and self.cv after I click the button.
I read about a similar issue here on this question and he suggests binding "<Configure>" To the canvas and passing the coords from the event to the canvas. However this has already been implemented in ResizingCanvas
from tkinter import *
from PIL import Image, ImageTk
class ResizingCanvas(Canvas):
# https://stackoverflow.com/a/22837522/992644
def __init__(self,parent,**kwargs):
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
def on_resize(self,event):
""" determine the ratio of old width/height to new width/height"""
wscale = float(event.width)/self.width
hscale = float(event.height)/self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class main():
def __init__(self, name = None):
self.root = Tk()
self.name = name # Filename
myframe = Frame(self.root)
myframe.pack(fill=BOTH, expand=YES)
self.cv = ResizingCanvas(myframe, width=850, height=400, bg="dark grey", highlightthickness=0)
self.cv.pack(fill=BOTH, expand=YES)
self.b = Button(self.cv, text = 'Fix', command = self.fix).grid(row=1,column=1)
self.open_img()
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))
def open_img(self, event=''):
self.pimg = Image.open(self.name)
self.root.state("zoomed")
self.draw_image(self.pimg)
def fix(self, event=''):
self.draw_image(self.pimg)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
path = 'example.png'
app = main(path)
app.run()
What should happen in the video:
I click run and the image is displayed immediately, without having to click the fix button.
What does happen in the video:
I click run and the image is not displayed until I click the fix button, afterwhich it works.
Changing
self.root.state("zoomed") to self.root.state("normal")
in your code (I am working on Python3) I can only get:
[
the image above, played a little bit starting from How to get tkinter canvas to dynamically resize to window width?
and now the code seems to work with me:
from time import sleep
from tkinter import *
from PIL import Image, ImageTk
class ResizingCanvas(Canvas):
# https://stackoverflow.com/a/22837522/992644
def __init__(self,parent, **kwargs):
# Canvas.__init__(self,parent,**kwargs)
print(kwargs)
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
# self.height = self.winfo_reqheight()
# self.width = self.winfo_reqwidth()
self.height = self.winfo_height()
self.width = self.winfo_width()
# self.height = height
# self.width = width
# self.__dict__.update(kwargs)
def on_resize(self,event):
""" determine the ratio of old width/height to new width/height"""
wscale = (event.width)//self.width
hscale = (event.height)//self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class main():
def __init__(self, name = None):
self.pippo = Tk()
self.name = name # Filename
self.myframe = Frame(self.pippo)
self.myframe.pack(side = BOTTOM, expand=YES)
# myframe.pack(fill=BOTH, expand='TRUE')
self.cv = ResizingCanvas(self.myframe, width=850, height=400, bg="dark grey", highlightthickness=0)
self.cv.pack(fill=BOTH, expand=YES)
# sleep(2)
self.b = Button(self.myframe, text = 'Fix', command = self.fix)#.grid(row=1,column=1)
self.b.pack(side=TOP)
self.open_img()
# self.pippo.mainloop() ## use it if you eliminate def run
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
# self.cv.create_image(self.pippo.winfo_screenwidth()/2,
# self.pippo.winfo_screenheight()/2, image=self.img, tags=('all'))
self.cv.create_image(self.pippo.winfo_width()/2,
self.pippo.winfo_reqheight()/2, image=self.img, tags=('all'))
def open_img(self, event=''):
self.pimg = Image.open(self.name)
self.pippo.state("normal")
self.draw_image(self.pimg)
def fix(self, event=''):
self.draw_image(self.pimg)
def run(self):
self.pippo.mainloop()
if __name__ == "__main__":
path = 'example.png'
app = main(path)
app.run()
don't know about your question though, but wanted to be sure your starting example works right. Let me know if it could be related to python/pillow/tkinter version or something else
Here my window image results before ad after pressing fix button :
At the end found out that your code does work as long as you use
self.root.attributes('-zoomed', True) instead of `self.root.state("zoomed")`
The problem is here. self.root.winfo_screenwidth()
Change it to self.cv.width. I don't know why.
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))
Change the last line to
self.cv.create_image(self.cv.width/2,
self.cv.height/2, image=self.img, tags=('all'))
Fixes the issue.
Tk.winfo_screenwidth() according to https://tkdocs.com/shipman/universal.html returns the width of the screen, indepedant of the size of the window, so even if you have a small window on a 1920x1080 display, this function will return 1920.
self.cv.width returns the width of the canvas object.
My plotting library needs to be able to show multiple plots at the same time, each of which is represented as a PIL image, and each of which should show up as its own window. The windows should be independent, so closing any one of them should not affect the others, but when all of them have been closed the main loop should exit. This behavior was easy to achieve in qt and wx, but in qt it's proving difficult so far.
Here's the closest I've come so far:
from six.moves import tkinter
from PIL import ImageTk
class Window:
def __init__(self, img):
self.window = tkinter.Toplevel()
self.window.minsize(img.width, img.height)
self.canvas = tkinter.Canvas(self.window, width=img.width, height=img.height)
self.canvas.pack()
self.canvas.configure(background="white")
self.photo = ImageTk.PhotoImage(img)
self.sprite = self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
windows = []
for img in imgs:
windows.append(Window(img))
if len(windows) > 0: windows[0].window.mainloop()
This displays an image in each window, and each of those windows can be closed independently. But it also displays an empty root window which needs to be closed for the main loop to exit, and which will cause all windows to close when closed, which is not the behavior I want.
If I replace tkinter.Toplevel() with tkinter.Tk(), then create_image fails for the second window with an obscure "pyimageX does not exist" error message, where X is an incrementing integer.
Will I have to make an invisible root window, and then manually count how many child windows have closed and trigger destruction of the invisible root window when all of them have closed in order to get the behavior I'm looking for? Or is there a simple way to achieve this?
Edit: Just to clarify: My program is not mainly a Tk app. It spends almost all its time doing other stuff, and only temporarily uses Tk in a single function to display some plots. That's why it's important that the main loop exits after the plots have been closed, to the program can resume its normal operation. Think about how show() in matplotlib works for an example of this scenario.
Here is an example of how you might want to do this. This example uses the root window to house a button that will open up all images at the top level.
Make sure you change self.path to your image folder.
import tkinter as tk
import os
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Button(self, text="Open Images", command=self.open_images).pack()
self.path = ".\RGB"
def open_images(self):
directory = os.fsencode(self.path)
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith(".gif"):
print(filename)
top = tk.Toplevel(self)
img = tk.PhotoImage(file="{}\{}".format(self.path, filename))
lbl = tk.Label(top, image=img)
lbl.image = img
lbl.pack()
if __name__ == '__main__':
app = App()
app.mainloop()
Here is my 2nd example where you can hide the root window and when the last top level window is closed the tkinter instance is also destroyed. This is maned with a simple tracking variable.
import tkinter as tk
import os
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.top_level_count = 0
self.path = ".\RGB"
self.open_images()
self.withdraw()
def open_images(self):
directory = os.fsencode(self.path)
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith(".gif"):
self.top_level_count += 1
image_top(self, self.path, filename)
def check_top_count(self):
print(self.top_level_count)
if self.top_level_count <= 0:
self.destroy()
class image_top(tk.Toplevel):
def __init__(self, controller, path, filename):
tk.Toplevel.__init__(self, controller)
self.controller = controller
self.protocol("WM_DELETE_WINDOW", self.handle_close)
img = tk.PhotoImage(file="{}\{}".format(path, filename))
lbl = tk.Label(self, image=img)
lbl.image = img
lbl.pack()
def handle_close(self):
self.controller.top_level_count -= 1
self.destroy()
self.controller.check_top_count()
if __name__ == '__main__':
app = App()
app.mainloop()
Ok so here's a couple of classes I came up with to solve this problem:
class ImgRoot(tkinter.Tk):
def __init__(self, imgs):
super(ImgRoot, self).__init__()
for i in imgs:
Window(self, i)
self.withdraw()
self.open=True
self.tick()
def tick(self):
if not self.open:
self.destroy()
self.open=False
self.after(100, self.tick)
def checkin(self):
self.open=True
class Window(tkinter.Toplevel):
def __init__(self, root, img):
super(Window, self).__init__()
self.root=root
self.tick()
self.minsize(img.width, img.height)
self.canvas = tkinter.Canvas(self, width=img.width, height=img.height)
self.canvas.pack()
self.canvas.configure(background="white")
self.photo = ImageTk.PhotoImage(img)
self.sprite = self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
def tick(self):
self.root.checkin()
self.after(100, self.tick)
The idea here is to create a main class (ImgRoot) which handles the whole thing. Then, every 0.1 seconds (100 miliseconds), it will check if any of the image windows have told it that they are still alive, and, if not, close. The image windows (Windows) do this by setting the ImgRoot's open attribute to True every 0.1 seconds that they are alive. Here is an example usage:
import tkinter
#above classes go here
ImgRoot(imgs) #imgs is a list as defined in your question
tkinter.mainloop()
print('done') #or whatever you want to do next
I have a multiple frame quiz app and a problem with adding an image to a Radiobutton widget, next to some text. I have removed the parts of code that had nothing to do with the image of the radio buttons, but I do have a working application otherwise, as well as working radio buttons. The problem is the images are shown next to text (as wanted) only once and when I create another frame and call create_radio_buttons again, I get an error saying:
File
"/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/init.py",
line 1330, in configure
return self._configure('configure', cnf, kw) File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/init.py",
line 1321, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: image "pyimage4" doesn't exist
I have tried doing this:
def create_radio_buttons(self):
photo = PhotoImage(file="wrong.gif")
self.photo_reference = photo
R = ttk.Radiobutton(self, compound="left", # for image justification
text=possible_answer,
image=photo))
and:
def create_radio_buttons(self):
R = ttk.Radiobutton(self, compound="left", # for image justification
text=possible_answer,
image=PhotoImage(file="wrong.gif")))
and also setting the image in a different class:
photo = PhotoImage(file="wrong.gif")
self.photo_reference = photo
and then creating the button in the class to which the method create_radio_buttons belongs to.
I get the same result every time.
I will have to set the image later in the code, not in the initialization process so I have also tried setting it like this:
for radio_button in self.radio_buttons:
radio_button.configure(image = self.quiz_reference.wrong_photo)
In this case my app crashes and I get this message:
2017-05-17 11:03:35.838 Python[19273:1316924]
CFURLCopyResourcePropertyForKey failed because it was passed an URL
which has no scheme
I suspect the former error is caused by the fact that Python frees memory every now and then and deletes the image, resulting in not being able to find it anymore. However I do not understand how I should avoid this (if this is the problem). I thought setting an additional reference will prevent it from being deleted but it apparently does not.
I would be very grateful for any help because I am about to lose my mind here.
EDIT: Below I have added my minimal working example. There is still quite a lot of code but without those things I think you wouldn't be able to see what the problem is.
from tkinter import *
from tkinter import ttk
import random
class Quiz(Tk):
frames = {}
number_of_questions = 5
question_count = 0
number_of_all_questions = 20 # per subject in SUBJECTdata.txt
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
Tk.wm_title(self, "Quiz")
self.wrong_photo = PhotoImage(file="wrong.gif")
self.initialize_container_frame()
self.initialize_start_page()
def initialize_container_frame(self):
self.container = ttk.Frame(self)
self.container.pack()
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
def initialize_start_page(self):
start_page = StartPage(self.container, self)
start_page.grid(row=0, column=0, sticky="nsew")
self.frames[0] = start_page
self.show_frame()
def show_frame(self):
if self.question_count <= self.number_of_questions:
frame = self.frames.get(self.question_count, None)
if frame != None: frame.tkraise()
self.question_count += 1
def set_subject(self):
self.create_random_questions()
self.show_frame()
def create_random_questions(self):
random_question_numbers = []
table_of_possible_question_numbers = list(range(1, self.number_of_all_questions + 1))
# here I just let it choose random question numbers
while len(random_question_numbers) < self.number_of_questions:
rand_number = random.choice(table_of_possible_question_numbers)
random_question_numbers.append(rand_number)
table_of_possible_question_numbers.remove(rand_number)
# load the questions:
question_count = 1
for number in random_question_numbers:
question = Question(self.container, self, number)
self.frames[question_count] = question
question_count += 1
question.grid(row=0, column=0, sticky="nsew")
class StartPage(ttk.Frame):
def __init__(self, parent, quiz_reference):
ttk.Frame.__init__(self, parent)
self.quiz_reference = quiz_reference
self.show_frame()
def show_frame(self):
ttk.Label(self, text="Start Page stuff.\n\n\nChoose the subject:").pack()
ttk.Button(self, text="Geography", command= lambda: self.quiz_reference.set_subject()).pack()
class Question(Frame):
possible_answers = ["Yes", "No", "Maybe"] # I am otherwise reading those from a file
radio_buttons = []
def __init__(self, parent, quiz_reference, number):
ttk.Frame.__init__(self, parent)
self.number = number
self.quiz_reference = quiz_reference
self.create_radio_buttons()
def create_radio_buttons(self):
self.radio_buttons = []
for possible_answer in self.possible_answers:
R = ttk.Radiobutton(self,
compound="left",
text=possible_answer,
command=self.show_confirm_button)
self.radio_buttons.append(R)
R.pack()
def show_confirm_button(self):
self.confirm_button = ttk.Button(self, text="Confirm",
command=self.change_text_on_confirm_button)
self.confirm_button.pack(pady=8, side="bottom")
self.is_confirm_button_showing = True
def change_text_on_confirm_button(self):
self.confirm_button.destroy()
for radio_button in self.radio_buttons:
radio_button.configure(state=DISABLED)
radio_button.configure(image = self.quiz_reference.wrong_photo)
# I will be checking which one is wrong later,
# for now I just want to see it setting the image works
ttk.Button(self, text="Next", command=self.quiz_reference.show_frame).pack()
app = Quiz()
app.mainloop()
With this I get to the third frame where I get the message that CFURLCopyResourcePropertyForKey failed because it was passed an URL which has no scheme (this is where tkinter should add that image).
Please try to run the code for yourself.
This is a screenshot of the point on which I get the error:
As you can see, the image does get displayed but as a white square. I still think this is because Python garbage collector deletes it, but how do I prevent it from doing so?
I tried adding
self.wrong_photo2 = self.wrong_photo
after creating the image, with no success (the same exact problem).
Any help would be very appreciated!
It seems like the radiobutton is not happy with being disabled and having an image. If I remove radio_button.configure(state=DISABLED) then the image works. A workaround which seems to work is to replace state=DISABLED with command=(lambda: None), i.e.:
radio_button.configure(image=self.quiz_reference.wrong_photo,
command= (lambda: None))
So my professor is making us use Graphis.py(zelle) to make a GUI I've made all the buttons my issue is that the module doesn't have any functionality that allows a image to be the background only the color. Do you guys have any idea how I can modify it so I can set the background to a image? The setBackground method is the one I believe needs to be edited
class GraphWin(tk.Canvas):
"""A GraphWin is a toplevel window for displaying graphics."""
def __init__(self, title="Graphics Window",
width=200, height=200, autoflush=True):
master = tk.Toplevel(_root)
master.protocol("WM_DELETE_WINDOW", self.close)
tk.Canvas.__init__(self, master, width=width, height=height)
self.master.title(title)
self.pack()
master.resizable(0,0)
self.foreground = "black"
self.items = []
self.mouseX = None
self.mouseY = None
self.bind("<Button-1>", self._onClick)
self.bind_all("<Key>", self._onKey)
self.height = height
self.width = width
self.autoflush = autoflush
self._mouseCallback = None
self.trans = None
self.closed = False
master.lift()
self.lastKey = ""
if autoflush: _root.update()
def __checkOpen(self):
if self.closed:
raise GraphicsError("window is closed")
def _onKey(self, evnt):
self.lastKey = evnt.keysym
def setBackground(self, color):
"""Set background color of the window"""
self.__checkOpen()
self.config(bg=color)
self.__autoflush()
If you store your image as a gif, for example, Python will usually be able to display it. Here are the steps. I will assume that you create a graphic window named "win".
First, save your image in a file such as TestPic.gif.
Second, import the image and assign it a name such as b:
b = Image(Point(100,100),"TestPic.gif")
The Point(100,100) is the point that the image will be centered on. Now you want to display it:
Image.draw(b,win)
That's pretty much it. You can manipulate the image of course by using standard Python Graphics commands, such as moving it around, etc.
For reference, look at the Graphics pdf at this link:
http://mcsp.wartburg.edu/zelle/python/graphics/graphics.pdf
It spells everything out pretty well.
I'm trying to show a jpg image as background for a GUI thing I'm building.
I can get it to work in a single method:
from Tkinter import *
from PIL import Image, ImageTk
class MakeGUI(object):
master = None
w = None
def __init__(self):
self.canvasSizeY = 400 #height
self.canvasSizeX = 640 #width
def setupCanvas(self):
"""
preps the canvas for drawing.
"""
self.master = Tk()
self.w = Canvas(self.master, width=self.canvasSizeX, height=self.canvasSizeY)
self.w.config(bg='white')
image = Image.open("background.jpg")
photo = ImageTk.PhotoImage(image)
self.w.create_image(0,0, image=photo, anchor=NW)
self.w.pack()
mainloop()
def main():
makeGUI = MakeGUI()
makeGUI.setupCanvas()
if __name__ == '__main__':
main()
But when I try and make the canvas in one method, and show the canvas in another, it doesn't show the jpg (when I've been testing, I've created and shown & text and rectangles using this approach):
from Tkinter import *
from PIL import Image, ImageTk
class MakeGUI(object):
master = None
w = None
def __init__(self):
self.canvasSizeY = 400 #height
self.canvasSizeX = 640 #width
def setupCanvas(self):
"""
preps the canvas for drawing.
"""
self.master = Tk()
self.w = Canvas(self.master, width=self.canvasSizeX, height=self.canvasSizeY)
self.w.config(bg='white')
image = Image.open("background.jpg")
photo = ImageTk.PhotoImage(image)
self.w.create_image(0,0, image=photo, anchor=NW)
def showImage(self):
"""
pushes the image to the screen
"""
self.w.pack()
self.w.mainloop()
def main():
makeGUI = MakeGUI()
makeGUI.setupCanvas()
if __name__ == '__main__':
main()
I want to use the GUI dynamically to show some text as I work through some editing, so I'm interested to understand what I've got wrong before I get too far into the build in case its a showstopper...
The most obvious problem is that in the second case you are never calling showImage. Even after you do call that function, your image probably won't show up. Images will be garbage-collected if there isn't a reference to them. It may seem like there's a reference because you're adding it to a canvas, but that isn't enough.
You'll need to do something like:
self.photo = ImageTk.PhotoImage(image)
Finally, I recommend that you take the call to mainloop out of showImage. mainloop must always be called exactly once, so most typically it is the last line of code in your program, or the last line of code in your main function.
A more common way to make a Tkinter application is to subclass either the Tk object or a Frame object, rather than having your main application be a generic object. For example:
class MyApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
...
self.setupCanvas(...)
...
if __name__ == "__main__":
app = MyApp()
app.mainloop()