2

Whenever I try to run a simple tkinter program - or any program for that mater - using tk.mainloop() on ubuntu 16.04.2, I cannot open the app window and a question mark is shown over the window icon.

Here the the program I am running:

import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        super(MainApplication, self).__init__(parent, *args, **kwargs)

if __name__ == '__main__':
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

And here is a screen shot showing the output of the program whenever I run the it:

enter image description here

Before this, I was receiving an error saying _tkinter could be loaded. To remedy this, I downloaded the latest version of tkinter for ubuntu by using sudo apt-get update and then sudo apt-get install python3-tk. If this also matters, I'm using ubuntu in a dual-boot configuration with windows 10.

I've also read How can I fix program icons that appear as a question mark in the Launcher? and Why do some open applications appear as “question marks” in the Unity launcher?, but those question seemed to be dealing with how to fix a applications desktop icon, not how to fix an application that wouldn't run. Further more, the question seemed to be dealing with specific ubuntu applications.

Does anyone know why ubuntu is not correctly running the tkinter app and what can be done to fix this?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Christian Dean
  • 22,138
  • 7
  • 54
  • 87

2 Answers2

2

While it my not seem like it, The tkinter window is in fact there. It is simple to small to see because no widgets are inside of it to expand it. Once you began to add widgets to the window, it becomes visible.

For example, to make a window appear using the code in question,

import tkinter as tk


class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        super(MainApplication, self).__init__(parent, *args, **kwargs)


if __name__ == '__main__':
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop() 

You could add a tk.Button() widget to force the window to expand:

import tkinter as tk


class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        super(MainApplication, self).__init__(parent, *args, **kwargs)
        # create a button widget to force the window to expand to fit
        # the button
        self.btn = tk.Button(self, text='btn', bg='blue')
        self.btn.pack(side='bottom')


if __name__ == '__main__':
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Which shows the following window:

enter image description here

Christian Dean
  • 22,138
  • 7
  • 54
  • 87
  • I would like to inform you, not to criticize you: what you stated is wrong. Quick proof: run this this on Terminal: `import tkinter as tk` and then `tk.Tk()` you will see a toplevel frame clearly visible. An other proof for this is the 2nd MVCE I provided in my answer. I covered what you said here in my answer. I repeat again; the good practice is to use `pack()` as I did in the first MVCE I provided (not doing so is partially responsible of the problem you faced as I explained in my answer) – Billal Begueradj Mar 26 '17 at 00:33
  • 1
    @BillalBEGUERADJ: you are correct that the two-liner you propose will show a window. The question at had has a critical difference: it has an additional 1 pixel frame, causing the whole window to shrink. This answer is 100% correct. – Bryan Oakley Mar 26 '17 at 03:44
0

Try to change the main portion of your program a little bit like this:

if __name__ == '__main__':
    root = tk.Tk()
    your_class_instance = MainApplication(root)
    print(your_class_instance)
    root.mainloop()

You will see on your Terminal something like this: .140449273960656

Now let us go back to your original code:

 if __name__ == '__main__':
        root = tk.Tk()
        your_class_instance = MainApplication(root).pack(side="top", fill="both", expand=True)
        print(your_class_instance)
        root.mainloop()

You will see None on your Terminal.

Why? That is because the layout managers of tkinter (pack(), place() and grid()) always return None and MainApplication class has no widgets.

In other words, you try to display nothing.

Note that pack(your_options_list) is useless in this line:

MainApplication(root).pack(side="top", fill="both", expand=True)

Not only it is useless but it prevents the application from further scalability because there is no way to use an instance of MainApplication class.

Maybe this is not necessary for your actual problem, but just as a side note, since you are using Python 3, you can use: super().__init__(parent, *args, **kwargs) instead of super(MainApplication, self).__init__(parent, *args, **kwargs)

Proving the provided explanation.

The explanation I provided for your problem (the above why?) states there are 2 facts that lead you to that situation. If one of them is satisfied, the problem disappears:

  1. Leaving pack() in place but ask MainApplication to draw at least one widget:

    import tkinter as tk
    
    
    class MainApplication(tk.Frame):
        def __init__(self, parent, *args, **kwargs):
            super(MainApplication, self).__init__(parent, *args, **kwargs)
            # I add these 2 following lines
            self.parent = parent
            self.initialize_gui()
    
    def initialize_gui(self):
       # Ok, let us draw one useless button for a test
       self.button_1 = tk.Button(self.parent, text="Do nothing")
       self.button_1.pack()
    
    
    if __name__ == '__main__':
        root = tk.Tk()
        MainApplication(root).pack(side="top", fill="both", expand=True)
        root.mainloop()
    

You will see the GUI:

