Die Elemente der Uhr erzeugen

Wir werden die Uhr nicht originalgetreu nachbauen, wie im Bild der Einleitung gezeigt, sondern mit einer abstrakten Version beginnen. Dafür reicht uns erst einmal ein Grundbaustein aus Blender, der Cube.

Für die Anordnung der Uhr-Elemente verwenden wir ein Array, in der jede Hashmarke(#) eine Lampe repräsentieren wird.

Aufgaben/Handlungsanweisungen

Teste diese erste Version mit den folgenden Arbeitsschritten:

  • starte mit einem neuen Python-Skript
  • Leg ein neues Skript mit Namen „clock_create.py“ an.
  • übernimm den gezeigten Quellcode
  • verwende die erste Funktion im clock_ctrl.py
  • drücke Alt+P (für die Ausführung des Skripts)
  • begutachte das Ergebnis/analysiere die Fehler, wenn notwendig verbessere die Skripte, bis sie fehlerfrei aufrufbar sind

Zusatz

  • Erzeuge statt eines Würfels ein anderes Mesh-Objekt.

Ein Array nimmt Nimmt die Platzhalter für die einzelnen Anzeige-Elemente auf.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#! bpy
"""
Name: 'clock_create.py'
Blender: 2.79/2.8x
Group: 'Berlin-Clock'
Tooltip: 'Part of the "Berlin Clock" project'
"""

import bpy


clockparts = ["#",
              "####",
              "####",
              "##########",
              "####"]

Nicht die Hashmarke (#) ist das Interessante, sondern die Position im Array, also der Index. In einer for-Schleife nutzen wir den index-Werte für das Erzeugen aller Anzeige-Elemente. Das sieht aber noch nicht wie eine Uhr aus! Besser wird es mit dem nächsten Schritt…

Die erste Version

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

def create_v1(parts):
    """Create and compose a berlin-clock Version 1

    - create all parts of a clock
    """

    rows = len(parts)

    for row in range(rows):
        cols = len(parts[row])
        for col in range(cols):
            bpy.ops.mesh.primitive_cube_add(location=(row, col, 0 ))

Wir beginnen mit der einfachsten Variante und werden davon ausgehend weitere Versonen erstellen. Das durchnummerieren der Funktionen ist eher ungewöhlich, zeigt aber wie sich der Quellcode verändert. Für gewöhnlich wird das mit einem Versionskontrollsystem verwaltet, aber das ist hier nicht das Thema.

Der Aufruf im Steuerungs-Skript

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#! bpy
"""
Name: 'clock-ctrl.py'
Blender: 2.79/2.8x
Group: 'Example'
Tooltip: 'Control script generating a berlin-clock.'
"""
import bpy
import sys
import os

dir = None


def main():
    """Create and compose a berlin-clock"""

    bpy.ops.object.select_by_type(type='MESH')
    bpy.ops.object.delete()
    dir = os.getcwd()
    if not dir:
        dir = os.path.dirname(bpy.data.filepath)
    if dir not in sys.path:
        sys.path.append(dir)

    import clock_create as cc
    clockparts = cc.clocknames
    create = cc.create_v6
    create(clockparts)
 
if __name__ == '__main__':

    main()

Zurück im Skript für die Steuerung der Uhr, hier werden wir die neuesten Funktionen testen und nutzten. Dazu importieren wir das Skript clock_create.py. Die Dateierweiterung lassen wir weg und erfinden gleichzeitig einen neuen kürzeren Namen für das Modul (cc steht nun für clock_create).

In den Zeilen 28 und 29 erzeugen wir lokale Kopien aus dem importierten Modul um endlich die Funktion mit dem richtigen Parameter aufzurufen.

Die letzten drei Zeilen hätten sich auch in einer einzigen Zeile zusammenfassen lassen. So aber sind die Teilschritte besser nachvollziehbar, was in diesem einfachen Beispiel demonstriert werden soll. Nachvollziehbarkeit für andere Entwickler ist ein wichtiges Thema. Vielleicht bist Du selbst der Entwickler, der nach einiger Zeit, den eigenen Text schnell wieder verstehen will!

cc.create(cc.clocknames)

Wie komplex die Anweisungen am Ende sind, ist eine Frage der Konventionen.

Erläuterungen im Detail

Zeile 1-2 – Funktionsaufruf mit Parameter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

def create_v1(parts):
    """Create and compose a berlin-clock Version 1

    - create all parts of a clock
    """

    rows = len(parts)

    for row in range(rows):
        cols = len(parts[row])
        for col in range(cols):
            bpy.ops.mesh.primitive_cube_add(location=(row, col, 0 ))

Wieder haben wir eine Funktion, diesmal erwartet sie beim Aufruf einen Parameter, hier parts genannt. Diese Liste wird dann intern in der Funktion verwendet und ist auch nur dort bekannt.

Zeile 8 – Länge ermitteln

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

def create_v1(parts):
    """Create and compose a berlin-clock Version 1

    - create all parts of a clock
    """

    rows = len(parts)

    for row in range(rows):
        cols = len(parts[row])
        for col in range(cols):
            bpy.ops.mesh.primitive_cube_add(location=(row, col, 0 ))

Für die Verarbeitung der Hash-Marken verwenden wir den Index. Den ermitteln wir für die Anzahl der Zeilen in der Liste parts, wir haben dann für die Anzahl der Zeilen eine Zahl. Das Gleiche wiederholt sich in Zeile 10, in der wir die Anzahl der Hash-Marken für eine Zeile ermitteln. Mit diesen Zahlen wiederholen wir den wichtigen Schritt der Objekt-Erzeugung.

Zeile 13 – Objekte erzeugen

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

def create_v1(parts):
    """Create and compose a berlin-clock Version 1

    - create all parts of a clock
    """

    rows = len(parts)

    for row in range(rows):
        cols = len(parts[row])
        for col in range(cols):
            bpy.ops.mesh.primitive_cube_add(location=(row, col, 0 ))

Hier wird nun in jedem Schleifendurchlauf ein Cube erzeugt, der Index für die Zeilen und Spalten bestimmt die Postion im 3D-Raum, der Parameter für die Z-Achse bleibt konstant auf 0.