Can the auto-placement of tkinter windows be turned off? - python

I have this very basic code
from tkinter import *
class GUI(Tk):
def __init__(self):
super().__init__()
self.geometry('600x400')
Button(self, text="Show new window", command=self.show_window).pack()
def show_window(self):
smallwin = display()
class display(Toplevel):
def __init__(self):
super().__init__()
self.geometry('300x300+30+30')
self.attributes('-topmost',True)
root = GUI()
root.mainloop()
When you click on the button, a child window appears. When you press it again, a second child window appears etc etc, BUT each new window is to the right and further down from the last one.
I would like to know if this automatic behavior can be turned off?

If you explicitly set the geometry for each window, they will go wherever you tell them to go.
You seem to be setting a geometry, but you aren't using it. If you pass that value to the geometry method, the window will go to that exact location.
class display(Toplevel):
def __init__(self):
super().__init__()
self.defaultgeometry='300x300+30+30'
self.wm_geometry(self.defaultgeometry)
...

you can just set the location of the window. Then all windows will open a this exact location.
E.g.
root.geometry('250x150+0+0')
More detailed solutions are described here:
How to specify where a Tkinter window opens?

Related

How to ceate child Modal window in Python tkinter?

Python version is 3.7.
Tried to use tkinter.TopLevel(), the second window is created behind the main window.
I need a true child window which is also modal: can't do anything on the main window before the child is closed.
tkinter.messagebox is very similar but I need my own customized window.
Thank you.
Use the Toplevel widget to set a parent modal dialog
class topDialog:
def __init__(self, parent):
top = self.top = Toplevel(parent)
Label(top, text="Text").pack()
Then follow that up with all of your widgets and such.

Object oriented Tkinter, best way to communicate between widgets in gui with many frames

