Tkinter PIL image not displaying inside of a function - python

I am using Tkinter to display images in a Toplevel() but when I put it inside of a function it doesn't work(This is very beginner I know)
# -*- coding: utf-8 -*-
import Tkinter
from Tkinter import *
import tkMessageBox
from tkMessageBox import *
import Image, ImageTk
import PIL.Image, PIL.ImageTk
import os
import subprocess
root = Tk()
root.title("Test")
quin=Toplevel()
C = Tkinter.Canvas(quin, bg="white", height = 350, width = 350)
directory=os.path.dirname(os.path.abspath(__file__))
filename=os.path.join(directory, 'un.png')
img=PIL.Image.open(filename)
tkimg=PIL.ImageTk.PhotoImage(img)
image = C.create_image(175,175,image=tkimg)
C.grid(row=5,column=5)
def Head():
h1 = Label(root, text = "How to Secure a Computer", fg ="white", bg = "#00b8ff", width = 6,bd=2, height =2, font = "Arial", relief = RAISED)
h1.grid(row= 0, column = 0, ipadx=122, pady=3, padx=5,columnspan=3)
def Mainmenu():
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.destroy)
menubar.add_cascade(label="Options", menu=filemenu)
helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_radiobutton(label="Help")
helpmenu.add_radiobutton(label="User Manual Security Configuration Guide")
menubar.add_cascade(label="Help", menu=helpmenu)
root.config(menu=menubar)
def Mainbuttons():
B1=Button(root,text="Services",height=2, width=6,bd=2, font = "Arial", fg = "#FFFFFF", bg = "#156cff",command=Services)
B2=Button(root,text="Account Policies",height=2, width=6,bd=2, font = "Arial", fg = "#FFFFFF", bg = "#156cff")
B3=Button(root,text="Firewall Config",height=2, width=6,bd=2, font = "Arial", fg = "#FFFFFF", bg = "#156cff")
B4=Button(root,text="User Logon Time",height=2, width=6,bd=2, font = "Arial", fg = "#FFFFFF", bg = "#156cff")
B5=Button(root,text="Security Policies",height=2, width=6,bd=2, font = "Arial", fg = "#FFFFFF", bg = "#156cff")
B1.grid(row = 1, column = 0, ipadx=120,pady=2,padx=5)
B2.grid(row = 1, column = 1, ipadx=120,pady=2,padx=5)
B3.grid(row = 2, column = 1, ipadx=120,pady=2,padx=5)
B4.grid(row = 2, column = 0, ipadx=120,pady=2,padx=5)
B5.grid(row = 3, column = 0, ipadx=120,pady=2,padx=5)
def Services():
serv=Toplevel()
servcanv=Canvas(serv,height=250, width=250)
servtext=Text(serv,width=26)
servtext.insert(INSERT, "To start go to your start menu, in the search bar\ntype services.msc")
servtext.grid(row=0, column=0)
servcanv.grid(row=0, column=1)
s = Tkinter.Canvas(serv, bg="white", height = 350, width = 350)
directory=os.path.dirname(os.path.abspath(__file__))
filename=os.path.join(directory, 'un.png')
img=PIL.Image.open(filename)
tkimg=PIL.ImageTk.PhotoImage(img)
image=s.create_image(175,175,image=tkimg)
s.grid(row=5,column=5)
Mainmenu()
Mainbuttons()
Head()
root.mainloop()
As you can see the code used to display the image is used twice, once inside a function and once outside. When outside it works perfectly, but when inside it doesn't work, it says the variable image is assigned but never used.

It does not work inside function, since tkimg is garbage collected after function finishes. You need to bind your images into variables that wont be garbage collected. For example to global variables, or instance variables in a class, rather than local variables.
To make tkimg be able to write to the global tkimg use global tkimg in the function.

Related

can you insert a canvas inside a frame in tkinter? [duplicate]

