Open existing class with button in another class - python

I have a problem with opening existing class from button which is in another class.
I don't have any idea how to make it working because I know I should in function add (Toplevel) but it's also not working.
class na_dane(object):
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.pack()
# CODE ........
def close_windows(self):
self.master.destroy()
# this code opens another window (class) which I have defined but not included here...
def new_window(self):
self.newWindow = tk.Toplevel.iconify(self.master)
self.app = sinus(self.newWindow)
# here is the main class where when I click button it should open na_dane Class
class menu(na_dane):
def __init__(self, master):
self.master = master
przycisk_Jednostkowo_Liniowa = tk.Button(
text="Funkcja jednostkowo-liniowa",
width=250,
height=2,
bg="blue",
fg="yellow"
).pack()
# HERE IS THE PROBLEM WHERE I TRY TO BIND BUTTON TO CLASS
przycisk_Jednostkowo_Liniowa.bind("<Button>", lambda e: na_dane(master))
przycisk_Jednostkowo_Liniowa.pack()
def main():
root = tk.Tk()
root.geometry("250x200")
root.title("GRUPA 3")
root['bg'] = '#FFFFFF'
app = menu(root)
root.mainloop()
if __name__ == '__main__':
main()

The problem seems to be that you attempt to pack przycisk_Jednostkowo_Liniowa in the same line as it is created. What this line of code is doing is creating a button with the characteristics described in your arguments, applying .pack() to it, then assigning the output of that .pack() execution to przycisk_Jednostkowo_Liniowa. Because .pack() doesn't have a return value, przycisk_Jednostkowo_Liniowa becomes None, so when you later attempt to bind a lambda to it, it doesn't recognize .bind() as a valid method. I tested the code with that .pack() removed and it worked for me.

Related

tkinter: Why am I getting a small window plus my main window and gridding is off? __init__ problem?

