Tkinter Toplevel not working - python

I'm trying to build a tkinter program that will take a search string from a text field on a main window, then create a child window with the results of the search string. Since I'm new tkinter I'm just trying to get the child window created and have the search string passed to that window for now. When running my program I receive the error:
AttributeError: '_tkinter.tkapp' object has no attribute 'Toplevel'
regarding line:
line 23, in search
which is:
results_window = self.parent.Toplevel(self)
Here is the code:
import tkinter as tk
#App Constants
APP_TITLE = 'TKINTER_TEST'
APP_ICON = '\icon\path.ico'
#Geo Constants
MAIN_WIDTH = 600
MAIN_HEIGHT = 300
MAIN_X = '-0'
MAIN_Y = '+0'
class MainApp():
def __init__(self, parent):
self.parent = parent
def get_search(self):
pass
def search(self, search_string):
results_window = self.parent.Toplevel(self)
def build_widgets(self):
search_string = tk.StringVar()
text = tk.Entry(root, textvariable = search_string).pack()
search_cmd = tk.Button(root, text="Search", command=self.search(search_string.get())).pack()
#MAIN
root = tk.Tk()
#root.geometry('500x300-0+0')
root.geometry('{}x{}{}{}'.format(MAIN_WIDTH, MAIN_HEIGHT, MAIN_X, MAIN_Y))
root.title(APP_TITLE)
main = MainApp(root)
main.build_widgets()
root.mainloop()
Am I approaching this the wrong way?

Toplevel is a widget like e.g. Frame
So the line 23 must be :
self.result_window = tk.Toplevel(self.parent)

Related

Getting input from another window in tkinter