This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 6 months ago.
I'm trying to display a frame with a canvas inside in order I'll be able to change of frame with a button, but when the code is executed it does not even display the button, I don't know if it happens because of all is on a function, if doing what I think is even possible or if I'm missing something
from tkinter import *
window = Tk()
window.geometry("1200x700")
window.configure(bg = "#ffffff")
def btn_clicked():
print("Button Clicked")
frame1 =Frame(window, width=1200, height=700)
frame1.grid(row=0, column=0)
def load_page():
frame1.tkraise()
frame1.pack_propagate(False)
canvas = Canvas(
frame1,
bg = "#263ff8",
height = 700,
width = 1200,
bd = 0,
highlightthickness = 0,
relief = "ridge")
canvas.place(x = 0, y = 0)
background_img = PhotoImage(file = f"background.png")
background = canvas.create_image(
601.0, 341.0,
image=background_img)
img0 = PhotoImage(file = f"img0.png")
boton = Button(
image = img0,
borderwidth = 0,
highlightthickness = 0,
command = btn_clicked,
relief = "flat")
boton.place(
x = 85, y = 71,
width = 430,
height = 99)
load_page()
window.resizable(False, False)
window.mainloop()
Rephrased the entire code and including image:
To see button display. I had to reduced height and width in Canvas. Also The button had to reduced. In line 21, I changed bd = 10. You can comment in , because I don't used PhotoImage. you can debug see in image.
from tkinter import *
window = Tk()
window.geometry("1200x700")
window.configure(bg = "#ffffff")
def btn_clicked():
print("Button Clicked")
frame1 =Frame(window, width=1200, height=700)
frame1.grid(row=0, column=0)
def load_page():
frame1.tkraise()
frame1.pack_propagate(False)
canvas = Canvas(
frame1,
bg = "#263ff8",
height = 100,
width = 200,
bd = 10,
highlightthickness = 0,
relief = "ridge")
canvas.place(x = 0, y = 0)
#background_img = PhotoImage(file = f"background.png")
#background = canvas.create_image(
#601.0, 341.0,
#image=background_img)
#img0 = PhotoImage(file = f"img0.png")
boton = Button(frame1,
#image = img0,
borderwidth = 0,
highlightthickness = 0,
command = btn_clicked,
relief = "flat")
boton.place(
x = 85, y = 71,
width = 30,
height =29)
load_page()
window.resizable(False, False)
window.mainloop()
Output result. you can see debug.

Variable not defined / function not defined

I am trying to code a CPS test for fun but I ran into a problem:
I defined a function at the beginning of the code and than I defined a tkinter button which has this function as command and the button should disappear in this function. When I do it like this it says that the variable of the button isn't defined yet but when I defined it vice versa so first the button and then the function it says that the function is not defined yet.
Can anyone help me?
(Sorry for my bad English)
import tkinter as tk
root = tk.Tk()
root.title("CPS test")
root.geometry("1000x1000")
root.configure(bg='white')
def getstring():
duration = entry.get()
entry.delete(0)
entry.place_forget()
okbutton.place_forget
lab1 = tk.Label(text = "How long should the CPS test last? 2,5 or 10 seconds?(Answer with 2,5,10)", font = "Ariel 20", bg = "#FFFFFF")
button = tk.Button(root, text = "Click me!", font = "Ariel 50", bg = "#FFFFFF", fg = "#000000")
entry = tk.Entry(root, font=('Arieal 20'), bd = 2)
okbutton = tk.Button(root, text = "OK", font = "Ariel 20", bg = "#FFFFFF", fg = "#000000", bd = 2, command = getstring())
lab1.place(x = 45,y = 250)
entry.place(x = 344, y = 350)
entry.insert(0, "Enter your answer here!")
okbutton.place(x = 465, y = 400)
root.mainloop()
I think the error is on the button name.
def getstring():
duration = entry.get()
entry.delete(0)
entry.place_forget()
button.place_forget
Just replace "okbutton" by "button" and it should work.
Else just declared the "okbutton" before the function

Tkinter app doesn't keep proportions when moved in other screen or run through ssh

