Skip to content

Commit

Permalink
Merge pull request #279 from jenshnielsen/pyqtstubs_fix
Browse files Browse the repository at this point in the history
Fix typechecking with latest PyQt5-stubs
  • Loading branch information
jenshnielsen committed May 3, 2022
2 parents 8df6c61 + 8157946 commit a73915d
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 63 deletions.
20 changes: 12 additions & 8 deletions plottr/apps/inspectr.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ def updateDates(self, dates: Sequence[str]) -> None:

i = 0
while i < self.count():
if self.item(i).text() not in dates:
elem = self.item(i)
if elem is not None and elem.text() not in dates:
item = self.takeItem(i)
del item
else:
Expand Down Expand Up @@ -116,7 +117,7 @@ class SortableTreeWidgetItem(QtWidgets.QTreeWidgetItem):
def __init__(self, strings: Iterable[str]):
super().__init__(strings)

def __lt__(self, other: "SortableTreeWidgetItem") -> bool:
def __lt__(self, other: QtWidgets.QTreeWidgetItem) -> bool:
col = self.treeWidget().sortColumn()
text1 = self.text(col)
text2 = other.text(col)
Expand Down Expand Up @@ -151,20 +152,23 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None):
def showContextMenu(self, position: QtCore.QPoint) -> None:
model_index = self.indexAt(position)
item = self.itemFromIndex(model_index)
assert item is not None
current_tag_char = item.text(1)

menu = QtWidgets.QMenu()

copy_icon = self.style().standardIcon(QtWidgets.QStyle.SP_DialogSaveButton)
copy_action = menu.addAction(copy_icon, "Copy")

star_action = self.window().starAction
star_action.setText('Star' if current_tag_char != self.tag_dict['star'] else 'Unstar')
menu.addAction(star_action)
window = cast(QCodesDBInspector, self.window())
starAction: QtWidgets.QAction = window.starAction # type: ignore[has-type]

cross_action = self.window().crossAction
cross_action.setText('Cross' if current_tag_char != self.tag_dict['cross'] else 'Uncross')
menu.addAction(cross_action)
starAction.setText('Star' if current_tag_char != self.tag_dict['star'] else 'Unstar')
menu.addAction(starAction)

crossAction: QtWidgets.QAction = window.crossAction # type: ignore[has-type]
crossAction.setText('Cross' if current_tag_char != self.tag_dict['cross'] else 'Uncross')
menu.addAction(crossAction)