I am trying to figure out what the best way to communicate between different widgets is, where the widgets are custom classes inheriting from tkinter widgets and I have several frames present (to help with layout management). Consider for example the following simple gui (written for python 3, change tkinter to Tkinter for python 2):
from tkinter import Frame,Button,Tk
class GUI(Frame):
def __init__(self, root):
Frame.__init__(self,root)
self.upper_frame=Frame(root)
self.upper_frame.pack()
self.lower_frame=Frame(root)
self.lower_frame.pack()
self.upper_btn1 = Button(self.upper_frame, text="upper button 1")
self.upper_btn2 = Button(self.upper_frame, text="upper button 2")
self.upper_btn1.grid(row=0,column=0)
self.upper_btn2.grid(row=0,column=1)
self.lower_btn = CustomButton(self.lower_frame, "lower button 3")
self.lower_btn.pack()
class CustomButton(Button):
def __init__(self,master,text):
Button.__init__(self,master,text=text)
self.configure(command=self.onClick)
def onClick(self):
print("here I want to change the text of upper button 1")
root = Tk()
my_gui = GUI(root)
root.mainloop()
The reason I put them in different frames is because I want to use different layout managers in the two different frames to create a more complicated layout. However, I want the command in lower_btn to change properties of eg upper_btn1.
However, I can not immediately access upper_btn1 from the instance lower_btn of the customized class CustomButton. The parent of lower_btn is frame2, and the frame2 parent is root (not the GUI instance). If I change the parent of lower_btn to the GUI instance, ie
self.lower_btn = CustomButton(self, "lower button")
it will not be placed correctly when using pack(). If I change the parent of frame1/frame2 to the GUI instance, ie
self.upper_frame=Frame(self)
self.upper_frame.pack()
self.lower_frame=Frame(self)
self.lower_frame.pack()
I could in principle access the upper_btn1 from lower_btn by self.master.master.upper_btn1, BUT then the frame1/frame2 are not placed correctly using pack() (this I don't understand why). I can of course pass the GUI instance as separate variable, on top of master, to CustomButton, ie something like
class CustomButton(Button):
def __init__(self,master,window,text):
Button.__init__(self,master,text=text)
self.window=window
self.configure(command=self.onClick)
def onClick(self):
self.window.upper_btn1.configure(text="new text")
and then change the construction of lower_btn to
self.lower_btn = CustomButton(self.lower_frame,self, "lower button 3")
but is that the "correct" way of doing it?
So, what is the best way to reorganize this gui? Should I pass GUI as a separate variable on top of the master/parent argument? Should I change the master/parent of the buttons and/or the frames (and somehow fix the issues with the layout management)? Or should I make the commands of the buttons as methods of the GUI instance so they can communicate with the widgets in that GUI, although this does not really feel like object oriented programming? I can't seem to find a working object oriented design that feels "correct". Also, an explanation why pack() does not work for frame1/frame2 if I make the GUI instance (self) the master, would also be appreciated, as that would have been my intuitive, most object oriented, approach.
Edit: Maybe the best way is to not use frames inside frames at all, and use only grid() and then use colspan/rowspan to give the layout management more flexibility.
A year late, but: One way to communicate between widgets is to instantiate some kind of control center object that receives knowledge about the state of your widgets and then compels other widgets to act on that information. This 'manager' functionality will be independent of the layout of your widgets.
Here's an implementation that's customized to your example. The idea is to add a .manager attribute to the lower button, and to notify this manager when clicked. The GUI class remains unchanged.
from tkinter import Frame,Button,Tk
class Manager(object):
def __init__(self, gui):
self.gui = gui
self.gui.lower_btn.manager = self
def onClick(self):
self.gui.upper_btn2.configure(text="changed text")
class GUI(Frame):
def __init__(self, root):
Frame.__init__(self,root)
self.upper_frame=Frame(root)
self.upper_frame.pack()
self.lower_frame=Frame(root)
self.lower_frame.pack()
self.upper_btn1 = Button(self.upper_frame, text="upper button 1")
self.upper_btn2 = Button(self.upper_frame, text="upper button 2")
self.upper_btn1.grid(row=0,column=0)
self.upper_btn2.grid(row=0,column=1)
self.lower_btn = CustomButton(self.lower_frame, "lower button 3")
self.lower_btn.pack()
class CustomButton(Button):
def __init__(self,master,text):
Button.__init__(self,master,text=text)
self.configure(command=self.onClick)
self.manager = None
def onClick(self):
if self.manager:
self.manager.onClick()
else:
print("here I want to change the text of upper button 1")
root = Tk()
my_gui = GUI(root)
Manager(my_gui)
root.mainloop()

Why is my tkinter button not displaying, have I installed everything in the module if not how can I install it?

#snakes and ladder
from tkinter import * #pygame is the module that has collections of functions that is used to create a window import everything from tkinter
import time
class window(Frame): #Frame comes from tkinter is what you think as a window
def __init__(self, master = None):#The master widget defines the settings upon initialisation
Frame.__init__(self, master) #This is called the frame class that has been built in python
self.master = master
def __init__window(self): #creation of the init window
self.master.title("Reagan Kambayi") #It changes the title of the title of our widget
self.pack(fill=BOTH, expand=1)#The pack function will pack this in our frame
#placing the button
stop = Button(self, master, message= "Stop")
#intialising the button that will start the stopwatch
stop.place(x=0, y=0)
screen = Tk() #It must be written with capitalised T because there will be an error and it holds the components of the tkinter module
screen.geometry("700x500")
app = window(screen) #The app variable is assigned to the window creation which has the tkinter module
screen.mainloop()
Ok, here we go.
from tkinter import * #pygame is the module that has collections of functions that is used to create a window import everything from tkinter
Pygame has nothing to do with tkinter and you're not importing pygame here, you're importing tkinter.
class window(Frame): #Frame comes from tkinter is what you think as a window
No, it isn't. A Frame widget is just that, a frame inside a window. It doesn't draw a window in and of itself. The parameter Frame in your example isn't even a Frame widget at all, it's value is Tk() which is the function called to draw the first window in tkinter.
def __init__(self, master = None):#The master widget defines the settings upon initialisation
I'm actually at a loss for what you're attempting to do here. master should equal Frame which equals screen which equals Tk() but if I'm correct you're overriding that and telling it to equal None?
Frame.__init__(self, master) #This is called the frame class that has been built in python
I don't think you're doing what you think you're doing here. This answer explains it better than I could.
def __init__window(self): #creation of the init window
If I'm reading your program correctly then window.__init__window() is never called, so none of this function ever actually happens.
self.pack(fill=BOTH, expand=1)#The pack function will pack this in our frame
You're attempting to call .pack() on self which is calling .pack() on Frame. Typically we wouldn't assign a value to self in this way (although this is valid), read this to find out what self should be used for.
#placing the button
stop = Button(self, master, message= "Stop")
This isn't placing the Button widget, this is assigning the Button widget to a variable. Also, Button widgets are declared as Button(parent, *attributes) and there is no message attribute built in. Meaning what you meant to call was Button(self.master, text="Stop").
#intialising the button that will start the stopwatch
stop.place(x=0, y=0)
This is where you're placing the button, but the function that contains this is never called, so it never happens.
app = window(screen) #The app variable is assigned to the window creation which has the tkinter module
What you're actually doing here is calling the class window, all this does in your current program is call window.__init__(), which in itself does essentially nothing.
This is meant with no offence but I think you're lacking a very basic understanding of tkinter and possibly even Pythonic OOP.
I believe what you're trying to do in your program is the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.root.title("Reagan Kambayi")
self.stop = Button(self.root, text="Stop")
self.stop.place(x=0, y=0)
root = Tk()
App(root)
root.mainloop()

Make tkinter prompt inherit parent window's icon

I am writing a program using tkinter, and have successfully managed to use a colour icon for my program using code that looks like this:
from tkinter import *
tk = Tk()
root.tk.call('wm', 'iconbitmap', self._w, '-default', 'iconfile.ico')
However, when I create a simple dialog, it has the default tkinter icon. I have tried setting the parent to my main window, but the icon is still the default one.
How could the icon be set to not be the default one?
Got an answer from a user on another site:
It's not a configurable option in the class. You'll need to make a subclass which sets the icon:
class StringDialog(simpledialog._QueryString):
def body(self, master):
super().body(master)
self.iconbitmap('icon.ico')
def ask_string(title, prompt, **kargs):
d = StringDialog(title, prompt, **kargs)
return d.result
You'll also need to subclass _QueryFloat and _QueryInteger if you want those versions. (These classes are supposed to be private, so you might have to fix this in future updates.)

Generated Button appears in wrong window

I started to learn Python and I got a problem that is not discussed in any of the tutorials I've found.
Basically when the program got more complicated I've lost the ability to control where the new elements are appearing. It's difficult to explain it for me since English is not my native language so i made a mock-up program that shows what is wrong with my main program.
import Tkinter as tk
import ttk as ttk
clutter=['1PLACEHOLDER', '2PLACEHOLDER', '3PLACEHOLDER', 'PICK ME']
class GRAPHIC_INTERFACE(tk.Frame):
def __init__(self,*args):
tk.Frame.__init__(self, *args)
self.grid()
self.first_window()
def first_window(self):
self.button1=tk.Button(self, text="PLACEHOLDER")
self.button1.grid()
self.button2=tk.Button(self, text="CLICK ME", command=self.second_window)
self.button2.grid()
self.button3=tk.Button(self, text="PLACEHOLDER")
self.button3.grid()
#the additional button apears here
def second_window(self):
alpha=tk.Toplevel(self)
self.button4=tk.Button(alpha, text="PLACEHOLDER")
self.button4.grid()
self.button5=tk.Button(alpha, text="CLICK ME", command= self.third_window)
self.button5.grid()
def third_window(self):
beta=tk.Toplevel(self)
self.BOXXY=ttk.Combobox(beta, values= clutter, state='readonly')
self.BOXXY.bind("<<ComboboxSelected>>", self.misplaced_button) #after choosing the third option an aditional button is created
self.BOXXY.current(0)
self.BOXXY.grid()
self.button6=tk.Button(beta, text="PLACEHOLDER")
self.button6.grid()
#the additional button needs to appear here
def misplaced_button(self, *args):
Catie=self.BOXXY.get()
if Catie=='PICK ME':
self.button7=tk.Button(self, text="I am that problematic button")#this button needs to be in the third window
self.button7.grid()
else:
print "that was not the chosen one"
root=tk.Tk()
root.title("Mockup")
root.geometry("180x200")
app=GRAPHIC_INTERFACE(root)
root.mainloop()
At first I was thinking that i can control the placement of the widgets by giving them names (i.e alpha, beta) but apparently I was wrong.
If self.button7 is supposed to be in the third window, all you have to do is use the third window as the parent of the button.
You can accomplish this many ways: you can save the window as an attribute, you can pass the window in when calling the function, or you can compute the window based on which widow caught the event.
Here's a solution that puts the button in the toplevel that got the event:
def misplaced_button(self, event):
...
toplevel = event.widget.winfo_toplevel()
self.button7=tk.Button(toplevel,...)
...

Categories

Resources