Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ dependencies = [
"matplotlib",
"jsonschema",
"zhinst-toolkit >= 0.3.3",
"pyqt5",
"qtpy",
"pyside6",
"versioningit ~= 2.2.0",
]

Expand Down Expand Up @@ -59,7 +60,6 @@ build-file = "qumada/_version.py"
addopts = [
"--import-mode=importlib",
]
qt_api = "pyqt5"

[tool.coverage.run]
source = [
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/mapping_gui_test.ipy
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ script.setup(


# %%
from PyQt5.QtWidgets import QApplication
from qtpy.QtWidgets import QApplication

# %%
print(QApplication.instance())
Expand Down
42 changes: 26 additions & 16 deletions src/qumada/instrument/mapping/mapping_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@
from collections.abc import Iterable, Mapping
from typing import Any

from PyQt5.QtCore import QItemSelectionModel, Qt, QTimer, pyqtSignal, pyqtSlot
from PyQt5.QtGui import (
from qcodes.instrument.channel import InstrumentModule
from qcodes.instrument.instrument import Instrument
from qcodes.instrument.parameter import Parameter
from qcodes.utils.metadata import Metadatable
from qtpy.QtCore import QItemSelectionModel, Qt, QTimer
from qtpy.QtCore import Signal as pyqtSignal
from qtpy.QtCore import Slot as pyqtSlot
from qtpy.QtGui import (
QBrush,
QColor,
QDropEvent,
Expand All @@ -34,10 +40,9 @@
QStandardItem,
QStandardItemModel,
)
from PyQt5.QtWidgets import (
from qtpy.QtWidgets import (
QAction,
QApplication,
QDesktopWidget,
QHBoxLayout,
QInputDialog,
QLabel,
Expand All @@ -51,10 +56,6 @@
QVBoxLayout,
QWidget,
)
from qcodes.instrument.channel import InstrumentModule
from qcodes.instrument.instrument import Instrument
from qcodes.instrument.parameter import Parameter
from qcodes.utils.metadata import Metadatable

from qumada.instrument.mapping.base import TerminalParameters, filter_flatten_parameters
from qumada.metadata import Metadata
Expand Down Expand Up @@ -203,7 +204,8 @@ def update_tree(self):

def import_data(self, terminal_parameters: TerminalParameters) -> None:
"""Build up tree with provided terminal parameters."""
root = self.model().invisibleRootItem()
model = self.model()
root = model.invisibleRootItem()
self.terminal_parameters = terminal_parameters
for terminal_name, terminal_params in terminal_parameters.items():
item = QStandardItem(terminal_name)
Expand All @@ -221,13 +223,18 @@ def import_data(self, terminal_parameters: TerminalParameters) -> None:
item.appendRow(subitem)

qidx = item.index()
self.model().setData(qidx.siblingAtColumn(1), QBrush(RED), Qt.BackgroundRole)
self.model().insertColumn(1, qidx)
self.model().insertColumn(2, qidx)
model.setData(qidx.siblingAtColumn(1), QBrush(RED), Qt.BackgroundRole)
model.insertColumn(1, qidx)
model.insertColumn(2, qidx)

for i in range(len(terminal_params.keys())):
self.model().setData(qidx.child(i, 1), "")
self.model().setData(qidx.child(i, 1), QBrush(RED), Qt.BackgroundRole)
self.model().setData(qidx.child(i, 2), "")
# this is written with questionable LLM support
idx1 = model.index(i, 1, qidx)
idx2 = model.index(i, 2, qidx)

self.model().setData(idx1, "")
self.model().setData(idx1, QBrush(RED), Qt.BackgroundRole)
self.model().setData(idx2, "")

self.setColumnHidden(2, not self.monitoring_enable)

Expand Down Expand Up @@ -590,7 +597,10 @@ def __init__(
idx = self.terminal_tree.model().invisibleRootItem().child(0, 0).index()
self.terminal_tree.selectionModel().select(idx, QItemSelectionModel.SelectionFlag.ClearAndSelect)
self.terminal_tree.setCurrentIndex(idx)
self.resize(QDesktopWidget().availableGeometry(self).size() * 0.45)

screen = self.screen() or QApplication.primaryScreen()
new_size = screen.availableGeometry().size() * 0.45
self.resize(new_size)

self.terminal_parameters = terminal_parameters

Expand Down
8 changes: 4 additions & 4 deletions src/qumada/utils/device_GUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import sys
import threading

from PyQt5.QtCore import (
from qtpy.QtCore import (
Q_ARG,
QMetaObject,
QObject,
Qt,
QThread,
QTimer,
pyqtSignal,
pyqtSlot,
)
from PyQt5.QtWidgets import (
from qtpy.QtCore import Signal as pyqtSignal
from qtpy.QtCore import Slot as pyqtSlot
from qtpy.QtWidgets import (
QApplication,
QLabel,
QPushButton,
Expand Down
88 changes: 88 additions & 0 deletions src/tests/device_gui_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""
This test was generated by an LLM.

It does not test actual functionality. It just tests that the module device_GUI.py can be imported and that some code
paths don't raise an exception.
"""

import multiprocessing as mp

import pytest
from qtpy.QtCore import QObject
from qtpy.QtCore import Signal as pyqtSignal

import qumada.utils.device_GUI as device_GUI


class DummyWorker(QObject):
"""Deterministic replacement for device_GUI.Worker used in most tests."""

data_ready = pyqtSignal(list)

def __init__(self, interval, parameters):
super().__init__()
self.interval = interval
self.parameters = parameters
self.running = True

def stop(self):
self.running = False


@pytest.fixture
def parameters():
p1 = device_GUI.Parent("P1")
p2 = device_GUI.Parent("P2")
return [
device_GUI.Parameter("A", p1),
device_GUI.Parameter("B", p1),
device_GUI.Parameter("C", p2),
]


@pytest.fixture
def gui(qtbot, parameters, monkeypatch):
# Replace Worker with a deterministic stub
monkeypatch.setattr(device_GUI, "Worker", DummyWorker)
q = mp.Queue()
w = device_GUI.MeasurementGUI(parameters, q)
qtbot.addWidget(w)
# keep intervals small if anything uses it
w.interval_spinbox.setValue(120)
return w


def _col_texts(table, col):
return [(table.item(r, col).text() if table.item(r, col) is not None else "") for r in range(table.rowCount())]


def test_initial_table_population(gui, parameters):
assert gui.table.rowCount() == len(parameters)
# Column 0: names "Param: <parent> <name>"
names = _col_texts(gui.table, 0)
assert names == [f"Param: {p._parent.name} {p.name}" for p in parameters]
# Column 1: initial values from __call__() -> "42"
values = _col_texts(gui.table, 1)
assert values == ["42"] * len(parameters)


def test_update_interval_changes_worker_field(gui, qtbot):
# Initial value is 120 from fixture
assert gui.worker.interval == 120
gui.interval_spinbox.setValue(333)
# Signal is connected in initUI; the slot sets worker.interval
assert gui.worker.interval == 333


def test_close_event_stops_worker_and_puts_quit(gui, qtbot):
q = gui.data_queue
# Thread should be running before close
assert gui.worker_thread.isRunning()
gui.close()

# After close, thread should be stopped
assert not gui.worker_thread.isRunning()
# Worker stop() should have been called
assert gui.worker.running is False
# Queue should receive "QUIT"
assert q.get(timeout=1) == "QUIT"
4 changes: 2 additions & 2 deletions src/tests/mapping_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@

import pytest
from jsonschema import ValidationError
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMessageBox
from pytest_cases import fixture_ref, parametrize
from pytest_mock import MockerFixture
from qcodes.instrument_drivers.mock_instruments import (
DummyChannelInstrument,
DummyInstrument,
)
from qcodes.station import Station
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QApplication, QMessageBox

import qumada.instrument.mapping as mapping
from qumada.instrument.custom_drivers.Dummies.dummy_dac import DummyDac
Expand Down