Pararell instance of tkinter application window python - python

I want to create some simple tkinter python app (like StickyNotes on Windows), i have create the class mainApplication and i do not know how to by just simply triggering the button create another instance of this class which will be displayed pararell to other window (or even multiple windows). I know how to assigned function to pushButton, and other simple stuff but the problem is with this pararell window displaying. Thanks in advance for help.
class mainApplication(Frame):
_ids = count(0)
def __init__(self, parent):
""" """
self.id = next(self._ids)
Frame.__init__(self, parent)
self.parent = parent
self.parent.minsize(width=200,height=100)
self.parent.geometry(('%dx%d+%d+%d' % (200, 100, 1700, 0+self.id*100)))
self.initUI()
def initUI(self):
""" """
self.parent.title("a2l")
self.pack(fill=BOTH, expand=True)
style = Style()
style.configure("TFrame", background="#333")
frame1 = Frame(self, style="TFrame")
frame1.pack(fill=X)
self.lbl0 = Label(frame1, text="api", width=7, background="#333", foreground = "red")
self.lbl0.pack(side=TOP, padx=5, pady=5)
self.closeButton = Button(self, text="new", command = self.createNewInstance)
self.closeButton.pack(side=RIGHT, padx=5, pady=5)
#=======================================================================
# self.generateButton = Button(self, text="GENERATE", command = self.)
# self.generateButton.pack(side=RIGHT, padx=5, pady=5)
#=======================================================================
def createNewInstance(self):
y = mainApplication()
return y
if __name__ == "__main__":
root = Tk()
x = mainApplication(root).pack(side="top", expand=False)
Tk().mainloop()

You shouldn't create more than one Tk() window in one application with tkinter. Instead tkinter provides a widget called Toplevel which can be useful for this kind of thing.
It creates another window which can exist along side the Tk() window and alongside other Toplevel widgets.
You could use this to create a series of persistent windows with whatever text or widgets you wanted on them at any kind of trigger including a Button.
See my example below for a demonstration:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.top = [] #list to contain the Toplevel widgets
self.entry = Entry(self.root)
self.button = Button(self.root, text="Create window", command=self.command)
self.entry.pack()
self.button.pack()
def command(self): #called when button is pressed
self.top.append(Toplevel(self.root)) #adds new Toplevel to the list
Label(self.top[len(self.top)-1], text=self.entry.get()).pack() #Adds label equal to the entry widget to the new toplevel
root = Tk()
App(root)
root.mainloop()

Related

Python tkinter 2 frames merge together

