I'm trying to make it so that new information shows in in a new window, but I want the new window to be connected to the parent window, even when the parent window is clicked the new window should still show up similar to how a dropdown menu works. I'm also planning on having some of the new windows have treeviews later on.
from tkinter import *
win = Tk()
win.geometry("500x500 0 0")
def button_function ():
win2 = Toplevel()
label = Label(win2,text='dropdown', width=7)
label.pack()
win2.geometry(f" {win.winfo_x()} {win.winfo_y() 30}")
button = Button(win, command=lambda: button_function (), width=12)
button.pack()
win.mainloop()
CodePudding user response:
Ok so with a little bit of googling I came across this post: tkinter-detecting-a-window-drag-event
In that post they show how you can keep track of when the window has moved.
By taking that code and making some small changes we can use the dragging()
and stop_drag()
functions to move the top level window back to where it was set to relative to the main window.
That said this will only work in this case. You will need to write something more dynamic to track any new windows you want so they are placed properly and on top of that you will probably want to build this in a class
so you do not have to manage global variables.
With a combination of this tracking function and using lift()
to bring the window up we get closer to what you are asking to do.
That said you will probably want remove the tool bar at the top of the root window to be more clean. I would also focus on using a dictionary or list to keep track of open and closed windows and their locations to make the dynamic part of this easier.
import tkinter as tk
win = tk.Tk()
win.geometry("500x500 0 0")
win2 = None
drag_id = ''
def dragging(event):
global drag_id
if event.widget is win:
if drag_id == '':
print('start drag')
else:
win.after_cancel(drag_id)
print('dragging')
drag_id = win.after(100, stop_drag)
if win2 is not None:
win2.lift()
win2.geometry(f" {win.winfo_x()} {win.winfo_y() 30}")
def stop_drag():
global drag_id, win2, win
print('stop drag')
drag_id = ''
if win2 is not None:
win2.lift()
win2.geometry(f" {win.winfo_x()} {win.winfo_y() 30}")
win.bind('<Configure>', dragging)
def button_function():
global win2
win2 = tk.Toplevel()
label = tk.Label(win2, text='drop down', width=7)
label.pack()
win2.geometry(f" {win.winfo_x()} {win.winfo_y() 30}")
tk.Button(win, command=button_function, width=12).pack()
win.mainloop()
EDIT:
Ok so I took some time to write this up in a class
so you could see how it could be done. I have also added some level of dynamic building of the buttons and pop up windows.
We use a combination of lists and lambdas to perform a little bit of tracking and in the end we pull off exactly what you were asking for. let me know if you have any questions.
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('500x500')
self.pop_up_list = []
self.drag_id = ''
self.button_notes = ['Some notes for new window', 'some other notes for new window', 'bacon that is all!']
self.bind('<Configure>', self.dragging)
for ndex, value in enumerate(self.button_notes):
print(ndex)
btn = tk.Button(self, text=f'Button {ndex 1}')
btn.config(command=lambda b=btn, i=ndex: self.toggle_button_pop_ups(i, b))
btn.grid(row=ndex, column=0, padx=5, pady=5)
self.pop_up_list.append([value, 0, None, btn])
def dragging(self, event):
if event.widget is self:
if self.drag_id == '':
pass
else:
self.after_cancel(self.drag_id)
self.drag_id = self.after(100, self.stop_drag)
for p in self.pop_up_list:
if p[1] == 1:
p[2].lift()
p[2].geometry(f" {p[3].winfo_rootx() 65} {p[3].winfo_rooty()}")
def stop_drag(self):
self.drag_id = ''
for p in self.pop_up_list:
if p[1] == 1:
p[2].lift()
p[2].geometry(f" {p[3].winfo_rootx() 65} {p[3].winfo_rooty()}")
def toggle_button_pop_ups(self, ndex, btn):
p = self.pop_up_list
if p[ndex][1] == 0:
p[ndex][1] = 1
p[ndex][2] = tk.Toplevel(self)
p[ndex][2].overrideredirect(1)
tk.Label(p[ndex][2], text=self.pop_up_list[ndex][0]).pack()
p[ndex][2].geometry(f" {btn.winfo_rootx() 65} {btn.winfo_rooty()}")
else:
p[ndex][1] = 0
p[ndex][2].destroy()
p[ndex][2] = None
if __name__ == '__main__':
Main().mainloop()