Python Forum

Full Version: pass a variable between tkinter and toplevel windows
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
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()
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.
A method that works well to send messages between queues is explained in this thread
(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.
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.
Thanks for input. I found a solution and will post it soon.
(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)
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.
(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 :)
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.
Pages: 1 2