Your First Window
The first window is the three-part shape from the overview made concrete: create the top-level window, add a widget, connect the close behavior, and enter the main loop. The window is the gtk.Window with the WINDOW_TOPLEVEL type, the content is whatever widget tree the app builds, and the two calls that complete the program are show_all() (making the hierarchy visible) and gtk.main() (entering the loop that makes it live). This chapter builds the window, walks the window's properties (title, size, the icon, the role), the close-event contract, and the minimal complete program - the skeleton every later chapter's app is built on.
Creating the Top-Level Window
import gtk
# the top-level window - the frame the OS manages:
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
# the window properties that define the frame:
window.set_title("NotePad")
window.set_default_size(600, 400) # the initial size
window.set_resizable(True)
window.set_wmclass("notepad", "NotePad")
window.set_icon_name("text-x-generic")
# a top-level window is INVISIBLE until shown - the widget tree
# can be fully built before the user sees a pixel of it
The gtk.Window(gtk.WINDOW_TOPLEVEL) is the application window - the frame the window manager decorates (title bar, borders, the close button) and the root of the widget hierarchy the app builds inside it. The type argument distinguishes the top-level window from the other window types (a popover, a menu shell), and for an application it is always WINDOW_TOPLEVEL. The properties that define the frame are set on the window object: the title (the title bar text), the default size (the initial dimensions - the user can resize after), the resizability, the WM class (the application identity the window manager uses), and the icon. The key state to internalize: a window is invisible until shown. The entire widget tree can be assembled, wired, and configured before show_all() makes any of it visible, which is why the program builds everything first and shows once at the end.
The Close Event and main_quit
import gtk
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("NotePad")
# the close behavior: the window manager's close button emits
# "delete-event", and the handler's return value decides what happens:
def on_delete(widget, event):
# True = "handled" - swallow the event, the window STAYS
# False = "not handled" - the default action runs, the window
# closes (and if it is the last one, the app can quit)
gtk.main_quit() # the clean exit: stop the loop
return False # let the default close proceed
window.connect("delete-event", on_delete)
window.show_all()
gtk.main() # blocks here until main_quit() is called
print("the loop has exited")
The close behavior is the delete-event signal: the window manager's close button (and the platform's equivalent) emits it, and the handler's return value is a contract - returning True means the event was handled and the default action (closing the window) is suppressed, returning False means the default proceeds. The clean app exit is gtk.main_quit() called inside the handler (or from any other signal): it stops the main loop, gtk.main() returns, and the program continues after the loop - the point where a real app does its shutdown (save state, close the display connection). The discipline the chapter drills: the handler both stops the loop and returns False, because the two are independent - the quit stops the Python side, the return value governs the window's close, and doing both is the complete, clean exit. A handler that returns True without quitting is the "close button does nothing" bug; one that quits but the window is not the last one is the "app quit with a window still open" case the multi-window chapter revisits.
The Minimal Complete Program
import gtk
# the minimal complete PyGTK program - every later chapter's app
# is this skeleton with the content and the behavior filled in:
def main():
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("NotePad")
window.set_default_size(600, 400)
# the content: a placeholder the later chapters replace
# with the menu, the toolbar, and the text view
label = gtk.Label("The editor goes here")
window.add(label)
# the behavior: the clean close
def on_delete(widget, event):
gtk.main_quit()
return False
window.connect("delete-event", on_delete)
window.show_all()
gtk.main()
if __name__ == "__main__":
main()
The minimal complete program is the skeleton: a main() function that builds the window, sets its frame properties, adds a placeholder child, connects the close handler, shows everything, and enters the loop. The if __name__ == "__main__" guard makes it runnable as a script and importable as a module (the later chapters' tests import main without running the loop). The placeholder label is deliberate - the structure (window, content slot, close behavior, loop) is complete even though the content is a stub, and each later chapter replaces the stub: the containers chapter puts a box in the slot, the menus chapter adds the menu bar, the text chapter fills the center with the editor. The skeleton is the contract - any PyGTK app, from a dialog to a full editor, is this shape with more in the content slot and more signals wired.
The Working Rules
- Build the whole tree, then
show_all()once. Showing widget-by-widget as it is built flickers and races; the hierarchy is assembled invisibly and made visible in one call. - The close handler both quits the loop and returns
False. The two are independent -main_quitstops the Python side, the return value governs the window's default close. - Set the frame properties on the window, not the content. Title, size, icon, and WM class belong to the top-level; the child widgets carry their own properties.
- Keep the program in a
main()behind the__main__guard - runnable as a script, importable for the tests that build the UI without entering the loop.
Summary
The first window is the three-part shape made concrete: the gtk.Window(gtk.WINDOW_TOPLEVEL) with its frame properties (title, default size, icon, WM class), the content added into it, the delete-event handler that calls gtk.main_quit() and returns False (the quit stops the loop, the return value governs the window's close), and the show_all() plus gtk.main() that make the tree visible and the loop live. The window is invisible until shown, so the whole hierarchy is built before the single show, and the minimal complete program - the main() behind the __main__ guard with a placeholder in the content slot - is the skeleton every later chapter fills: the containers go in the slot, the menus and toolbar above it, the editor at its center. The close contract and the show-once discipline are the two rules the whole tutorial assumes.