0

I have 2 python Files, FirstWindowFile and SecondWindowFile. I create one button in 1st and I want, after pressing it, to create a new window with a button in the 2nd file. After pressing the button in the 2nd file, I need to change the value of the global variable in 1st.

FirstWindowFile code:

import tkinter as tk
from tkinter import*
import SecondWindowFile

root = Tk()  # create a main window
root.geometry("750x250")

global myglobal  # this is the global i want to change
myglobal = 0

btn1 = tk.Frame(root, borderwidth=5, relief="ridge")
btn1.grid(column=0, row=0)
# when I press this button I send the SecondWindowFile to ChangeValue()
Analyze = tk.Button(btn1, text="Analyze",
                    command=lambda: SecondWindowFile.ChangeValue()).grid(row=0, column=0)

# myglobal has to take new value (sent from SecondWindowFile) so to
# be used for new calculations
print(myglobal)
root.mainloop()

SecondWindowFile code:

import tkinter as tk
from tkinter import*


def changeMyNum():
    gl=1
    # I need this value of gl to be returned to the FirstWindowFile and be the
    # new value of global myglobal

def ChangeValue():
    secondWindow = Tk()  # create a 2nd window
    secondWindow.geometry("150x150")

    btn2 = tk.Frame(secondWindow, borderwidth=5, relief="ridge")  # create a button
    btn2.grid(column=0, row=0)
    # by pressing the button goes to changeMyNum()
    ChangeVal = tk.Button(secondWindow, text="Press to Change Global",
                          command=lambda: changeMyNum).grid(row=0, column=0)
martineau
  • 119,623
  • 25
  • 170
  • 301