I am having a little trouble with this project that I am working on. My project is this GUI application. In my test.py file, I call another file that contains instructions for another GUI window. This is where I am having trouble. In the test.py file, if you click run, a small window will appear. Click TEST in the small window. Then another window will appear that contains text fields if you enter numbers into the text fields for the window and then click enter. My IDE gets these error messages. It says that " ValueError: could not convert string to float: ' ' " My question is how do I fix this so that I do not get this error message? It is supposed to print the input that was entered into the window. I have two files, test.py, and model_objects.py. If you run model_objects.py by itself, it works perfectly. But when I try to import this file into test.py, it does not want to work right. This is programmed in Python. Also, my model_objects.py file is placed in a folder called util in the project. The values that I entered are floating-point values. I am having trouble with this. If you can help, I would greatly appreciate it.
Here is my code:
model_objects.py (This is in a folder called util in the project.)
import tkinter as tk
from tkinter import ttk
from tkinter.ttk import Style
import numpy as np
from util import InputData
class Harmonic_Oscillator:
def __init__(self):
self.type = 0
self.name = "Harmonic Oscillator"
self.nparam = 2
self.label = ["\u03BC", "k"]
self.param = np.zeros(self.nparam, float)
def set_param(self, param_list):
for i in range(self.nparam):
self.param[i] = param_list[i]
return
class Morse_Oscillator:
def __init__(self):
self.type = 1
self.name = "Morse Oscillator"
self.nparam = 3
self.label = ["\u03BC", "De", "a"]
self.param = np.zeros(self.nparam, float)
def set_param(self, param_list):
for i in range(self.nparam):
self.param[i] = param_list[i]
return
class Test_Oscillator:
def __init__(self):
self.type = 2
self.name = "Test Oscillator"
self.nparam = 4
self.mu = 0
self.label = ["a", "b", "c", "d"]
self.param = np.zeros(self.nparam, float)
def set_param(self, param_list):
for i in range(self.nparam):
self.param[i] = param_list[i]
return
def model_prompt(potential_model):
window1 = tk.Tk()
style = Style()
window1.title('PyFGH Parameters')
box_length = 103
for q in range(3):
box_length = box_length + 33 * potential_model[q].nparam
box_len_str = '300x' + str(box_length)
window1.geometry(box_len_str)
entries = []
qvar = np.empty(3, dtype=list)
for i in range(3):
qvar[i] = []
j = 0
y = 5
for q in range(3):
for qparam in range(potential_model[q].nparam):
qvar[q].append(tk.StringVar())
ttk.Label(window1, text=potential_model[q].label[qparam] + " for Q:" + str(q + 1) + ":",
font=("Times New Roman", 15)).place(x=50, y=y)
# set text variable as q1var[j] , each entry will have separate index in the list
a1 = ttk.Entry(window1, textvariable=qvar[q][qparam], font=("Times New Roman", 10)).place(x=140, y=y)
j += 1
y += 35
def enter_button():
for q in range(3):
param_list = []
for qparam in range(potential_model[q].nparam):
param_list.append(qvar[q][qparam].get())
potential_model[q].set_param(param_list) # This is giving me error. Not working properly!!!
for q in range(3):
for qparam in range(potential_model[q].nparam):
print(potential_model[q].param[qparam])
InputData.output.items.model_data = potential_model
print(InputData.output.items.model_data)
window1.destroy()
enter = tk.Button(window1, text='Enter', bd='20', bg='green', fg='white',
command=enter_button).place(x=110, y=y)
window1.mainloop()
def output2():
sections = []
for i in range(3):
if InputData.output.items.v[i] == "Model-Harmonic Oscillator":
sections.append(Harmonic_Oscillator())
elif InputData.output.items.v[i] == "Model-Morse Oscillator":
sections.append(Harmonic_Oscillator())
elif InputData.output.items.v[i] == "Model-Test Oscillator":
sections.append(Harmonic_Oscillator())
#test = [Harmonic_Oscillator(), Morse_Oscillator(), Test_Oscillator()]
#model_prompt(test)
Here is another file called test.py
from util import InputData
from util import model_objects
from util import model_objects
from util.model_objects import Harmonic_Oscillator, Morse_Oscillator, Test_Oscillator, model_prompt
import tkinter as tk
def write_slogan():
test = [Harmonic_Oscillator(), Morse_Oscillator(), Test_Oscillator()]
model_prompt(test)
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame,
text="QUIT",
fg="red",
command=quit)
button.pack(side=tk.LEFT)
slogan = tk.Button(frame,
text="TEST",
command=write_slogan)
slogan.pack(side=tk.LEFT)
root.mainloop()
It's a bit esoteric, but the issue is this line in your model_prompt function:
qvar[q].append(tk.StringVar())
The quick fix would be:
qvar[q].append(tk.StringVar(window1))
The tkinter variable types' constructors accept an optional parameter - a handle to the window to which the associated entry widget is attached. If none is supplied, by default, it will pick the first tk.Tk window that was instantiated (in your case, that was root in test.py). The StringVars will update themselves whenever the event queue of the bound window has cleared. Since you spawned a new tk.Tk, this interrupted your root event queue, and since your variables were bound to root, they did not update despite text being entered in the entries.
If you wanted to be super proper, you should look into tk.Toplevel or tkinter.simpledialog to spawn child windows. Your program should never have more than one tk.Tk window in the first place.
i made a little example, i hope it matches yourr mainproblem. if you want to work with classes, in the most cases you need references.
from tkinter import *
class MainWindow(Tk):
def __init__(self):
super(MainWindow, self).__init__()
self.test = Test(self) # test class import
self.outputLbl = Label(self) # output Label
self.outputLbl.pack(side="top", fill="x", ipady=20)
class Test(Frame):
def __init__(self, parent):
super(Test, self).__init__()
self.parent = parent # you can use this way to call between classes
self._input = Entry(self.parent)
self._input.pack()
self._input.bind("<Return>", self.outputMW)
def outputMW(self, event): # function, when pressing return it gives changes the text in your label
var = self._input.get()
self.parent.outputLbl.config(text=var) # self.parent makes the reference to your other class
if __name__ == '__main__':
mw = MainWindow()
mw.geometry("500x500")
mw.mainloop()

