I am new to Tkinter and I am having some trouble getting my button command to switch to a different class.
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
button = tk.Button(self, text="Login", command=self.gotoMainMenu)
button.pack()
def gotoMainMenu(self):
root2=tk.Toplevel(self)
myGUI=MainMenu(root2)
def finish(self):
self.parent.destroy()
class MainMenu(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
button = tk.Button(self, text="Visit Page 1")
button.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
This is the current code I am working on. I have tried looking examples, but I can't seem to figure it out. I currently receive an attribute error. "AttributeError: MainApplication instance has no attribute 'gotoMainMenu'." Also, any errors you find or advice you have is greatly appreciated.
You simply forgot to pack your MainMenu frame. Add the following line to the end of your MainMenu class's constructor:
self.pack()
Related
I'm relatively new to OOPs and thus know it theoretically. I need a project to be implemented in Tkinter. So, I was going through it. I was just playing around with the most basic code like below
# https://python-textbok.readthedocs.io/en/1.0/Introduction_to_GUI_Programming.html
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
root = tk.Tk()
app = MainApplication(root)
app.mainloop()
Here, I can say confidently that MainApplication inherits tk.Frame. So, the root is a TK object and it is passed in MainApplication constructor which assigns it as the parent of the MainApplication object.
So, when we call the app.mainloop(), the MainApplication object is called which in turn can invoke the root somehow because we have the root as a parent of the app object.
Correct me if I understood it wrong.
Now, as I browsed and another popular method people are using is
# https://docs.python.org/2/library/tkinter.html#a-simple-hello-world-program
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
root = tk.Tk()
MainApplication(root)
root.mainloop()
Here also, as I can understand the MainApplication is invoked and root becomes the parent of it. But how does that link the MainApplication object with the root object, so that when the root.mainloop() is called, it can refer back to MainApplication object?
Any help is appreciated. Thanks in advance.
This is more about the actual structure of the tkinter module than it is about OOP principles.
mainloop is a method of the Misc class, which is an ancestor of both Tk and Frame. When called, it essentially calls the mainloop method using a reference it has to the root Tk object, so whether you invoke it from root or app, the result is the same.
I want to hide a window immediately after it is created. It works only if I do this with the help of button or something.
class Example(QWidget):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.hide() # doesn't work
self.btn = QPushButton('Hide', self)
self.btn.clicked.connect(self.click) # works
self.btn.show()
def click(self): # works
self.hide()
Apparently it seems that the code should work. What may be happening is that you are calling show() after creating the object. For example:
example = Example()
example.show()
Read this answer about hide() and show(): What's the difference in Qt between setVisible, setShown and show/hide
You can use QtCore.QTimer
class Example(QWidget):
def __init__(self, app):
QWidget.__init__(self)
QTimer.singleShot(0, self.hide)
My main target is to add something like an hidden tag or string to a widget, to save short information on it.
I got the idea of creating a new custom Button class (in this case I need buttons), which inherits all the old options.
This is the code:
form tkinter import *
class NButton(Button):
def __init__(self, master, tag=None, *args, **kwargs):
Button.__init__(self, master, *args, **kwargs)
self.master, self.tag = master, tag
No trouble when creating a new NButton instance:
aria1 = NButton(treewindow, bd=2, relief=GROOVE, text="Trasmissione\naerea 1", bg="#99c4ff", tag="aria 1")
aria1.place(x=20, y=20)
The problems come out when I try to get the value of tag:
aria1["tag"]
it returns:
_tkinter.TclError: unknown option "-tag"
How can I solve this?
You need to access your custom options as object attributes:
print(aria1.tag)
In the spirit of decoupling, rather than cramming many widgets into one giant class, I tried splitting them into separate classes.
The problem I ran into is the FileMenu class does not know about root, and so cannot call root.quit or root.destroy. On a whim I tried self.quit, and it works, but I have not seen this used before.
My questions are:
Is self.quit a safe way to quit the app?
Is sys.exit a safe way to quit the app?
If neither are safe, is there a good way to approach decoupling the design and safely quit without passing root as a parameter everywhere or making it a global?
My example:
import Tkinter as tk
class FileMenu(tk.Menu):
def __init__ (self, parent):
tk.Menu.__init__(self, parent, tearoff=False)
self.add_command(label='Exit', command=self.quit)
class MainMenu(tk.Menu):
def __init__ (self, parent):
tk.Menu.__init__(self, parent, tearoff=False)
self.file_menu = FileMenu(self)
self.add_cascade(label='File', menu=self.file_menu)
class View:
def __init__ (self, parent):
self.frame = tk.Frame(parent)
self.parent = parent
self.menu = MainMenu(self.frame)
self.parent.configure(menu=self.menu)
self.parent.geometry('200x200')
self.frame.pack(fill='both', expand=True)
class App:
def __init__ (self):
self.root = tk.Tk()
self.view = View(self.root)
def run (self):
self.root.title('Window Title')
self.root.mainloop()
if __name__ == '__main__':
app = App()
app.run()
Both use for different purpose:
sys.exit(): Close complete application.
self.quit: If you have multiple windows in your application and you don't want to close your whole application then you should use self.quit().
Both are safe to use but uses are different.
If the class doesn't know about the root object but you expect it to act on the root object, you should pass it in.
In App, you pass the root object into View:
self.view = View(self.root)
In View, you create a MainMenu and pass it self.frame:
self.menu = MainMenu(self.frame)
Instead, also pass it the root object:
self.menu = MainMenu(self.frame, parent)
And make MainMenu expect a parameter of that object, and pass it to FileMenu:
class MainMenu(tk.Menu):
def __init__ (self, parent, root):
tk.Menu.__init__(self, parent, tearoff=False)
self.file_menu = FileMenu(self, root)
self.add_cascade(label='File', menu=self.file_menu)
Then make FileMenu expect a parameter of that root object. Now it has access to it:
class FileMenu(tk.Menu):
def __init__ (self, parent, root):
tk.Menu.__init__(self, parent, tearoff=False)
self.add_command(label='Exit', command=root.destroy)
And you can use the Tkinter root object's destroy method to quit the application properly.
I would like to create a widget that has a child widget that I can dynamically change. Here is what I tried:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Widget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QVBoxLayout())
self.child = QLabel("foo", self)
self.layout().addWidget(self.child)
def update(self):
self.layout().removeWidget(self.child)
self.child = QLabel("bar", self)
self.layout().addWidget(self.child)
app = QApplication(sys.argv)
widget = Widget()
widget.show()
widget.update()
app.exec_()
The problem is that this doesn't actually remove the "foo" label visually. It is still rendered on top of "bar". Screenshot of the problem. How do I remove the old widget so that only the new widget is shown?
I know that I can change the text property of the label. This is not what I want in my application, I need to change the actual widget (to a different widget type).
removeWidget() only removes the item from the layout, it doesn't delete it. You can delete the child widget by calling setParent(None).
def update(self):
self.layout().removeWidget(self.child)
self.child.setParent(None)
self.child = QLabel("bar", self)
self.layout().addWidget(self.child)