Python Forum
pass a variable between tkinter and toplevel windows
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
pass a variable between tkinter and toplevel windows
#1
hello.
I have 2 windows:
a)window = tkinter.Tk(), a mapview and an Entry to write a filepath

b)top=Toplevel() and a label to view an image

I need to pass a dynamic variable (full filepath.i.e.: "d:/bilde/Rød.png" from window a to a PhotoImage function in window b. At present time, without success.

Plan is to make an event (sv), view an image on label,in window b (exactly below an tkintermap in window a).

It works ok as long as I hardcode the filepath in window b.

My doubt is, does the event (sv) trigger the PhotoImage function after I manually write in filepath into an tkinter Entry in window a?

(I might write/read a file to pass variable between windows, but that would be a none alternative and slow process).

Below is some copies of code in each window.

var1 in window a, shows correct path (entered from keyboard)
No image is shown from window b. No errors detected.

SOME CODE IN WINDOW A:
import tkinter
from Scripts import dati
from Scripts import rutiner
from tkinter.ttk import *
import tkintermapview
import customtkinter
from tkinter import Canvas
from tkinter import *
from datetime import datetime
from pynput.mouse import Button, Controller
import tkinter as tk
from PIL import ImageTk, Image
import cv2



#---------------------GLOBAL VARIABLES-------------------------
mouse = Controller()
zoom=1
db = "D:/Python311/Scripts/markers.db"
today = datetime.today( ).strftime("%d-%m-%Y-%H-%M-%S")
verdi=0
stinavn1=""
stinavn2=""
stinavn3=""
stinavn4=""
textentered1=""
var1=""

root_tk = tkinter.Tk()
window = root_tk
window.geometry("1920x1200+0+0")
window.minsize(1920, 1200)
window.resizable(False, False)
window.overrideredirect(True)
window.attributes('-topmost',True)
.
.
def callback(sv):
    textentered1= str(sv.get())
    var1 =textentered1
    if "htt" in var1:stitype="web"
    elif ".png" in var1:stitype= "fil"
    elif ".mp4" in var1:stitype = "vid"
    elif ".docx" in var1:stiltype = "word"

print(var1)
sv = StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
sti1 = customtkinter.CTkEntry(window, textvariable=sv, width=360, font=("Ariel",14,"bold"), placeholder_text="Sti til bilde 1 som skal behandles.(d:/bilde/abc.png)", justify=("left"))
sti1.place(x=12,y=1010)
.
.
TOPLEVEL WINDOW:
top = Toplevel()
top.geometry("1000x1000")   
top.resizable(False, False) 
top.overrideredirect(True)

histimg=PhotoImage(file="d:/bilde/Rød.png")                        (a png image with red circle)
label=Label(top,image=histimg)
label.place(x=0,y=0)

window.mainloop()

Attached Files

Thumbnail(s)
   
Reply
#2
You cannot pass variables to functions in python. Python variables are only names that can be used to reference objects. When you use a variable name as an argument to a python function, you are passing the object referenced by the variable.

Please do not write multiple statements on one line. It looks really odd.
def callback(sv):
    textentered1= str(sv.get())  #<- This is a local variable, and why are you calling str()?
    var1 =textentered1
    if "htt" in var1:
        stitype="web"   # <- This is a local variable
    elif ".png" in var1:
        stitype= "fil"
    elif ".mp4" in var1:
        stitype = "vid"
    elif ".docx" in var1:
        stiltype = "word"
A problem with this function is textentered1 is a local variable, not the global variable declared here:
#---------------------GLOBAL VARIABLES-------------------------
mouse = Controller()
zoom=1
db = "D:/Python311/Scripts/markers.db"
today = datetime.today( ).strftime("%d-%m-%Y-%H-%M-%S")
verdi=0
stinavn1=""
stinavn2=""
stinavn3=""
stinavn4=""
textentered1=""
var1=""
You can fix this by adding "global textentered1, stiltype" at the top of the callback() function. "global" tells python to look for the variable name in the global scope instead of creating a local variable.

I don't like using global variables, and the recommendation is to avoid using their use. Instead of having callback() set a global variable, it should perform some the actions that use the value in sv. Somewhere you must have some code that uses textentered1 and stiltype. That code should be in callback(), or should be called by callback(). I think that is what you refer to here:
Quote:My doubt is, does the event (sv) trigger the PhotoImage function after I manually write in filepath into an tkinter Entry in window a?
I think this is exactly what you should do. callback() should be bound to the control used to enter the filename. When you enter the filename, callback() should directly call the PhotoImage function and update the window.
Reply
#3
A method that works well to send messages between queues is explained in this thread
Reply
#4
(Oct-02-2023, 08:55 AM)Larz60+ Wrote: A method that works well to send messages between queues is explained in this thread
Is this a response to a different post? I don't see any mention of thread use. The "multiple windows" referenced in the post are all in the main thread.
Reply
#5
deanhystad Wrote:Is this a response to a different post? I don't see any mention of thread use in the OP.
Perhaps so. Just woke up.
Reply
#6
Thanks for input. I found a solution and will post it soon.
Reply
#7
(Oct-03-2023, 02:45 AM)janeik Wrote: Thanks for input. I found a solution and will post it soon.

Used the callback and global variable var1,containing a filepath for an image to be shown on a label in top level window.

MAIN WINDOW, ALLWAYS ON TOP
root_tk = tkinter.Tk()
window = root_tk
window.geometry("1920x1200+0+0")
window.minsize(1920, 1200)
window.resizable(False, False)
window.overrideredirect(True)
window.attributes('-topmost',True)
TOP LEVEL WINDOW (ALLOCATED BELOW MAINWINDOW
def callback(sv):
   global var1
   var1 = (sv.get())

sv = StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
# sti1 = customtkinter.CTkEntry(window, textvariable=sv)
sti1 = customtkinter.CTkEntry(window, textvariable=sv, width=360, font=("Ariel",14,"bold"), placeholder_text="Sti til bilde 1 som skal behandles.(d:/bilde/abc.png)", justify=("left"))
sti1.place(x=12,y=1010)
#------------------------create top level window and a label imglbl to hold image-------------------------
top = Toplevel()
top.geometry("1000x1000")   
top.resizable(False, False) 
top.overrideredirect(True)
#------------------------function view image from path given in var1------------
def loadimg():
    global var1
    var1.strip()
    photo = PhotoImage(file=var1)
    image_label = Label(top,image=photo)
    image_label.photo = photo
    image_label.place(x=0,y=0)
#----------button, at main window, execute loadimg() function
button = tk.Button(window, text="Submit",command = loadimg)
button.place(x=45,y=1140)

Attached Files

Thumbnail(s)
   
Reply
#8
I would do it like this:
import tkinter as tk
import customtkinter as ctk


class OverlayWindow(tk.Toplevel):
    def __init__(self, overlay_file):
        self.overlay_file = overlay_file
        super().__init__()
        self.geometry("1000x1000")   
        self.resizable(False, False) 
        self.overrideredirect(True)
        tk.Button(self, text="Submit", command=self.loading).place(x=45,y=1140)

    def loadimg(self):
        """Set overlay image"""
        photo = tk.PhotoImage(file=self.overlay_file.get().strip())
        image_label = tk.Label(self, image=photo)
        image_label.photo = photo
        image_label.place(x=0, y=0)


class MainWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry("1920x1200+0+0")
        self.minsize(1920, 1200)
        self.resizable(False, False)
        self.overrideredirect(True)
        self.attributes('-topmost',True)

        self.overlay_file = tk.StringVar()
        ctk.CTkEntry(
            self,
            textvariable=self.overlay_file,
            width=360,
            font=("Ariel", 14, "bold"),
            placeholder_text="Sti til bilde 1 som skal behandles.(d:/bilde/abc.png)",
            justify=("left")).place(x=12,y=1010)

window = MainWindow()
top = OverlayWindow(window.overlay_file)
If there are several values that top needs to get from window, I pass window to top, and use that to reference the values.
class OverlayWindow(tk.Toplevel):
    def __init__(self, main_window):
        self.main_window = main_window
        super().__init__()
        self.geometry("1000x1000")   
        self.resizable(False, False) 
        self.overrideredirect(True)
        tk.Button(self, text="Submit", command=self.loading).place(x=45,y=1140)

    def loadimg(self):
        """Set overlay image"""
        photo = tk.PhotoImage(file=self.main_window.overlay.get().strip())
        image_label = tk.Label(self, image=photo)
        image_label.photo = photo
        image_label.place(x=0, y=0)
Another approach is to make a data model that is used by both windows. Think of it as a blackboard that both windows can read and write.
import tkinter as tk
import customtkinter as ctk


class ApplicationData():
    """I have all data shared between the main and overlay window."""
    def __init__(self):
        self.overlay_file = tk.StringVar()
        # additional shared vaiables go here


class OverlayWindow(tk.Toplevel):
    def __init__(self, data):
        self.data = data
        super().__init__()
        self.geometry("1000x1000")   
        self.resizable(False, False) 
        self.overrideredirect(True)
        tk.Button(self, text="Submit", command=self.loading).place(x=45,y=1140)

    def loadimg(self):
        """Set overlay image"""
        photo = tk.PhotoImage(file=self.data.overlay_file.get().strip())
        image_label = tk.Label(self, image=photo)
        image_label.photo = photo
        image_label.place(x=0, y=0)


class MainWindow(tk.Tk):
    def __init__(self, data):
        super().__init__()
        self.geometry("1920x1200+0+0")
        self.minsize(1920, 1200)
        self.resizable(False, False)
        self.overrideredirect(True)
        self.attributes('-topmost',True)

        ctk.CTkEntry(
            self,
            textvariable=self.data.overlay_file,
            width=360,
            font=("Ariel", 14, "bold"),
            placeholder_text="Sti til bilde 1 som skal behandles.(d:/bilde/abc.png)",
            justify=("left")).place(x=12,y=1010)


data = ApplicationData()
window = MainWindow(data)
top = OverlayWindow(data)
You should start studying Python classes. There is a reason that tkinter and customtkinter use classes. It is hard to write good looking GUI code without them.
Reply
#9
(Oct-03-2023, 01:48 PM)deanhystad Wrote: I would do it like this:
import tkinter as tk
import customtkinter as ctk


class OverlayWindow(tk.Toplevel):
    def __init__(self, overlay_file):
        self.overlay_file = overlay_file
        super().__init__()
        self.geometry("1000x1000")   
        self.resizable(False, False) 
        self.overrideredirect(True)
        tk.Button(self, text="Submit", command=self.loading).place(x=45,y=1140)

    def loadimg(self):
        """Set overlay image"""
        photo = tk.PhotoImage(file=self.overlay_file.get().strip())
        image_label = tk.Label(self, image=photo)
        image_label.photo = photo
        image_label.place(x=0, y=0)


class MainWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry("1920x1200+0+0")
        self.minsize(1920, 1200)
        self.resizable(False, False)
        self.overrideredirect(True)
        self.attributes('-topmost',True)

        self.overlay_file = tk.StringVar()
        ctk.CTkEntry(
            self,
            textvariable=self.overlay_file,
            width=360,
            font=("Ariel", 14, "bold"),
            placeholder_text="Sti til bilde 1 som skal behandles.(d:/bilde/abc.png)",
            justify=("left")).place(x=12,y=1010)

window = MainWindow()
top = OverlayWindow(window.overlay_file)
If there are several values that top needs to get from window, I pass window to top, and use that to reference the values.
class OverlayWindow(tk.Toplevel):
    def __init__(self, main_window):
        self.main_window = main_window
        super().__init__()
        self.geometry("1000x1000")   
        self.resizable(False, False) 
        self.overrideredirect(True)
        tk.Button(self, text="Submit", command=self.loading).place(x=45,y=1140)

    def loadimg(self):
        """Set overlay image"""
        photo = tk.PhotoImage(file=self.main_window.overlay.get().strip())
        image_label = tk.Label(self, image=photo)
        image_label.photo = photo
        image_label.place(x=0, y=0)
Another approach is to make a data model that is used by both windows. Think of it as a blackboard that both windows can read and write.
import tkinter as tk
import customtkinter as ctk


class ApplicationData():
    """I have all data shared between the main and overlay window."""
    def __init__(self):
        self.overlay_file = tk.StringVar()
        # additional shared vaiables go here


class OverlayWindow(tk.Toplevel):
    def __init__(self, data):
        self.data = data
        super().__init__()
        self.geometry("1000x1000")   
        self.resizable(False, False) 
        self.overrideredirect(True)
        tk.Button(self, text="Submit", command=self.loading).place(x=45,y=1140)

    def loadimg(self):
        """Set overlay image"""
        photo = tk.PhotoImage(file=self.data.overlay_file.get().strip())
        image_label = tk.Label(self, image=photo)
        image_label.photo = photo
        image_label.place(x=0, y=0)


class MainWindow(tk.Tk):
    def __init__(self, data):
        super().__init__()
        self.geometry("1920x1200+0+0")
        self.minsize(1920, 1200)
        self.resizable(False, False)
        self.overrideredirect(True)
        self.attributes('-topmost',True)

        ctk.CTkEntry(
            self,
            textvariable=self.data.overlay_file,
            width=360,
            font=("Ariel", 14, "bold"),
            placeholder_text="Sti til bilde 1 som skal behandles.(d:/bilde/abc.png)",
            justify=("left")).place(x=12,y=1010)


data = ApplicationData()
window = MainWindow(data)
top = OverlayWindow(data)
You should start studying Python classes. There is a reason that tkinter and customtkinter use classes. It is hard to write good looking GUI code without them.
Thanks for thee advices. I will probably change to oop affter Im finished without oop :)
Reply
#10
After doing following, using variations in transparency between main window hosting a fixed tkinger mapview and a historical map imagefile, seemes to work as expected.

SETTING TOPMOST TO FALSE IN MAIN WINDOW
window.attributes('-topmost',False)

SETTING TOPMOST TO TRUE IN TOP LEVEL WINDOW
top.attributes('-topmost',TRUE)

ASSIGNING TRANSPARENCY ATTRIBUTE TO TOP LEVEL WINDOW FROM EVENTFUNCTION LOCATED IN MAIN WINDOW

def slider1_event(value):
    top.attributes("-alpha",slider1.get())
    slide1val.configure(text=str(round(value,2)))
Activated slider with value initial set to 0.0 (no transparency.

Removed 4 duplicate lines.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  tkinter two windows instead of one jacksfrustration 7 898 Feb-08-2024, 06:18 PM
Last Post: deanhystad
  Tkinter multiple windows in the same window tomro91 1 868 Oct-30-2023, 02:59 PM
Last Post: Larz60+
  [Tkinter] Open tkinter colorchooser at toplevel (so I can select/focus on either window) tabreturn 4 1,926 Jul-06-2022, 01:03 PM
Last Post: deanhystad
  [Tkinter] Toplevel window menator01 5 3,082 Apr-18-2022, 06:01 PM
Last Post: menator01
  how to add two numbers and pass the result to the next page in tkinter? pymn 7 4,362 Feb-15-2022, 04:40 AM
Last Post: pymn
  [Tkinter] Not able to get image as background in a Toplevel window finndude 4 3,934 Jan-07-2022, 10:10 PM
Last Post: finndude
  [Tkinter] tkinter best way to pass parameters to a function Pedroski55 3 4,900 Nov-17-2021, 03:21 AM
Last Post: deanhystad
  [Tkinter] Update variable using tkinter entry methon drSlump 6 5,245 Oct-15-2021, 08:01 AM
Last Post: drSlump
  [Tkinter] tkinter.Menu – How to make text-variable? Sir 3 5,673 Mar-10-2021, 04:21 PM
Last Post: Sir
  [Tkinter] Images in Toplevel() finndude 4 4,333 Mar-09-2021, 09:39 AM
Last Post: finndude

Forum Jump:

User Panel Messages

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