# kreisel.py # ILS Kreisel - PyRx-Implementierung # Ersetzt alle Kreisel-Routinen aus KreiselInsert.lsp # Befehle: KreiselInsert, KreiselConnect, KreiselRedraw, # KreiselQuick, KreiselEdit, KreiselParams, # KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe # ============================================ import math import PyRx as Rx import PyGe as Ge import PyDb as Db import PyEd as Ed import wx from elemente.utils import ( get_block_path, component_next_number, merge_attribs, read_block_attribs, ensure_layer, ensure_textstyle, ensure_block_from_dwg, create_attrib_defs, insert_block_with_attribs, ) # ============================================================ # Konstanten (aus KreiselInsert.lsp) # ============================================================ KREISEL_DURCHMESSER = 800.0 KREISEL_PIN_ABSTAND = 100.0 KREISEL_DEFAULT_LAENGE = 2300.0 KREISEL_DEFAULT_HOEHE = 2000.0 KREISEL_ATTRIB_DEFS = [ ("NAME", ""), ("DREHRICHTUNG", "UZS"), ("N_SEPARATOREN", "2"), ("KREISELART", "STANDARD"), ("N_SCANNER", "0"), ("N_RAMPEN", "0"), ("DREHUNG", "0"), ("ABSTAND", "2300"), ("NUMMER", "0"), ("HOEHE", "0"), ] # Ausrichtungs-Definitionen: (Label, Rotation in Grad) KREISEL_AUSRICHTUNGEN = [ ("Horizontal MS", 0.0), ("Horizontal SM", 180.0), ("Vertikal MS", 90.0), ("Vertikal SM", 270.0), ] # Globaler Zustand (pro Sitzung) _kreisel_typ = "STANDARD" # Beschriftungs-Einstellungen _beschriftung_layer = "S_KRS_BESCHR" _beschriftung_hoehe = 100.0 _beschriftung_farbe = 7 _beschriftung_abstand_oben = 0.0 _beschriftung_abstand_x = 0.0 _beschriftung_abstand_y = 0.0 # ============================================================ # Kreisel-spezifische Hilfsfunktionen # ============================================================ def rotation_to_idx(rot): """Rotation (Grad) -> Index in KREISEL_AUSRICHTUNGEN.""" best_idx = 0 for i, (_, r) in enumerate(KREISEL_AUSRICHTUNGEN): diff = abs((rot % 360) - (r % 360)) if diff < 1.0: best_idx = i return best_idx def idx_to_rotation(idx): """Index in KREISEL_AUSRICHTUNGEN -> Rotation (Grad).""" return KREISEL_AUSRICHTUNGEN[idx][1] # ============================================================ # Kern-Funktion: draw_module (entspricht draw-module in LISP) # ============================================================ def draw_module(db, base_point, abstand, rotation_deg, attribs=None): """Kreisel-Geometrie zeichnen, Block definieren, einfuegen, Attribute setzen. Parameter: db - aktuelle Datenbank base_point - Ge.Point3d (x, y, z) Basispunkt AN-Seite abstand - Tangentenlaenge zwischen Kreismitten (mm) rotation_deg - Einfuegewinkel in Grad attribs - dict mit Attribut-Ueberschreibungen (optional) Rueckgabe: ObjectId der eingefuegten BlockReference """ block_path = get_block_path() radius = KREISEL_DURCHMESSER / 2.0 pin_y = radius - KREISEL_PIN_ABSTAND # Attribute vorbereiten merged = merge_attribs(attribs, KREISEL_ATTRIB_DEFS) merged["ABSTAND"] = f"{abstand:.0f}" merged["DREHUNG"] = f"{rotation_deg:.1f}" # Hoehe bestimmen z_hoehe = base_point.z if z_hoehe > 0: merged["HOEHE"] = f"{z_hoehe:.0f}" else: try: z_hoehe = float(merged.get("HOEHE", "0")) except ValueError: z_hoehe = 0.0 merged["HOEHE"] = f"{z_hoehe:.0f}" if z_hoehe > 0 else merged.get("HOEHE", "0") is_pin = merged.get("KREISELART", "STANDARD") == "PIN" # Nummer: bestehende beibehalten (Redraw/Edit), sonst neue vergeben try: kreisel_nummer = int(merged.get("NUMMER", "0")) except ValueError: kreisel_nummer = 0 if kreisel_nummer <= 0: kreisel_nummer = component_next_number(db, "KREISEL_") merged["NUMMER"] = str(kreisel_nummer) merged["NAME"] = f"Kreisel{kreisel_nummer}" # Blockname: KREISEL_1_PIN_2300 oder KREISEL_1_STANDARD_2300 art_str = "PIN" if is_pin else "STANDARD" bname = f"KREISEL_{kreisel_nummer}_{art_str}_{abstand:.0f}" bname = bname.replace(".", "") bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) block_exists = bt.has(bname) if not block_exists: # AN8.dwg und SP8.dwg laden if not ensure_block_from_dwg(db, "AN8.dwg", block_path): return None if not ensure_block_from_dwg(db, "SP8.dwg", block_path): return None bt.upgradeOpen() new_btr = Db.BlockTableRecord() new_btr.setName(bname) bt.add(new_btr) # AN8 bei (radius, 0) an8_id = bt.getAt("AN8") bref_an8 = Db.BlockReference(Ge.Point3d(radius, 0, 0), an8_id) new_btr.appendAcDbEntity(bref_an8) # SP8 bei (radius+abstand, 0) sp8_id = bt.getAt("SP8") bref_sp8 = Db.BlockReference(Ge.Point3d(radius + abstand, 0, 0), sp8_id) new_btr.appendAcDbEntity(bref_sp8) # Tangenten-Linien auf Layer S_LP ensure_layer(db, "S_LP", 7) lp_lt = Db.LayerTable(db.layerTableId(), Db.OpenMode.kForRead) lp_layer_id = lp_lt.getAt("S_LP") line_color = Db.Color() line_color.setColorIndex(5 if is_pin else 1) # Obere Tangente line_top = Db.Line(Ge.Point3d(radius, radius, 0), Ge.Point3d(radius + abstand, radius, 0)) line_top.setLayer(lp_layer_id) line_top.setColor(line_color) new_btr.appendAcDbEntity(line_top) # Untere Tangente line_bot = Db.Line(Ge.Point3d(radius, -radius, 0), Ge.Point3d(radius + abstand, -radius, 0)) line_bot.setLayer(lp_layer_id) line_bot.setColor(line_color) new_btr.appendAcDbEntity(line_bot) # PIN-Linien if is_pin: ensure_layer(db, "pinbereich", 3) pin_lt = Db.LayerTable(db.layerTableId(), Db.OpenMode.kForRead) pin_layer_id = pin_lt.getAt("pinbereich") pin_color = Db.Color() pin_color.setColorIndex(3) pin_top = Db.Line(Ge.Point3d(KREISEL_DURCHMESSER, pin_y, 0), Ge.Point3d(abstand, pin_y, 0)) pin_top.setLayer(pin_layer_id) pin_top.setColor(pin_color) new_btr.appendAcDbEntity(pin_top) pin_bot = Db.Line(Ge.Point3d(KREISEL_DURCHMESSER, -pin_y, 0), Ge.Point3d(abstand, -pin_y, 0)) pin_bot.setLayer(pin_layer_id) pin_bot.setColor(pin_color) new_btr.appendAcDbEntity(pin_bot) # Beschriftung ensure_layer(db, _beschriftung_layer, _beschriftung_farbe) style_name = ensure_textstyle(db) tst = Db.TextStyleTable(db.textStyleTableId(), Db.OpenMode.kForRead) style_id = tst.getAt(style_name) descr_lt = Db.LayerTable(db.layerTableId(), Db.OpenMode.kForRead) descr_layer_id = descr_lt.getAt(_beschriftung_layer) label_text = f"KREISEL {kreisel_nummer}: {art_str}_{abstand:.0f}" mitte_x = radius + abstand / 2.0 mitte_y = _beschriftung_abstand_oben label_color = Db.Color() label_color.setColorIndex(_beschriftung_farbe) txt = Db.Text() txt.setPosition(Ge.Point3d(mitte_x, mitte_y, 0)) txt.setAlignmentPoint(Ge.Point3d(mitte_x, mitte_y, 0)) txt.setHeight(_beschriftung_hoehe) txt.setTextString(label_text) txt.setTextStyle(style_id) txt.setLayer(descr_layer_id) txt.setColor(label_color) txt.setHorizontalMode(Db.TextHorzMode.kTextCenter) txt.setVerticalMode(Db.TextVertMode.kTextVertMid) new_btr.appendAcDbEntity(txt) # Unsichtbare Attribut-Definitionen create_attrib_defs(new_btr, KREISEL_ATTRIB_DEFS) print(f"\n-> Neue Blockdefinition: {bname}") else: print(f"\n-> Verwende bestehende Blockdefinition: {bname}") # Block am Zielpunkt einfuegen pt = Ge.Point3d(base_point.x, base_point.y, z_hoehe) obj_id = insert_block_with_attribs(db, bname, pt, rotation_deg, merged) print(f"\n-> Kreisel #{kreisel_nummer} eingefuegt: {bname} (Hoehe={z_hoehe:.0f})") return obj_id # ============================================================ # Befehle # ============================================================ @Rx.command("KreiselInsert") def cmd_kreisel_insert(): """KreiselInsert: Manuell Position + Parameter.""" ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() # 1. Einfuegepunkt res = ed.getPoint("\nBasispunkt (AN-Seite): ") if res[0] != Ed.PromptStatus.eOk: print("\nBefehl abgebrochen.") return pt = res[1] # 2. Hoehe bestimmen hoehe = _get_hoehe(ed, pt) # 3. Abstand pdo = Ed.PromptDistanceOptions("\nAbstand (Tangentenlaenge): ") pdo.setDefaultValue(KREISEL_DEFAULT_LAENGE) pdo.setAllowNone(True) res_dist = ed.getDist(pdo) if res_dist[0] == Ed.PromptStatus.eOk: abstand = res_dist[1] else: abstand = KREISEL_DEFAULT_LAENGE print(f"\n-> Abstand: {abstand:.0f} mm") # 4. Rotation res_rot = ed.getString("\nRotation in Grad <0>: ") if res_rot[0] == Ed.PromptStatus.eOk and res_rot[1]: try: rotation = float(res_rot[1]) except ValueError: rotation = 0.0 else: rotation = 0.0 # 5. Kreiselart typ = _ask_kreiselart(ed) print(f"\n-> Rotation: {rotation:.1f} Hoehe: {hoehe:.0f} Kreiselart: {typ}") # 6. Modul einfuegen draw_module(db, Ge.Point3d(pt.x, pt.y, hoehe), abstand, rotation, {"KREISELART": typ, "HOEHE": f"{hoehe:.0f}"}) print("\nKreisel Modul eingefuegt.") @Rx.command("KreiselConnect") def cmd_kreisel_connect(): """KreiselConnect: Linie zeichnen fuer Kreisel-Platzierung (AN -> SP).""" ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() print("\n--- Kreisel durch Linie definieren (AN -> SP) ---") # 1. Startpunkt (AN-Seite) res_start = ed.getPoint("\nStartpunkt (AN-Seite aussen): ") if res_start[0] != Ed.PromptStatus.eOk: print("\nAbgebrochen.") return pt_start = res_start[1] # 2. Endpunkt (SP-Seite) mit Gummiband res_end = ed.getPoint("\nEndpunkt (SP-Seite aussen): ", pt_start) if res_end[0] != Ed.PromptStatus.eOk: print("\nAbgebrochen.") return pt_end = res_end[1] # 3. Geometrie berechnen dx = pt_end.x - pt_start.x dy = pt_end.y - pt_start.y dist = math.sqrt(dx * dx + dy * dy) abstand = dist - KREISEL_DURCHMESSER if abstand <= 0.0: print(f"\nDistanz zu klein! ({dist:.0f} < {KREISEL_DURCHMESSER:.0f})") return # 4. Rotation rotation = math.degrees(math.atan2(dy, dx)) # 5. Hoehe hoehe = pt_start.z if pt_start.z > 0 else 0.0 if hoehe <= 0.0: res_h = ed.getDouble(f"\nHoehe (Z-Koordinate) <{KREISEL_DEFAULT_HOEHE:.0f}>: ") hoehe = res_h[1] if res_h[0] == Ed.PromptStatus.eOk else KREISEL_DEFAULT_HOEHE else: print(f"\n-> Hoehe aus Startpunkt uebernommen: {hoehe:.0f}") # 6. Kreiselart typ = _ask_kreiselart(ed) print(f"\n-> Abstand: {abstand:.0f} mm Rotation: {rotation:.1f} Grad" f" Hoehe: {hoehe:.0f} Kreiselart: {typ}") # 7. Modul einfuegen draw_module(db, Ge.Point3d(pt_start.x, pt_start.y, hoehe), abstand, rotation, {"KREISELART": typ, "HOEHE": f"{hoehe:.0f}"}) print("\nKreisel Connect erfolgreich!") @Rx.command("KreiselRedraw") def cmd_kreisel_redraw(): """KreiselRedraw: Bestehenden Kreisel neu zeichnen.""" ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() # 1. Bestehenden Kreisel-Block auswaehlen res = ed.entSel("\nKreisel-Block auswaehlen: ") if res[0] != Ed.PromptStatus.eOk: return ent_id = res[1] ent = Db.Entity(ent_id, Db.OpenMode.kForRead) if not ent.isKindOf(Db.BlockReference.desc()): print("\nKein Block ausgewaehlt!") return bref = Db.BlockReference(ent_id, Db.OpenMode.kForRead) attribs = read_block_attribs(bref) base_point = bref.position() rotation = math.degrees(bref.rotation()) if "ABSTAND" not in attribs: print("\nKein Kreisel-Block! (Attribut ABSTAND fehlt)") return print(f"\n-> Bestehend: Abstand={attribs.get('ABSTAND', '?')}" f" Drehung={attribs.get('DREHUNG', '?')}" f" Hoehe={attribs.get('HOEHE', '?')}" f" Art={attribs.get('KREISELART', '?')}") # 2. Neuen Abstand abfragen old_abstand = float(attribs.get("ABSTAND", "2300")) res_a = ed.getDouble(f"\nNeuer Abstand <{old_abstand:.0f}>: ") new_abstand = res_a[1] if res_a[0] == Ed.PromptStatus.eOk else old_abstand # 3. Neue Hoehe abfragen old_hoehe = float(attribs.get("HOEHE", "0")) res_h = ed.getDouble(f"\nNeue Hoehe <{old_hoehe:.0f}>: ") new_hoehe = res_h[1] if res_h[0] == Ed.PromptStatus.eOk else old_hoehe # 4. Alten Block loeschen bref.upgradeOpen() bref.erase() # 5. Neu zeichnen mit bestehenden Attributen + neuem Abstand draw_module(db, Ge.Point3d(base_point.x, base_point.y, new_hoehe), new_abstand, rotation, attribs) print(f"\nKreisel neu gezeichnet. Abstand={new_abstand:.0f}") @Rx.command("KreiselQuick") def cmd_kreisel_quick(): """KreiselQuick: Schnell mit Defaults (horizontal).""" ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() res = ed.getPoint("\nBasispunkt (AN-Seite): ") if res[0] != Ed.PromptStatus.eOk: return pt = res[1] res_h = ed.getDouble("\nHoehe (Z-Koordinate) <0>: ") hoehe = res_h[1] if res_h[0] == Ed.PromptStatus.eOk else 0.0 draw_module(db, Ge.Point3d(pt.x, pt.y, hoehe), KREISEL_DEFAULT_LAENGE, 0.0, {"HOEHE": f"{hoehe:.0f}"}) print("\nSchnell Einfuegen fertig.") def kreisel_edit_entity(db, ent_id): """Kreisel per wxPython-Dialog bearbeiten (fuer eine bestimmte Entity-ID). Wird von KreiselEdit und SSG_BLOCKEDIT aufgerufen. """ bref = Db.BlockReference(ent_id, Db.OpenMode.kForRead) attribs = read_block_attribs(bref) base_point = bref.position() rotation = math.degrees(bref.rotation()) if "ABSTAND" not in attribs: print("\nKein Kreisel-Block! (Attribut ABSTAND fehlt)") return # Fehlende Attribute mit Defaults auffuellen for tag, default in KREISEL_ATTRIB_DEFS: if tag not in attribs: attribs[tag] = default # HOEHE aus Z-Koordinate attribs["HOEHE"] = f"{base_point.z:.1f}" if base_point.z > 0 else attribs.get("HOEHE", "0") print(f"\n-> Bestehend: Abstand={attribs.get('ABSTAND')}" f" Drehung={rotation:.1f}" f" Art={attribs.get('KREISELART')}") # wxPython-Dialog dlg = KreiselEditDialog(None, attribs, rotation) result = dlg.ShowModal() if result == wx.ID_OK: new_values = dlg.get_values() dlg.Destroy() attribs["NAME"] = new_values["name"] attribs["HOEHE"] = new_values["hoehe"] attribs["KREISELART"] = new_values["kreiselart"] attribs["DREHRICHTUNG"] = new_values["drehrichtung"] new_abstand = float(new_values["abstand"]) new_rotation = new_values["rotation"] # Alten Block loeschen bref.upgradeOpen() bref.erase() # Neu zeichnen draw_module(db, Ge.Point3d(base_point.x, base_point.y, float(new_values["hoehe"])), new_abstand, new_rotation, attribs) print(f"\nKreisel aktualisiert: Name={new_values['name']}" f" Abstand={new_abstand:.0f} Hoehe={new_values['hoehe']}" f" Rotation={new_rotation:.1f}") else: dlg.Destroy() print("\nAbgebrochen.") @Rx.command("KreiselEdit") def cmd_kreisel_edit(): """KreiselEdit: Bestehenden Kreisel per wxPython-Dialog bearbeiten.""" ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() res = ed.entSel("\nKreisel-Block auswaehlen: ") if res[0] != Ed.PromptStatus.eOk: return ent_id = res[1] ent = Db.Entity(ent_id, Db.OpenMode.kForRead) if not ent.isKindOf(Db.BlockReference.desc()): print("\nKein Block ausgewaehlt!") return kreisel_edit_entity(db, ent_id) @Rx.command("KreiselParams") def cmd_kreisel_params(): """KreiselParams: Aktuelle Parameter anzeigen.""" print("\n=== Kreisel Parameter ===") print(f"\nKreiselart: {_kreisel_typ}") print(f"\nDurchmesser: {KREISEL_DURCHMESSER:.0f}") print(f"\nDefault-Laenge: {KREISEL_DEFAULT_LAENGE:.0f}") print(f"\nDefault-Hoehe: {KREISEL_DEFAULT_HOEHE:.0f}") print("\nAttribute im Block:") for tag, default in KREISEL_ATTRIB_DEFS: print(f"\n {tag} = {default}") print("\n=========================\n") @Rx.command("KreiselLabelSetup") def cmd_kreisel_label_setup(): """KreiselLabelSetup: Beschriftungs-Einstellungen aendern.""" global _beschriftung_hoehe, _beschriftung_farbe, _beschriftung_abstand_oben ed = Ed.Editor() res = ed.getDouble(f"\nSchrifthoehe <{_beschriftung_hoehe:.0f}>: ") if res[0] == Ed.PromptStatus.eOk: _beschriftung_hoehe = res[1] res = ed.getInteger(f"\nSchriftfarbe (1-7) <{_beschriftung_farbe}>: ") if res[0] == Ed.PromptStatus.eOk: _beschriftung_farbe = res[1] res = ed.getDouble(f"\nAbstand ueber Kreisel-Mitte <{_beschriftung_abstand_oben:.0f}>: ") if res[0] == Ed.PromptStatus.eOk: _beschriftung_abstand_oben = res[1] print(f"\nNeue Einstellungen: Hoehe={_beschriftung_hoehe:.0f}" f", Farbe={_beschriftung_farbe}" f", Y-Versatz={_beschriftung_abstand_oben:.0f}" f", Schriftart=Arial") @Rx.command("KreiselLabelPos") def cmd_kreisel_label_pos(): """KreiselLabelPos: Beschriftungsposition aendern.""" global _beschriftung_abstand_x, _beschriftung_abstand_y ed = Ed.Editor() res = ed.getDouble(f"\nX-Abstand von AN8-Mitte <{_beschriftung_abstand_x:.0f}>: ") if res[0] == Ed.PromptStatus.eOk: _beschriftung_abstand_x = res[1] res = ed.getDouble(f"\nY-Abstand von AN8-Mitte <{_beschriftung_abstand_y:.0f}>: ") if res[0] == Ed.PromptStatus.eOk: _beschriftung_abstand_y = res[1] print(f"\nNeue Beschriftungsposition: X={_beschriftung_abstand_x:.0f}" f" Y={_beschriftung_abstand_y:.0f}") @Rx.command("KreiselLabelHoehe") def cmd_kreisel_label_hoehe(): """KreiselLabelHoehe: Texthoehe aendern.""" global _beschriftung_hoehe ed = Ed.Editor() res = ed.getDouble(f"\nTexthoehe <{_beschriftung_hoehe:.0f}>: ") if res[0] == Ed.PromptStatus.eOk: _beschriftung_hoehe = res[1] print(f"\nNeue Texthoehe: {_beschriftung_hoehe:.0f}") # ============================================================ # Interne Helfer fuer Befehle # ============================================================ def _get_hoehe(ed, pt): """Hoehe aus 3D-Punkt oder manuell abfragen.""" if pt.z > 0: print(f"\n-> Hoehe aus 3D-Punkt uebernommen: {pt.z:.0f}") return pt.z res = ed.getDouble(f"\nHoehe (Z-Koordinate) <{KREISEL_DEFAULT_HOEHE:.0f}>: ") if res[0] == Ed.PromptStatus.eOk: return res[1] return KREISEL_DEFAULT_HOEHE def _ask_kreiselart(ed): """Kreiselart (PIN/STANDARD) abfragen.""" global _kreisel_typ default_str = "2" if _kreisel_typ == "PIN" else "1" print(f"\n[1] STANDARD (ohne PIN)") print(f"\n[2] PIN") res = ed.getString(f"\nBitte waehlen <{default_str}>: ") if res[0] == Ed.PromptStatus.eOk and res[1] == "2": _kreisel_typ = "PIN" else: _kreisel_typ = "STANDARD" return _kreisel_typ # ============================================================ # wxPython-Dialog: Ersetzt kreisel_edit.dcl # ============================================================ class KreiselEditDialog(wx.Dialog): """Dialog zum Bearbeiten eines bestehenden Kreisels. Ersetzt den DCL-Dialog kreisel_edit.dcl.""" def __init__(self, parent, attribs, rotation): super().__init__(parent, title="Kreisel bearbeiten", size=(380, 300), style=wx.DEFAULT_DIALOG_STYLE) panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) # Name row_name = wx.BoxSizer(wx.HORIZONTAL) row_name.Add(wx.StaticText(panel, label="Kreiselname:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.txt_name = wx.TextCtrl(panel, value=attribs.get("NAME", ""), size=(200, -1)) row_name.Add(self.txt_name, 1) sizer.Add(row_name, 0, wx.EXPAND | wx.ALL, 5) # Abstand + Hoehe row_ah = wx.BoxSizer(wx.HORIZONTAL) row_ah.Add(wx.StaticText(panel, label="Abstand (mm):"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.txt_abstand = wx.TextCtrl(panel, value=attribs.get("ABSTAND", "2300"), size=(80, -1)) row_ah.Add(self.txt_abstand, 0, wx.RIGHT, 15) row_ah.Add(wx.StaticText(panel, label="Hoehe (mm):"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.txt_hoehe = wx.TextCtrl(panel, value=attribs.get("HOEHE", "0"), size=(80, -1)) row_ah.Add(self.txt_hoehe, 0) sizer.Add(row_ah, 0, wx.EXPAND | wx.ALL, 5) # Ausrichtung row_aus = wx.BoxSizer(wx.HORIZONTAL) row_aus.Add(wx.StaticText(panel, label="Ausrichtung:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) choices = [label for label, _ in KREISEL_AUSRICHTUNGEN] self.cb_ausrichtung = wx.Choice(panel, choices=choices) self.cb_ausrichtung.SetSelection(rotation_to_idx(rotation)) row_aus.Add(self.cb_ausrichtung, 1) sizer.Add(row_aus, 0, wx.EXPAND | wx.ALL, 5) # Drehrichtung + Kreiselart row_dk = wx.BoxSizer(wx.HORIZONTAL) row_dk.Add(wx.StaticText(panel, label="Drehrichtung:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.cb_drehrichtung = wx.Choice(panel, choices=["UZS", "GUZ"]) self.cb_drehrichtung.SetSelection(1 if attribs.get("DREHRICHTUNG") == "GUZ" else 0) row_dk.Add(self.cb_drehrichtung, 0, wx.RIGHT, 15) row_dk.Add(wx.StaticText(panel, label="Typ:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5) self.cb_kreiselart = wx.Choice(panel, choices=["STANDARD", "PIN"]) self.cb_kreiselart.SetSelection(1 if attribs.get("KREISELART") == "PIN" else 0) row_dk.Add(self.cb_kreiselart, 0) sizer.Add(row_dk, 0, wx.EXPAND | wx.ALL, 5) # OK / Cancel btn_sizer = self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL) sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 10) panel.SetSizer(sizer) self.Fit() self.CenterOnScreen() def get_values(self): """Dialog-Werte als dict zurueckgeben.""" idx = self.cb_ausrichtung.GetSelection() return { "name": self.txt_name.GetValue(), "abstand": self.txt_abstand.GetValue(), "hoehe": self.txt_hoehe.GetValue(), "rotation": idx_to_rotation(idx), "drehrichtung": "GUZ" if self.cb_drehrichtung.GetSelection() == 1 else "UZS", "kreiselart": "PIN" if self.cb_kreiselart.GetSelection() == 1 else "STANDARD", }