yamlui - Declarative UI in pygame

Started by Son of the King, August 14, 2016, 01:14:32 AM

Previous topic - Next topic

Son of the King

yamlui - Declarative UI in pygame
Code is here on GitHub

Introduction

So, I've begun the process of making a thing that I had an idea for just over a year ago. One of the most convoluted parts of the unreleased python version of Township I did was the UI code. I had tried to generalise it, and mostly succeeded, but it was still a horror show. The 3d Township thing also ran out of steam because of no easy way to combine my existing code with some kind of easy UI layout tool.

So I decided to think about how I wanted to be able to define a UI. Most importantly, I wanted to not have to write much code to do it. Something that was great when I was first learning to program using Visual Basic was the ease of creating a UI by dragging elements onto a "form" and writing code that is run when events happen on those UI elements. It made it wonderfully easy to throw together a UI for something. This is much easier than the approach used by tkinter and pyqt (though Qt has a declarative thing called QML which is nice), which involves lots of wrangling UI elements in the code itself (so not really much of an improvement on the unreleased python Township I mentioned).

Another benefit of being able to describe a user interface in a configuration file is that it is then really easy for modders to change the UI as they see fit. One can customise as much as they like without having to actually rewrite any of the code.

The result of my thinking was a plan for yamlui. This would theoretically let me write a yaml file which defines everything about the user interface of a pygame application, from the size, colour, and positioning of things, to how they change (and what behaviour they should perform) when events happen, such as them being clicked on, or them being typed at.

Example

So the idea is that. So far, I've implemented only a few UI widgets, so its not even as functional as my old Township UI. However, it is all definable easily in a configuration file. Here is an example:

object: window
properties:
  text: Township Test UI
  image: examples/images/background.png
  width: 1280
  height: 800
children:
- object: container
  properties:
    opacity: 50%
    colour: [0, 0, 0]
    position: [10, 100]
    width: 625
    height: 600
  children:
  - object: label
    properties:
      text: World Settings
      width: 605
      font: arial
      font-size: 18
      position: [10, 10]
      display: relative
  - object: label
    properties:
      text: These options allow you to customise the settings used by the map generator when generating your world.
      width: 605
      font: arial
      font-size: 14
      font-colour: [175, 175, 175]
      position: [10, 50]
      display: relative
- object: container
  properties:
    opacity: 50%
    colour: [0, 0, 0]
    position: [645, 100]
    width: 625
    height: 600
  children:
  - object: label
    properties:
      text: Township Settings
      width: 605
      font: arial
      font-size: 18
      position: [10, 10]
      display: relative
  - object: label
    properties:
      text: These options allow you to customise the starting situation of your township's populace.
      width: 605
      font: arial
      font-size: 14
      font-colour: [175, 175, 175]
      position: [10, 50]
      display: relative


That file defines this UI:

Spoiler

The best part of that is that the only code I need to write outside of the yamlui library is this:

import pygame

import yamlui


pygame.init()

window = yamlui.generate_ui('examples/testui.yaml')

while True:
    for event in pygame.event.get():
        window.handle_event(event)
    window.update()
    window.draw()


Playing around

If this is of interest to anyone, it should be pretty easy for you to play around with. Just grab the latest version of yamlui from its GitHub page (there is a big green download button on the right), put the example code from this post in a script in the root directory (alongside the README and what-have-you) and run it with python.

It works with both python 2.7 and python 3.4 (probably other 3.x versions too, but I've only tested 3.4). If you're on Windows I'd recommend using python 3.4, since that makes it nice and easy to install pygame these days.

Assuming you installed python 3.4 on Windows, open a command prompt and run
C:\Python34\python.exe -m pip install -U pip
C:\Python34\python.exe -m pip install pygame pyyaml


to install the dependencies. You'll need to do this before you can successfully run the examples provided, obviously.

If you're on Linux (or probably OSX too) you can disregard the C:\Python34\python.exe -m bit of those commands.

I'll post/be cheerful in this thread when I implement cool new parts of it, like textboxes, and functional buttons.

Son of the King

I implemented clickable buttons!

Spoiler

Next up is text boxes. Slowly heading to a thing I can use to (easily?) build a usable UI for things.

Jubal

Ooh, I may one day test this out - I've been meaning to move to using pygame more. Though I also want to get my two current games, both of which use tkinter, done and dusted as well...
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Son of the King

Now textboxes are "done". Still slightly buggy, in that its possible to type off the end of them (and not see what you typed there).

Spoiler

Next might be sliders, or the ability to display dynamic content in labels easily, or maybe even some work to create something akin to CSS classes for the yaml definitions themselves, since the example I'm using is threatening to become unwieldy (and full of duplication).

Son of the King

I went for the second option, labels can now display the value of a variable rather than some predefined text if needed.

Spoiler

Next is definitely something analogous to CSS classes, because this UI is now driving me insane to modify.

I'm putting off the thing I know I need to do soon, that is, the ability to only show things based on some condition being true (ie, only show the building buttons when a villager is selected or similar). I'm also struggling to decide how to deal with something that needs to contain a dynamic set of UI elements, for example a list of names that can be added to or subtracted from. Maybe I need some kind of table widget.

Son of the King

I did the CSS classes type thing, less powerful than CSS but at least makes the definitions slightly more readable.

Next up is a table with template-able rows.