Exemplo n.º 1
0
def test_laser_widget(qtbot: QtBot):
    x = rand_data(["A1", "B2"])
    y = x["A1"].copy()
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()

    view = viewspace.activeView()
    view.addLaser(Laser(x))
    widget = view.activeWidget()

    widget.applyConfig(Config(1.0, 1.0, 1.0))
    assert widget.laser.config.spotsize == 1.0
    widget.applyCalibration({"B2": Calibration(2.0, 2.0)})
    assert widget.laser.calibration["B2"].intercept == 2.0

    widget.updateNames({"A1": "A1", "B2": "2B"})
    assert np.all(viewspace.uniqueElements() == ["2B", "A1"])

    widget.transform(flip="horizontal")
    assert np.all(widget.laser.get("A1") == np.flip(y, axis=1))
    widget.transform(flip="horizontal")
    widget.transform(flip="vertical")
    assert np.all(widget.laser.get("A1") == np.flip(y, axis=0))
    widget.transform(flip="vertical")
    assert np.all(widget.laser.get("A1") == y)
    widget.transform(rotate="right")
    assert np.all(widget.laser.get("A1") == np.rot90(y, k=1, axes=(1, 0)))
    widget.transform(rotate="left")
    assert np.all(widget.laser.get("A1") == y)
