summaryrefslogtreecommitdiff
path: root/src/ui.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui.py')
-rw-r--r--src/ui.py421
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