From 5a952899e095c82ff706449ee4d3f8c5eaac7911 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Fri, 3 May 2019 14:40:08 +0200 Subject: Start reworking the layout using a QTableWidget --- src/main.py | 13 +- src/ui.py | 403 +++++++++++++++--------------------------------------- src/ui_table.py | 222 ++++++++++++++++++++++++++++++ src/ui_widgets.py | 5 + 4 files changed, 344 insertions(+), 299 deletions(-) create mode 100644 src/ui_table.py diff --git a/src/main.py b/src/main.py index ea1122c..ce3a9ff 100644 --- a/src/main.py +++ b/src/main.py @@ -5,15 +5,10 @@ from PyQt5.QtCore import * import ui, phys, file -system = phys.System() - -class Window(QMainWindow): - def __init__(self): - super().__init__() - self.statusBar() - self.setWindowTitle("Polarizzazione italiana") - ui.setup(self, system) +#system = phys.System() +system = file.open_system("4.woz") app = QApplication(sys.argv) -win = Window() +win = ui.MainWindow(system) +win.show() sys.exit(app.exec_()) diff --git a/src/ui.py b/src/ui.py index f54134f..fb0932a 100644 --- a/src/ui.py +++ b/src/ui.py @@ -9,7 +9,6 @@ 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) @@ -38,213 +37,7 @@ class Pens: radii.setStyle(Qt.DashDotLine) from ui_widgets import * - - - -class ElementEditorWindow(QMainWindow): - windows = list() - - @staticmethod - def open(pol): - win = ElementEditorWindow(pol) - win.show() - ElementEditorWindow.windows.append(win) - - @staticmethod - def close_all(pol=None): - for win in ElementEditorWindow.windows: - if pol is None or pol == win.pol: - win.close() - del(win) - - def __init__(self, pol): - super().__init__(flags=Qt.Dialog) - self.pol = pol - - self.setWindowTitle("Element editor") - root = QVBoxLayout() - root_fucking_random_container = QWidget() - root_fucking_random_container.setLayout(root) - self.setCentralWidget(root_fucking_random_container) - - # Name - box = QHBoxLayout() - root.addLayout(box) - box.addWidget(QLabel("Name")) - self.name = QLineEdit(self.pol.name) - self.name.textChanged.connect(self.change_name) - box.addWidget(self.name) - - # OPTICS - root.addWidget(MeinGroßLabel("Optics")) - - # 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(self.change_type) - root.addWidget(self.type) - - # TRANSMITTANCE - root.addWidget(MeinGroßLabel("Transmittance")) - - box = QHBoxLayout() - root.addLayout(box) - - box.addWidget(QLabel("T")) - self.t1 = QLineEdit("%g" % self.pol.t1) - self.t1.textChanged.connect(self.change_t) - box.addWidget(self.t1) - self.t2 = QLineEdit("%g" % self.pol.t2) - self.t2.textChanged.connect(self.change_t) - box.addWidget(self.t2) - - # ARRANGEMENT - root.addWidget(MeinGroßLabel("Arrangement")) - - # Angle reference - self.ref = QComboBox() - self.populate_ref() - self.ref.currentIndexChanged.connect(update) - root.addWidget(self.ref) - - # Delta angle - self.delta = AngleSlider("Delta") - self.delta.on_change = self.change_delta - root.addLayout(self.delta) - - root.addItem(ExpandingSpacer()) - - # Done - self.done = QPushButton("Done") - self.done.pressed.connect(self.close) - root.addWidget(self.done) - - def change_name(self): - self.pol.name = self.name.text() - self.pol.row.optbox.name.setText(self.pol.name) - - def change_type(self): - self.pol.set_type(self.type.currentData()) - update() - - def change_delta(self): - try: - self.pol.delta = self.delta.angle / 180 * np.pi - update() - except ValueError: - pass - - def change_t(self): - try: - self.pol.t1 = float(self.t1.text()) - self.pol.t2 = float(self.t2.text()) - update() - except ValueError: - pass - - def populate_ref(self): - self.ref.addItem("Absolute", False) - for i, _ in enumerate(system.elements): - if i >= self.pol.rownum - 1: - continue - self.ref.addItem("Relative to #%d" % (i + 1), i) - - if self.pol.ref is not False and i == self.pol.ref: - self.ref.setCurrentIndex(self.pol.ref + 1) - - - -class OptBox(QVBoxLayout): - def __init__(self, row, pol, rownum): - super().__init__() - self.row = row - self.rownum = rownum - - # Name - self.name = MeinGroßLabel(pol.name) - self.name.setContextMenuPolicy(Qt.CustomContextMenu) - self.name.customContextMenuRequested.connect(lambda pos: OptBox.open_menu(self, pos)) - self.addWidget(self.name) - - # Enable checkbox - self.enable = QCheckBox("Enable") - self.enable.setChecked(True) - self.enable.stateChanged.connect(self.change_enable) - self.addWidget(self.enable) - - # Angle - self.angle = AngleSlider() - self.angle.setValue(pol.angle * 180 / np.pi) - self.angle.on_change = lambda: TableRow.angle_change(row) - self.addLayout(self.angle) - self.addItem(ExpandingSpacer()) - - def open_menu(self, pos): - menu = QMenu() - menu.addAction("Edit", lambda: ElementEditorWindow.open(self.row.pol)) - menu.addAction("Insert before", lambda: half_assed_element_creation(self.rownum - 1)) - menu.addAction("Insert after", lambda: half_assed_element_creation(self.rownum)) - menu.addAction("Delete", lambda: half_assed_element_deletion(self.rownum - 1)) - menu.exec_(self.name.mapToGlobal(pos)) - - def change_enable(self): - self.row.pol.enable = self.enable.isChecked() - update() - - -class TableRow: - def __init__(self, pol, grid, rownum): - self.pol = pol - pol.row = self - pol.rownum = rownum - 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.pol.angle = row.angle - update() - - - -def populate_table(): - GUI.input_intensity.setText("%g" % system.input_intensity) - - 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) +from ui_table import * def update(): for i, pol in enumerate(system.elements): @@ -406,96 +199,126 @@ def setup_menubar(win): 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 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.angle += 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)) + +class MainWindow(QMainWindow): + def __init__(self, system_): + super().__init__() + self.statusBar() + self.setWindowTitle("Polarizzazione italiana") + + global system + system = system_ + + ## Needless to say, I'm getting real tired of this whole Layout/Widget + ## clusterfuck in Qt + #root = QVBoxLayout() + #root_fucking_random_container = QWidget() + #root_fucking_random_container.setLayout(root) + #self.setCentralWidget(root_fucking_random_container) + # + ## Top bar (input intensity) + #box = QHBoxLayout() + #root.addLayout(box) + # + #box.addWidget(QLabel("Input intensity")) + #GUI.input_intensity = QLineEdit("1") + #GUI.input_intensity.textChanged.connect(self.change_input_intensity) + #box.addWidget(GUI.input_intensity) + # + #hbox = QHBoxLayout() + #root.addLayout(hbox) + # + #rhs = QVBoxLayout() + # + #Widocques.image = QImage("jones.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.table = QTableWidget() + #populate_table() + # + #hbox.addWidget(GUI.scroll) + #hbox.addLayout(rhs) + + setup_menubar(self) - def I(theta): - op.angle = theta - if op.ref is not False: - op.angle += system.elements[op.ref].angle + root = QVBoxLayout() + self.setCentralWidget(LayoutWrapper(root)) - 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())) + box = QHBoxLayout() + root.addLayout(box) - if which == "min": - f = I - else: - f = lambda theta: -I(theta) + # Top bar + box.addWidget(QLabel("Input intensity")) + GUI.input_intensity = QLineEdit("%g" % system.input_intensity) + GUI.input_intensity.textChanged.connect(self.change_input_intensity) + box.addWidget(GUI.input_intensity) - 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)) + # Splitter + split = QSplitter() + root.addWidget(split) + # Table (LHS) + GUI.table = SystemTable() + split.addWidget(GUI.table) + # Splitter RHS + rhs = QVBoxLayout() + split.addWidget(LayoutWrapper(rhs)) -def change_input_intensity(): - try: - system.input_intensity = float(GUI.input_intensity.text()) - update() - except ValueError: - pass + Widocques.image = QImage("jones.jpg") + GUI.widok = Widocques() + rhs.addWidget(GUI.widok) -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 = QVBoxLayout() - root_fucking_random_container = QWidget() - root_fucking_random_container.setLayout(root) - win.setCentralWidget(root_fucking_random_container) - - # Top bar (input intensity) - box = QHBoxLayout() - root.addLayout(box) - - box.addWidget(QLabel("Input intensity")) - GUI.input_intensity = QLineEdit("1") - GUI.input_intensity.textChanged.connect(change_input_intensity) - box.addWidget(GUI.input_intensity) - - hbox = QHBoxLayout() - root.addLayout(hbox) - - rhs = QVBoxLayout() - - Widocques.image = QImage("jones.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) + GUI.table.populate(system) - hbox.addWidget(GUI.scroll) - hbox.addLayout(rhs) + #update() - setup_menubar(win) + def change_input_intensity(self): + try: + system.input_intensity = float(GUI.input_intensity.text()) + update() + except ValueError: + pass - update() - win.show() \ No newline at end of file diff --git a/src/ui_table.py b/src/ui_table.py new file mode 100644 index 0000000..3d9a6fa --- /dev/null +++ b/src/ui_table.py @@ -0,0 +1,222 @@ +import numpy as np +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * + +from ui import GUI, Pens +from ui_widgets import * + + + +class ElementEditorWindow(QMainWindow): + windows = list() + + @staticmethod + def open(pol): + win = ElementEditorWindow(pol) + win.show() + ElementEditorWindow.windows.append(win) + + @staticmethod + def close_all(pol=None): + for win in ElementEditorWindow.windows: + if pol is None or pol == win.pol: + win.close() + del(win) + + def __init__(self, pol): + super().__init__(flags=Qt.Dialog) + self.pol = pol + + self.setWindowTitle("Element editor") + root = QVBoxLayout() + root_fucking_random_container = QWidget() + root_fucking_random_container.setLayout(root) + self.setCentralWidget(root_fucking_random_container) + + # Name + box = QHBoxLayout() + root.addLayout(box) + box.addWidget(QLabel("Name")) + self.name = QLineEdit(self.pol.name) + self.name.textChanged.connect(self.change_name) + box.addWidget(self.name) + + # OPTICS + root.addWidget(MeinGroßLabel("Optics")) + + # 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(self.change_type) + root.addWidget(self.type) + + # TRANSMITTANCE + root.addWidget(MeinGroßLabel("Transmittance")) + + box = QHBoxLayout() + root.addLayout(box) + + box.addWidget(QLabel("T")) + self.t1 = QLineEdit("%g" % self.pol.t1) + self.t1.textChanged.connect(self.change_t) + box.addWidget(self.t1) + self.t2 = QLineEdit("%g" % self.pol.t2) + self.t2.textChanged.connect(self.change_t) + box.addWidget(self.t2) + + # ARRANGEMENT + root.addWidget(MeinGroßLabel("Arrangement")) + + # Angle reference + self.ref = QComboBox() + self.populate_ref() + self.ref.currentIndexChanged.connect(update) + root.addWidget(self.ref) + + # Delta angle + self.delta = AngleSlider("Delta") + self.delta.on_change = self.change_delta + root.addLayout(self.delta) + + root.addItem(ExpandingSpacer()) + + # Done + self.done = QPushButton("Done") + self.done.pressed.connect(self.close) + root.addWidget(self.done) + + def change_name(self): + self.pol.name = self.name.text() + self.pol.row.optbox.name.setText(self.pol.name) + + def change_type(self): + self.pol.set_type(self.type.currentData()) + update() + + def change_delta(self): + try: + self.pol.delta = self.delta.angle / 180 * np.pi + update() + except ValueError: + pass + + def change_t(self): + try: + self.pol.t1 = float(self.t1.text()) + self.pol.t2 = float(self.t2.text()) + update() + except ValueError: + pass + + def populate_ref(self): + self.ref.addItem("Absolute", False) + for i, _ in enumerate(system.elements): + if i >= self.pol.rownum - 1: + continue + self.ref.addItem("Relative to #%d" % (i + 1), i) + + if self.pol.ref is not False and i == self.pol.ref: + self.ref.setCurrentIndex(self.pol.ref + 1) + + + +class OptBox(QVBoxLayout): + def __init__(self, row, pol): + super().__init__() + self.row = row + + # Name + self.name = MeinGroßLabel(pol.name) + self.name.setContextMenuPolicy(Qt.CustomContextMenu) + self.name.customContextMenuRequested.connect(lambda pos: OptBox.open_menu(self, pos)) + self.addWidget(self.name) + + # Enable checkbox + self.enable = QCheckBox("Enable") + self.enable.setChecked(True) + self.enable.stateChanged.connect(self.change_enable) + self.addWidget(self.enable) + + # Angle + self.angle = AngleSlider() + self.angle.setValue(pol.angle * 180 / np.pi) + self.angle.on_change = lambda: TableRow.angle_change(row) + self.addLayout(self.angle) + self.addItem(ExpandingSpacer()) + + def open_menu(self, pos): + menu = QMenu() + menu.addAction("Edit", lambda: ElementEditorWindow.open(self.row.pol)) + menu.addAction("Insert before", lambda: half_assed_element_creation(self.rownum - 1)) + menu.addAction("Insert after", lambda: half_assed_element_creation(self.rownum)) + menu.addAction("Delete", lambda: half_assed_element_deletion(self.rownum - 1)) + menu.exec_(self.name.mapToGlobal(pos)) + + def change_enable(self): + self.row.pol.enable = self.enable.isChecked() + update() + + + +class SystemTableRow: + def __init__(self, parent, pol, row_number): + self.parent = parent + self.pol = pol + pol.row = self + + self.angle = pol.angle + self.delta = pol.delta + self.ref = pol.ref + + self.optbox = OptBox(self, pol) + parent.setCellWidget(row_number, 0, LayoutWrapper(self.optbox)) + + self.ellipse = EllipseWidget(pol) + parent.setCellWidget(row_number, 1, self.ellipse) + + 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()) + parent.setCellWidget(row_number, 2, LayoutWrapper(self.info)) + + @staticmethod + def angle_change(row): + row.angle = row.optbox.angle.angle / 180 * np.pi + row.pol.angle = row.angle + update() + + + +class SystemTable(QTableWidget): + def __init__(self): + super().__init__() + self.rows = list() + + self.setColumnCount(3) # this won't change + + self.hh = self.horizontalHeader() + self.hh.setSectionResizeMode(QHeaderView.ResizeToContents) + #self.hh.hide() + + self.vh = self.verticalHeader() + self.vh.setSectionResizeMode(QHeaderView.ResizeToContents) + + def populate(self, system): + self.system = system + + self.rows = list() + self.clearContents() + self.setRowCount(len(system.elements)) + + for i, pol in enumerate(system.elements): + self.rows.append(SystemTableRow(self, pol, i)) + diff --git a/src/ui_widgets.py b/src/ui_widgets.py index 9eabd2e..3babfa6 100644 --- a/src/ui_widgets.py +++ b/src/ui_widgets.py @@ -21,6 +21,11 @@ class MeinGroßLabel(QLabel): self.setFont(GUI.bigfont) self.setAlignment(Qt.AlignHCenter) +class LayoutWrapper(QWidget): + def __init__(self, layout): + super().__init__() + self.setLayout(layout) + class EllipseWidget(QWidget): def __init__(self, pol): QWidget.__init__(self) -- cgit