Quickstart

In this guide, we’ll learn how to create a simple GUI using peng3d.

See also

For a more complex example, see Designing a basic 3D Application with peng3d.

Basic Structure

In this guide, we will be writing our app using only a single Python file for simplicity. For more complex projects, it is recommended to split your application by menus or even submenus into different files.

First, here’s the minimum example required to show anything with peng3d:

import peng3d

peng = peng3d.Peng()

peng.createWindow(caption="Hello World!", resizable=True)

main_menu = peng3d.Menu("main", peng.window)
peng.window.addMenu(main_menu)

peng.window.changeMenu("main")
peng.run()

If you run the app now, you should see a black window with a title of Hello World!:

../_images/quickstart_black_window.png

While most of the lines should be fairly self-explanatory, let’s go through them one by one.

First, we start by importing peng3d and creating an instance of the peng3d.Peng() class:

import peng3d

peng = peng3d.Peng()

There should only be one instance of this class per app, shared between all components. It manages the event system and some other globally shared resources.

Next, we create our window with the desired caption:

peng.createWindow(caption="Hello World!", resizable=True)

Since we want to keep this example very simple, we only pass a caption and activate resizing. All arguments to Peng.createWindow() are optional and should be passed as keyword arguments. Any arguments not recognized by peng3d are passed through to the underlying PengWindow class, which will in turn pass through unrecognized arguments to pyglet.

See also

See the pyglet.window module docs for a list of all arguments.

Now that we have created our window, we’ll create our first menu and register it:

main_menu = peng3d.Menu("main", peng.window)
peng.window.addMenu(main_menu)

The basic Menu class is designed for layer-based rendering. We will later change this, since we want to create a GUI with widgets.

Also, we always need to register menus, so it is a good practice to always register a menu right after creating it.

Lastly, we’ll switch to our main menu and start the application:

peng.window.changeMenu("main")
peng.run()

The call to changeMenu() can be used to switch between different menus, here we use it to define which menu our app starts with. Note that we pass in the name of our menu, not the menu object itself.

The final call to peng.run() starts the internal event loop of pyglet and blocks until the application exits, usually by clicking the X button.

Note

The code described in this subsection can also be found in examples/quickstart/quickstart_basic.py here.

Creating our first widget

Now that we have a basic skeleton running, let’s add some actual functionality. We’ll modify the code from the previous subsection bit by bit.

First, lets switch to a more advanced GUIMenu instead of the simple Menu we used earlier.

Note

For most widget-oriented apps, this is what you’ll use, although custom 3D canvases usually use a plain Menu with a GUILayer for overlayed widgets.

To do this, we’ll first change the class name:

main_menu = peng3d.GUIMenu("main", peng.window)

Then, let’s set the background to a more appealing color. For now, we’ll use a light grey, although many more variants are possible. You could even use an image or a custom callback as a background. To set the background, simply call GUIMenu.setBackground() with the color you want:

main_menu.setBackground([240, 240, 240])

By setting the background in the menu instead of the submenu, all submenus of this menu will automatically inherit the background unless they overwrite it. This makes it easier to e.g. swap themes.

If you try to run the app now, you’ll notice that it won’t start. This is because GUIMenu menus require an active submenu at all times that they are active. So, let’s add a submenu:

main_main = peng3d.SubMenu("main_sub", main_menu, peng.window, peng)

While it may not matter much in this simple app, we have chosen a name for this submenu that is different from the main menu. Any string can be used as a name, so feel free to create your own naming convention.

Note

While submenus of different menus could have identical names, this is strongly discouraged, as it can lead to confusion in larger projects. Ideally, each named object should have a unique name.

We’ll also have to tell the main menu to use this submenu, just before it is activated itself:

main_menu.changeSubMenu("main_sub")

If you run the app now, you should see a grey window instead of a black window:

../_images/quickstart_grey_window.png

Now, this is a bit better than just a black window, but not by much. Let’s go a bit further and add a single button that prints whenever it is clicked.

To do this, we’ll have to first create the button and register it:

button = peng3d.Button(
    "btn",
    main_main,
    pos=[100, 100],
    size=[200, 100],
    label="Press me!",
    borderstyle="oldshadow",
)
main_main.addWidget(button)

The Button class takes a lot of arguments, so let’s go over them.

In the first line, we pass the name of the button. Here, the same caveats apply as with submenu and menu names. We also pass the submenu this widget belongs to, from which the window and Peng singleton references are gathered.

In the next two lines, we pass the position and size of the widget.

Note

Positions in peng3d widgets are always from the bottom-left corner of the screen. Both positions and sizes are in pixels.

Next, we pass the label. For now, we give it a static label, though peng3d also supports easy translation capabilities.

Lastly, we pass what style of border to use. There are several border styles available, further information is available in the documentation of the Button class.

See also

There are many more optional arguments that the Button class can take. See the API documentation for details.

Now, we have a button. But it does not do anything yet, so let’s add an action that prints something whenever it is called:

button.addAction("click", print, "Clicked!")

The addAction() method is quite flexible. It takes the name of the action as the first parameter, a function to call as the second parameter and passes all other arguments to each call of the function. So while we could write a one-line function to print out our message, we can just pass the argument to print. Obviously, you’ll still have to write proper functions or methods for more complicated handlers.

Now, let’s take a look at our current app:

../_images/quickstart_widget.png

If you run the app yourself, try clicking on the button and watching the console output. You should see Clicked! every time you release the button.

If you want, try playing around with the parameters to the Button class and see how they effect the look or behaviour of the app.

Once you are done, move on to the next subsection, where we learn how to use and switch between multiple menus.

Note

The code described in this subsection can also be found in examples/quickstart/quickstart_widget.py here.

Switching between menus

Todo

Write this subsection

Dynamically adjusting our layout to the window size

Todo

Write this subsection

Further reading

There are other, more advanced guides available. For example, take a look at Designing a basic 3D Application with peng3d.

See also

See the examples/ folder on the main repository for more examples of various peng3d features.