How to get variable from a class to another Tkinter window

The goal is to pass the variable string1 from class Display to be used in another Tkinter window.
So when the button named Next [in class Display load function] is clicked, it would open a new Tkinter window. And in the new window, the variable string1 from class Display needs to be retrieved for further action. May i know should i create another class Display2, or should i just add a method in the class Display?
Currently the string variable can be passed as reference from class Display to the class Action_Data. But how can it be passed to another Tkinter window when the button Next is clicked?
I am trying to get the variable via the callback function new_window. Just not sure if it's how it's done. Any pointer would be appreciated. Many thanks.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self):
self.master = tk.Tk() # create another Tk instance
var_string2 = Label(self, text="<<string1 value>>")
var_string2.pack()
print (var_string2)
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command=self.new_window)
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()
Issue
Now the new window is blank and cannot retrieve the value of variable string1 from the load function
Error
Use the lambda function to pass the button's command, that way you can pass the needed string as an argument.
You do not need to create two instances of Tk, if you need another window, create a Toplevel.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self, string):
#self.master.destroy() # close the current window
new_window = tk.Toplevel() # create toplevel
var_string2 = Label(new_window, text=string)
var_string2.pack()
new_window.focus()
print (var_string2.cget("text"))
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command= lambda : self.new_window(string1))
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()

How to save askdirectory result in a variable I can use using tkinter with OOP?

