Table of contents > Widget definition presets and more widgets

Widget definition presets and more widgets

Presenting widget definition presets

The full syntax for defining widgets helps us request our widgets as precisely as needed. However, some combinations of options are expected to be used more often than others, since they have particular meanings.

For instance, in order to define a widget to edit natural numbers, you'd have to request an intfloat entry to edit integers with the minimum value set to 0 (zero). Since this is a very common usage of intfloat entries, in order to make the life of our users easier, instead of typing all the configuration, our users can simply assign the string 'natural_number' to the parameter annotation. Here's an example:

### intfloat widget to edit natural numbers

### standard library import
from random import randint


### support function

def get_random_color():
    return tuple(randint(0, 255) for _ in range(3))

### main callable

def get_random_colors(quantity:'natural_number'=1):

    return [

      get_random_color()
      for _ in range(quantity)

    ]

main_callable = get_random_colors

To make life even easier, there are also equivalent alternatives to 'natural_number' like 'non_negative_int' and 'non_negative_integer'.

The full list of available widget definition presets can be found in the Widget presets appendix.

The literal entry widget

The literal entry widget works just like the string entry widget, but instead of handling strings exclusively it can handle any Python literal. That is, it can handle the following types:

It is useful:

A big list or tuple would not display nicely on the entry, since it doesn't have much space. Don't worry about this, since we have another widget for handling larger Python literals.

However, the literal entry is still useful for small lists or tuples (or any other small literal) and those lists/tuples are very useful for representing a lot of common data. For instance, tuples with 02 or 03 integers fit nicely in the literal entry and they are commonly used to represent points. Tuples with 03 or 04 integers can be used to represent colors (values from red, green, blue and alpha channels).

Here is a simple example of a node that returns equidistant points representing a line segment and uses literal entries to help define points in some of its parameters:

### node that returns equidistant points from line
### segment

### third-party import
from pygame.math import Vector2


def get_line_points(

      start_point: {

        'widget_name': 'literal_entry',

        # you can use the type or combination of types
        # you want; for this and the next parameter
        # we think using tuple makes sense, but in no
        # way this will limit the types you can type
        # in the entry
        'type': tuple, 

      } = (0, 0),

      end_point: {

        'widget_name': 'literal_entry',
        'type': tuple,

      } = (100, 0),

      quantity : {

        'widget_name': 'int_float_entry',

        # at least 02 points should be returned, that is
        # the start and end points, all additional points
        # will be the middle ones
        'widget_kwargs': {'min_value': 2}, 

        'type': int,

      } = 2,

    ):

    if quantity < 2:
        raise ValueError("'quantity' must be => 2")

    if quantity == 2:
        return [start_point, end_point]


    ### we only reach this point in the function when
    ### quantity > 2

    increment = 1/(quantity-1)

    start_v = Vector2(start_point)
    end_v   = Vector2(end_point)

    points = []

    percentage = 0

    for _ in range(quantity):

        resulting_v = start_v.lerp(end_v, percentage)

        points.append(tuple(resulting_v))

        percentage += increment

    return points

main_callable = get_line_points
Node generated from function

If you want, instead of using the full syntax, you can just assign 'python_literal' (a widget definition preset) to the annotation of the start_point and end_point parameters and the widget will be defined as well. In that case, however, you won't be able to be specific about the expected type (tuple) as you were when specifying the type using the full widget definition syntax.

The literal display widget

The literal display widget serves the same purpose of the literal entry widget, with the only difference being the fact that it is better suited to display and edit large python literals like collections with many items.

### node with widget to process larger literals

### standard library import
from statistics import mean


def process_age_info(

      data : {

        'widget_name': 'literal_display',

        # the type can be whichever you want, it won't
        # limit the widget
        'type': (tuple, dict),

      } = (
        ('Anne', 20),
        ('Mark', 14),
        ('Carol', 17),
        ('Don', 42),
        ('Jane', 23),
        ('Maria', 38),
      ),

    ):

    data = dict(data)

    processed_data = {}

    processed_data['mean'] = mean(data.values())

    ordered = sorted(
                data.items(),
                key = lambda item: item[1],
              )

    processed_data['youngest_person'] = ordered[0][0]
    processed_data['oldest_person'] = ordered[-1][0]

    return processed_data

main_callable = process_age_info
Node generated from function

You can also just assign 'python_multiline_literal' (a widget definition preset) to the annotation of the data parameter. The full syntax allows you to specify the expected types, though.

The option menu widget