I've built a GUI using Tkinter. On my dual monitor local machine, the appearance of the gui is as it was designed for.
I have the following issues though:
If I move the window to the second monitor, then the frames inside the main window don't resize according to the dimensions of the new monitor, so the gui looks like so I guess it has to do with the fact that I'm using the size of the monitor that the app is open to columnconfigure my frames.
screen_width = int(window.winfo_geometry().split('x', 1)[0])
tab_Digitizer.columnconfigure(0, minsize=int(0.75*screen_width), weight=80)
The second issue is that, if I connect from a remote machine (MacOS with XQuartz) using ssh, by default a small window opens and my gui looks like so
Oddly enough if I maximize the window, the layout gets completely messed up
Since I just started in Python and Tkinter probably I'm doing something wrong but can't figure out what.
Is there a way to automatically resize/rescale/shrink everything depending on the dimensions of the root window?
Also is it possible to do the same for the children of every parent i.e. widgets inside a frame
The code I have is super big to paste here, so I'm giving a link to it
https://pastebin.com/hHEYJ1bR but I have a snippet that might help
import tkinter as tk
import tkinter.font
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from PIL import ImageTk, Image
import screeninfo
from screeninfo import get_monitors
import os
# Method to make Label(Widget) invisible
def hide_frame(frame):
# This will remove the widget
frame.grid_remove()
# Method to make Label(widget) visible
def show_frame(frame, c, r, S, pdx):
# This will recover the widget
frame.grid(column=c, row=r, sticky=S, ipadx=(pdx,0))
# Method to make Label(widget) visible
def show_frame_padx(frame, c, r, S, pdx):
# This will recover the widget
frame.grid(column=c, row=r, sticky=S, padx=(pdx,0))
def populate(frame, rows):
'''Put in some fake data'''
for row in range(rows):
tk.Label(frame,
text = "%s" % row,
).grid(row=row, column=0)
t="Blah blah blah blah blah blah blah blah %s" %row
tk.Label(frame, text=t).grid(row = row,
column = 1,
sticky = EW
)
#____________________________________________________________________________________________
#This will be the main window
window = tk.Tk()
window.resizable(True, True)
window.attributes('-zoomed', True)
window.title("DICER daq")
default_font = tkinter.font.Font(font='TkDefaultFont')
screen_height = window.winfo_screenheight()
window.update()
screen_width = int(window.winfo_geometry().split('x', 1)[0])
screen_height = int(window.winfo_geometry().split('x', 1)[1].split('+', 1)[0])
#____________________________________________________________________________________________
#Upper frame
frame_logo = Frame(window, bd=10)
frame_logo.place(rely=0.0, relx=0.0, relwidth=1.0)
logo_label = tk.Label(frame_logo, text="daq", fg="red", font='Helvetica 18 bold')
logo_label.pack(anchor=W)
label_version = tk.Label(frame_logo, text="version test.0", fg="blue")
label_version.pack(side=LEFT)
#____________________________________________________________________________________________
#Lower frame
frame_main = Frame(window)
frame_main.place(rely=0.10, relx=0.0, relwidth=1.0)
#Create a tabcontrol
tabControl = ttk.Notebook(frame_main)
tabControl.grid(column=0, row=1)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Digitizer tab
tab_Digitizer = ttk.Frame(tabControl)
tabControl.add(tab_Digitizer, text=' Digitizer ')
tab_Digitizer.columnconfigure(0, minsize=int(0.75*screen_width), weight=80)
tab_Digitizer.columnconfigure(1, minsize=75, weight=10)
tab_Digitizer.columnconfigure(2, minsize=75, weight=10)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#This is to select available digitizer channels
def Digitizer_channel_selection(event):
print ("Digitizer channel selected:", var_digitizer_channel.get() )
pass
lbl_general_board_settings = Label(tab_Digitizer, text="Digitizer channel number", font='-weight bold')
lbl_general_board_settings.grid(column=0, row=0, sticky=W)
opt_digitizer_channel = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
var_digitizer_channel = IntVar(tab_Digitizer)
var_digitizer_channel.set(opt_digitizer_channel[0]) # default value
digitizer_channel = OptionMenu(tab_Digitizer, var_digitizer_channel, *opt_digitizer_channel, command = Digitizer_channel_selection)
digitizer_channel.grid(column=1, row=0, sticky=NSEW)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#General board settings
lbl_general_board_registers = Label(tab_Digitizer,
text = "General board registers",
font = '-weight bold',
)
lbl_general_board_registers.grid(column = 0,
row = 1,
sticky = W
)
btn_hide_general_board_registers = Button(tab_Digitizer,
text = "Hide",
font = '-weight bold',
command = lambda: hide_frame(frame_general_board_registers)
)
btn_hide_general_board_registers.grid(column = 1,
row = 1,
sticky = EW
)
btn_show_general_board_registers = Button(tab_Digitizer,
text = "Show",
font = '-weight bold',
command = lambda: show_frame_padx(frame_general_board_registers, 0, 2, EW, 0)
)
btn_show_general_board_registers.grid(column = 2,
row = 1,
sticky = EW
)
frame_general_board_registers = Frame(tab_Digitizer
)
frame_general_board_registers.columnconfigure(0, minsize=int(0.49*0.75*screen_width), weight=50)
frame_general_board_registers.columnconfigure(1, minsize=int(0.25*0.75*screen_width), weight=25)
frame_general_board_registers.columnconfigure(2, minsize=int(0.25*0.75*screen_width), weight=25)
frame_general_board_registers.config(highlightbackground="black", highlightthickness=3)
frame_general_board_registers.grid(column = 0,
row = 2,
sticky = EW,
ipadx = 0
)
populate(frame_general_board_registers, 35)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Channel settings
lbl_channel_registers = Label(tab_Digitizer,
text = "Channel registers",
font = '-weight bold'
)
lbl_channel_registers.grid(column = 0,
row = 3,
sticky = W
)
btn_hide_channel_registers = Button(tab_Digitizer,
text="Hide",
font="-weight bold",
command=lambda: hide_frame(frame_channel_registers)
)
btn_hide_channel_registers.grid(column = 1,
row = 3,
sticky = EW
)
btn_show_channel_registers = Button(tab_Digitizer,
text = "Show",
font = '-weight bold',
command = lambda: show_frame_padx(frame_channel_registers, 0, 4, W, 0)
)
btn_show_channel_registers.grid(column = 2,
row = 3,
sticky = EW
)
frame_channel_registers = Frame(tab_Digitizer
)
frame_channel_registers.grid(column = 0,
row = 4,
sticky = W
)
frame_channel_registers.columnconfigure(0, minsize=int(0.840*0.75*screen_width), weight=84)
frame_channel_registers.columnconfigure(1, minsize=int(0.075*0.75*screen_width), weight=8)
frame_channel_registers.columnconfigure(2, minsize=int(0.075*0.75*screen_width), weight=8)
frame_channel_registers.config(highlightbackground="black", highlightthickness=3)
##Channel settings - PSD
def onFrameConfigure(event):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
def handle_user_scrolling_up(event):
canvas.yview_scroll(-5, "units")
def handle_user_scrolling_down(event):
canvas.yview_scroll(5, "units")
def handle_scrollbar_scrolling(event):
canvas.configure(scrollregion=canvas.bbox("all"))
lbl_PSD_channel_registers = Label(frame_channel_registers,
text = "DPP-PSD registers",
font = (default_font.actual(), "10", "bold")
)
lbl_PSD_channel_registers.grid(column = 0,
row = 3,
ipadx = 20,
sticky = W)
btn_hide_PSD_channel_registers = Button(frame_channel_registers,
text = "Hide",
font = (default_font.actual(), "10", "bold"),
command = lambda: [hide_frame(canvas), hide_frame(myscrollbar)]
)
btn_hide_PSD_channel_registers.grid(column = 1,
row = 3,
sticky = EW
)
btn_show_PSD_channel_registers = Button(frame_channel_registers,
text = "Show",
font = (default_font.actual(), "10", "bold"),
command = lambda: [show_frame_padx(canvas, 0, 4, EW, 50), show_frame_padx(myscrollbar, 1, 4, NS, 0)])
btn_show_PSD_channel_registers.grid(column = 2,
row = 3,
sticky = EW
)
canvas = tk.Canvas(frame_channel_registers,
height = int(0.20*screen_height)
)
frame_PSD_channel_registers = Frame(canvas
)
frame_PSD_channel_registers.grid(column = 0,
row = 0,
sticky = EW
)
frame_PSD_channel_registers.bind("<Button-4>" , handle_user_scrolling_up)
frame_PSD_channel_registers.bind("<Button-5>" , handle_user_scrolling_down)
frame_PSD_channel_registers.bind("<Configure>", handle_scrollbar_scrolling)
myscrollbar=Scrollbar(frame_channel_registers ,orient="vertical", command=canvas.yview)
canvas.configure(scrollregion=canvas.bbox("all"))
myscrollbar.grid(column = 1,
row = 4,
sticky = NS,
ipadx = 0)
canvas.grid(column = 0,
row = 4,
sticky = EW,
padx = (50,0))
canvas.create_window((0, 0), window=frame_PSD_channel_registers, anchor="nw")
canvas.configure(yscrollcommand=myscrollbar.set)
canvas.columnconfigure(0, minsize=int(0.90*0.84*0.75*screen_width), weight=99)
canvas.columnconfigure(1, minsize=int(0.10*0.84*0.75*screen_width), weight=1)
frame_PSD_channel_registers.columnconfigure(0, minsize=int(0.20*0.84*0.75*screen_width), weight=20)
frame_PSD_channel_registers.columnconfigure(1, minsize=int(0.10*0.84*0.75*screen_width), weight=10)
frame_PSD_channel_registers.columnconfigure(2, minsize=int(0.60*0.84*0.75*screen_width), weight=50)
frame_PSD_channel_registers.config(highlightbackground="black", highlightthickness=1)
populate(frame_PSD_channel_registers, 35)
#This keeps the window open - has to be at the end
window.mainloop()