Hey guys I have to classes that both create a Frame. The first one contains a button that is supposed to close its frame. The second frame simply contains a Label. My code should first create the frame with the button and when the button is pressed the second window should show up. What happens is that when pressing the button a "merged" window is created that contains the button and the label.
import tkinter as tk
class Window1(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.grid()
self.btn = tk.Button(self,text = "button",command = self.run)
self.btn.grid(row = 0,column = 0)
def run(self):
tk.Frame.quit(self)
class Window2(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.grid()
self.label = tk.Label(self,text = "label ")
self.label.grid(row = 0,column = 0)
w = Window1()
w.mainloop()
v = Window2()
v.mainloop()
The first picture is before you press the button, the next one after you pressed the button. The problem seems that tk.Frame.quit(self) doesn't work correctly. I tried similar ways to close the window such as:
tk.Frame.destroy(self)
but that doesn't help either.
edit: I solved it by inheriting the class from tk.TK instead of tk.Frame
Frame doesn't create window - it only group elements. Tk() creates window.
To close window you have to destroy() object create by Tk(). But you don't creat it manually root = tk.Tk() so tkinter create it automatically, but you have no access to this root to close it.
If widget doesn't have parent then it uses root and your Frame does it too.
import tkinter as tk
class Window1(tk.Frame):
def __init__(self, master):
# send `root` to `Frame` as its parent
tk.Frame.__init__(self, master)
# `Frame` will keep `master as `self.master`
# so we don't have to do `self.master = master` manually
self.grid()
self.btn = tk.Button(self, text="Hello Button", command=self.run)
self.btn.grid(row=0, column=0)
def run(self):
# use `master` (`root`) to destroy it
self.master.destroy()
class Window2(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.grid()
self.label = tk.Label(self, text="Hello Label")
self.label.grid(row=0, column=0)
root = tk.Tk() # create main window as `root`
Window1(root) # send `root` to `Window1` and later to `Frame`
root.mainloop()
root = tk.Tk()
Window2(root)
root.mainloop()

how to insert a widget to another, if they are from different classes. and how to create a universal function of closing the child widgets?

this is my code. I can't put label2 to self.main, and I don't know, how to write a generic function code, that would close the child widgets, that can be specified in the arguments.
import tkinter
class mainwin:
def __init__(self):
self.root = tkinter.Tk()
self.main = tkinter.Canvas(self.root, width=200, height=400)
self.main.place(x=0, y=0, relwidth=1, relheight=1)
self.main.config(bg='green')
self.root.mainloop()
class addlabel:
def __init__(self):
self.label2 = tkinter.Label(mainwin.main, height=2, width=50, text='Hello Noob!!')
#can't put on the canvas 'main'
self.label2.place(x=0, y=50)
self.exit_button = tkinter.Button(self.label2, text='Exit')
self.exit.button.bind('<1>', quit_from_widget)
'''
class quit_from_widget:
def __init__(self):
# what code should be written here, to quit any child widget.
'''
mainwin()
addlabel()
You might be able to use:
mylist = parent.winfo_children();
Then use a for loop and destroy() to close them off
The main reason you can't put the label in is because you are calling mainloop() before addLabel. The program loops through the code and doesn't execute addlabel() until you close the mainwin() function.
secondly, you can't do mainw.main. the class has no reference to that function. instead try adding a parent function to your addlabel like so:
class addlabel:
def __init__(self, parent):
self.label2 = tkinter.Label(parent, height=2, width=50, text='Hello Noob!!')
self.label2.place(x=0, y=50)
self.exit_button = tkinter.Button(self.label2, text='Exit')
self.exit_button.bind('<1>', quit)
Then, when you call the function in mainw class (before the self.root.mainloop() line) you would write:
addlabel(self.main)
import tkinter
class mainwin:
def __init__(self):
self.root = tkinter.Tk()
self.main = tkinter.Canvas(self.root, width=200, height=400)
self.main.place(x=0, y=0, relwidth=1, relheight=1)
self.main.config(bg='green')
self.root.mainloop()
class CustomLabel(tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
self.label = tkinter.Label(self, height=20, width=30, bg='Red', fg='white', text='Hello')
self.exit_button = tkinter.Button(self, command=self.destroy)
# pack these widgets into this frame. You can use grid or
# place, but pack is easiest for such a simple layout
self.exit_button.pack(side="right")
self.label.pack(side="left", fill="both", expand=True)
window = mainwin()
label = CustomLabel(window.main)
nothing happens. gives green background and the child widget is not visible. but when closing, he writes an error:
....
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "frame" command: application has been destroyed
Process finished with exit code 1

Blocking Input Dialog Box

How can I get a blocking modal input dialog box in standard Python?
I need the user to input a value before the code can proceed.
Here is some not-working test code, but the idea is that I should be able to call MyDialog from anywhere in the script, so this is just a simplified example.
import tkinter
class MyDialog:
def __init__(self, prompt):
self.top = tkinter.Toplevel()
tkinter.Label(self.top, text=prompt).pack()
self.e = tkinter.Entry(self.top)
self.e.pack(padx=5)
tkinter.Button(self.top, text="OK", command=self.ok).pack(pady=5)
def ok(self):
self.top.destroy()
return self.e.get()
root = tkinter.Tk()
userName = MyDialog('Enter your name')
tkinter.Label(root, text="Hello {}".format(userName)).pack()
root.mainloop()
The dialog should not only disable the master window, but block whatever code called it. And it should be able to pass the value back to the calling code.
The solution requires two critical pieces. First, use grab_set to block all events in the other window (or, more correctly, send all events to the dialog window). Second, use wait_window to prevent the method from returning until the dialog has been destroyed.
That being said, you shouldn't be using it like in your example. You need to have the mainloop running before you create the window. It might work OK on some platforms, but in general you shouldn't expect your GUI to behave properly until mainloop is running.
Here's a simple example:
import Tkinter as tk
class MyDialog(object):
def __init__(self, parent, prompt):
self.toplevel = tk.Toplevel(parent)
self.var = tk.StringVar()
label = tk.Label(self.toplevel, text=prompt)
entry = tk.Entry(self.toplevel, width=40, textvariable=self.var)
button = tk.Button(self.toplevel, text="OK", command=self.toplevel.destroy)
label.pack(side="top", fill="x")
entry.pack(side="top", fill="x")
button.pack(side="bottom", anchor="e", padx=4, pady=4)
def show(self):
self.toplevel.grab_set()
self.toplevel.wait_window()
value = self.var.get()
return value
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.button = tk.Button(self, text="Click me!", command=self.on_click)
self.label = tk.Label(self, text="", width=40)
self.label.pack(side="top", fill="x")
self.button.pack(padx=20, pady=20)
def on_click(self):
result = MyDialog(self, "Enter your name:").show()
self.label.configure(text="your result: '%s'" % result)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

Python: Tkinter OK button callback function

I'm working on my very first Python GUI and I'm trying to modify this tkinter example, but I simply cannot figure out how to write a callback function for the OK button that will pass on the entered value to the main program.
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Tkinter import Tk, BOTH, StringVar, IntVar
from ttk import Frame, Button, Style, Label, Entry
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Get Value")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
valueLabel = Label(self, text="Value: ")
valueLabel.place(x=10, y=10)
value=StringVar(None)
value.set("this is the default value")
valueEntry=Entry(self, textvariable=value)
valueEntry.place(x=70, y=10)
quitButton = Button(self, text="Quit", command=self.quit)
quitButton.place(x=10, y=50)
okButton = Button(self, text="OK", command=self.quit)
okButton.place(x=120, y=50)
def main():
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
I've read a gazillion of tutorials, but none of them explains this clearly. Theoretically, I should be able to get the selected value with value.get(), but I keep getting error messages no matter where I put it. Also, AFAIK, I should be able to define a default value with value.set(), but this doesn't seem to have an effect, since the text box is empty when I run the program.
What is the easiest way to pass on values to the main python program after root.mainloop() terminates? (The actual dialog box contains several entry boxes for entering string and integer values.)
I.e. I want to be able to use something like:
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
print value
print value2
print value3
How do I define default values for entry boxes?
Change every occurrence of the value variable with self.value. This should fix it and the default value will be displayed.
UPDATE
from Tkinter import Tk, BOTH, StringVar, IntVar
from ttk import Frame, Button, Style, Label, Entry
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def showMe(self):
print(self.value.get())
def initUI(self):
self.parent.title("Get Value")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
valueLabel = Label(self, text="Value: ")
valueLabel.place(x=10, y=10)
self.value=StringVar(None)
self.value.set("this is the default value")
valueEntry=Entry(self, textvariable=self.value)
valueEntry.place(x=70, y=10)
quitButton = Button(self, text="Quit", command=self.quit)
quitButton.place(x=10, y=50)
okButton = Button(self, text="OK", command=self.showMe)
okButton.place(x=120, y=50)
def main():
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
Both your quitButton and okButton call the self.quit functions. So no mater what value you enter when you press the OK button you are calling the quit function which has its own problems as well outside the scope of your question.
Try to define value as self.value and make the okButton call a function that does: print self.value.get().

Adding python functionality to Tkinter front end

This is the front end I developed for my application using Tkinter:
from Tkinter import *
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple")
self.pack(fill=BOTH, expand=1)
frame = Frame(self, relief="flat", borderwidth=1)
label=Label(frame,text="Scope:")
label.pack(side="left", fill=None, expand=False)
var = StringVar()
var.set("today")
list = OptionMenu(frame, var, "today","yesterday","this week","last week","this month","last month")
list.pack(side="left", fill=None, expand=False)
fetchButton = Button(frame, text="Fetch",command=self.handle(var))
fetchButton.pack(side="left", fill=None, expand=False)
frame.grid(row=1,column=1,pady=4,padx=5,sticky=W)
area = Text(self,height=15,width=60)
area.grid(row=2,column=1,rowspan=1,pady=4,padx=5)
scroll = Scrollbar(self)
scroll.pack(side=RIGHT, fill=Y)
area.config(yscrollcommand=scroll.set)
scroll.config(command=area.yview)
scroll.grid(row=2, column=2, sticky='nsew')
quitButton = Button(self, text="Cancel",command=self.quit)
quitButton.grid(pady=4,padx=5,sticky=W,row=3, column=1)
root = Tk()
app = Example(root)
root.mainloop()
Where exactly do I have to put the handle() method so it can write repeatedly to the text widget? When I put handle() within the Example class and use self.area.insert(), it shows an error saying
Example instance has no attribute 'area'
Please help out.
You need to pass the function object to the Button instance, not a function call. i.e.
fetchButton = Button(frame, text="Fetch",command=self.handle)
To make the handle work in the context of the rest of the code:
from Tkinter import *
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("Simple")
self.pack(fill=BOTH, expand=1)
self.init_ui()
def init_ui(self):
self.frame = Frame(self, relief="flat", borderwidth=1)
self.frame.grid(row=1,column=1,pady=4,padx=5,sticky=W)
self.label=Label(self.frame,text="Scope:")
self.label.pack(side="left", fill=None, expand=False)
self.var = StringVar()
self.var.set("today")
self.list = OptionMenu(self.frame, self.var, "today","yesterday",
"this week","last week","this month",
"last month")
self.list.pack(side="left", fill=None, expand=False)
self.fetchButton = Button(self.frame, text="Fetch",command=self.handle)
self.fetchButton.pack(side="left", fill=None, expand=False)
self.area = Text(self,height=15,width=60)
self.area.grid(row=2,column=1,rowspan=1,pady=4,padx=5)
self.scroll = Scrollbar(self)
self.scroll.pack(side=RIGHT, fill=Y)
self.area.config(yscrollcommand=self.scroll.set)
self.scroll.config(command=self.area.yview)
self.scroll.grid(row=2, column=2, sticky='nsew')
self.quitButton = Button(self, text="Cancel",command=self.quit)
self.quitButton.grid(pady=4,padx=5,sticky=W,row=3, column=1)
def handle(self):
self.area.delete(1.0, END)
self.area.insert(CURRENT,self.var.get())
if __name__ == "__main__":
root = Tk()
app = Example(root)
root.mainloop()
Declaring your widgets as attributes will save you a lot of pain an suffering as your application expands. Also keeping references to everything in Tk can stop some unwanted garbage collection, particularly with images in Label instances.
It is also worth noting that using grid and pack interchangeably is likely to lead to bugs later on.

Categories

Resources