Creates two windows and gridding is not correct. Some additional comments in the code initiation.
I have used this approach, without the super init with no problem, many times.
Advice appreciated.
Thanks
# timhockswender#gmail.com
import tkinter as tk
from tkinter import ttk
class constants_page(tk.Frame):
def __init__(self):
super(constants_page, self).__init__() # from stackoverflow
# if not used error = 'constants_page' object has no attribute 'tk'
# if used, another tiny window is opened
# in addtion to the constants_page
self.constants_page = tk.Tk()
self.constants_page.geometry("1000x500") #width*Length
self.constants_page.title("Owen's Unit Conversion App")
self.constants_page.configure(background='light blue')
self.CreateWidgets()
def CreateWidgets(self):
self.value_label = ttk.Label(self.constants_page,text="Value----->" , width =10 )
self.value_label.grid(row=0, column=1, columnspan=1, sticky='nse')
# Problem: not gridding properly
self.title_label = ttk.Label(self.constants_page, text="Important Physical Constants",
anchor=tk.CENTER, font=("Arial",20)).grid(row=2, columnspan=2)
for r in range(2):
self.constants_page.rowconfigure(r, weight=1, uniform='row')
for c in range(2):
self.constants_page.columnconfigure(c, weight=1 )
def Show_Page():
# Create the entire GUI program
program = constants_page()
program.mainloop()
if __name__ == "__main__":
Show_Page()
The super call expects you to provide a root window (an instance of tk.Tk()). If you don't provide one it defaults to the first root window opened, and if none has been opened yet then it helpfully opens one for you. A few lines later you open a second one yourself.
The easy fix is to remove the self.constants_page = tk.Tk() line. The proper fix is to make the Tk() instance outside of the class and pass it in. This allows you to use the Frame class itself to lay out widgets (use self instead of self.constants_page). Try this:
import tkinter as tk
from tkinter import ttk
class constants_page(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
master.geometry("1000x500") #width*Length
master.title("Owen's Unit Conversion App")
self.configure(background='light blue')
self.CreateWidgets()
def CreateWidgets(self):
self.value_label = ttk.Label(self,text="Value----->" , width =10 )
self.value_label.grid(row=0, column=1, columnspan=1, sticky='nse')
self.title_label = ttk.Label(self, text="Important Physical Constants",
anchor=tk.CENTER, font=("Arial",20)).grid(row=2, columnspan=2)
for r in range(2):
self.rowconfigure(r, weight=1, uniform='row')
for c in range(2):
self.columnconfigure(c, weight=1 )
def Show_Page():
# Create the entire GUI program
program = tk.Tk()
win = constants_page(program)
win.pack()
program.mainloop()
if __name__ == "__main__":
Show_Page()

Updating value of attribute of a created instance of a class from an instance of another class created later, in tkinter python 3

MainTicTacToe.py
import tkinter as tk
import MenubarCommand as mbc
class Game(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self,parent)
self.parnt=parent
# self.parnt.geometry('500x300')
self.parnt.title("Tic Tac Toe")
# self.pack()
menubar=tk.Menu(parent)
# 'settings' menu
settingsOption=tk.Menu(menubar, tearoff=0)
settingsOption.add_command(label='Player Settings', command=self.doit)
settingsOption.add_command(label='Board Settins', command=self.doit)
menubar.add_cascade(label='Setings', menu=settingsOption)
# without using this method, menubar isn't shown in Frame
self.parnt.config(menu=menubar)
def doit(self):
root=self.win()
set=mbc.playerSettings(root)
# print(set.p1name)
root.mainloop()
def win(self):
return tk.Tk()
def main():
root=tk.Tk()
Game(root)
root.mainloop()
main()
MenubarCommand.py
import tkinter as tk
class playerSettings(tk.Frame):
def __init__(self,parent=tk.Frame):
tk.Frame.__init__(self,parent)
self.parnt=parent
self.parnt.title("Player Setting")
self.p1name='Player 1'
self.p2name='Player 2'
self.p1symbol='x'
self.p2symbol='o'
# **********************************#
self.p1NameLabel = tk.Label(parent, text='Player 1: Name ')
self.p1NameLabel.grid(row=0, column=0)
self.p1NameEntry = tk.Entry(parent)
self.p1NameEntry.insert(0,self.p1name)
self.p1NameEntry.bind('<FocusIn>', lambda event:self.p1NameEntry.delete(0,'end'))
self.p1NameEntry.grid(row=0, column=1)
apply=tk.Button(parent, text="Apply Settings", fg='white', bg='gray', command=self.saveStat)
apply.grid(row=2, rowspan=1, columnspan=4)
def saveStat(self):
print('Settings Window Destroyed')
self.p1name=self.p1NameEntry.get()
print(self.p1name)
self.parnt.destroy()
I want to change the value of attribute of an instance in one file from the instance of another class in another file already created.
When I change default Player name in MenubarComman.py file, I want to access the changed name from MainTicTacToe.py class. How can I do this?
I'm new new in Python.
Thanks in Advance.
You problems stem from 2 instances of Tk(), and sloppy programming, i.e. sometimes you use parent, and sometimes self.parnt which is a bad habit to get into, so everything is changed to self.top so an error will pop up if any of those two remains.. You also have to have a way to signal when PlayerSetting() is finished. The way the program is structured, the only way that I know of is to check for "alive" using recursion. You could also have PlayerSettings call a function in Game() when finished, which would print the value. I have cleaned up your code and it works as I understand it should.. Note that the 2 classes are in the same file to make it easier to test and post here.
import tkinter as tk
##import MenubarCommand as mbc
class Game():
def __init__(self,parent):
self.parnt=parent
# self.parnt.geometry('500x300')
self.parnt.title("Tic Tac Toe")
# self.pack()
menubar=tk.Menu(parent)
# 'settings' menu
settingsOption=tk.Menu(menubar, tearoff=0, bg="yellow")
settingsOption.add_command(label='Player Settings', command=self.doit)
settingsOption.add_command(label='Board Settins', command=self.doit)
menubar.add_cascade(label='Setings', menu=settingsOption)
# without using this method, menubar isn't shown in Frame
self.parnt.config(menu=menubar)
def doit(self):
## do not open more than one Tk() instance
##root=self.win()
self.top=tk.Toplevel(self.parnt)
self.set_it=PlayerSettings(self.top)
self.get_variable()
##root.mainloop()
def get_variable(self):
""" check continuously if PlayerSettings has exited and
if so, get the Entry's value
"""
if self.set_it:
self.parnt.after(1000, self.get_variable)
else:
print("from Game", self.set_it.p1name)
def win(self):
return tk.Tk()
class PlayerSettings():
def __init__(self, parent):
self.top=parent
self.p1name='Player 1'
self.p2name='Player 2'
self.p1symbol='x'
self.p2symbol='o'
# **********************************#
self.p1NameLabel = tk.Label(self.top, text='Player 1: Name ', bg="lightblue")
self.p1NameLabel.grid(row=0, column=0)
self.p1NameEntry = tk.Entry(self.top)
self.p1NameEntry.focus_set()
self.p1NameEntry.insert(0,self.p1name)
##self.p1NameEntry.bind('<FocusIn>', lambda event:self.p1NameEntry.delete(0,'end'))
self.p1NameEntry.grid(row=0, column=1, sticky="nsew")
apply=tk.Button(self.top, text="Apply Settings", fg='white', bg='gray', command=self.saveStat)
apply.grid(row=2, rowspan=1, columnspan=4)
def saveStat(self):
self.p1name=self.p1NameEntry.get()
print(self.p1name)
print('Settings Window Destroyed')
self.top.destroy()
root=tk.Tk()
Game(root)
root.mainloop()

Submit button not showing in second module in Tkinter

I have two modules file1.py and file2.py. In file1.py i have created a class and a function with label and entry widgets. In file2.py, i inherit the class of file1.py and create a submit button in a function. So, when i click the submit button, the value entered in entry widget in file1.py should be displayed. But what i observe is, submit button is not dislayed and when i close the window, the entered value is displayed. I'm unable to understand this behavior, can anyone correct my mistake.
file1.py
from Tkinter import *
top = Tk()
class TestClass(object):
def __init__(self, master = None):
self.frame = Frame(master)
self.frame.pack()
self.func()
def func(self):
self.label = Label(self.frame, text = "LabelName")
self.label.pack()
self.x = StringVar()
self.entry = Entry(self.frame, textvariable=self.x)
self.entry.pack()
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
file2.py
from file1 import *
class ImportClass(TestClass):
def __init__(self):
super(ImportClass,self).__init__(master=None)
self.imp_func()
def imp_func(self):
def get_func():
print app.x.get()
self.s = Button(self.frame, text="Submit", command=get_func())
self.s.pack()
Im = ImportClass()
I see your problem, to get this to work you have to fix a few things:
First, you need to use your imported class as app, which has the submit button, to be still able to run just file1 you can check in file1.py the __name__ whether it's '__main__' like:
if __name__ == '__main__':
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
Secondly, your function is not called because you call the function and give the result to Button, here you should just pass the function without calling it:
self.s = Button(self.frame, text="Submit", command=get_func())
in the function itself you should not use a global variable like app, because for example if you have multiple instances of the same class they would all depend on one instance and in the TestClass you have set self.x which is also accessible in ImportClass so you should replace the print statement with print self.x.get() instead of print app.x.get() to set the master from ImportClass to top. I also added *args and **kwargs to be passed on in the __init__ method so all in all you get:
file1.py
from Tkinter import *
class TestClass(object):
def __init__(self, master = None):
self.frame = Frame(master)
self.frame.pack()
self.func()
def func(self):
self.label = Label(self.frame, text = "LabelName")
self.label.pack()
self.x = StringVar()
self.entry = Entry(self.frame, textvariable=self.x)
self.entry.pack()
if __name__ == '__main__':
#just run if the file is called as main
top = Tk()
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
and file2.py
from file1 import *
from Tkinter import *
class ImportClass(TestClass):
def __init__(self, *args, **kwargs):
#passing all args and kwargs to super
super(ImportClass,self).__init__(*args, **kwargs)
self.imp_func()
def imp_func(self):
def get_func():
print self.x.get()
#using class property instead of global
self.s = Button(self.frame, text="Submit", command=get_func)
#pass function not it's return value which has been None
self.s.pack()
if __name__ == '__main__':
top = Tk()
app = ImportClass(master = top)
#using the ImportClass to display the window
top.minsize(400, 400)
top.mainloop()
so this should work. Hopefully, this helps you to prevent further problems like this.
The main reason you're having trouble is that no lines will run after mainloop until the Tk instance is closed or an event happened. When you import file1, mainloop is eventually run and then the GUI is waited to be closed in order to first define the ImportClass and then later to initialize an object for it.
Simply remove:
top.mainloop()
from file1 and add:
top.mainloop()
to file2 as the last line.
After which there's another issue, command option of a button expects a reference to a callable object, as opposed to an actual call. Replace:
self.s = Button(..., command=get_func())
with:
self.s = Button(..., command=get_func)
Also, note that I think your imports are in reverse order, obtain GUI objects by the module that has the Tk instance as opposed to vice-versa.

Python Tkinter, how to disable a button in a class?

I have seen many explanations of how to turn an enabled button disabled but not when classes are involved. The error here is in the line 'button_1.config...' and the error message is that button_1 is not defined. I think this is because it is in a different method but im not sure how to disable a button from a different method. any help is appreciated.
from tkinter import *
class menu:
def __init__(self, master):
self.master = master
button_1 = Button(self.master, text = 'test', command = self.correct).pack()
def correct(self):
button_1.config(state = DISABLED)
def window():
root = Tk()
menu(root)
root.mainloop()
if __name__ == '__main__':
window()
The button needs to be an instance variable, if you're accessing it between methods in the class. Just add self. in front of it. It's also going to need to be packed on a separate line, otherwise the instance variable self.button_1 will return None:
class menu:
def __init__(self, master):
self.master = master
self.button_1 = Button(self.master, text = 'test', command = self.correct)
self.button_1.pack()
def correct(self):
self.button_1.config(state = DISABLED)

Tkinter Large Text Dialogue (save string on button click)

I am trying to make a large text-entry popup as part of a gui. The idea is to get paragraph-long user-input. The problem is that the method get_big_text() returns before the button is pushed. How can I have a separate window pop-up like this, and be able to save the user's text to a variable in my control program? Everything else in my program has been working out, until I tried to implement this. I am new to gui programming. I get the feeling that there is something fundamentally different about waiting for user input here, but I can't wrap my head around it in the functional context.
My goal is to have the line print(foo.get_big_text()) print the user's text, but of course it prints None because the get_big_text() method finishes.
I have left out the details of the rest of the gui, and wrote an __init__() that probably doesn't need to be there, but this is the basics of how my gui is coming along. The Toplevel widget is the only widget in my gui that is not somehow connected to root.
from tkinter import *
class Gui:
def __init__(self, root):
tframe = Frame(root)
tframe.pack(side='top')
bframe = Frame(root)
bframe.pack(side='bottom')
self.txt = Text(tframe)
self.txt.insert('0.0', 'Totally foobar')
self.txt.pack()
self.btn = Button(bframe, text='OK')
self.btn.pack()
def get_big_text(self, title='', text=''):
popup = Toplevel(height=160, width=180)
popup.title(title)
txtframe = Frame(popup)
txtframe.pack()
big_text = Text(txtframe)
big_text.insert('0.0',text)
big_text.pack()
btnframe = Frame(popup)
btnframe.pack()
grab_text = Button(btnframe)
grab_text.config(text="Done", command=lambda: big_text.get('0.0', 'end'))
grab_text.pack()
root=Tk()
root.title('Example')
foo = Gui(root)
print(foo.get_big_text())
root.mainloop()
You should pass the text to one function in your class and then do whatever you want with it (like printing):
from Tkinter import *
class Gui:
def __init__(self, root):
tframe = Frame(root)
tframe.pack(side='top')
bframe = Frame(root)
bframe.pack(side='bottom')
self.txt = Text(tframe)
self.txt.insert('0.0', 'Totally foobar')
self.txt.pack()
self.btn = Button(bframe, text='OK')
self.btn.pack()
def f(self, text):
print(text)
def get_big_text(self, title='', text=''):
popup = Toplevel(height=160, width=180)
popup.title(title)
txtframe = Frame(popup)
txtframe.pack()
big_text = Text(txtframe)
big_text.insert('0.0',text)
big_text.pack()
btnframe = Frame(popup)
btnframe.pack()
grab_text = Button(btnframe)
grab_text.config(text="Done", command=lambda: self.f(big_text.get('0.0', 'end')))
grab_text.pack()
root=Tk()
root.title('Example')
foo = Gui(root)
foo.get_big_text()
root.mainloop()
If you want to print the text after the gui finished you can do this modifications:
On Gui.f:
def f(self, text):
self.text = text
At the end of your code:
root.mainloop()
print(foo.text)
After some suggestions from #xndrme, and some hard thinking, I realized the solution is quite simple. It's just that I'm not used to programing in this functional style. Really fun to discover this, though.
I wanted the get_big_text() method to return the text so that I could pass it somewhere else and "do something" with it when the text comes. The solution was to pass an anonymous function to the method and "tell it" what should be done with it when it does come.
Note the new callback parameter in get_big_text()
from tkinter import *
class Gui:
def __init__(self, root):
tframe = Frame(root)
tframe.pack(side='top')
bframe = Frame(root)
bframe.pack(side='bottom')
self.txt = Text(tframe)
self.txt.insert('0.0', 'Totally foobar')
self.txt.pack()
self.btn = Button(bframe, text='OK')
self.btn.pack()
def get_big_text(self, callback, title='', text=''):
popup = Toplevel(height=160, width=180)
popup.title(title)
txtframe = Frame(popup)
txtframe.pack()
big_text = Text(txtframe)
big_text.insert('0.0',text)
big_text.pack()
btnframe = Frame(popup)
btnframe.pack()
grab_text = Button(btnframe)
grab_text.config(text="Done", command=lambda:callback(big_text.get('0.0', 'end')))
grab_text.pack()
root=Tk()
root.title('Example')
foo = Gui(root)
foo.get_big_text(lambda x:print(x))
root.mainloop()
The general flow for a dialog is to create the window, then call wait_window to wait until the window has been dismissed by the user. Your function can then return whatever you want.
There's a bit of a chicken-and-egg thing going on, in that you need to get the value from the dialog before the dialog is destroyed since the text widget will be destroyed when the toplevel is destroyed. You do this by explicitly managing the destruction of the window (read: get the value before actually destroying the window).
Here's a working example, trying to preserve as much as code as possible but without using a global import:
import Tkinter as tk
class CustomDialog(object):
def __init__(self, parent, title="Enter a paragraph", default_text=""):
self.parent = parent
self.title = title
self.default = default_text
def show(self):
self.popup = tk.Toplevel(self.parent)
self.popup.title(self.title)
txtframe = tk.Frame(self.popup)
txtframe.pack()
self.big_text = tk.Text(txtframe)
self.big_text.insert('1.0',self.default)
self.big_text.pack()
btnframe = tk.Frame(self.popup)
btnframe.pack()
grab_text = tk.Button(btnframe)
grab_text.config(text="Done", command=self.done)
grab_text.pack()
# make sure our "done" method gets called even if the
# user destroys the window
self.popup.wm_protocol("WM_DELETE_WINDOW", self.done)
# wait for the window to be destroyed
root.wait_window(self.popup)
return self.data
def done(self, *args):
# get the data from the window, then destroy
# the window and return to the caller
self.data = self.big_text.get("1.0", "end-1c")
self.popup.destroy()
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
b = tk.Button(self, text="Get Input", command=self.go)
b.pack()
def go(self):
dialog = CustomDialog(self, default_text="totally foobar")
result = dialog.show()
print "result:", result
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Another option that you have is to pass a callback to your dialog, and tie that callback to the "done" button. That way, whenever the user clicks the button, you execute the callback to do whatever you want with the data before destroying the window.
That is how you implement a non-modal dialog, since you don't necessarily have to destroy the window. Font dialogs are a good example of this, where you might want to keep the dialog open for quite a while, and affect whatever is currently selected.
The effbot site has a decent writeup on dialogs. See http://effbot.org/tkinterbook/tkinter-dialog-windows.htm

Categories

Resources