The option menu widget is a pretty straightforward one. It allows you to pick a value from a list of specified values. Here's how you define an option menu widget:

### node with an option menu widget

def greet(

      name : str = 'Bruce',

      # the 'greeting' parameter defines an option menu

      greeting : {

        'widget_name' : 'option_menu',

        'widget_kwargs': {

          'options': [

            'Hi',
            'Hello',
            'Good morning',
            'Good afternoon',
            'Good evening',

          ]

        },

        'type': str,

      } = 'Hi',

    ):
    print(f"{greeting}, {name}!")

main_callable = greet
Node generated from function

Make sure the default value provided is within the available options defined in the 'options' key.

You can use any python literal as the available options and default value.

The option tray widget

The option tray works just like the option menu, but instead of presenting its options in a dropdown menu when clicked, the options are always laid side by side on the node itself, so that the user can click on them directly.

It was made to hold a small quantity of options or at least options small enough that don't take too much space. If the options you provide take too much space (surpass the width available within the node) Nodezator will raise an exception until you either fix the problem by reducing the space used by the options (perhaps by abbreviating them) or using an option menu widget instead.

### node with an option tray widget

def get_channel_value(

      color,

      channel : {

        'widget_name' : 'option_tray',

        'widget_kwargs': {

          'options': [

            'red',
            'green',
            'blue',

          ]

        },

        'type': str,

      } = 'red',

    ):

    if   channel == 'red'  : index = 0
    elif channel == 'green': index = 1
    elif channel == 'blue' : index = 2

    else: raise ValueError(
                  "'channel' must be either 'red',"
                  " 'green' or 'blue'"
                )

    return color[index]

main_callable = get_channel_value
Node generated from function

The sorting button widget

The sorting button widget allows users to pick and sort items from a set of available items.

For instance, let's say you have a function that sorts a list of colors according to a list of specified properties like the values of red, green and blue channels.

In this widget you specify all available items in a set and by clicking the icon on the left of the widget, a special sorting editor pops up and allows you to pick and sort items by dragging them on the screen. If you click any other part of the widget a text view appears describing the current value of the widget and the available items for picking/sorting.

### sorting button widget to edit order of
### color properties

### standard library import
from operator import itemgetter

property_to_index = {
  'red'   : 0,
  'green' : 1,
  'blue'  : 2,
}

def get_sorted_colors(

      colors: {
        'widget_name': 'color_button',
        'type': tuple,
      } = (
        (128, 128, 128),
        (30, 130, 70),
        (40, 40, 90),
      ),

      properties : {
        'widget_name': 'sorting_button',
        'widget_kwargs': {
          'available_items': {'red', 'green', 'blue'},
        },
        'type': str, # the type actually describes the
                     # available items; the value itself
                     # must be a sequence, either a tuple
                     # or a list;

      } = ('red', 'blue',)

    ):

    ### if properties were not specified, return the
    ### colors without sorting them
    if not properties: return colors

    ### otherwise, sort the colors

    ## get indices related to each specified property name

    indices = [
      property_to_index[property_name]
      for property_name in properties
    ]

    ## get a function to return the chosen indices for
    ## each color
    indices_getter = itemgetter(*indices)

    ## return a sorted version of the colors received,
    ## using the getter function as the key function

    return sorted(
             colors,
             key = indices_getter,
           )

main_callable = get_sorted_colors
Node generated from function

If you want to be more direct, the widget can handle numeric values as well:

### sorting button widget to edit order of
### color properties

### standard library import
from operator import itemgetter

def get_sorted_colors(

      colors: {
        'widget_name': 'color_button',
        'type': tuple,
      } = (
        (128, 128, 128),
        (30, 130, 70),
        (40, 40, 90),
      ),

      property_indices : {
        'widget_name': 'sorting_button',
        'widget_kwargs': {
          'available_items': {0, 1, 2},
        },
        'type': int, # the type actually describes the
                     # available items; the value itself
                     # must be a sequence, either a tuple
                     # or a list;

      } = (0, 2,)

    ):

    ### if property indices were not specified, return the
    ### colors without sorting them
    if not property_indices: return colors

    ### otherwise, sort the colors

    ## get a function to return the chosen indices for
    ## each color
    indices_getter = itemgetter(*property_indices)

    ## return a sorted version of the colors received,
    ## using the getter function as the key function

    return sorted(
             colors,
             key = indices_getter,
           )

main_callable = get_sorted_colors
Node generated from function

The widget can also handle float and a combination of integers and floats.

Previous chapter | Table of contents | Next chapter