Python Forum
[Tkinter] How to determine the stack order or topmost top-level window
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] How to determine the stack order or topmost top-level window
#1
I have some Tkinter top-level windows that were created by matplotlib (backend: TkAgg), in Python 3 on debian.

Naturally the user can change their stacking order by clicking on their title bars to raise them.

The windows have type tkinter.Tk.

I can raise a window w programmatically by a statement like w.tkraise().

But what i need to do is determine which window the user has raised to the top (so that the program i'm writing can then do something to it).

That is, i need to determine which window is on top, rather than just sending one to the front.

The frontend matplotlib.pyplot module has a function gcf() which is documented to return the current figure (gcf == 'get current figure'). And from this figure, i can determine the its window (matplotlib.pyplot.gcf().canvas.manager.window). However, the return value of gcf() does not track which window is on top, so it seems to be using something else to decide what's current.

I would appreciate very much any clues about how to programmatically determine what window is actually on top. (Any advice is fine, no matter whether hackey, or a pointer to the manual, or anything else. This would seem to be a natural question, but i can't seem to find it on google.)

dan
Reply
#2
use wininfo_children, see: http://effbot.org/tkinterbook/widget.htm...ren-method
Reply
#3
Thank you Larz for your reply.

I'm having a little trouble making sense of the lists returned by winfo_children() --- they appear to all be canvas objects, and all of them, from all of the windows, are distinct.

Here's an example:
import matplotlib.pyplot as plt
figs=[None]*3
axs =[None]*3
for i in range(3):
    figs[i],axs[i] = plt.subplots()
plt.show(block=False)
windows=[None]*3
for i in range(3):
    windows[i] = figs[i].canvas.manager.window
for i in range(3):
    print(len(windows[i].winfo_children()))
    print(windows[i].winfo_children())
If you run this code (python3, debian, with TkAgg backend for matplotlib), it creates 3 top level windows. Each window has 2 winfo_children, but none of them are children of any of the others.

The page you pointed to has a bunch of other interesting functions in it, such as winfo_parent(). I thought maybe they would all of the same parent (some kind of hidden root or something, that i could call winfo_children() on). But they don't, they seem to have 3 distinct parents.

Thanks again for your reply, and the pointer to the page, and would appreciate any suggestion!! :)
Reply
#4
The stacking order is the order by which your widgets are written in your script, even if a Toplevel(or any container widget) is a parent of another widget(ex: Label ,Button) if your write a Label(or Button) first in your script the Label is higher in stacking order in comparison with Toplevel widget.
The first written is high in stacking order, the last written is lower in the stacking order, even if the last one is a parent of first one.https://wiki.tcl.tk/12752
Reply
#5
Thanks Sebastian for the link.

The link says that winfo children returns a list of children "in stacking order with the lowest first (except that toplevel windows are not returned in stacking order)". Now, as it happens, the only thing i care about is the toplevel windows, which seem to be excluded from winfo children according to the quoted part of the link.

Given two top level windows A and B, how do you determine, programmatically, which one is drawn in front of the other?

Especially, how do you determine this after the user has had a chance to drag around the windows and select one and then the other a few times?

So 'stacking order' may be a poor choice of words on my part.

Thanks for the link, which i will continue to study, because maybe i'm just being very dense!! :)

dan
Reply
#6
(Jan-23-2018, 10:30 PM)dan Wrote: Given two top level windows A and B, how do you determine, programmatically, which one is drawn in front of the other?

My search engine exhumed this answer so I suggest this (needs testing)
def stackorder(root):
    """return a list of root and toplevel windows in stacking order (topmost is last)"""
    c = root.children
    s = root.tk.eval('wm stackorder {}'.format(root))
    L = [x.lstrip('.') for x in s.split()]
    return [(c[x] if x else root) for x in L]

def topmost(root, include_root=True):
    """return the topmost toplevel window in stacking order (including root by default)"""
    L = stackorder(root)
    if include_root:
        return L[-1]
    else:
        return L[-2] if L[-1] is root else L[-1]
Reply
#7
Wow!

Thanks Gribouillis, for putting together that code.

I had actually seen the page you exhumed when i was searching earlier, but did not know what to do with it. So thanks for digesting it and creating a function from it.

In any event, i put your code in a file, gribouillis.py, and i put my little example code from above in a function in a file, make_windows.py. So make_windows.py looks like this:
import matplotlib.pyplot as plt
def make_wins( cnt = 3 ):
    figs=[None]*cnt
    axs =[None]*cnt
    for i in range(cnt):
        figs[i],axs[i] = plt.subplots()
    plt.show(block=False)
    wins=[None]*3
    for i in range(3):
        wins[i] = figs[i].canvas.manager.window
    return figs, wins
Then i tried them out together:

import tkinter as tk
from gribouillis import *
from make_windows import *
root = tk.Tk()
f,w = make_wins(3)
stackorder(root)
The returned list is just the root.

If i also create a child window of the root and call the stackorder function
win1 = tk.Toplevel(bg = 'red')
stackorder(root)
then i get a list showing both of the windows (but of course still not the 3 windows created by matplotlib).

So i think the problem is that they need some kind of a common root for the approach through children to work (???)

Anyhow, i appreciate your creation of the stackorder() function, and i guess i'll have to see if there's some way i can persuade matplotlib to yield up (or create) a root window (or maybe some other approach, such as talking to x windows directly, that somebody can suggest).

Thanks again everybody for your help.
Reply
#8
Thanks again Larz, Sebastian, and Gribouillis for your help. All of the links were useful and educational.

I have a solution to the problem of finding which of the matplotlib windows is frontmost which avoids the issue that they have no common root. I'm posting it because somebody may stumble over this thread and just possibly it will be useful. However, it's not really ideal for a number of reasons, such as dependency on backend. I think that somewhere there's some simple call that would let me skip all of this.

Anyhow, here's my modified version: the idea is to put a callback on the FocusIn event so that the program keeps itself keeps track of where the focus last was.

This part would be stored in a file vers2_mw.py ('vers2', because it's version 2):

front_win = None
def make_wins( cnt = 3 ):
    def make_func( w ):
        def replace_front( event ):
            global front_win
            front_win = w
        return replace_front
    figs=[None]*cnt
    axs =[None]*cnt
    for i in range(cnt):
        figs[i],axs[i] = plt.subplots()
    plt.show(block=False)
    wins=[None]*cnt
    for i in range(cnt):
        wins[i] = figs[i].canvas.manager.window
        wins[i].bind( '<FocusIn>', make_func( wins[i] ) )
    global front_win
    front_win = wins[cnt-1]
    return wins
Then you can demonstrate it like this:
import vers2_mw
from vers2_mw import *
w = make_wins()
vers2_mw.front_win == w[2]
# This last line should print True
# Now click around and select window 0:
vers2_mw.front_win == w[0]
# The previous line should also print True
vers2_mw.front_win == w[1]
# The previous line here will print False unless window 1 was chosen.
So this works, and is good enough for the program that i'm developing, but a more universal solution would be useful (and thanks for any suggestions, btw).

Thanks again everybody! :)
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Interaction between Matplotlib window, Python prompt and TKinter window NorbertMoussy 3 343 Mar-17-2024, 09:37 AM
Last Post: deanhystad
  tkinter window and turtle window error 1885 3 6,624 Nov-02-2019, 12:18 PM
Last Post: 1885
  [Tkinter] Top Level Window - from tkinter import * francisco_neves2020 6 4,092 Apr-23-2019, 09:27 PM
Last Post: francisco_neves2020
  top level window juliolop 3 3,873 Nov-07-2018, 02:52 PM
Last Post: juliolop
  update a variable in parent window after closing its toplevel window gray 5 8,976 Mar-20-2017, 10:35 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020