From 61bca58382a72be45006f1be7233ec8276361e47 Mon Sep 17 00:00:00 2001 From: Sven Date: Mon, 15 Jun 2026 11:16:29 +0200 Subject: [PATCH] Good progress in adding features --- beamline_editor/nodes.py | 155 ++++++++++++++++++++++++++++++++++++- beamline_editor/palette.py | 6 +- elements.py | 31 ++++++-- main.py | 94 +++++++++++++++------- propertytypes.py | 37 ++++++++- ui/BeamlineGUI.py | 17 +++- ui/BeamlineGUI.ui | 19 ++++- 7 files changed, 311 insertions(+), 48 deletions(-) diff --git a/beamline_editor/nodes.py b/beamline_editor/nodes.py index b3fc2b2..e4d74c4 100644 --- a/beamline_editor/nodes.py +++ b/beamline_editor/nodes.py @@ -21,7 +21,7 @@ import uuid, math from PyQt5.QtWidgets import QGraphicsItem, QGraphicsTextItem from PyQt5.QtCore import QRectF, QPointF, Qt from PyQt5.QtGui import ( - QBrush, QPen, QColor, QPainter, + QBrush, QPen, QColor, QPainter, QPainterPath, QFont, QLinearGradient, QPolygonF, ) from .ports import Port @@ -37,6 +37,13 @@ _STYLES: dict[str, dict] = { "Overlap": {"top": "#4A148C", "bot": "#2A0A5C", "w": 72, "h": 40}, "Line": {"top": "#263238", "bot": "#1A2428", "w": 96, "h": 38}, "Variable Line": {"top": "#1C313A", "bot": "#0F1F26", "w": 96, "h": 38}, + "Solenoid": {"top": "#00838F", "bot": "#005662", "w": 60, "h": 40}, + "Corrector": {"top": "#6A1B9A", "bot": "#4A148C", "w": 44, "h": 44}, + "RF": {"top": "#AD1457", "bot": "#880E4F", "w": 64, "h": 44}, + "Undulator": {"top": "#558B2F", "bot": "#33691E", "w": 88, "h": 36}, + "Diagnostics": {"top": "#4527A0", "bot": "#311B92", "w": 64, "h": 44}, + "Target": {"top": "#B71C1C", "bot": "#7B0000", "w": 36, "h": 36}, + "Alignment": {"top": "#1565C0", "bot": "#0D3B8C", "w": 36, "h": 36}, "Start": {"top": "#00695C", "bot": "#004D40", "w": 54, "h": 40}, "End": {"top": "#4E342E", "bot": "#3E2723", "w": 54, "h": 40}, } @@ -306,18 +313,160 @@ class EndNode(BeamlineNode): min(sq, self.H - r * sq))) +class SolenoidNode(BeamlineNode): + """Solenoid coil — teal rounded rect with circular coil symbol.""" + def __init__(self, pos): super().__init__("Solenoid", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#00838F"), 1.5)) + p.drawRoundedRect(QRectF(0, 0, self.W, self.H), 6, 6) + # coil loops + p.setPen(QPen(QColor("#80DEEA"), 1.2)) + step = 10 + y1, y2 = 8.0, float(self.H - 8) + x = 10.0 + while x < self.W - 8: + p.drawEllipse(QPointF(x + step / 2, self.H / 2), + step / 2, (y2 - y1) / 2) + x += step + +class CorrectorNode(BeamlineNode): + """Corrector — purple square with crossed-arrows symbol.""" + def __init__(self, pos): super().__init__("Corrector", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#7B1FA2"), 1.5)) + p.drawRect(QRectF(0, 0, self.W, self.H)) + cx, cy = self.W / 2, self.H / 2 + r = min(cx, cy) - 5 + p.setPen(QPen(QColor("#CE93D8"), 1.5)) + # vertical arrow + p.drawLine(QPointF(cx, cy - r), QPointF(cx, cy + r)) + p.drawLine(QPointF(cx, cy - r), QPointF(cx - 4, cy - r + 6)) + p.drawLine(QPointF(cx, cy - r), QPointF(cx + 4, cy - r + 6)) + # horizontal arrow + p.drawLine(QPointF(cx - r, cy), QPointF(cx + r, cy)) + p.drawLine(QPointF(cx + r, cy), QPointF(cx + r - 6, cy - 4)) + p.drawLine(QPointF(cx + r, cy), QPointF(cx + r - 6, cy + 4)) + +class RFNode(BeamlineNode): + """RF cavity — crimson rect with sine-wave decoration.""" + def __init__(self, pos): super().__init__("RF", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#C2185B"), 1.5)) + p.drawRoundedRect(QRectF(0, 0, self.W, self.H), 5, 5) + # sine wave + path = QPainterPath() + cy = self.H / 2 + amp = self.H / 4 - 2 + pts = 40 + x0, x1 = 8.0, float(self.W - 8) + for i in range(pts + 1): + t = i / pts + x = x0 + t * (x1 - x0) + y = cy - amp * math.sin(t * 2 * math.pi * 2) + if i == 0: + path.moveTo(x, y) + else: + path.lineTo(x, y) + p.setPen(QPen(QColor("#F48FB1"), 1.5)) + p.setBrush(Qt.NoBrush) + p.drawPath(path) + +class UndulatorNode(BeamlineNode): + """Undulator / wiggler — olive-green rect with alternating pole marks.""" + def __init__(self, pos): super().__init__("Undulator", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#558B2F"), 1.5)) + p.drawRect(QRectF(0, 0, self.W, self.H)) + # alternating pole blocks + pole_w = 10 + n_poles = int(self.W // pole_w) + for i in range(n_poles): + col = QColor("#AED581") if i % 2 == 0 else QColor("#33691E") + p.setBrush(QBrush(col)) + p.setPen(Qt.NoPen) + x = i * pole_w + # top half pole + p.drawRect(QRectF(x, 2, pole_w, self.H / 2 - 3)) + # bottom half pole (opposite polarity → alternate colour swapped) + col2 = QColor("#33691E") if i % 2 == 0 else QColor("#AED581") + p.setBrush(QBrush(col2)) + p.drawRect(QRectF(x, self.H / 2 + 1, pole_w, self.H / 2 - 3)) + +class DiagnosticsNode(BeamlineNode): + """Diagnostics — indigo rect with oscilloscope / eye symbol.""" + def __init__(self, pos): super().__init__("Diagnostics", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#512DA8"), 1.5)) + p.drawRoundedRect(QRectF(0, 0, self.W, self.H), 5, 5) + cx, cy = self.W / 2, self.H / 2 + # eye outline + rx, ry = self.W / 2 - 8, self.H / 2 - 6 + p.setPen(QPen(QColor("#B39DDB"), 1.5)) + p.setBrush(Qt.NoBrush) + p.drawEllipse(QPointF(cx, cy), rx, ry) + # pupil + p.setBrush(QBrush(QColor("#7E57C2"))) + p.setPen(Qt.NoPen) + p.drawEllipse(QPointF(cx, cy), 5, 5) + + +class TargetNode(BeamlineNode): + """Target marker — red circle with crosshair.""" + def __init__(self, pos): super().__init__("Target", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#B71C1C"), 1.5)) + p.drawEllipse(QRectF(0, 0, self.W, self.H)) + cx, cy = self.W / 2, self.H / 2 + r = min(cx, cy) - 4 + p.setPen(QPen(QColor("#EF9A9A"), 1.2)) + p.drawLine(QPointF(cx - r, cy), QPointF(cx + r, cy)) + p.drawLine(QPointF(cx, cy - r), QPointF(cx, cy + r)) + p.setBrush(QBrush(QColor("#EF5350"))) + p.setPen(Qt.NoPen) + p.drawEllipse(QPointF(cx, cy), 4, 4) + +class AlignmentNode(BeamlineNode): + """Alignment marker — blue square with corner tick marks.""" + def __init__(self, pos): super().__init__("Alignment", pos) + def _draw_body(self, p): + p.setBrush(QBrush(self._gradient())) + p.setPen(QPen(QColor("#1565C0"), 1.5)) + p.drawRect(QRectF(0, 0, self.W, self.H)) + t = 6.0 # tick length + p.setPen(QPen(QColor("#90CAF9"), 1.5)) + for cx, cy in [(0.0, 0.0), (float(self.W), 0.0), + (0.0, float(self.H)), (float(self.W), float(self.H))]: + dx = t if cx == 0 else -t + dy = t if cy == 0 else -t + p.drawLine(QPointF(cx, cy), QPointF(cx + dx, cy)) + p.drawLine(QPointF(cx, cy), QPointF(cx, cy + dy)) + + # ── Factory ─────────────────────────────────────────────────────────────────── _NODE_MAP: dict[str, type] = { "Dipole": DipoleNode, "Quadrupole": QuadrupoleNode, "Sextupole": SextupoleNode, + "Solenoid": SolenoidNode, + "Corrector": CorrectorNode, + "RF": RFNode, + "Undulator": UndulatorNode, + "Diagnostics": DiagnosticsNode, "Reference": ReferenceNode, + "Target": TargetNode, + "Alignment": AlignmentNode, "Overlap": OverlapNode, - "Line": LineNode, - "Variable Line": VariableLineNode, "Start": StartNode, "End": EndNode, + "Line": LineNode, + "Variable Line": VariableLineNode, } diff --git a/beamline_editor/palette.py b/beamline_editor/palette.py index 7a37fcf..6f297f6 100644 --- a/beamline_editor/palette.py +++ b/beamline_editor/palette.py @@ -15,10 +15,10 @@ from PyQt5.QtCore import QRectF from .nodes import _STYLES ELEMENT_GROUPS: list[tuple[str, list[str]]] = [ - ("Elements", ["Dipole", "Quadrupole", "Sextupole"]), - ("Markers", ["Reference", "Overlap", "Start", "End"]), + ("Elements", ["Dipole", "Quadrupole", "Sextupole", + "Solenoid", "Corrector", "RF", "Undulator", "Diagnostics"]), + ("Markers", ["Reference", "Start", "End", "Target", "Alignment"]), ("Containers", ["Line", "Variable Line"]), - ("Templates", []), ] PALETTE_SS = """ diff --git a/elements.py b/elements.py index dccfb41..0501f2d 100644 --- a/elements.py +++ b/elements.py @@ -34,6 +34,9 @@ class SimpleContainer: if not ('OffsetS' in prop): prop.update({'OffsetS': 0.}) + if not ('relOff' in prop): + prop.update({'relOff': True}) + if not ('Tilt' in prop): # prop.has_key('Tilt')): prop.update({'Tilt': 0.}) @@ -87,6 +90,8 @@ class Magnet(SimpleContainer): SimpleContainer.__init__(self, prop) if not ('Group' in prop): # prop.has_key('Group')): self.__dict__.update({'Group': 'Magnets'}) + if not ('PSChannels' in prop): # prop.has_key('Group')): + self.__dict__.update({'PSChannels': 1}) class Dipole(Magnet): def __init__(self, prop): @@ -101,9 +106,8 @@ class Dipole(Magnet): self.__dict__.update({'e2': 0.}) if not ('Tag' in prop): # prop.has_key('Tag')): self.__dict__.update({'Tag': 'MBND'}) - - def getSettings(self): - return {'Angle':self.design_angle, 'Entrance Edge':self.e1, 'Exit Edge':self.e2} + if not ('branching' in prop): # prop.has_key('Tag')): + self.__dict__.update({'Branch': False}) def getResLength(self): if (self.angle == 0.): @@ -135,8 +139,6 @@ class Quadrupole(Magnet): if not ('Tag' in prop): # prop.has_key('Tag')): self.__dict__.update({'Tag': 'MQUA'}) - def getSettings(self): - return {'k1':self.k1} def writeElement(self, app): app.writeQuadrupole(self) @@ -177,6 +179,8 @@ class Solenoid(Magnet): class Corrector(Magnet): def __init__(self, prop): Magnet.__init__(self, prop) + if not ('Tag' in prop): # prop.has_key('Tag')): + self.__dict__.update({'Tag': 'MCOR'}) def writeElement(self, app): app.writeCorrector(self) @@ -206,6 +210,10 @@ class Undulator(SimpleContainer): self.__dict__.update({'Group': 'ID'}) if not ('K' in prop): # prop.has_key('K')): self.__dict__.update({'K': 0.}) + if not ('helical' in prop): # prop.has_key('K')): + self.__dict__.update({'helical': False}) + if not ('lambdau' in prop): # prop.has_key('K')): + self.__dict__.update({'lambdau': 0.015}) if not ('ku' in prop): # prop.has_key('ku')): self.__dict__.update({'ku': 0.}) if not ('kx' in prop): # prop.has_key('kx')): @@ -219,7 +227,14 @@ class Undulator(SimpleContainer): if not ('Waist' in prop): # prop.has_key('Waist')): self.__dict__.update({'Waist': 1.}) - + def checkElement(self): + if self.helical: + self.kx=0.5 + self.ky=0.5 + else: + self.kx=0. + self.ky=1. + self.ku = 2*np.pi/self.lambdau def writeElement(self, app): app.writeUndulator(self) @@ -302,6 +317,8 @@ class Alignment(SimpleContainer): SimpleContainer.__init__(self, prop) if not ('Group' in prop): # prop.has_key('Group')): self.__dict__.update({'Group': 'Alignment'}) + if not ('Tag' in prop): # prop.has_key('Tag')): + prop.update({'Tag': 'QALI'}) def writeElement(self, app): app.writeAlignment(self) @@ -316,6 +333,8 @@ class Marker(SimpleContainer): SimpleContainer.__init__(self, prop) if not ('Group' in prop): # prop.has_key('Group')): self.__dict__.update({'Group': 'Marker'}) + if not ('Tag' in prop): # prop.has_key('Tag')): + prop.update({'Tag': 'QREF'}) def writeElement(self, app): app.writeMarker(self) diff --git a/main.py b/main.py index 3a4ef99..c42dabb 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,6 @@ import sys import json +import pickle from PyQt5 import QtWidgets, QtCore, QtGui from ui.BeamlineGUI import Ui_BeamlineGUI @@ -18,22 +19,62 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI): get_properties = self.getProperties, on_property_changed = self.updateProperties) + # editor = BeamlineEditorWidget( + # on_element_added = fn(element_type: str, node_id: str), + # on_elements_deleted = fn(node_ids: list[str]), + # on_element_selected = fn(node_id: str | None), # None on deselect + # get_properties = fn(node_id: str) -> dict, # called on selection + # on_property_changed = fn(node_id: str, data: dict), + # ) + QtWidgets.QVBoxLayout(self.UIEditorBox).addWidget(self.editor) self.actionSave_Templates.triggered.connect(self.saveTemplates) self.actionLoad_Templates.triggered.connect(self.loadTemplates) + self.actionSave_as.triggered.connect(self.saveas) self.actionSave.triggered.connect(self.save) self.actionLoad.triggered.connect(self.load) # elements, lines and branch points self.elementDB={} + self.savefile='' + + + def saveas(self): + options = QtWidgets.QFileDialog.Options() + options |= QtWidgets.QFileDialog.DontUseNativeDialog + fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save Layout", + "Layouts/newSetting.json", + "Json Files (*.json)", options=options) + if not fileName: + return + self.savefile = fileName + self.save() def save(self): - self.editor.saveLayout("Layouts/lattice.json") + if len(self.savefile) < 5: + self.saveas() + if len(self.savefile) < 5: + return + with (open(self.savefile,'wb') as file): + pickle.dump(self.elementDB, file) + self.editor.saveLayout(self.savefile.replace('.json','_gui.json')) def load(self): - self.editor.loadLayout("Layouts/lattice.json") + options = QtWidgets.QFileDialog.Options() + options |= QtWidgets.QFileDialog.DontUseNativeDialog + fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open Layout", + "Layouts/ReferenceSetting.json", + "Json Files (*.json)", options=options) + if not fileName: + return + self.elementDB.clear() + self.savefile=fileName + with (open(self.savefile,'rb') as file): + self.elementDB = pickle.load(file) + self.savefile=fileName + self.editor.loadLayout(self.savefile.replace('.json','_gui.json')) def saveTemplates(self): template=self.editor.get_templates() @@ -45,16 +86,9 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI): template=json.load(file) self.editor.set_templates(template) - -#editor = BeamlineEditorWidget( -# on_element_added = fn(element_type: str, node_id: str), -# on_elements_deleted = fn(node_ids: list[str]), -# on_element_selected = fn(node_id: str | None), # None on deselect -# get_properties = fn(node_id: str) -> dict, # called on selection -# on_property_changed = fn(node_id: str, data: dict), -#) - def addElement(self,element_type: str, node_id: str): + if node_id in self.elementDB.keys(): + return # this is a repopulation of the database when loading or importing print("Adding element of type:",element_type) match element_type: case "Quadrupole": @@ -63,6 +97,26 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI): ele = Dipole({}) case "Sextupole": ele = Sextupole({}) + case "Corrector": + ele = Corrector({}) + case "Solenoid": + ele = Solenoid({}) + case "RF": + ele = RF({}) + case "Undulator": + ele = Undulator({}) + case "Diagnostics": + ele = Diagnostic({}) + case "Reference": + ele = Marker({"Tag":"QREF"}) + case "Start": + ele = Marker({"Tag":"QBEG","RefID":""}) + case "Stop": + ele = Marker({"Tag","QEND"}) + case "Target": + ele = Marker({"Tag":"QTAR","Label":"Dump"}) + case "Alignment": + ele = Alignment({"Tag":"QALI","height":0.01}) case "Line": ele = LineContainer() case _: @@ -79,8 +133,7 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI): if not id in self.elementDB.keys(): return {} ele = self.elementDB[id] - type = self.editor.getElementType(id) - proplist = getPropertyList(type) + proplist = getPropertyList(self.editor.getElementType(id)) retdict={} for key in proplist.keys(): path = proplist[key].split('/') @@ -91,8 +144,6 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI): retdict['Name']['ID']=id return retdict - - def updateProperties(self,node_id: str, data: dict): # make sure that the ID is not changed data['Name']['ID']=node_id @@ -123,19 +174,6 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI): return - #print(type,elements) - #print(data) - for key in elements.keys(): - field = key - prop=elements[key].split('/') - #print(prop) - if prop[0] in data.keys() and prop[1] in data[prop[0]].keys(): - value = data[prop[0]][prop[1]] - print('Updating:',field,prop[0],prop[1],value) - setattr(self.elementDB[node_id], field, value) - - print(type) - if __name__ == '__main__': diff --git a/propertytypes.py b/propertytypes.py index eff75ab..dd85598 100644 --- a/propertytypes.py +++ b/propertytypes.py @@ -1,16 +1,19 @@ +from elements import Corrector # Editor Names Name = ['Name','Tag','Index'] -Position=['Offset','Length','Reserved Length','Reference Position','Displacement in X','Displacement in Y','Tilt'] +Position=['Offset','Relative Offset', 'Length','Reserved Length','Reference Position','Displacement in X','Displacement in Y','Tilt'] Type = ['Group','Baugruppe'] # element class attributes eName = ['Name','Tag','index'] -ePosition = ['OffsetS','Length','LengthRes','sRef','DisplacementX','DisplacementY','Tilt'] +ePosition = ['OffsetS','relOff','Length','LengthRes','sRef','DisplacementX','DisplacementY','Tilt'] eType = ['Group','Baugruppe'] -def getPropertyList(element_type: str) -> dict: +def getPropertyList(element_type: str | None) -> dict: + if element_type is None: + return {} if element_type == 'Line': return {'Name':'Name/Name','LengthEnding':'Position/Reserved Length','firstElementID':'Anchor/Reference'} fields={} @@ -23,12 +26,40 @@ def getPropertyList(element_type: str) -> dict: match element_type: case "Quadrupole": + fields['PSChannels'] = 'Type/PS Channels' fields['k1']='Setting/k1' case "Dipole": + fields['PSChannels'] = 'Type/PS Channels' fields['design_angle']='Setting/Angle' + fields['branching'] = 'Setting/Branching Point' fields['e1']='Setting/Entrance Angle' fields['e2'] = 'Setting/Exit Angle' case "Sextupole": + fields['PSChannels'] = 'Type/PS Channels' fields['k2'] = 'Setting/k2' + case "Solenoid": + fields['PSChannels'] = 'Type/PS Channels' + fields['ks'] = 'Setting/ks' + case "Corrector": + fields['PSChannels'] = 'Type/PS Channels' + case "RF": + fields['Gradient'] = 'Setting/Gradient' + fields['Phase'] = 'Setting/Phase' + fields['Band'] = 'Setting/Band' + case "Undulator": + fields['K'] = 'Setting/K' + fields['lambdau'] = 'Setting/Undulator Period' + fields['helical'] = 'Setting/Helical' + case "Diagnostic": + fields['Seval'] = 'Position/Evaluation Point' + case "Start": + fields['RefID'] = 'Setting/Reference Element ID' + case "Target": + fields['Label'] = 'Name/Destination Name' + case "Alignment": + fields['height'] = "Settings/Height" + + + return fields \ No newline at end of file diff --git a/ui/BeamlineGUI.py b/ui/BeamlineGUI.py index fd2e5d2..2f6c239 100644 --- a/ui/BeamlineGUI.py +++ b/ui/BeamlineGUI.py @@ -15,6 +15,10 @@ class Ui_BeamlineGUI(object): def setupUi(self, BeamlineGUI): BeamlineGUI.setObjectName("BeamlineGUI") BeamlineGUI.resize(1491, 641) + BeamlineGUI.setStyleSheet("font: 10pt \"Segoe UI\";\n" +"\n" +"color: rgb(79, 142, 247);\n" +"background-color: rgb(13, 15, 26);") self.centralwidget = QtWidgets.QWidget(BeamlineGUI) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) @@ -24,9 +28,16 @@ class Ui_BeamlineGUI(object): self.horizontalLayout.addWidget(self.UIEditorBox) BeamlineGUI.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(BeamlineGUI) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1491, 22)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1491, 24)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(10) + font.setBold(False) + font.setItalic(False) + font.setWeight(50) + self.menuFile.setFont(font) self.menuFile.setObjectName("menuFile") BeamlineGUI.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(BeamlineGUI) @@ -58,13 +69,13 @@ class Ui_BeamlineGUI(object): self.menubar.addAction(self.menuFile.menuAction()) self.retranslateUi(BeamlineGUI) - self.actionQuit.triggered.connect(self.centralwidget.close) # type: ignore + self.actionQuit.triggered.connect(BeamlineGUI.close) # type: ignore QtCore.QMetaObject.connectSlotsByName(BeamlineGUI) def retranslateUi(self, BeamlineGUI): _translate = QtCore.QCoreApplication.translate BeamlineGUI.setWindowTitle(_translate("BeamlineGUI", "MainWindow")) - self.menuFile.setTitle(_translate("BeamlineGUI", "File")) + self.menuFile.setTitle(_translate("BeamlineGUI", "FILE")) self.actionNew_Layout.setText(_translate("BeamlineGUI", "New Layout")) self.actionSave.setText(_translate("BeamlineGUI", "Save")) self.actionSave.setShortcut(_translate("BeamlineGUI", "Ctrl+S")) diff --git a/ui/BeamlineGUI.ui b/ui/BeamlineGUI.ui index 1a62b3e..386df57 100644 --- a/ui/BeamlineGUI.ui +++ b/ui/BeamlineGUI.ui @@ -13,6 +13,12 @@ MainWindow + + font: 10pt "Segoe UI"; + +color: rgb(79, 142, 247); +background-color: rgb(13, 15, 26); + @@ -26,12 +32,21 @@ 0 0 1491 - 22 + 24 + + + Segoe UI + 10 + 50 + false + false + + - File + FILE