action = menu.exec_(self.mapToGlobal(position))
if action == copy_action:
Expand Down
7 changes: 4 additions & 3 deletions plottr/apps/ui/monitr.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ def finditem(parent: Union["DataFileList", QtWidgets.QTreeWidgetItem], name: str

item = None
for item_ in existingItems:
if item_.text(0) == name:
item = item_
break
if item_ is not None:
if item_.text(0) == name:
item = item_
break
return item

def itemPath(self, item: QtWidgets.QTreeWidgetItem) -> str:
Expand Down
2 changes: 1 addition & 1 deletion plottr/data/qcodes_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def pathAndId(self) -> Tuple[Optional[str], Optional[int]]:
return self._pathAndId

# see https://github.com/python/mypy/issues/1362
@pathAndId.setter # type: ignore
@pathAndId.setter # type: ignore[misc]
@updateOption('pathAndId')
def pathAndId(self, val: Tuple[Optional[str], Optional[int]]) -> None:
if val != self.pathAndId:
Expand Down
7 changes: 4 additions & 3 deletions plottr/gui/data_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ def setShape(self, shape: Dict[str, Tuple[int, ...]]) -> None:
"""Set shapes of given elements"""
for i in range(self.topLevelItemCount()):
item = self.topLevelItem(i)
name = item.text(0)
if name in shape:
item.setText(2, str(shape[name]))
if item is not None:
name = item.text(0)
if name in shape:
item.setText(2, str(shape[name]))

def clear(self) -> None:
"""Clear the tree, and make sure all selections are cleared."""
Expand Down
19 changes: 11 additions & 8 deletions plottr/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Common GUI widgets that are re-used across plottr.
"""
from typing import Union, List, Tuple, Optional, Type, Sequence, Dict, Any, Type
from typing import Union, List, Tuple, Optional, Sequence, Dict, Any, Type, Generic, TypeVar

from .tools import dictToTreeWidgetItems, dpiScalingFactor
from plottr import QtGui, QtCore, Flowchart, QtWidgets, Signal, Slot
Expand All @@ -15,20 +15,22 @@
__author__ = 'Wolfgang Pfaff'
__license__ = 'MIT'

ElementType = TypeVar("ElementType", bound=QtWidgets.QWidget)

class FormLayoutWrapper(QtWidgets.QWidget):

class FormLayoutWrapper(QtWidgets.QWidget, Generic[ElementType]):
"""
Simple wrapper widget for forms.
Expects a list of tuples of the form (label, widget),
creates a widget that contains these using a form layout.
Labels have to be unique.
"""

def __init__(self, elements: List[Tuple[str, QtWidgets.QWidget]],
def __init__(self, elements: List[Tuple[str, ElementType]],
parent: Union[None, QtWidgets.QWidget] = None):
super().__init__(parent)

self.elements = {}
self.elements: Dict[str, ElementType] = {}

layout = QtWidgets.QFormLayout()
for lbl, widget in elements:
Expand Down Expand Up @@ -462,10 +464,11 @@ def setSelected(self, selected: List[str]) -> None:
"""
for i in range(self.count()):
item = self.item(i)
if item.text() in selected:
item.setSelected(True)
else:
item.setSelected(False)
if item is not None:
if item.text() in selected:
item.setSelected(True)
else:
item.setSelected(False)

def emitSelection(self) -> None:
self.dimensionSelectionMade.emit(self.getSelected())
Expand Down
16 changes: 10 additions & 6 deletions plottr/node/autonode.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Any, Callable, Optional, Type
from typing import Dict, Any, Callable, Optional, Type, cast

from .. import QtGui, QtWidgets
from .node import Node, NodeWidget, updateOption
Expand Down Expand Up @@ -62,13 +62,15 @@ def addOption(self, name: str, specs: Dict[str, Any], confirm: bool) -> None:
func = self.widgetConnection.get(optionType, None)
if func is not None:
widget = func(self, name, specs, confirm)

self.layout().addRow(name, widget)
layout = cast(QtWidgets.QFormLayout, self.layout())
if widget is not None:
layout.addRow(name, widget)

def addConfirm(self) -> None:
widget = QtWidgets.QPushButton('Confirm')
widget.pressed.connect(self.signalAllOptions)
self.layout().addRow('', widget)
layout = cast(QtWidgets.QFormLayout, self.layout())
layout.addRow('', widget)


class AutoNode(Node):
Expand Down Expand Up @@ -101,6 +103,10 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None,
self.addConfirm()

class AutoNode_(AutoNode):

_uiClass = AutoNodeGui_
useUi = True

def __init__(self, name: str):
super().__init__(name)
for optName, optSpecs in options.items():
Expand All @@ -110,8 +116,6 @@ def __init__(self, name: str):
AutoNode_.nodeName = nodeName
AutoNode_.nodeOptions = options
AutoNode_.process = func # type: ignore[assignment]
AutoNode_.useUi = True
AutoNode_.uiClass = AutoNodeGui_

return AutoNode_

Expand Down
1 change: 0 additions & 1 deletion plottr/node/data_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,3 @@ def setupUi(self) -> None:
assert self.ui is not None
self.newDataStructure.connect(self.ui.setData)
self.dataShapesChanged.connect(self.ui.setShape)

15 changes: 8 additions & 7 deletions plottr/node/dim_reducer.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def setDimInfo(self, dim: str, info: str = '') -> None:
@Slot(dict)
def setDimInfos(self, infos: Dict[str, str]) -> None:
for ax, info in infos.items():
self.setInfo(ax, info)
self.setDimInfo(ax, info)


class DimensionReductionAssignmentWidget(DimensionAssignmentWidget):
Expand Down Expand Up @@ -367,18 +367,19 @@ def setRole(self, dim: str, role: Optional[str] = None, **kw: Any) -> None:
self.setRole(d, 'None')


class DimensionReducerNodeWidget(NodeWidget):
class DimensionReducerNodeWidget(NodeWidget[DimensionReductionAssignmentWidget]):

def __init__(self, node: Optional[Node] = None):
super().__init__(embedWidgetClass=DimensionReductionAssignmentWidget)
# Not clear how to type that self.widget is not None
# iff embedWidgetClass is not None
assert self.widget is not None
self.optSetters = {
'reductions': self.setReductions,
}
self.optGetters = {
'reductions': self.getReductions,
}

self.widget.rolesChanged.connect(
lambda x: self.signalOption('reductions'))

Expand Down Expand Up @@ -640,7 +641,7 @@ def setupUi(self) -> None:
self.newDataStructure.connect(self.ui.setData)


class XYSelectorNodeWidget(NodeWidget):
class XYSelectorNodeWidget(NodeWidget[XYSelectionWidget]):

def __init__(self, node: Optional[Node] = None):
self.icon = get_xySelectIcon()
Expand All @@ -658,10 +659,10 @@ def __init__(self, node: Optional[Node] = None):
lambda x: self.signalOption('dimensionRoles')
)