Update text widget in tkinter from function

The problem:
I am trying to update the same text widget box from a function that contains some text. Instead a whole new text window appears every time.
Here is my code:
from tkinter import *
import os
#Tkinter graphics
homepage = Tk()
homepage.title("My first GUI")
# set size of window
homepage.geometry('1200x400')
# Add image file
bg = PhotoImage(file = "maxresdefault.png")
# Show image using label
label1 = Label( homepage, image = bg)
label1.place(x = 0, y = 0)
label2 = Label( homepage, text = "Test App")
label2.pack()
# Create Frame
frame1 = Frame(homepage)
frame1.pack()
#button initatiors
def buttonupdate():
S = Scrollbar(homepage)
T = Text(homepage, height=100, width=30)
T.pack()
T.pack(side=RIGHT, fill= Y)
S.pack(side = RIGHT, fill = Y)
S.config(command=T.yview)
T.insert(END, "test")
T.config(yscrollcommand=S.set, state=DISABLED)
# Static buttons
tickets30button = Button(text = "This is button 1", command=buttonupdate)
tickets30button.place(x=0, y=26)
mcibutton = Button(text = "This is button 2")
mcibutton.place(x=0, y=52)
hdebutton = Button(text = "This is button 3")
hdebutton.place(x=0, y=78)
homepage.mainloop()
Here is the result if I click on the first button three times:
Let me know if you have any suggestions that I can try.
Thank you for your time,
I was able to update my text window instead of create a new one, upon each click of a button, thanks to #TheLizzard.
He mentioned to move the section of code that creates the text window outside of the function and keep the section of code that creates the text, inside the function.
Before:
#button initiators
def buttonupdate():
S = Scrollbar(homepage)
T = Text(homepage, height=100, width=30)
T.pack()
T.pack(side=RIGHT, fill= Y)
S.pack(side = RIGHT, fill = Y)
S.config(command=T.yview)
T.insert(END, "test")
T.config(yscrollcommand=S.set, state=DISABLED)
After: (UPDATED)
S = Scrollbar(homepage)
T = Text(homepage, height=100, width=30)
T.pack(side=RIGHT, fill= Y)
S.pack(side = RIGHT, fill = Y)
S.config(command=T.yview)
T.config(yscrollcommand=S.set, state=DISABLED)
#button initatiors
def myTicketstatusbutton():
T.delete(1.0,END)
T.insert(END, "test")