Exemplo n.º 2
0
def test_tool_filter(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()
    widget = view.addLaser(Laser(linear_data(["a", "b", "c"])))
    tool = DriftTool(view.activeWidget())
    view.addTab("Tool", tool)
    qtbot.waitExposed(tool)

    tool.combo_element.setCurrentText("a")
    tool.combo_element.activated.emit(0)

    tool.spinbox_degree.setValue(1)
    tool.apply()

    assert np.all(np.isclose(widget.laser.data["a"], widget.laser.data["a"][0][0]))

    tool.combo_element.setCurrentText("b")
    tool.combo_element.activated.emit(0)

    assert not np.all(np.isclose(widget.laser.data["b"], widget.laser.data["c"][0][0]))

    tool.check_apply_all.setChecked(True)
    tool.apply()

    assert np.all(np.isclose(widget.laser.data["b"], widget.laser.data["c"][0][0]))
Exemplo n.º 3
0
def test_export_dialog_names(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    view = viewspace.activeView()

    widget = view.addLaser(
        Laser(
            rand_data(["A", "\\B", "C>_<"]),
            info={
                "Name": "inv@|d",
                "File Path": "/invalid.npz"
            },
        ))
    dlg = ExportDialog(widget)
    dlg.lineedit_directory.setText(str(Path(".")))
    assert dlg.isComplete()
    dlg.lineedit_filename.setText("invalid.csv")
    assert dlg.isComplete()
    dlg.check_export_all.setChecked(True)

    paths = dlg.generatePaths(widget.laser)

    assert paths[0][0].name == "invalid_A.csv"
    assert paths[1][0].name == "invalid__B.csv"
    assert paths[2][0].name == "invalid_C___.csv"
Exemplo n.º 4
0
def test_standards_tool(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()
    data = linear_data(["A1", "B2"])
    data["B2"][6] = 1.0
    view.addLaser(Laser(data))
    tool = StandardsTool(view.activeWidget())
    view.addTab("Tool", tool)
    qtbot.waitExposed(tool)

    # Units
    tool.lineedit_units.setText("unit")
    tool.lineedit_units.editingFinished.emit()
    tool.combo_weighting.setCurrentIndex(2)

    # Table
    tool.spinbox_levels.setValue(5)

    assert not tool.isComplete()
    assert not tool.button_plot.isEnabled()
    for i in range(0, tool.table.model().rowCount()):
        index = tool.table.model().index(i, 0)
        tool.table.model().setData(index, i)

    # Change element, check weighting
    tool.combo_element.setCurrentIndex(1)
    assert not tool.table.isComplete()
    for i in range(0, tool.table.model().rowCount()):
        index = tool.table.model().index(i, 0)
        tool.table.model().setData(index, i)
    assert tool.isComplete()
    assert tool.button_plot.isEnabled()

    assert tool.combo_weighting.currentIndex() == 0
    assert tool.lineedit_units.text() == ""
    tool.lineedit_units.setText("none")
    assert tool.calibration["B2"].gradient == 1.75

    # Check weighting updates results
    tool.combo_weighting.setCurrentText("y")
    assert tool.calibration["B2"].weighting == "y"
    assert np.isclose(tool.calibration["B2"].gradient, 1.954022988)

    # Change element back, check weighting, unit, table restored
    tool.combo_element.setCurrentIndex(0)
    assert tool.isComplete()
    assert tool.lineedit_units.text() == "unit"
    assert tool.combo_weighting.currentIndex() == 2

    # Test SD weighting
    tool.combo_weighting.setCurrentIndex(1)
    assert np.all(tool.calibration["A1"].weights == 4.0)

    tool.combo_element.setCurrentIndex(1)
    dlg = tool.showCurve()
    qtbot.waitExposed(dlg)
    dlg.close()
Exemplo n.º 5
0
def test_tool_calculator(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()

    view = viewspace.activeView()
    widget = view.addLaser(Laser(rand_data(["a", "b"])))
    tool = CalculatorTool(view.activeWidget())
    view.addTab("Tool", tool)
    qtbot.waitExposed(tool)

    assert tool.lineedit_name.text() == "calc0"
    tool.apply()
    assert "calc0" in widget.laser.elements
    assert tool.lineedit_name.text() == "calc1"

    # overwrite
    tool.lineedit_name.setText("a")
    tool.apply()
    assert len(widget.laser.elements) == 3

    # Inserters
    assert tool.formula.toPlainText() == "a"
    tool.insertFunction(1)
    assert tool.formula.toPlainText() == "abs(a"
    tool.insertVariable(2)
    assert tool.formula.toPlainText() == "abs(ba"

    # Test output of previewData and output lineedit
    x = np.array(np.random.random((10, 10)), dtype=[("a", float)])

    tool.formula.setPlainText("mean(a)")
    assert tool.previewData(x) is None
    assert tool.output.text() == f"{np.mean(x['a']):.10g}"

    # Array access in output
    tool.formula.setPlainText("a[0]")
    assert tool.previewData(x) is None
    assert tool.output.text() == f"{list(map('{:.4g}'.format, x['a'][0]))}"

    # Simple op
    tool.formula.setPlainText("a + 1.0")
    assert np.all(tool.previewData(x) == x["a"] + 1.0)
    assert tool.isComplete()

    # Invalid input
    tool.formula.setPlainText("fail")
    assert tool.previewData(x) is None
    assert not tool.isComplete()
Exemplo n.º 6
0
def test_laser_widget_cursor(qtbot: QtBot):
    main = QtWidgets.QMainWindow()
    qtbot.addWidget(main)
    main.statusBar()  # Create bar
    viewspace = LaserViewSpace()
    main.setCentralWidget(viewspace)

    view = viewspace.activeView()
    view.addLaser(Laser(rand_data(["a"])))
    widget = view.activeWidget()

    # Cursor
    widget.updateCursorStatus(2.0, 2.0, 1.0)
    assert main.statusBar().currentMessage() == "2,2 [1]"

    widget.updateCursorStatus(1.0, 3.0, np.nan)
    assert main.statusBar().currentMessage() == "1,3 [nan]"
Exemplo n.º 7
0
def test_laser_widget_actions(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()
    view.addLaser(
        Laser(rand_data(["a", "b"]),
              info={"File Path": "/home/pewpew/real.npz"}))
    widget = view.activeWidget()

    dlg = widget.actionCalibration()
    dlg.close()
    dlg = widget.actionConfig()
    dlg.close()
    widget.actionDuplicate()
    widget.actionCopyImage()
    dlg = widget.actionExport()
    dlg.close()
    dlg = widget.actionSave()
    dlg.close()
    dlg = widget.actionStatistics()
    dlg.close()
    dlg = widget.actionSelectDialog()
    dlg.close()
    dlg = widget.actionColocal()
    dlg.close()

    widget.contextMenuEvent(
        QtGui.QContextMenuEvent(QtGui.QContextMenuEvent.Mouse,
                                QtCore.QPoint(0, 0)))

    # Test contextmenu
    widget.graphics.mask = np.ones((10, 10), dtype=bool)
    widget.contextMenuEvent(
        QtGui.QContextMenuEvent(
            QtGui.QContextMenuEvent.Mouse,
            widget.graphics.mapFromScene(QtCore.QPointF(0, 0)),
        ))

    widget.actionCopySelectionText()
    widget.actionCropSelection()
    dlg = widget.actionStatisticsSelection()
    dlg.close()
    dlg = widget.actionColocalSelection()
    dlg.close()
Exemplo n.º 8
0
def test_laser_view(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()
    laser = view.addLaser(Laser(rand_data(["A1", "B2", "C3"])))
    qtbot.waitExposed(laser)

    view.tabs.setTabText(0, "newname")
    assert view.stack.widget(0).laserName() == "newname"

    view.contextMenuEvent(
        QtGui.QContextMenuEvent(QtGui.QContextMenuEvent.Mouse,
                                QtCore.QPoint(0, 0)))

    # Drop event
    drag_mime = QtCore.QMimeData()
    path = Path(__file__).parent.joinpath("data", "io", "npz", "test.npz")
    drag_mime.setUrls([QtCore.QUrl.fromLocalFile(str(path.resolve()))])
    drag_event = QtGui.QDragEnterEvent(
        QtCore.QPoint(0, 0),
        QtCore.Qt.CopyAction,
        drag_mime,
        QtCore.Qt.LeftButton,
        QtCore.Qt.NoModifier,
    )
    view.dragEnterEvent(drag_event)
    assert drag_event.isAccepted()
    drop_event = QtGui.QDropEvent(
        QtCore.QPoint(0, 0),
        QtCore.Qt.CopyAction,
        drag_mime,
        QtCore.Qt.LeftButton,
        QtCore.Qt.NoModifier,
    )
    with qtbot.waitSignal(view.numTabsChanged):
        view.dropEvent(drop_event)
    assert drop_event.isAccepted()
    assert len(view.widgets()) == 2

    dlg = view.actionOpen()
    dlg.show()
    dlg.close()
Exemplo n.º 9
0
def test_merge_tool(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()

    data = rand_data(["A", "B"])
    view.addLaser(Laser(data, info={"Name": "Laser 1", "File Path": "/test/laser1"}))
    data = rand_data(["B", "C"])
    view.addLaser(Laser(data, info={"Name": "Laser 2", "File Path": "/test/laser1"}))

    tool = MergeTool(view.activeWidget())
    view.addTab("Tool", tool)
    qtbot.waitExposed(tool)

    assert tool.list.count() == 1

    # Test load via dialog
    dlg = tool.addLaserDialog()
    assert dlg.comboBoxItems() == ["Laser 2"]
    dlg.accept()

    assert tool.list.count() == 2
    assert [row.offset() == (0, 0) for row in tool.list.rows]
    assert tool.list.rows[0].combo_element.currentText() == "A"
    assert tool.list.rows[1].combo_element.currentText() == "B"

    tool.action_align_horz.trigger()

    assert tool.list.rows[0].offset() == (0, 0)
    assert tool.list.rows[1].offset() == (0, 10)

    tool.action_align_vert.trigger()

    assert tool.list.rows[0].offset() == (0, 0)
    assert tool.list.rows[1].offset() == (10, 0)

    tool.action_align_auto.trigger()

    assert tool.list.rows[0].offset() == (0, 0)
    # Second image position unknown

    tool.apply()
Exemplo n.º 10
0
def test_tool_filter(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()
    view.addLaser(Laser(rand_data(["a"])))
    tool = FilteringTool(view.activeWidget())
    view.addTab("Tool", tool)
    qtbot.waitExposed(tool)

    tool.combo_filter.setCurrentText("Mean")
    tool.combo_filter.activated.emit(0)
    tool.lineedit_fparams[0].setText("3.0")
    tool.lineedit_fparams[1].setText("3.0")
    tool.lineedit_fparams[0].editingFinished.emit()
    assert np.all(tool.fparams == [3.0, 3.0])
    assert tool.isComplete()

    tool.lineedit_fparams[0].setText("5.0")
    tool.lineedit_fparams[0].editingFinished.emit()
    assert np.all(tool.fparams == [5.0, 3.0])
    assert tool.isComplete()

    tool.combo_filter.setCurrentText("Median")
    tool.combo_filter.activated.emit(0)
    assert np.all(tool.fparams == [5.0, 3.0])
    assert tool.isComplete()

    tool.lineedit_fparams[0].setText("4.0")
    tool.lineedit_fparams[0].editingFinished.emit()
    assert not tool.isComplete()

    tool.combo_filter.setCurrentText("Mean")
    tool.combo_filter.activated.emit(0)
    assert np.all(tool.fparams == [5.0, 3.0])
    assert tool.isComplete()

    tool.apply()
Exemplo n.º 11
0
def test_tool_widget(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()

    view = viewspace.activeView()
    widget = view.addLaser(Laser(rand_data("A1"), info={"Name": "Widget"}))
    tool = ToolWidget(widget, apply_all=True)
    index = widget.index

    widget.view.removeTab(index)
    widget.view.insertTab(index, "Tool", tool)
    qtbot.waitExposed(tool)

    tool.requestClose()
    view.tabs.tabText(index) == "Widget"

    with qtbot.wait_signal(tool.applyPressed):
        button = tool.button_box.button(QtWidgets.QDialogButtonBox.Apply)
        button.click()

    with qtbot.wait_signal(tool.applyPressed):
        button = tool.button_box.button(QtWidgets.QDialogButtonBox.Ok)
        button.click()
Exemplo n.º 12
0
def test_overlay_tool(qtbot: QtBot):
    data = np.zeros((10, 10), dtype=[("r", float), ("g", float), ("b", float)])
    data["r"][:, :] = 1.0
    data["g"][:10, :] = 1.0
    data["b"][:, :10] = 1.0

    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    viewspace.show()
    view = viewspace.activeView()
    view.addLaser(
        Laser(data,
              info={
                  "Name": "real",
                  "File Path": "/home/pewpew/real.npz"
              }))
    tool = OverlayTool(view.activeWidget())
    view.addTab("Tool", tool)
    qtbot.waitExposed(tool)

    # Test rgb mode
    assert tool.rows.color_model == "rgb"
    tool.comboAdd(1)  # r
    assert np.all(tool.graphics.data[0, 0] == (255 << 24) + (255 << 16))

    tool.comboAdd(2)  # g
    assert np.all(tool.graphics.data[:10] == (255 << 24) + (255 << 16) +
                  (255 << 8))
    assert np.all(tool.graphics.data[10:] == (255 << 24) + (255 << 16))

    tool.comboAdd(3)  # g
    assert np.all(tool.graphics.data[:10, :10] == (255 << 24) + (255 << 16) +
                  (255 << 8) + 255)
    assert np.all(tool.graphics.data[10:, :10] == (255 << 24) + (255 << 16))
    assert np.all(tool.graphics.data[10:, 10:] == (255 << 24) + (255 << 16) +
                  (255 << 8))
    assert np.all(tool.graphics.data[10:, 10:] == (255 << 24) + (255 << 16))

    # Test cmyk mode
    tool.radio_cmyk.toggle()
    assert tool.rows.color_model == "cmyk"
    assert np.all(tool.graphics.data[:10, :10] == (255 << 24))
    assert np.all(tool.graphics.data[10:, :10] == (255 << 24) + (255 << 8))
    assert np.all(tool.graphics.data[10:, 10:] == (255 << 25) + 255)
    assert np.all(tool.graphics.data[10:,
                                     10:] == (255 << 24) + (255 << 8) + 255)

    # Check that the rows are limited to 3
    assert tool.rows.max_rows == 3
    assert not tool.combo_add.isEnabled()
    assert tool.rows.rowCount() == 3
    with qtbot.assert_not_emitted(tool.rows.rowsChanged):
        tool.addRow("r")
    assert tool.rows.rowCount() == 3

    # Check color buttons are not enabled
    for row in tool.rows.rows:
        assert not row.button_color.isEnabled()

    # Test any mode
    tool.radio_custom.toggle()
    assert tool.rows.color_model == "any"
    assert tool.combo_add.isEnabled()
    assert tool.check_normalise.isEnabled()
    for row in tool.rows.rows:
        assert row.button_color.isEnabled()
    tool.addRow("g")
    tool.rows[3].setColor(QtGui.QColor.fromRgbF(0.0, 1.0, 1.0))
    assert tool.rows.rowCount() == 4

    # Test normalise
    assert np.amin(tool.graphics.data) > (255 << 24)
    tool.check_normalise.setChecked(True)
    tool.refresh()
    assert tool.graphics.data.min() == (255 << 24) + (255 << 8) + 255  # No red
    tool.check_normalise.setChecked(False)

    # Test export
    dlg = tool.openExportDialog()

    dlg2 = dlg.selectDirectory()
    dlg2.close()

    with tempfile.NamedTemporaryFile() as tf:
        dlg.export(Path(tf.name))
        assert Path(tf.name).exists()

    with tempfile.TemporaryDirectory() as td:
        dlg.lineedit_directory.setText(td)
        dlg.lineedit_filename.setText("test.png")
        dlg.check_individual.setChecked(True)
        dlg.accept()
        qtbot.wait(300)
        assert Path(td).joinpath("test_1.png").exists()
        assert Path(td).joinpath("test_2.png").exists()
        assert Path(td).joinpath("test_3.png").exists()

    # Test close
    with qtbot.wait_signal(tool.rows.rowsChanged):
        tool.rows.rows[-1].close()
    assert tool.rows.rowCount() == 3

    # Test hide
    tool.radio_rgb.toggle()
    with qtbot.wait_signal(tool.rows.rows[0].itemChanged):
        tool.rows.rows[0].button_hide.click()
    assert np.all(tool.graphics.data <= ((255 << 24) + (255 << 8) + 255))

    dlg = tool.rows[0].selectColor()
    dlg.close()
Exemplo n.º 13
0
class MainWindow(QtWidgets.QMainWindow):
    """Pewpew mainwindow, holding a LaserViewSpace.
    Actions for the menu and status bars are created and stored here.
    """
    def __init__(self, parent: QtWidgets.QWidget = None):
        super().__init__(parent)
        self.resize(1280, 800)

        self.log = LoggingDialog()
        self.help = HelpDialog()

        self.viewspace = LaserViewSpace()
        self.viewspace.numTabsChanged.connect(self.updateActionAvailablity)
        self.viewspace.activeViewChanged.connect(self.updateActionAvailablity)
        self.setCentralWidget(self.viewspace)

        self.createActions()
        self.createMenus()
        self.statusBar().showMessage(f"Welcome to pew² version {__version__}.")
        self.button_status_um = QtWidgets.QRadioButton("μ")
        self.button_status_index = QtWidgets.QRadioButton("i")
        self.button_status_um.setChecked(True)
        self.button_status_um.toggled.connect(self.buttonStatusUnit)
        self.button_status_index.toggled.connect(self.buttonStatusUnit)
        self.statusBar().addPermanentWidget(self.button_status_um)
        self.statusBar().addPermanentWidget(self.button_status_index)

        self.updateActionAvailablity()

    def createActions(self) -> None:
        self.action_about = qAction("help-about", "&About", "About pew².",
                                    self.actionAbout)
        self.action_help = qAction("help-contents", "&Help",
                                   "Show the help contents.", self.actionHelp)
        self.action_colortable_range = qAction(
            "",
            "Set &Range",
            "Set the range of the colortable.",
            self.viewspace.colortableRangeDialog,
        )
        self.action_colortable_range.setShortcut("Ctrl+R")
        self.action_config = qAction(
            "document-edit",
            "Default Config",
            "Edit the default config.",
            self.viewspace.configDialog,
        )
        self.action_config.setShortcut("Ctrl+K")

        self.action_exit = qAction("application-exit", "Quit",
                                   "Exit the program.", self.close)
        self.action_exit.setShortcut("Ctrl+Shift+Q")

        self.action_export_all = qAction(
            "document-save-all",
            "E&xport All",
            "Export all open documents.",
            self.actionExportAll,
        )
        self.action_export_all.setShortcut("Ctrl+Shift+X")

        self.action_fontsize = qAction(
            "insert-text",
            "Fontsize",
            "Set the font size in points.",
            self.viewspace.fontsizeDialog,
        )
        self.action_group_colortable = qActionGroup(
            self,
            list(self.viewspace.options.colortables.keys()),
            self.actionGroupColortable,
            checked=self.viewspace.options.colortable,
            statuses=list(self.viewspace.options.colortables.values()),
        )
        self.action_smooth = qAction(
            "smooth",
            "&Smooth",
            "Smooth images with bilinear interpolation.",
            self.viewspace.toggleSmooth,
        )
        self.action_smooth.setCheckable(True)
        self.action_smooth.setChecked(self.viewspace.options.smoothing)
        self.action_wizard_import = qAction(
            "",
            "Import Wizard",
            "Start the line-wise import wizard. .",
            self.actionWizardImport,
        )
        self.action_wizard_spot = qAction(
            "",
            "Spotwise Wizard",
            "Start the import wizard for data collected spot-wise.",
            self.actionWizardSpot,
        )
        self.action_wizard_srr = qAction(
            "",
            "Kriss Kross Wizard",
            "Start the Super-Resolution-Reconstruction import wizard.",
            self.actionWizardSRR,
        )
        self.action_log = qAction("clock", "&Show Log",
                                  "Show the pew² event and error log.",
                                  self.actionLog)
        self.action_open = qAction("document-open", "&Open",
                                   "Open new document(s).", self.actionOpen)
        self.action_open.setShortcut("Ctrl+O")

        self.action_toggle_calibrate = qAction(
            "go-top",
            "Ca&librate",
            "Toggle calibration.",
            self.viewspace.toggleCalibrate,
        )
        self.action_toggle_calibrate.setShortcut("Ctrl+L")
        self.action_toggle_calibrate.setCheckable(True)
        self.action_toggle_calibrate.setChecked(
            self.viewspace.options.calibrate)
        self.action_toggle_colorbar = qAction(
            "", "Show Colorbar", "Toggle colorbars.",
            self.viewspace.setColorbarVisible)
        self.action_toggle_colorbar.setCheckable(True)
        self.action_toggle_colorbar.setChecked(
            self.viewspace.options.items["colorbar"])
        self.action_toggle_label = qAction("", "Show Labels",
                                           "Toggle element labels.",
                                           self.viewspace.setLabelVisible)
        self.action_toggle_label.setCheckable(True)
        self.action_toggle_label.setChecked(
            self.viewspace.options.items["label"])
        self.action_toggle_scalebar = qAction(
            "", "Show Scalebar", "Toggle scalebar.",
            self.viewspace.setScalebarVisible)
        self.action_toggle_scalebar.setCheckable(True)
        self.action_toggle_scalebar.setChecked(
            self.viewspace.options.items["scalebar"])
        self.action_tool_calculator = qAction(
            "document-properties",
            "Calculator",
            "Open the calculator.",
            self.actionToolCalculator,
        )
        self.action_tool_drift = qAction(
            "document-properties",
            "Drift Compensation",
            "Open the drift compensation tool.",
            self.actionToolDrift,
        )
        self.action_tool_filter = qAction(
            "document-properties",
            "Filtering",
            "Open the filtering tool.",
            self.actionToolFilter,
        )
        self.action_tool_merge = qAction(
            "align-vertical-top",
            "Merge Tool",
            "Open tool for merging multiple images.",
            self.actionToolMerge,
        )
        self.action_tool_standards = qAction(
            "document-properties",
            "Calibration Standards",
            "Open the standards calibration tool.",
            self.actionToolStandards,
        )
        self.action_tool_overlay = qAction(
            "document-properties",
            "Image Overlay",
            "Open the overlay tool.",
            self.actionToolOverlay,
        )

        self.action_transform_flip_horizontal = qAction(
            "object-flip-horizontal",
            "Flip Horizontal",
            "Flip the image about vertical axis.",
            self.actionTransformFlipHorz,
        )
        self.action_transform_flip_vertical = qAction(
            "object-flip-vertical",
            "Flip Vertical",
            "Flip the image about horizontal axis.",
            self.actionTransformFlipVert,
        )
        self.action_transform_rotate_left = qAction(
            "object-rotate-left",
            "Rotate Left",
            "Rotate the image 90° counter clockwise.",
            self.actionTransformRotateLeft,
        )
        self.action_transform_rotate_right = qAction(
            "object-rotate-right",
            "Rotate Right",
            "Rotate the image 90° clockwise.",
            self.actionTransformRotateRight,
        )

        self.action_refresh = qAction("view-refresh", "Refresh",
                                      "Redraw documents.",
                                      self.viewspace.refresh)
        self.action_refresh.setShortcut("F5")

    def actionAbout(self) -> QtWidgets.QDialog:
        dlg = QtWidgets.QMessageBox(
            QtWidgets.QMessageBox.Information,
            "About pew²",
            ("Import, process and export of LA-ICP-MS data.\n"
             f"Version {__version__}\n"
             "Developed by the Atomic Medicine Initiative.\n"
             "https://github.com/djdt/pewpew"),
            parent=self,
        )
        if self.windowIcon() is not None:
            dlg.setIconPixmap(self.windowIcon().pixmap(64, 64))
        dlg.open()
        return dlg

    def actionExportAll(self) -> QtWidgets.QDialog:
        widgets = [
            w for v in self.viewspace.views for w in v.widgets()
            if isinstance(w, LaserWidget)
        ]
        dlg = ExportAllDialog(widgets, self)
        dlg.open()
        return dlg

    def actionGroupColortable(self, action: QtWidgets.QAction) -> None:
        text = action.text().replace("&", "")
        self.viewspace.options.colortable = text
        self.viewspace.refresh()

    def actionHelp(self) -> None:
        self.help.show()

    def actionLog(self) -> None:
        self.log.show()

    def actionOpen(self) -> QtWidgets.QDialog:
        view = self.viewspace.activeView()
        return view.actionOpen()

    def openTool(self, tool: ToolWidget, name: str) -> None:
        widget = self.viewspace.activeWidget()
        if widget is None:
            return
        index = widget.index
        if isinstance(widget, ToolWidget):
            widget = widget.widget
        tool = tool(widget)
        name = f"{name}: {widget.laserName()}"
        widget.view.removeTab(index)
        widget.view.insertTab(index, name, tool)
        tool.activate()

    def actionToolCalculator(self) -> None:
        self.openTool(CalculatorTool, "Calculator")

    def actionToolDrift(self) -> None:
        self.openTool(DriftTool, "Drift")

    def actionToolFilter(self) -> None:
        self.openTool(FilteringTool, "Filter")

    def actionToolMerge(self) -> None:
        self.openTool(MergeTool, "Merge")

    def actionToolStandards(self) -> None:
        self.openTool(StandardsTool, "Standards")

    def actionToolOverlay(self) -> None:
        self.openTool(OverlayTool, "Overlay")

    def actionTransformFlipHorz(self) -> None:
        widget = self.viewspace.activeWidget()
        if widget is None:
            return
        widget.transform(flip="horizontal")

    def actionTransformFlipVert(self) -> None:
        widget = self.viewspace.activeWidget()
        if widget is None:
            return
        widget.transform(flip="vertical")

    def actionTransformRotateLeft(self) -> None:
        widget = self.viewspace.activeWidget()
        if widget is None:
            return
        widget.transform(rotate="left")

    def actionTransformRotateRight(self) -> None:
        widget = self.viewspace.activeWidget()
        if widget is None:
            return
        widget.transform(rotate="right")

    def actionWizardImport(self) -> QtWidgets.QWizard:
        wiz = ImportWizard(config=self.viewspace.config, parent=self)
        wiz.laserImported.connect(self.viewspace.activeView().addLaser)
        wiz.open()
        return wiz

    def actionWizardSpot(self) -> QtWidgets.QWizard:
        config = SpotConfig(self.viewspace.config.spotsize,
                            self.viewspace.config.spotsize)
        wiz = SpotImportWizard(config=config,
                               options=self.viewspace.options,
                               parent=self)
        wiz.laserImported.connect(self.viewspace.activeView().addLaser)
        wiz.open()
        return wiz

    def actionWizardSRR(self) -> QtWidgets.QWizard:
        wiz = SRRImportWizard(config=self.viewspace.config, parent=self)
        wiz.laserImported.connect(self.viewspace.activeView().addLaser)
        wiz.open()
        return wiz

    def createMenus(self) -> None:
        # File
        menu_file = self.menuBar().addMenu("&File")
        menu_file.addAction(self.action_open)
        # File -> Import
        menu_import = menu_file.addMenu("&Import")
        menu_import.addAction(self.action_wizard_import)
        menu_import.addAction(self.action_wizard_spot)
        menu_import.addAction(self.action_wizard_srr)

        menu_file.addSeparator()

        menu_file.addAction(self.action_export_all)

        menu_file.addSeparator()

        menu_file.addAction(self.action_exit)

        # Edit
        menu_edit = self.menuBar().addMenu("&Edit")
        menu_edit.addAction(self.action_config)
        menu_edit.addAction(self.action_toggle_calibrate)

        menu_edit.addSeparator()

        menu_edit.addAction(self.action_transform_flip_horizontal)
        menu_edit.addAction(self.action_transform_flip_vertical)
        menu_edit.addAction(self.action_transform_rotate_left)
        menu_edit.addAction(self.action_transform_rotate_right)

        menu_tools = self.menuBar().addMenu("&Tools")
        menu_tools.addAction(self.action_tool_calculator)
        menu_tools.addAction(self.action_tool_drift)
        menu_tools.addAction(self.action_tool_filter)
        menu_tools.addAction(self.action_tool_merge)
        menu_tools.addAction(self.action_tool_standards)
        menu_tools.addAction(self.action_tool_overlay)

        # View
        menu_view = self.menuBar().addMenu("&View")
        menu_cmap = menu_view.addMenu("&Colortable")
        menu_cmap.setIcon(QtGui.QIcon.fromTheme("color-management"))
        menu_cmap.setStatusTip("Colortable of displayed images.")
        menu_cmap.addActions(self.action_group_colortable.actions())
        menu_cmap.addAction(self.action_colortable_range)

        # View - interpolation
        menu_view.addAction(self.action_smooth)

        menu_view.addAction(self.action_fontsize)

        menu_view.addSeparator()

        menu_view.addAction(self.action_toggle_colorbar)
        menu_view.addAction(self.action_toggle_label)
        menu_view.addAction(self.action_toggle_scalebar)

        menu_view.addSeparator()

        menu_view.addAction(self.action_refresh)

        # Help
        menu_help = self.menuBar().addMenu("&Help")
        menu_help.addAction(self.action_log)
        menu_help.addAction(self.action_help)
        menu_help.addAction(self.action_about)

    def buttonStatusUnit(self, toggled: bool) -> None:
        """Callback for 'button_status_um'."""
        if self.button_status_um.isChecked():
            self.viewspace.options.units = "μm"
        elif self.button_status_index.isChecked():
            self.viewspace.options.units = "index"

    def updateActionAvailablity(self) -> None:
        """Enables tools if at least one view is present."""
        enabled = self.viewspace.countViewTabs() > 0
        self.action_export_all.setEnabled(enabled)

        # Tools require an active view
        enabled = enabled and self.viewspace.activeView().tabs.count() > 0

        self.action_tool_calculator.setEnabled(enabled)
        self.action_tool_drift.setEnabled(enabled)
        self.action_tool_filter.setEnabled(enabled)
        self.action_tool_standards.setEnabled(enabled)
        self.action_tool_overlay.setEnabled(enabled)

    def exceptHook(self, etype: type, value: BaseException,
                   tb: TracebackType) -> None:  # pragma: no cover
        """Redirect errors to the log."""
        if etype == KeyboardInterrupt:
            logger.info("Keyboard interrupt, exiting.")
            sys.exit(1)
        logger.exception("Uncaught exception", exc_info=(etype, value, tb))
        QtWidgets.QMessageBox.critical(self, "Uncaught Exception", str(value))
Exemplo n.º 14
0
def test_export_dialog(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    view = viewspace.activeView()

    widget = view.addLaser(
        Laser(rand_data("A1"),
              info={
                  "Name": "laser",
                  "File Path": "/home/user/laser.npz"
              }))
    dlg = ExportDialog(widget)
    dlg.open()

    assert dlg.lineedit_directory.text() == "/home/user"
    assert dlg.lineedit_filename.text() == "laser.npz"
    assert dlg.lineedit_preview.text() == "laser.npz"
    assert dlg.options.currentExt() == ".npz"

    assert not dlg.check_export_all.isEnabled()
    assert not dlg.check_calibrate.isEnabled()

    dlg.lineedit_filename.setText("laser.png")
    assert dlg.lineedit_preview.text() == "laser.png"
    assert dlg.options.currentExt() == ".png"

    dlg.lineedit_filename.setText("laser")
    assert dlg.lineedit_preview.text() == "laser.png"
    assert dlg.options.currentExt() == ".png"

    dlg.check_export_all.click()
    assert dlg.lineedit_preview.text() == "laser_<element>.png"

    dlg.lineedit_filename.setText("laser.npz")
    assert dlg.options.currentExt() == ".npz"
    assert not dlg.check_export_all.isEnabled()
    assert dlg.lineedit_preview.text() == "laser.npz"

    dlg.lineedit_filename.setText("laser.abc")
    assert not dlg.isComplete()
    dlg.lineedit_filename.setText("laser.npz")

    dir_dlg = dlg.selectDirectory()
    dir_dlg.close()

    dlg.lineedit_filename.setText("/fake/directory")
    assert not dlg.isComplete()

    with tempfile.TemporaryDirectory() as tempdir:
        # Test export
        dlg.lineedit_directory.setText(tempdir)
        dlg.lineedit_filename.setText("temp.npz")
        paths = dlg.generatePaths(dlg.widget)
        assert paths == [(Path(tempdir, "temp.npz"), "A1", None)]
        dlg.export(paths[0][0], paths[0][1], None, dlg.widget)
        assert Path(tempdir, "temp.npz").exists()
        # Test export all elements and png

    with tempfile.TemporaryDirectory() as tempdir:
        dlg.lineedit_directory.setText(tempdir)
        dlg.lineedit_filename.setText("temp.png")
        dlg.accept()
        assert Path(tempdir, "temp_A1.png").exists()

    with tempfile.TemporaryDirectory() as tempdir:
        dlg.lineedit_directory.setText(tempdir)
        dlg.lineedit_filename.setText("temp.vti")
        dlg.accept()
        assert Path(tempdir, "temp.vti").exists()

    dlg.close()
Exemplo n.º 15
0
def test_export_all_dialog(qtbot: QtBot):
    viewspace = LaserViewSpace()
    qtbot.addWidget(viewspace)
    view = viewspace.activeView()

    lasers = [
        Laser(
            rand_data("A1"),
            info={
                "Name": "laser1",
                "File Path": "/fake/directory/laser1.npz"
            },
        ),
        Laser(
            rand_data("B2"),
            info={
                "Name": "laser2",
                "File Path": "/fake/directory/laser2.npz"
            },
        ),
        Laser(
            rand_data("C3"),
            info={
                "Name": "laser3",
                "File Path": "/fake/directory/laser3.npz"
            },
        ),
        Laser(
            rand_data(["B2", "C3"]),
            info={
                "Name": "laser4",
                "File Path": "/fake/directory/laser4.npz"
            },
        ),
    ]
    widgets = [view.addLaser(laser) for laser in lasers]

    dlg = ExportAllDialog(widgets)
    dlg.open()

    assert dlg.lineedit_directory.text() == "/fake/directory"
    assert dlg.lineedit_filename.text() == "<name>.npz"
    assert dlg.lineedit_preview.text() == "<name>.npz"
    assert not dlg.isComplete()

    dlg.lineedit_prefix.setText("01")

    assert dlg.lineedit_preview.text() == "01_<name>.npz"
    assert not dlg.check_export_all.isEnabled()
    assert not dlg.check_calibrate.isEnabled()
    assert not dlg.combo_element.isEnabled()

    dlg.options.setCurrentIndex(dlg.options.indexForExt(".csv"))
    assert dlg.lineedit_preview.text() == "01_<name>.csv"
    assert dlg.check_export_all.isEnabled()
    assert dlg.check_calibrate.isEnabled()
    assert dlg.combo_element.isEnabled()

    dlg.check_export_all.click()
    assert dlg.lineedit_preview.text() == "01_<name>_<element>.csv"
    assert not dlg.combo_element.isEnabled()

    with tempfile.TemporaryDirectory() as tempdir:
        dlg.lineedit_directory.setText(tempdir)
        assert dlg.isComplete()
        dlg.accept()

        assert Path(tempdir, "01_laser1_A1.csv").exists()
        assert Path(tempdir, "01_laser2_B2.csv").exists()
        assert Path(tempdir, "01_laser3_C3.csv").exists()
        assert Path(tempdir, "01_laser4_B2.csv").exists()
        assert Path(tempdir, "01_laser4_C3.csv").exists()