Binding callbacks to minimize and maximize events in Toplevel windows
<p>I've read through related answers and it seems that the accepted way to do this is by binding callbacks to <code>&lt;Map&gt;</code> and <code>&lt;Unmap&gt;</code> events in the Toplevel widget. I've tried the following but to no effect:</p>

<pre><code>from Tkinter import *

tk = Tk()

def visible(event):
    print 'visible'

def invisible(event):
    print 'invisible'

tk.bind('&lt;Map&gt;', visible)
tk.bind('&lt;Unmap&gt;', invisible)

tk.mainloop()
</code></pre>

<p>I'm running python 2.7 on Linux. Could this be related to window manager code in different operating systems?</p>

<p>Calling <code>tk.iconify()</code> before <code>tk.mainloop()</code> has no effect either. In fact, the only command that produces the correct behavior is <code>tk.withdraw()</code> which is certainly not the same thing as minimizing the window. Additionally, if <code>&lt;Map&gt;</code> and <code>&lt;Unmap&gt;</code> events are triggered by calling <code>pack()</code>, <code>grid()</code>, or <code>place()</code>, why is <code>&lt;Map&gt;</code> triggered when the application window is minimized on Windows and/or Mac, as <a href="http://stackoverflow.com/a/3840216/2592879">this</a> and <a href="http://stackoverflow.com/a/26160588/2592879">this</a> answer suggest. And why would they be triggered when calling <code>withdraw()</code> and <code>deiconify()</code> on Linux?</p>

<h3>Unmapping on Linux</h3>

<p>The term <code>Unmap</code> has a quite different meaning on Linux than it has on Windows. On Linux, <em>Unmapping</em> a window means making it (nearly) untraceable; It does not appear in the application's icon, nor is it listed anymore in the output of <code>wmctrl -l</code>. We can unmap / map a window by the commands:</p>

<pre><code>xdotool windowunmap &lt;window_id&gt;
</code></pre>

<p>and:</p>

<pre><code>xdotool windowmap &lt;window_id&gt;
</code></pre>

<p>To see if we can even possibly make tkinter detect the window's state <em>minimized</em>, I added a thread to your basic window, printing the window's state once per second, using:</p>

<pre><code>root.state()
</code></pre>

<p>Minimized or not, the thread always printed:</p>

<pre><code>normal
</code></pre>

<h3>Workaround</h3>

<p>Luckily, if you <em>must</em> be able to detect the window's minimized state, on Linux we have alternative tools like <code>xprop</code> and <code>wmctrl</code>. Although as dirty as it gets, it is very well scriptable reliably inside your application.</p>

<p>As requested in a comment, below a simplified example to create your own version of the bindings with external tools.</p>

<p><a href="http://i.stack.imgur.com/pLSI1.png" rel="nofollow"><img src="http://i.stack.imgur.com/pLSI1.png" alt="enter image description here"></a></p>

<h3>How it works</h3>

<ul>
<li>When the window appears (the application starts), We use <code>wmctrl -lp</code> to get the window's <code>id</code> by checking both <em>name</em> and <em>pid</em> (<code>tkinter</code> windows have pid 0).</li>
<li>Once we have the <code>window id</code>, we can check if the string <code>_NET_WM_STATE_HIDDEN</code> is in output of <code>xprop -id &lt;window_id&gt;</code>. If so, the window is minimized.</li>
</ul>

<p>Then we can easily use <code>tkinter</code>'s <a href="http://stackoverflow.com/a/459131/1391444">after() method</a> to include a periodic check. In the example below, the comments should speak for themselves.</p>

<h3>What we need</h3>

<p>We need both <a href="https://linux.die.net/man/1/wmctrl" rel="nofollow">wmctrl</a> and <a href="https://linux.die.net/man/1/xprop" rel="nofollow">xprop</a> to be installed. On Dedian based systems:</p>

<pre><code>sudo apt-get install wmctrl xprop
</code></pre>

<h3>The code example</h3>

<pre><code>import subprocess
import time
from Tkinter import *

class TestWindow:

    def __init__(self, master):
        self.master = master
        self.wintitle = "Testwindow"
        self.checked = False
        self.state = None
        button = Button(self.master, text = "Press me")
        button.pack()
        self.master.after(0, self.get_state)
        self.master.title(self.wintitle)

    def get_window(self):
        """
        get the window by title and pid (tkinter windows have pid 0)
        """
        return [w.split() for w in subprocess.check_output(
            ["wmctrl", "-lp"]
            ).decode("utf-8").splitlines() if self.wintitle in w][-1][0]

    def get_state(self):
        """
        get the window state by checking if _NET_WM_STATE_HIDDEN is in the
        output of xprop -id &lt;window_id&gt;
        """
        try:
            """
            checked = False is to prevent repeatedly fetching the window id
            (saving fuel in the loop). after window is determined, it passes further checks.
            """
            self.match = self.get_window() if self.checked == False else self.match
            self.checked = True
        except IndexError:
            pass
        else:
            win_data = subprocess.check_output(["xprop", "-id", self.match]).decode("utf-8")
            if "_NET_WM_STATE_HIDDEN" in win_data:
                newstate = "minimized"
            else:
                newstate = "normal"
            # only take action if state changes
            if newstate != self.state:
                print newstate
                self.state = newstate
        # check once per half a second
        self.master.after(500, self.get_state)

def main(): 
    root = Tk()
    app = TestWindow(root)
    root.mainloop()

if __name__ == '__main__':
    main()
</code></pre>

python linux tkinter