runveran
  • 1
  • 1
  • 1
    Values returned from command callback functions are ignored, so there's no way to do it that way. It's also generally a bad idea to create more than one instance of `Tk`, see [Why are multiple instances of Tk discouraged?](https://stackoverflow.com/questions/48045401/why-are-multiple-instances-of-tk-discouraged) If you want another window call [`Toplevel`](https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/toplevel.html) instead. – martineau Oct 18 '21 at 00:28
  • you could use `command=SecondWindowFile.ChangeValue` without `lambda` and without `()`. ANd the same `command=changeMyNum` – furas Oct 18 '21 at 03:42
  • second file has no access to global values in first file - but if you would use `IntVar` as `myglobal` then you could send `myglobal` to other function as argument and use `myglobal.set(...)` to change its value. OR you would have to keep value on list or in dictionary and send it as argument to function and change value in this list or dictionary. – furas Oct 18 '21 at 03:45
  • all variable created outside functions are global and you don't need use `global myglobal`. You may need to use `global myglobal` only inside function to inform it that you want to assign new value to external/global variable. – furas Oct 18 '21 at 03:48
  • 1
    if you run `analyze = widget(...).grid()` then you assign `None` to `analyze` because `grid()`/`pack()`/`place()` returns `None`. If you need to use `analyze` in other place then you should do it in two steps `analyze = widget(...)` and `analyze.grid()` – furas Oct 18 '21 at 03:51
  • all code before `mainloop()` is executed before it even display window - so your `print(myglobal)` before `root.mainloop()` will always display `0`. You should display it in function assigned to `Button` - when you change value. – furas Oct 18 '21 at 03:52

3 Answers3

1

You can't access global from one file in another. And it is not good idea. You should use variable in an explicit way - if you would use list, dict or tk.IntVar, tk.StringVar to keep value then you could send it as argument and function could change value in list, dict or tk.IntVar, tk.StringVar and you could this value in other functions.

tk.IntVar can be also useful because you can assign it to label and when you change IntVar then it automatically update value in Label.

You have to only rembember that IntVar needs .get() and .set() to works with value.

main.py

myglobal = tk.IntVar(value=0)  # you have to create after `root`


Button(..., command=lambda:second_window.change_value(myglobal)

second_window.py

def change_value(variable):
    
    Button(..., command=lambda:change_my_num(variable))


def change_my_num(variable):  

    variable.set( variable.get()+1 )
    
    print(variable.get())

And now you can assing it to Label to display current value

Label(..., textvariable=myglobal)

Full code:

main.py

import tkinter as tk
#from tkinter import *  # PEP8: `import *` is not prefered
import second_window

# 
root = tk.Tk()  # create a main window
root.geometry("750x250")

myglobal = tk.IntVar(value=0)  # you have to create after `root`

btn1 = tk.Frame(root)
btn1.grid(column=0, row=0)

# PEP8: `lower_case_names` for variables
analyze = tk.Button(btn1, text="Analyze", command=lambda:second_window.change_value(myglobal))
analyze.grid(row=0, column=0)

l = tk.Label(btn1, textvariable=myglobal)
l.grid(row=1, column=0)

print(myglobal.get())  # it runs it before `mainloop` starts GUI and shows window - so it is useless.

root.mainloop()

print(myglobal.get())  # it runs it after closing window

second_window.py

import tkinter as tk  # PEP8: `import *` is not preferred

def change_my_num(variable):  # PEP8: `lower_case_names` for functions

    variable.set( variable.get()+1 )
    
    print(variable.get())
    

def change_value(variable):
    second_window = tk.Toplevel()  # use `Toplevel to create a 2nd window
    
    b = tk.Button(second_window, text="Press to Change Global", command=lambda:change_my_num(variable))
    b.grid(row=0, column=0)

PEP 8 -- Style Guide for Python Code


PEP 20 -- Python Zen

... Explicit is better than implicit. ...
martineau
  • 119,623
  • 25
  • 170
  • 301
furas
  • 134,197
  • 12
  • 106
  • 148
  • 1
    @runveran: Besides not being explicit, [Global variables are bad](http://wiki.c2.com/?GlobalVariablesAreBad) and [Global Variables Considered Harmful](http://wiki.c2.com/?GlobalVariablesConsideredHarmful). One simple way to avoid them is by explicitly passing them (as a, or inside a, mutable container) as arguments as is shown in this answer, another is to define and use a class that represents your application. – martineau Oct 18 '21 at 08:39
1

My answer is very similar to @furas', but illustrates how to define and use a class to avoid (or at least minimize) using global variables — which won't work for what you want to do anyway. In this case by storing it in a tkinter IntVar and explicitly passing that as an argument to a function defined in the file of another module.

I've also largely tried to follow PEP 8 - Style Guide for Python Code as he (and I) strongly recommend, especially with respect to those in the naming conventions section (which also apply to module file names).

first_window.py

import tkinter as tk
import second_window as sw


class MyApp:
    def __init__(self,  master):
        self.frame = tk.Frame(master, borderwidth=5, relief="ridge")
        self.frame.grid(row=0, column=0)
        self.my_var = tk.IntVar(master=master, value=0)
        analyze_btn = tk.Button(self.frame, text="Analyze",
                                command=lambda: sw.change_value(self.my_var))
        analyze_btn.grid(row=0, column=0, columnspan=2)
        tk.Label(self.frame, text="My var:").grid(row=1, column=0)
        tk.Label(self.frame, textvariable=self.my_var).grid(row=1, column=1)

if __name__ == '__main__':
    root = tk.Tk()  # Create a main window.
    root.geometry("750x250")
    my_app = MyApp(root)
    root.mainloop()

second_window.py

import tkinter as tk


def change_my_num(var):
    var.set(var.get() + 1)  # Increment value in IntVar.


def change_value(var):
    second_window = tk.Tk()  # Create a 2nd window.
    second_window.geometry("150x150")

    frame2 = tk.Frame(second_window, borderwidth=5, relief="ridge")
    frame2.grid(row=0, column=0)

    change_val_btn = tk.Button(frame2, text="Press to Change Global",
                               command=lambda: change_my_num(var))
    change_val_btn.grid(row=0, column=0)

    done_btn = tk.Button(frame2, text="Done", command=second_window.destroy)
    done_btn.grid(row=1, column=0)
martineau
  • 119,623
  • 25
  • 170
  • 301
0

Here's a more general answer to the problem I think your having:

Starting with the SecondWindowFile, define a function that creates a GUI that ends itself and returns a variable when a button is pressed.

In FirstWindowFile define your variable as the return of your SecondWindowFile function.

Eg:

FirstWindowFile:
global myVar
def onButtonPress():
    return SecondWindowFile.getValue()
myVar = onButtonPress()

SecondWindowFile:
def getValue():
   def onButtonPress():
      return 'The Value You Want'

getValue() being the function you created that returns the var

If you need further clarification, please ask!

SlavaCat
  • 101
  • 6
  • 1
    First `global myVar` does not create the variable. Second `myVar` inside `onButtonPress()` (in FirstWindowFile) is a local variable, not the global one you expect because you haven't declared it as global inside the function. Finally `getValue()` does not return anything. – acw1668 Oct 18 '21 at 05:44
  • This wasn't made to be fleshed out code functioning code but more of a template to guide in the general direction. As for the global variable defining, I probably should have tested this, but the main point was that the variable was being set to the return of a function. – SlavaCat Oct 18 '21 at 12:48