Opening a Window - Demo 01¶
Objective¶
Learn how to :
open a window
make a black screen
close the window

Demo 01¶
How to Execute¶
Load src/modelviewprojection/demo01.py in Spyder and hit the play button.
Terminology¶
The device connected to a computer that displays information to the user is called a monitor. A monitor consists of a two-dimensional grid of tiny light-emitting elements, called pixels. Each pixel typically has three components: red, green, and blue. At any given moment in time, the computer instructs each pixel to display a specific color, which is represented by 3 numbers. The two dimensional array of all these pixel colors at a single point in time is called a frame, which forms a picture that conveys meaning to the user. Within the frame, a pixel is referenced using an x and y index on the frame. (0,0) on the frame is the lower left pixel, and and upper right pixel is at index (width, height)

Computer Monitor¶
Pixels, each with a Red, Green, and Blue component.¶
1024x768 monitor¶
1920x1200 monitor¶
Frames are generated by the computer and sent to the monitor ideally at a constant rate, called the frame rate, measured in Hertz (Hz). By updating these frames rapidly and consistently, the computer creates the illusion of motion for the user.

14 Hertz Motion¶







Code¶
That’s enough terms for now, let’s get on to a working program!
Importing Libraries¶
Import Python modules, which are Python programmer’s main form of libraries.
19import sys
The “sys” module is imported. To call functions from this module, the syntax is “sys.function”.
27from OpenGL.GL import (
28 GL_COLOR_BUFFER_BIT,
29 GL_DEPTH_BUFFER_BIT,
30 GL_MODELVIEW,
31 GL_PROJECTION,
32 glClear,
33 glClearColor,
34 glLoadIdentity,
35 glMatrixMode,
36 glViewport,
37)
GL is a submodule of the OpenGL module. By directly importing specific functions from GL into our current module, we avoid having to write OpenGL.GL.function every time. This keeps our code cleaner and easier to read, especially since we’ll be using these functions frequently.
23import glfw
Opening A Window¶
Desktop operating systems allow users to run multiple programs simultaneously. Each program displays its output within a dedicated area of the monitor, called a window.

