def move_selected(axis, mode):
objs = get_selected_objects()
if not objs: return vs.AlrtDialog("Keine Objekte ausgewählt.")
if axis=='x':
if mode=='min': ref = min(o[1] for o in objs)
elif mode=='max': ref = max(o[3] for o in objs)
elif mode=='mid': ref = sum((o[1]+o[3])/2 for o in objs)/len(objs)
for h,x1,y1,x2,y2 in objs:
dx = ref - ((x1+x2)/2 if mode=='mid' else (x1 if mode=='min' else x2))
vs.HMove(h, dx, 0)
else:
if mode=='min': ref = min(o[2] for o in objs)
elif mode=='max': ref = max(o[4] for o in objs)
elif mode=='mid': ref = sum((o[2]+o[4])/2 for o in objs)/len(objs)
for h,x1,y1,x2,y2 in objs:
dy = ref - ((y1+y2)/2 if mode=='mid' else (y1 if mode=='min' else y2))
vs.HMove(h, 0, dy)
# ---------- Verteilen ----------
def distribute_horizontal():
objs = get_selected_objects()
if len(objs)<3: return vs.AlrtDialog("Mindestens 3 Objekte auswählen.")
objs.sort(key=lambda o: (o[1]+o[3])/2)
left = (objs[0][1]+objs[0][3])/2
right = (objs[-1][1]+objs[-1][3])/2
step = (right-left)/(len(objs)-1)
for i,(h,x1,y1,x2,y2) in enumerate(objs):
dx = (left + i*step) - (x1+x2)/2
vs.HMove(h, dx, 0)
def distribute_vertical():
objs = get_selected_objects()
if len(objs)<3: return vs.AlrtDialog("Mindestens 3 Objekte auswählen.")
objs.sort(key=lambda o: (o[2]+o[4])/2)
bottom = (objs[0][2]+objs[0][4])/2
top = (objs[-1][2]+objs[-1][4])/2
step = (top-bottom)/(len(objs)-1)
for i,(h,x1,y1,x2,y2) in enumerate(objs):
dy = (bottom + i*step) - (y1+y2)/2
vs.HMove(h, 0, dy)
# Gleichmäßiger Abstand zwischen Objekten
def distribute_horizontal_spaced():
objs = get_selected_objects()
if len(objs)<2: return vs.AlrtDialog("Mindestens 2 Objekte auswählen.")
objs.sort(key=lambda o: o[1])
left = min(o[1] for o in objs)
right = max(o[3] for o in objs)
total_width = sum(o[3]-o[1] for o in objs)
spacing = (right-left-total_width)/(len(objs)-1)
pos = left
for h,x1,y1,x2,y2 in objs:
dx = pos - x1
vs.HMove(h, dx, 0)
pos += (x2-x1) + spacing
def distribute_vertical_spaced():
objs = get_selected_objects()
if len(objs)<2: return vs.AlrtDialog("Mindestens 2 Objekte auswählen.")
objs.sort(key=lambda o: o[2])
bottom = min(o[2] for o in objs)
top = max(o[4] for o in objs)
total_height = sum(o[4]-o[2] for o in objs)
spacing = (top-bottom-total_height)/(len(objs)-1)
pos = bottom
for h,x1,y1,x2,y2 in objs:
dy = pos - y1
vs.HMove(h, 0, dy)
pos += (y2-y1) + spacing
Frage
nfedl
Hallo Ihr Meister der Programmierung. (ich kenn mich leider gar nicht aus und versuche es derzeit mit ChatGPT)
Ich hätte gerne eine bessere Version von "2D Ausrichten und Verteilen" und zwar so, dass ich ein Toolset mit folgenden Buttons bekomme:
Danke für euren Input!
import vs
# ---------- IDs ----------
BTN_ALIGN_LEFT = 101
BTN_ALIGN_RIGHT = 102
BTN_ALIGN_UP = 103
BTN_ALIGN_DOWN = 104
BTN_ALIGN_CENTER_X = 105
BTN_ALIGN_CENTER_Y = 106
BTN_DIST_H = 201
BTN_DIST_V = 202
BTN_DIST_H_SPACED = 301
BTN_DIST_V_SPACED = 302
# ---------- Hilfsfunktionen ----------
def _normalize_bbox(b):
try:
if isinstance(b[0], (list, tuple)):
x1,y1 = b[0]
x2,y2 = b[1]
else:
x1,y1,x2,y2 = b
except Exception:
x1,y1,x2,y2 = 0,0,0,0
return float(x1), float(y1), float(x2), float(y2)
def get_selected_objects():
h = vs.FSActLayer()
objs = []
while h:
x1,y1,x2,y2 = _normalize_bbox(vs.GetBBox(h))
objs.append((h, x1, y1, x2, y2))
h = vs.NextSObj(h)
return objs
# ---------- Ausrichten ----------
def align_left(): move_selected('x','min')
def align_right(): move_selected('x','max')
def align_up(): move_selected('y','max')
def align_down(): move_selected('y','min')
def align_center_x(): move_selected('x','mid')
def align_center_y(): move_selected('y','mid')
def move_selected(axis, mode):
objs = get_selected_objects()
if not objs: return vs.AlrtDialog("Keine Objekte ausgewählt.")
if axis=='x':
if mode=='min': ref = min(o[1] for o in objs)
elif mode=='max': ref = max(o[3] for o in objs)
elif mode=='mid': ref = sum((o[1]+o[3])/2 for o in objs)/len(objs)
for h,x1,y1,x2,y2 in objs:
dx = ref - ((x1+x2)/2 if mode=='mid' else (x1 if mode=='min' else x2))
vs.HMove(h, dx, 0)
else:
if mode=='min': ref = min(o[2] for o in objs)
elif mode=='max': ref = max(o[4] for o in objs)
elif mode=='mid': ref = sum((o[2]+o[4])/2 for o in objs)/len(objs)
for h,x1,y1,x2,y2 in objs:
dy = ref - ((y1+y2)/2 if mode=='mid' else (y1 if mode=='min' else y2))
vs.HMove(h, 0, dy)
# ---------- Verteilen ----------
def distribute_horizontal():
objs = get_selected_objects()
if len(objs)<3: return vs.AlrtDialog("Mindestens 3 Objekte auswählen.")
objs.sort(key=lambda o: (o[1]+o[3])/2)
left = (objs[0][1]+objs[0][3])/2
right = (objs[-1][1]+objs[-1][3])/2
step = (right-left)/(len(objs)-1)
for i,(h,x1,y1,x2,y2) in enumerate(objs):
dx = (left + i*step) - (x1+x2)/2
vs.HMove(h, dx, 0)
def distribute_vertical():
objs = get_selected_objects()
if len(objs)<3: return vs.AlrtDialog("Mindestens 3 Objekte auswählen.")
objs.sort(key=lambda o: (o[2]+o[4])/2)
bottom = (objs[0][2]+objs[0][4])/2
top = (objs[-1][2]+objs[-1][4])/2
step = (top-bottom)/(len(objs)-1)
for i,(h,x1,y1,x2,y2) in enumerate(objs):
dy = (bottom + i*step) - (y1+y2)/2
vs.HMove(h, 0, dy)
# Gleichmäßiger Abstand zwischen Objekten
def distribute_horizontal_spaced():
objs = get_selected_objects()
if len(objs)<2: return vs.AlrtDialog("Mindestens 2 Objekte auswählen.")
objs.sort(key=lambda o: o[1])
left = min(o[1] for o in objs)
right = max(o[3] for o in objs)
total_width = sum(o[3]-o[1] for o in objs)
spacing = (right-left-total_width)/(len(objs)-1)
pos = left
for h,x1,y1,x2,y2 in objs:
dx = pos - x1
vs.HMove(h, dx, 0)
pos += (x2-x1) + spacing
def distribute_vertical_spaced():
objs = get_selected_objects()
if len(objs)<2: return vs.AlrtDialog("Mindestens 2 Objekte auswählen.")
objs.sort(key=lambda o: o[2])
bottom = min(o[2] for o in objs)
top = max(o[4] for o in objs)
total_height = sum(o[4]-o[2] for o in objs)
spacing = (top-bottom-total_height)/(len(objs)-1)
pos = bottom
for h,x1,y1,x2,y2 in objs:
dy = pos - y1
vs.HMove(h, 0, dy)
pos += (y2-y1) + spacing
# ---------- Dialog-Handler ----------
def dialog_handler(item, data):
mapping = {
BTN_ALIGN_LEFT: align_left,
BTN_ALIGN_RIGHT: align_right,
BTN_ALIGN_UP: align_up,
BTN_ALIGN_DOWN: align_down,
BTN_ALIGN_CENTER_X: align_center_x,
BTN_ALIGN_CENTER_Y: align_center_y,
BTN_DIST_H: distribute_horizontal,
BTN_DIST_V: distribute_vertical,
BTN_DIST_H_SPACED: distribute_horizontal_spaced,
BTN_DIST_V_SPACED: distribute_vertical_spaced
}
if item in mapping: mapping[item]()
return item
# ---------- Dialog mit Untermenü ----------
def run_dialog():
dlg = vs.CreateLayout("2D Ausrichten & Verteilen", False, "Schließen", "Abbrechen")
# Untermenü Ausrichten
vs.CreateStaticText(dlg, 10, "Ausrichten:", -1)
vs.CreatePushButton(dlg, BTN_ALIGN_LEFT, "← Links")
vs.CreatePushButton(dlg, BTN_ALIGN_RIGHT, "→ Rechts")
vs.CreatePushButton(dlg, BTN_ALIGN_UP, "↑ Oben")
vs.CreatePushButton(dlg, BTN_ALIGN_DOWN, "↓ Unten")
vs.CreatePushButton(dlg, BTN_ALIGN_CENTER_X, "↔ Mitte X")
vs.CreatePushButton(dlg, BTN_ALIGN_CENTER_Y, "↕ Mitte Y")
# Untermenü Verteilen
vs.CreateStaticText(dlg, 20, "Verteilen:", -1)
vs.CreatePushButton(dlg, BTN_DIST_H, "⇄ Horizontal")
vs.CreatePushButton(dlg, BTN_DIST_V, "⇅ Vertikal")
vs.CreatePushButton(dlg, BTN_DIST_H_SPACED, "⇄ Gleichmäßig H")
vs.CreatePushButton(dlg, BTN_DIST_V_SPACED, "⇅ Gleichmäßig V")
# Layout: vertikal angeordnet
vs.SetFirstLayoutItem(dlg, 10)
vs.SetBelowItem(dlg, 10, BTN_ALIGN_LEFT, 0, 0)
vs.SetBelowItem(dlg, BTN_ALIGN_LEFT, BTN_ALIGN_RIGHT, 0, 5)
vs.SetBelowItem(dlg, BTN_ALIGN_RIGHT, BTN_ALIGN_UP, 0, 5)
vs.SetBelowItem(dlg, BTN_ALIGN_UP, BTN_ALIGN_DOWN, 0, 5)
vs.SetBelowItem(dlg, BTN_ALIGN_DOWN, BTN_ALIGN_CENTER_X, 0, 5)
vs.SetBelowItem(dlg, BTN_ALIGN_CENTER_X, BTN_ALIGN_CENTER_Y, 0, 5)
vs.SetBelowItem(dlg, BTN_ALIGN_CENTER_Y, 20, 0, 15) # Verteilen Überschrift
vs.SetBelowItem(dlg, 20, BTN_DIST_H, 0, 5)
vs.SetBelowItem(dlg, BTN_DIST_H, BTN_DIST_V, 0, 5)
vs.SetBelowItem(dlg, BTN_DIST_V, BTN_DIST_H_SPACED, 0, 5)
vs.SetBelowItem(dlg, BTN_DIST_H_SPACED, BTN_DIST_V_SPACED, 0, 5)
vs.RunLayoutDialog(dlg, dialog_handler)
# ---------- Hauptausführung ----------
if __name__ == "__main__":
run_dialog()
Nikolaus Fedl
VW 2025 Designer, Win 11, i9, 32GB, www.fedl.eu; www.gartenplanung-fedl.at; www.freiraumarchitektur.at; www.schattenbild.at; www.gartenarchitekten.at
5 Antworten auf diese Frage
Empfohlene Beiträge
Erstelle ein Benutzerkonto oder melde Dich an, um zu kommentieren
Du musst ein Benutzerkonto haben, um einen Kommentar verfassen zu können
Benutzerkonto erstellen
Neues Benutzerkonto für unsere Community erstellen. Es ist einfach!
Neues Benutzerkonto erstellenAnmelden
Du hast bereits ein Benutzerkonto? Melde Dich hier an.
Jetzt anmelden