Cannot make that scroll bar shows frame untill the bottom

I am trying to create a pop up help window for a tool. I managed to add the scrollbar to a frame that contains canvas and label widget. However that Scrollbar does not shows the img_canvas_3 that I added.
Here is my code:
from tkinter import *
import tkinter.filedialog
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import askdirectory
import PIL
from PIL import Image, ImageTk
help_window = tkinter.Tk()
help_window.title("Help")
help_window.geometry("400x800")
#frame.grid(row=0,column=0)
canvas=Canvas(help_window,bg='red',width=300,height=800,scrollregion=(0,0,500,500))
scroll_y = Scrollbar(help_window, orient="vertical", command = canvas.yview)
frame=Frame(canvas,width=300,height=800, bg="green")
description_label = Label(frame, text = "\n\tMy Tool\n",anchor = "w").pack(side = TOP, fill = "both")
introduction = "This tool provides a basic workflow"
introduction_text = Label(frame, text = introduction, anchor = "w", font = "Verdana 10", bg="yellow").pack(fill = "both")
label_1 = Label(frame, text = "Input Data 1", anchor = "w", font = "Verdana 10", justify=CENTER).pack(fill = "both")
img_canvas_1 = Canvas(frame, bg = "black", height = 300, width = 300)
img_canvas_1.pack(side = TOP)
label_2 = Label(frame, text = "Input Data 2", anchor = "w", font = "Verdana 10", justify = CENTER).pack(fill = "both")
img_canvas_2 = Canvas(frame, bg = "black", height = 300, width = 300)
img_canvas_2.pack(side = TOP)
label_3 = Label(frame, text = "Input Data 3", anchor = "w", font = "Verdana 10", justify = CENTER).pack(fill = "both")
img_canvas_3 = Canvas(frame, bg = "black", height = 300, width = 300)
img_canvas_3.pack(side = TOP)
canvas.create_window(0, 0, anchor = 'nw', window = frame)
canvas.update_idletasks
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand = scroll_y.set)
canvas.pack(fill = 'both', expand = TRUE, side = 'left')
scroll_y.pack(fill = 'y', side = 'right')
help_window.mainloop()
I expected to have the scroll bar working, basically showing all the widgets and elements in the frame as I am adding more and more. That is only true for the untill img_canvas_2
Does anyone have an idea why this is not the case with my code? I tried by playing around with the scrollregion parameter in the canvas variable, but it did not work.
Thanks in advance.

Categories