I have ran into some trouble.
I'm quite new to OOP and working with tkinter and GUI's in general.
I have managed to find some code on the Internet and meshed it all together to create something and I'm nearly where I want to be.
So what I want is some help figuring this out.
How can I assign results of askdirectory to a variable I can use elsewhere?
# coding=utf-8
import tkinter as tk
from tkinter import font as tkfont
from tkinter import filedialog
class MainApp(tk.Tk):
....
class SelectFunction(tk.Frame):
....
class FunctionChangeName(tk.Frame):
....
a = Gui(self)
# this gets me the askdirectory but how to add it to a variable?
Above is the call to run askdirectory code, and it works, just need to find out how to save it to a variable so I can use it, I have tried to print it in several ways, but all I get is something along the lines .!frame.!functionchangename.!gui.
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
self.initial = initial
self.selected = initial
self.options = {'parent': container,'title': title,'initialdir':initial,}
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
def get(self):
return self.selected
class Gui(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
frame = tk.Frame(container)
frame.pack()
self.seldir = SelectDir(self, "Select directory", "D:\\MyPgm\\Python\\Tiles_8")
button = tk.Button(frame, text="Select directory", command=self.select_dir)
button.grid(column=0, row=0)
self.act_dir = tk.StringVar()
self.act_dir.set("D:\\MyPgm\\Python\\Tiles_8")
entry = tk.Entry(frame, textvariable=self.act_dir, width=30)
entry.grid(column=0, row=1)
def select_dir(self):
self.seldir.show()
self.act_dir.set(self.seldir.get())
# or
# result = seldir.show()
# self.act_dir.set(result)
if __name__ == "__main__":
app = MainApp()
app.mainloop()
I had an idea:
example, if you have f inside a function, you can make it global to have access as variable
def print_path():
# select working directory
global f #make f global to access the path
f = filedialog.askdirectory(parent=root, initialdir="/", title='Select Dir')

import user input from TKinter button to a different .py module

On ticket.py module i have a Tkinter frame; value = Entry(frame), and on the same module I have a button where command=exoutput;
def exoutput():
print value.get()
I would like to import the value to othermodule.py on the button command/ when I hit the button.
Currently when I import, the print is generated from the exoutput() function, rather than from the othermodule.py file.
Suggestions on how to print the value on othermodule.py?
# ticket.py
from Tkinter import*
window = Tk()
window.title("Entry")
frame = Frame(window)
value = Entry(frame)
def exoutput():
print value.get()
btnStage = Button(frame, text='ACTION', command=exoutput)
btnStage.pack(side=RIGHT, padx=2)
value.pack(side=LEFT)
frame.pack(padx=10, pady=10)
window.resizable(0, 0)
window.mainloop()
The other file, I've tried something like this;
# othermodule.py
import ticket
usersinput = ticket.value.get()
print usersinput
I think you either need multi-threading, or swap your file contents. Nothing runs after mainloop until the Tk instance is destroyed.
Alternatively, you could structure your ticket.py in OOP, and fetch GUI object from it by your othermodule to use it as you please. Below is an example:
ticket.py:
#import tkinter as tk
import Tkinter as tk
class Window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Entry")
self.resizable(False, False)
# create widgets
self.frame = tk.Frame(self)
self.value = tk.Entry(self.frame)
self.btn_stage = tk.Button(self.frame, text="ACTION")
# widgets layout
self.frame.pack(padx=10, pady=10)
self.btn_stage.pack(side='right', padx=2)
self.value.pack(side='left')
if __name__ == "__main__":
root = Window()
root.mainloop()
and othermodule.py:
import ticket
def put():
global my_var_in_othermodule
my_var_in_othermodule = ticket_GUI.value.get()
ticket_GUI.destroy()
my_var_in_othermodule = ""
ticket_GUI = ticket.Window()
ticket_GUI.btn_stage['command'] = put
ticket_GUI.mainloop()
print(my_var_in_othermodule)
input()

Python, Tkinter library, Attribute Error in Object involving GUI

I'm making a very simple program for class that involves multiplying the number of a GUI slider by another number of another GUI slider. But, for some reason when I run the program now, I get an AttributeError saying that 'gui' object has no attribute 'slider1'. Any ideas? Here's the code:
import tkinter
import random
class gui:
def __init__(self):
self.main_window = tkinter.Tk()
#widgets
self.__canvas = tkinter.Canvas(self.main_window,bg='white',width=300,height=10)
self.label = tkinter.Label(self.main_window,text=('Product:',0))
self.slider1 = tkinter.Scale(self.main_window,from_=0, to=12)
self.slider2 = tkinter.Scale(self.main_window,from_=0, to=12)
#packs
self.__canvas.pack()
self.label.pack(side='top')
self.slider1.pack(side='left')
self.slider2.pack(side='right')
self.button = tkinter.Button(self.main_window,text='Click to multiply',command=self.multiply())
self.button.pack(side='bottom')
tkinter.mainloop()
def multiply(self):
x = int(self.slider1.get())
y = int(self.slider2.get())
num = x*y
self.label.config(text=('Product:',num))
gui()
There is a few syntax error in the program, I commented those. As well as you should put orientations on the scales. Here is the code.
import tkinter as tk
class gui:
def __init__(self):
self.root = tk.Tk()
# the widgets
self.button = tk.Button(self.root, text="Multiply!", command=self.multiply)
# you need no '()' for the function when inputing it in tkinter.
self.label = tk.Label(self.root, text="Product: 0") # the '0 must be a string
self.sliderX = tk.Scale(self.root, from_=0, to=12, orient=tk.HORIZONTAL)
self.sliderY = tk.Scale(self.root, from_=0, to=12, orient=tk.VERTICAL)
# add an orient to the scales.
# now pack the widgets.
self.button.pack()
self.label.pack()
self.sliderX.pack()
self.sliderY.pack()
def multiply(self):
x = int(self.sliderX.get())
y = int(self.sliderY.get())
num = str(x * y) # need to turn the int to a string.
self.label.config(text="Product: "+num)
app = gui()
app.root.mainloop()
The reason it isn't working for you is because there is no instance of the program. This is what I do at the very end. Python's garbage collecting collects the instance made with gui() and so Tkinter can't reference an instance of the class.

Categories