def getRoles(self) -> Dict[str, str]:
def getRoles(self) -> Dict[str, Union[str, Tuple[ReductionMethod, List[Any], Dict[str, Any]]]]:
assert self.widget is not None
widgetRoles = self.widget.getRoles()
roles = {}
roles: Dict[str, Union[str, Tuple[ReductionMethod, List[Any], Dict[str, Any]]]] = {}
for dimName, rolesOptions in widgetRoles.items():
role = rolesOptions['role']
opts = rolesOptions['options']
Expand All @@ -677,7 +678,7 @@ def getRoles(self) -> Dict[str, str]:
return roles

def setRoles(self, roles: Dict[str, str]) -> None:
assert isinstance(self.widget, XYSelectionWidget)
assert self.widget is not None
# when this is called, we do not want the UI to signal changes.
self.widget.emitRoleChangeSignal = False

Expand Down
38 changes: 22 additions & 16 deletions plottr/node/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

from enum import Enum, unique

from typing import Tuple, Dict, Any, List, Union, Optional, Sequence
from typing import Tuple, Dict, Any, List, Optional, Sequence, cast

from typing_extensions import TypedDict

from plottr import QtGui, Signal, Slot, QtWidgets
from .node import Node, NodeWidget, updateOption, updateGuiFromNode
Expand All @@ -18,10 +20,6 @@
__license__ = 'MIT'


#: Type for additional options when specifying the shape
SpecShapeType = Dict[str, Tuple[Union[str, int], ...]]


@unique
class GridOption(Enum):
"""Options for how to grid data."""
Expand All @@ -38,6 +36,14 @@ class GridOption(Enum):
#: read the shape from DataSet Metadata (if available)
metadataShape = 3

class _WidgetDict(TypedDict):
name: QtWidgets.QComboBox
shape: QtWidgets.QSpinBox

#: Type for additional options when specifying the shape
class SpecShapeType(TypedDict):
order: Tuple[str, ...]
shape: Tuple[int, ...]