enter image description here

  1. Let MainApplication class not drawing any widget but remove the pack():

    import tkinter as tk
    
    
    class MainApplication(tk.Frame):
        def __init__(self, parent, *args, **kwargs):
            super(MainApplication, self).__init__(parent, *args, **kwargs)       
    
    
    if __name__ == '__main__':
        root = tk.Tk()
        MainApplication(root) # Remove pack(...)
        root.mainloop()
    

You can see the GUI:

enter image description here

Christian Dean
  • 22,138
  • 7
  • 54
  • 87
Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
  • 1
    `MainApplication(root).pack()` is not useless; it packs the frame, which shrinks the root display area to the size of the frame, which in the test case is 0x0. The program as posted runs as expected on Windows from IDLE editor, showing a minimal (title bar only) Tk() Window that is otherwise normal (can be moved, maximized, or iconified). Your edit suggests that a title-bar-only window acts different on the Ubuntu graphics manager in use and (being rather useless) is not shown and does not work normally. – Terry Jan Reedy Mar 25 '17 at 21:46
  • _"Note that pack(your_options_list) is useless in this line[...]"_ - Really? That's strange because as @TerryJanReedy said above, this program works perfectly find on windows. Also, the code in my question is not my own. I was using it from Bryan Oakley's answer to [this](http://stackoverflow.com/questions/17466561/best-way-to-structure-a-tkinter-application) question. I will try your suggestions though. – Christian Dean Mar 25 '17 at 21:50
  • Your right. When I remove `.pack()` from my code, the program ran fine showing a small `tkinter` window. However, I don't think the `.pack()` part is useless because Mr. Oakley used it in his example in my above comment. – Christian Dean Mar 25 '17 at 21:53
  • @algerbrex: correct. `pack` is _not_ useless in this context. It serves a very real purpose. – Bryan Oakley Mar 25 '17 at 22:01
  • @BryanOakley I said it is useless because the natural way to place elements is as I did it in the first MCVE I provided. Using `pack()` the way provided by the OP is a code duplication at best and it prevents scalability at worse (because we can not use the class instance we just created, as I explained) – Billal Begueradj Mar 25 '17 at 22:05
  • @BryanOakley Alright, thanks for that conformation. Do you how I can use `pack()` in my example though. Because while I agree it severs a purpose, whenever I try to use it, my `tkinter` will not work. – Christian Dean Mar 25 '17 at 22:30
  • @algerbrex: I think it's working fine. It's just that you have no widgets so the window shrinks down to 1 pixel. The window is there, you just can't see it. – Bryan Oakley Mar 25 '17 at 22:52
  • @BillalBEGUERADJ: there is no duplication in the original code. The frame is packed exactly once. And it definitely doesn't prevent scaleability. I have no idea why you think that. The OP's code has a window, `Tk()`, and in that window is a single frame. That frame _must_ be put on the screen with `pack`, `place`, or `grid` for it to be visible. – Bryan Oakley Mar 25 '17 at 22:54
  • Thank you for the comment. I can not teach you tkinter, but excuse me to be explicit:The best practices state that we set the master as an instance variable (`self.master = master` as I did in the first MVCE) so that we can later use the layout managers you mentioned for the purpose you mentioned. If we follow this good practice, the doing what you encourage is simply a code duplication which not only is useless bad bad, very bad, indeed. @BryanOakley – Billal Begueradj Mar 25 '17 at 23:43
  • As for scalability: here again, I am not pretending to teach you things, but let me be explicit: what if the OP wants to extends his program and use the instances of that class? Surely it is impossible to do that if the OP keeps using `pack()` in the main as you insist in encouraging him to do. To conclude, instead of providing only a solution which works, I provided a solution which highlights good practices. This is my last comment to this post. Thank you for your contribution ... and the 2 consecutive downvotes :) @BryanOakley – Billal Begueradj Mar 25 '17 at 23:46
  • @BryanOakley Thanks Brian. You were right. My problem was that my window was so small it wasn't visible. After adding a widget or two though, the window appeared. Do you mind if I write that as an answer to my question, or would you rather write one yourself? – Christian Dean Mar 25 '17 at 23:57
  • @algerbrex: go ahead and write your own answer. – Bryan Oakley Mar 26 '17 at 00:01
  • @BillalBEGUERADJ: I think you misunderstand the code. The frame is the only widget that goes in the root or master window. Everything else goes inside the frame. Doing it this way gives a nice, self-contained application. If the programmer wants to switch to a multi-windowed application, all they need to do is create another instance of the app and set its master to a toplevel. Or if they do want to put more widgets in the root they can without having to change anything in the existing app because the whole app can be treated as if it were a single widget. This pattern is highly scaleable. – Bryan Oakley Mar 26 '17 at 00:08
  • @BillalBEGUERADJ: _"Surely it is impossible to do that if the OP keeps using pack() in the main as you insist in encouraging him to do."_ This is simply not true. I don't care if they use `pack`, `place` or `grid`. The point is, they have to use _something_ to make the frame visible. That is a fact. – Bryan Oakley Mar 26 '17 at 00:12