Window¶
To create and open a window in a cross-platform way, this book uses functions provided by the widely supported GLFW library, which works on Windows, macOS, and Linux. In addition to window management, GLFW also provides functions for handling input from keyboards and game controllers.
GLFW/OpenGL Initialization¶
Initialize GLFW.¶
42if not glfw.init():
43 sys.exit()
Initialize GLFW. If initialization fails, the program should terminate. What does the initialization do? I don’t know. Think of it like a constructor for a class; it initializes some state that it needs for later function calls.
Set the version of OpenGL¶
OpenGL has been around a long time, and has multiple, possibly incompatible versions. For this demo, we use OpenGL 1.4. By the end of this book, we will be using OpenGL 3.3.
47glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 1)
48glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 4)
Create a Window¶
52window = glfw.create_window(500, 500, "ModelViewProjection Demo 1", None, None)
By calling glfw.create_window, we are creating a window. What are the arguments passed? It’s likely that the first two arguments are the width and height of the window, and the third likely the title of the window. What do the fourth and fifth arguments, both of which are “None”, mean? The author does not know. We could look it up the APIs online. But, Python has something called keyword arguments, which we will talk about later, where the intent of each parameter is clear at the site of calling the function.
56if not window:
57 glfw.terminate()
58 sys.exit()
If GLFW cannot open the window, quit.
62glfw.make_context_current(window)
Make the window’s context current. The details of this do not matter for this book.
67def on_key(win, key, scancode, action, mods):
68 if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
69 glfw.set_window_should_close(win, 1)
70
71
If the user presses the “Escape” key while the program is running, inform GLFW that the user wants to quit, which we will handle this later in the event loop.
To Note¶
Functions are first class values in Python, and are objects just like anything else. The can be passed as arguments, stored in variables, and applied later zero, 1, or more times.
>>> def doubler(x):
... return x * 2
...
>>> doubler
<function doubler at 0x10165c0e0>
>>> def add_five_to_result_of(f, x):
... return 5 + f(x)
...
>>> add_five_to_result_of
<function add_five_to_result_of at 0x10165c040>
>>> add_five_to_result_of(doubler, 3)
11
77glClearColor(0.0289, 0.071875, 0.0972, 1.0)
Before a frame is drawn, it is first turned into a blank slate, where the color of each pixel is set to some value representing a color. We are not clearing the frame-buffer right now, but setting what color will be used for a later clear. Calling “glClearColor” “0,0,0,1”, means black “0,0,0”, without transparency (the “1”).
81glMatrixMode(GL_PROJECTION)
82glLoadIdentity()
83glMatrixMode(GL_MODELVIEW)
84glLoadIdentity()
Don’t worry about the 4 lines here. Although they are necessary, we will cover them in depth later in this book. After all, this book is called ModelViewProjection. :-)
The Event Loop¶
When you pause a movie, motion stops and you see one picture. Movies are composed of sequence of pictures, when rendered in quick succession, provide the illusion of motion. Interactive computer graphics are rendered the same way, one “frame” at a time.
For our program, we need to render a frame, flush the complete frame-buffer to the monitor. Repeat indefinitely until the user closes the window, or the program needs to terminate.
Define the event loop
89while not glfw.window_should_close(window):
90 glfw.poll_events()
91
92 width, height = glfw.get_framebuffer_size(window)
93 glViewport(0, 0, width, height)
94 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
95 glfw.swap_buffers(window)
Poll the operating system for any events, such as mouse movements, keyboard input, etc. This call does not handle them, just registers them as having happened for handling later.
Get the size of the frame-buffer. The frame-buffer contains 2D data representing all the pixels in a complete video frame. Python allows the returning of multiple values in the form of a tuple. Assigning to the variables width and height this way is a form of “destructuring”
Tell OpenGL that we wish to draw in the entire frame-buffer, from the bottom left corner to the upper right corner.
Make the frame-buffer a blank slate by setting all of the pixels to have the same color. The color of each pixel will be the clear color. If we hadn’t cleared the frame-buffer, then frame number n+1 would be drawing on top of whatever was drawn on frame number n. Programming in OpenGL is a bit different than normal programming in a normal language, in that individual function calls do not complete self-contained tasks, as subroutines typically do. Instead, the procedure calls to OpenGL functions only make sense based off of the context in which they are evaluated, and the sequence of OpenGL calls to complete a task.
We have colored every pixel to be black, so flush the frame-buffer to the monitor, and swap the back and front buffers.
Exercise¶
Run the program, close it by hitting Escape.
Before the call to glClear, enter two new lines. On the first, type “import pdb”. On the second type “pdb.set_trace()”. Now run the program again and observe what is different. (pdb.set_trace() sets a breakpoint, meaning that the program pauses execution, although the GLFW window is still on screen over time)
One frame is created incrementally at a time, but the frame is sent to the monitor only when frame is completely drawn, and each pixel has a color. The act of sending the frame to the monitor is called flushing the frame.
OpenGL has two frame-buffers (regions of memory which will eventually contain the full data for a frame), only one of which is “active”, or writable, at a given time. “glfwSwapBuffers” initiates the flushing the current buffer, and which switches the current writable frame-buffer to the other one.
Black Screen¶
Load src/modelviewprojection/demo01.py in Spyder and hit the play button.
The first demo is the least interesting graphical program possible.
Sets the color at every pixel black. (A constant color is better than whatever color happened to be the previous time it was drawn.)
If the user resized the window, reset OpenGL’s mappings from normalized-device-coordinates to screen-coordinates.
Cleared the color buffer and the depth buffer (don’t worry about the latter for now).
When this code returns, the event loop flushes (i.e) sends the frame to the monitor. Since no geometry was drawn, the color value for each pixel is still black.
Each color is represented by a number, so the frame is something like this, where ‘b’ represents black
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
The event loop then calls this code over and over again, and since we retain no state and we draw nothing, a black screen will be displayed every frame until the user closes the window, and says to himself, “why did I buy Doom 3”?