class ShapeSpecificationWidget(QtWidgets.QWidget):
"""A widget that allows the user to specify a grid shape.
Expand All @@ -55,7 +61,7 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None):
super().__init__(parent)

self._axes: List[str] = []
self._widgets: Dict[int, Dict[str, QtWidgets.QWidget]] = {}
self._widgets: Dict[int, _WidgetDict] = {}
self._processChanges = True

layout = QtWidgets.QFormLayout()
Expand All @@ -82,7 +88,7 @@ def _addAxis(self, idx: int, name: str) -> None:
'name': nameWidget,
'shape': dimLenWidget,
}
self.layout().insertRow(idx, nameWidget, dimLenWidget)
cast(QtWidgets.QFormLayout, self.layout()).insertRow(idx, nameWidget, dimLenWidget)

nameWidget.currentTextChanged.connect(
lambda x: self._processAxisChange(idx, x)
Expand All @@ -96,11 +102,11 @@ def setAxes(self, axes: List[str]) -> None:
"""
if axes != self._axes:
self._axes = axes

for i in range(self.layout().rowCount() - 1):
layout = cast(QtWidgets.QFormLayout, self.layout())
for i in range(layout.rowCount() - 1):
self._widgets[i]['name'].deleteLater()
self._widgets[i]['shape'].deleteLater()
self.layout().removeRow(0)
layout.removeRow(0)

self._widgets = {}

Expand Down Expand Up @@ -133,7 +139,7 @@ def _processAxisChange(self, idx: int, newName: str) -> None:
self._widgets[prevIdx]['name'].setCurrentText(unused[0])
self._processChanges = True

def setShape(self, shape: Dict[str, Tuple[Union[str, int], ...]]) -> None:
def setShape(self, shape: SpecShapeType) -> None:
""" Set the shape, will be reflected in the values set in the widgets.
:param shape: A dictionary with keys `order` and `shape`. The value
Expand All @@ -148,7 +154,7 @@ def setShape(self, shape: Dict[str, Tuple[Union[str, int], ...]]) -> None:
self._widgets[i]['shape'].setValue(s)
self._processChanges = True

def getShape(self) -> Dict[str, Tuple[Union[str, int], ...]]:
def getShape(self) -> SpecShapeType:
"""get the currently specified shape.
:returns: a dictionary with keys `order` and `shape`.
Expand Down Expand Up @@ -220,7 +226,7 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None):
self.buttons[GridOption.noGrid].setChecked(True)
self.enableShapeEdit(False)

def getGrid(self) -> Tuple[GridOption, Dict[str, Any]]:
def getGrid(self) -> Tuple[GridOption, Optional[SpecShapeType]]:
"""Get grid option from the current widget selections
:returns: the grid specification, and the options that go with it.
Expand All @@ -230,7 +236,7 @@ def getGrid(self) -> Tuple[GridOption, Dict[str, Any]]:
"""
activeBtn = self.btnGroup.checkedButton()
activeId = self.btnGroup.id(activeBtn)
opts = {}
opts: Optional[SpecShapeType] = None

if GridOption(activeId) == GridOption.specifyShape:
opts = self.shapeSpec.getShape()
Expand Down Expand Up @@ -281,7 +287,7 @@ def gridButtonSelected(self, btn: QtWidgets.QAbstractButton, checked: bool) -> N
def shapeSpecified(self) -> None:
self.signalGridOption(self.getGrid())

def signalGridOption(self, grid: Tuple[GridOption, Dict[str, Any]]) -> None:
def signalGridOption(self, grid: Tuple[GridOption, Optional[SpecShapeType]]) -> None:
self.optionSelected.emit(grid)

def setAxes(self, axes: List[str]) -> None:
Expand Down Expand Up @@ -338,7 +344,7 @@ def setShape(self, shape: Dict[str, Tuple[int, ...]]) -> None:
self.widget.setShape(shape)


class DataGridder(Node):
class DataGridder(Node[DataGridderNodeWidget]):
"""
A node that can put data onto or off a grid.
Has one property: :attr:`grid`. Its possible values are governed by a main option,
Expand Down

0 comments on commit a73915d

Please sign in to comment.