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¶







Reference to Learn More¶
For more information on Computer Graphics Hardware, see [SM09b], or [HBC11b].
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
20import glfw
21import OpenGL.GL as GL
The “sys” module is imported. Python has modules to let developers organize code into reusable, manageable pieces instead of keeping everything in one large file. Modules help separate functionality, avoid name conflicts, and make it easier to share and maintain code. By importing modules, you can use prewritten libraries or your own organized code across different projects.
To call functions from this module, the syntax is “sys.function”. In code below, “sys.exit()” means “call the exit function from the “sys” module to exit the current process. There is also a function “terminate” from the “glfw” module, which is a different module. Even if glfw defined a “exit” function, there would be no name collision, as the functions are prefaced by their module names.
39if not window:
40 glfw.terminate()
41 sys.exit()
Modules can have submodules, for further organization. GL is a submodule of the OpenGL module. Sometimes submodule names can get quite longer, so we would like to create a shorter name for use within our Python file. “import OpenGL.GL as GL” allows use to write “GL.function” instead of “OpenGL.GL.function”
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.¶
25if not glfw.init():
26 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, occasionally incompatible versions. For this demo, we use OpenGL 1.4. By the end of this book, we will be using OpenGL 3.3 Core Profile.
Inform GLFW that we wish to used OpenGL 1.4:
30glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 1)
31glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 4)
Create a Window¶
35window = 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 are used extensively throughout the rest of the book, where the intent of each parameter is clear at the site of calling the function.
39if not window:
40 glfw.terminate()
41 sys.exit()
If GLFW cannot open the window, the Python process terminates by calling sys.exit.
45glfw.make_context_current(window)
Make the window’s context current. The details of this do not matter for this book, but we need to call it anyways.
50def on_key(win, key, scancode, action, mods):
51 if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
52 glfw.set_window_should_close(win, 1)
53
54
55glfw.set_key_callback(window, on_key)
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.
See First Class Functions for more information on functions as parameters to other functions.
59GL.glClearColor(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 will see the importance of this in the first project). We are not clearing the framebuffer 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”).
63GL.glMatrixMode(GL.GL_PROJECTION)
64GL.glLoadIdentity()
65GL.glMatrixMode(GL.GL_MODELVIEW)
66GL.glLoadIdentity()
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 framebuffer to the monitor. Repeat indefinitely until the user closes the window, or the program needs to terminate.
Define the Event Loop
71while not glfw.window_should_close(window):
72 glfw.poll_events()
73
74 width, height = glfw.get_framebuffer_size(window)
75 GL.glViewport(0, 0, width, height)
76 GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
77 glfw.swap_buffers(window)
Run a loop that will run infinitely, until some event causes it to decide to exit.
Poll the operating system via the “poll_events” function (defined in the glfw module) for any events, such as mouse movements, keyboard input, etc. This call does not handle them, just queries to see if any of them have happened recently.
Get the size of the Frame Buffer from glfw. A framebuffer 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”
Using “glViewport” from the GL module, tell OpenGL that we wish to draw in the entire framebuffer, from the bottom left corner to the upper right corner.
Make the framebuffer a blank slate by setting all of the pixels to have the same color. The color of each pixel will be value set to the clear color, which we set using glClearColor.. If we hadn’t cleared the framebuffer, 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 framebuffer to the monitor, and swap the back and front buffers.
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 framebuffers (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 framebuffer 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”?