diff options
author | Paweł Redman <pawel.redman@gmail.com> | 2019-04-28 17:29:18 +0200 |
---|---|---|
committer | Paweł Redman <pawel.redman@gmail.com> | 2019-04-28 19:07:20 +0200 |
commit | 52a8a95a8553f5b2a1224db474e6cf1d649533c8 (patch) | |
tree | 003e0c94839b6daebef19049c75ba0ed43082a7b /src/ui.py |
Initial commit
Diffstat (limited to 'src/ui.py')
-rw-r--r-- | src/ui.py | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/src/ui.py b/src/ui.py new file mode 100644 index 0000000..7803d3d --- /dev/null +++ b/src/ui.py @@ -0,0 +1,421 @@ +import numpy as np, scipy.optimize +import traceback +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * + +import phys, file + +# global GUI-related shit +class GUI: + table = None + table_rows = list() + monospace = QFont("monospace") + bigfont = QFont("sans-serif", pointSize=10, weight=1000) + +class Pens: + axes = QPen(Qt.gray) + + ellipse = QPen(Qt.black) + ellipse.setWidth(2) + + axis_linear = QPen(QColor(201, 141, 0)) + axis_linear.setWidth(2) + axis_linear.setStyle(Qt.DashDotLine) + + axis_fast = QPen(QColor(51, 87, 123)) + axis_fast.setWidth(2) + axis_fast.setStyle(Qt.DashDotLine) + + axis_slow = QPen(QColor(55, 123, 51)) + axis_slow.setWidth(2) + axis_slow.setStyle(Qt.DashDotLine) + + alpha = QPen(Qt.red) + theta = QPen(Qt.blue) + + radii = QPen(Qt.black) + radii.setStyle(Qt.DashDotLine) + +from ui_widgets import * + + + +class OptBox(QVBoxLayout): + def __init__(self, row, pol, rownum): + super().__init__() + + # Name + self.name = MeinGroßLabel("Element %d" % rownum) + self.addWidget(self.name) + + # Type / Enable box + self.hbox = QHBoxLayout() + self.addLayout(self.hbox) + + # Type combo + self.type = QComboBox() + self.type.addItem("Linear", "linear",) + self.type.addItem("Quarterwave plate", "quarterwave") + self.type.setCurrentIndex(0 if pol.type == "linear" else 1) + self.type.currentIndexChanged.connect(lambda : TableRow.type_change(row)) + self.hbox.addWidget(self.type) + + # Enable checkbox + self.enable = QCheckBox("Enable") + self.enable.setChecked(True) + self.enable.stateChanged.connect(update) + self.hbox.addWidget(self.enable) + + # Delta angle + self.delta = AngleSlider("Delta") + self.delta.setValue(pol.delta * 180 / np.pi) + self.delta.on_change = lambda: TableRow.angle_change(row) + self.addLayout(self.delta) + + # Angle reference + self.ref = QComboBox() + self.ref.addItem("Absolute", False) + for i, _ in enumerate(system.elements): + if i >= rownum - 1: + continue + self.ref.addItem("Relative to element #%d" % (i + 1), i) + + if row.ref is not False and i == row.ref: + self.ref.setCurrentIndex(row.ref + 1) + + self.ref.currentIndexChanged.connect(update) + self.addWidget(self.ref) + + # Angle + self.angle = AngleSlider("Angle") + self.angle.setValue(pol.angle * 180 / np.pi) + self.angle.on_change = lambda: TableRow.angle_change(row) + self.addLayout(self.angle) + self.addItem(ExpandingSpacer()) + + # Add + self.add_above = QPushButton("Add above") + self.add_above.clicked.connect(lambda: half_assed_element_creation(rownum - 1)) + self.add_below = QPushButton("Add below") + self.add_below.clicked.connect(lambda: half_assed_element_creation(rownum)) + self.delete = QPushButton("Delete") + self.delete.clicked.connect(lambda: half_assed_element_deletion(rownum - 1)) + + self.buttons = QHBoxLayout() + self.buttons.addWidget(self.add_above) + self.buttons.addWidget(self.add_below) + self.buttons.addWidget(self.delete) + self.addLayout(self.buttons) + + + +class TableRow: + def __init__(self, pol, grid, rownum): + self.pol = pol + self.grid = grid + self.rownum = rownum + self.angle = pol.angle + self.delta = pol.delta + self.ref = pol.ref + + self.optbox = OptBox(self, pol, rownum) + grid.addLayout(self.optbox, rownum, 1) + + self.ellipse = EllipseWidget(pol) + grid.addWidget(self.ellipse, rownum, 2) + + self.info = QVBoxLayout() + self.info_angle = MeinLabel() + self.info_angle.setAlignment(Qt.AlignLeft) + self.info.addWidget(self.info_angle) + self.info.addWidget(QLabel("Polarization state")) + self.info_jones = MeinLabel() + self.info_jones.setAlignment(Qt.AlignLeft) + self.info.addWidget(self.info_jones) + self.info.addItem(ExpandingSpacer()) + grid.addLayout(self.info, rownum, 3) + + + @staticmethod + def angle_change(row): + row.angle = row.optbox.angle.angle / 180 * np.pi + row.delta = row.optbox.delta.angle / 180 * np.pi + update() + + @staticmethod + def type_change(row): + row.pol.set_type(row.optbox.type.currentData()) + update() + + +def populate_table(): + GUI.table = QGridLayout() + GUI.table.setColumnStretch(1, 1) + GUI.table.setColumnStretch(2, 2) + GUI.table.setColumnStretch(3, 5) + + while GUI.opt_operand.count() > 0: # FIXME + GUI.opt_operand.removeItem(0) + + GUI.table_rows = list() + for i, pol in enumerate(system.elements): + GUI.table_rows.append(TableRow(pol, GUI.table, i + 1)) + GUI.opt_operand.addItem("Element #%d" % (i + 1), i) + +def update(): + system.ignore = [False] * len(system.elements) + for i, pol in enumerate(system.elements): + row = GUI.table_rows[i] + + pol.angle = row.angle + pol.delta = row.delta + pol.ref = row.optbox.ref.currentData() + if pol.ref is not False: + pol.angle += system.elements[pol.ref].angle + + if not row.optbox.enable.isChecked(): + system.ignore[i] = True + + system.recalculate() + + I = 1 + for i, pol in enumerate(system.elements): + row = GUI.table_rows[i] + + # update all the diagrams + row.ellipse.state = system.states[i] + row.ellipse.ellipse = system.ellipses[i] + row.ellipse.is_used = not system.ignore[i] + row.ellipse.repaint() + + text = "%s (%f°)\nat %f°" \ + % (pol.type, pol.delta * 180 / np.pi, + (pol.angle + pol.delta) * 180 / np.pi) + row.info_angle.setText(text) + + state = system.states[i] + ellipse = system.ellipses[i] + + if state is None: + I = 1 + row.info_jones.setText("Unpolarized") + else: + I = np.abs(state[0] * state[0].conjugate() \ + + state[1] * state[1].conjugate()) + + if state is None: + continue + + A, B = state + text = "I = %f\n" % I + text += "%f ∠ %+f°\n%f ∠ %+f°\n" % \ + (np.abs(A), 180 / np.pi * np.angle(A), \ + np.abs(B), 180 / np.pi * np.angle(B)) + text += "α = %f°\nφ = %+f°" % \ + (180 / np.pi * ellipse.alpha, 180 / np.pi * ellipse.theta) + text = text.replace("-", "- ").replace("+", "+ ") + row.info_jones.setText(text) + + GUI.widok.intensity = I + GUI.widok.repaint() + + #if GUI.auto_optimize.isChecked(): + # optimize() + + +#TODO later +#class AddElementWindow(QMainWindow): +# dialog = None +# +# def __init__(self): +# super().__init__() +# +# chuj = QLabel("adolf") +# self.setCentralWidget(chuj) +# self.show() +# +# +# def __del__(self): +# AddElementWindow.dialog = None +# +# def open(): +# if not AddElementWindow.dialog: +# AddElementWindow.dialog = AddElementWindow() +def half_assed_element_creation(index=None): + if index is None: + system.elements.append(phys.Polarizer("linear")) + else: + system.elements.insert(index, phys.Polarizer("linear")) + + populate_table() + GUI.table_frame = QFrame() + GUI.table_frame.setLayout(GUI.table) + GUI.scroll.setWidget(GUI.table_frame) + update() + +def half_assed_clear(): + system.elements = list() + populate_table() + GUI.table_frame = QFrame() + GUI.table_frame.setLayout(GUI.table) + GUI.scroll.setWidget(GUI.table_frame) + update() + +def half_assed_element_deletion(index): + del(system.elements[index]) + + populate_table() + GUI.table_frame = QFrame() + GUI.table_frame.setLayout(GUI.table) + GUI.scroll.setWidget(GUI.table_frame) + update() + + +last_save_path = None +file_filter = "Wery Omportant Zejta (*.woz)" + +def do_save(win, reuse_old): + global system, last_save_path + + if reuse_old and last_save_path: + path = last_save_path + else: + path, _ = QFileDialog.getSaveFileName(win, filter=file_filter) + if path == "": + return + + try: + file.save_system(path, system) + except: + traceback.print_exc() + + last_save_path = path + +def do_open(win): + global system + + path, _ = QFileDialog.getOpenFileName(win, filter=file_filter) + if path == "": + return + + try: + system = file.open_system(path) + except: + traceback.print_exc() + + populate_table() + GUI.table_frame = QFrame() + GUI.table_frame.setLayout(GUI.table) + GUI.scroll.setWidget(GUI.table_frame) + update() + +def setup_menubar(win): + menu = win.menuBar() + + # File + open = QAction("&Open a system", win) + open.setShortcut("Ctrl+O") + open.triggered.connect(lambda: do_open(win)) + save = QAction("&Save a system", win) + save.setShortcut("Ctrl+S") + save.triggered.connect(lambda: do_save(win, True)) + save_as = QAction("&Save a system as...", win) + save_as.triggered.connect(lambda: do_save(win, False)) + close = QAction("Exit", win) + close.setShortcut("Ctrl+Q") + close.triggered.connect(exit) + + file = menu.addMenu("&File") + file.addAction(open) + file.addAction(save) + file.addAction(save_as) + file.addAction(close) + + # System + add = QAction("&Add a new element", win) + add.setShortcut("Ctrl+N") + add.triggered.connect(half_assed_element_creation) + clear = QAction("&Remove all elements", win) + clear.triggered.connect(half_assed_clear) + + system = menu.addMenu("&System") + system.addAction(add) + system.addAction(clear) + + win.statusBar() + +# FIXME: refactor +def optimize(which): + if len(system.elements) == 0: + return + + op_idx = GUI.opt_operand.currentData() + op = system.elements[op_idx] + + def I(theta): + op.angle = theta + if op.ref is not False: + op.ref += system.elements[op.ref].angle + + state = None + for i, pol in enumerate(system.elements): + if i >= len(system.ignore) or not system.ignore[i]: + state = pol.mul(state) + if state is None: + return 1 + else: + return float(np.abs(state[0] * state[0].conjugate() \ + + state[1] * state[1].conjugate())) + + if which == "min": + f = I + else: + f = lambda theta: -I(theta) + + opt = scipy.optimize.minimize_scalar(f, bounds=[0, np.pi], method="bounded") + GUI.table_rows[op_idx].optbox.angle.edit.setText("%g" % round(opt.x * 180 / np.pi, 3)) + + +def setup(win, system_): + global system + system = system_ + + # Needless to say, I'm getting real tired of this whole Layout/Widget + # clusterfuck in Qt + root = QHBoxLayout() + root_fucking_random_container = QWidget() + root_fucking_random_container.setLayout(root) + win.setCentralWidget(root_fucking_random_container) + + rhs = QVBoxLayout() + + Widocques.image = QImage("/home/enneract/lab/test/c.jpg") + GUI.widok = Widocques() + rhs.addWidget(GUI.widok) + + optbox = QHBoxLayout() + rhs.addLayout(optbox) + + GUI.opt_operand = QComboBox() + optbox.addWidget(GUI.opt_operand) + button = QPushButton("Find a minimum") + button.pressed.connect(lambda: optimize("min")) + optbox.addWidget(button) + button = QPushButton("Find a maximum") + button.pressed.connect(lambda: optimize("max")) + optbox.addWidget(button) + + GUI.scroll = QScrollArea() + populate_table() + GUI.table_frame = QFrame() + GUI.table_frame.setLayout(GUI.table) + GUI.scroll.setWidget(GUI.table_frame) + + root.addWidget(GUI.scroll) + root.addLayout(rhs) + + setup_menubar(win) + + update() + win.show()
\ No newline at end of file |