Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PySide6 #9586

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Use PySide6 #9586

wants to merge 2 commits into from

Conversation

JHolba
Copy link
Contributor

@JHolba JHolba commented Dec 18, 2024

Upgrade from qtpy with pyqt5 to PySide6.
Using PySide6 directly should give us better typing.

  • PR title captures the intent of the changes, and is fitting for release notes.
  • Added appropriate release note label
  • Commit history is consistent and clean, in line with the contribution guidelines.
  • Make sure unit tests pass locally after every commit (git rebase -i main --exec 'pytest tests/ert/unit_tests -n logical -m "not integration_test"')

When applicable

  • When there are user facing changes: Updated documentation
  • New behavior or changes to existing untested code: Ensured that unit tests are added (See Ground Rules).
  • Large PR: Prepare changes in small commits for more convenient review
  • Bug fix: Add regression test for the bug
  • Bug fix: Create Backport PR to latest release

@JHolba JHolba self-assigned this Dec 18, 2024
@JHolba JHolba marked this pull request as draft December 18, 2024 12:20
@JHolba JHolba added the release-notes:maintenance Automatically categorise as maintenance change in release notes label Dec 18, 2024
@JHolba JHolba force-pushed the use-pyside6 branch 10 times, most recently from be4a6dc to c7090c4 Compare December 18, 2024 13:24
Copy link

codspeed-hq bot commented Dec 18, 2024

CodSpeed Performance Report

Merging #9586 will not alter performance

Comparing JHolba:use-pyside6 (fcb1ddc) with main (d9088eb)

Summary

✅ 22 untouched benchmarks

@JHolba JHolba force-pushed the use-pyside6 branch 2 times, most recently from 429fc1e to a397c37 Compare December 18, 2024 15:01
@jonathan-eq
Copy link
Contributor

It fails locally on MacOS with python3.12, but I got it working by modifying:

@pytest.fixture
def panel_with_localization_on(qtbot: QtBot):
    def func(settings, ensemble_size):
        widget = AnalysisModuleVariablesPanel(settings, ensemble_size)
        qtbot.addWidget(widget)
        widget.show()
        check_box = widget.findChild(QCheckBox, name="localization")
        qtbot.mouseClick(check_box, Qt.MouseButton.LeftButton)
        return settings, widget

    yield func

@jonathan-eq
Copy link
Contributor

When it failed it emitted:

---------------------------------------------------------------------------------------------------------------------------------------- Captured Qt messages ----------------------------------------------------------------------------------------------------------------------------------------
QtWarningMsg: Populating font family aliases took 77 ms. Replace uses of missing font family "Sans Serif" with one that exists to avoid this cost. 
QtWarningMsg: This plugin does not support propagateSizeHints()

@codecov-commenter
Copy link

codecov-commenter commented Dec 19, 2024

Codecov Report

Attention: Patch coverage is 87.39837% with 62 lines in your changes missing coverage. Please review.

Project coverage is 91.84%. Comparing base (e5c7953) to head (fcb1ddc).
Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
.../tools/plot/customize/limits_customization_view.py 30.00% 14 Missing ⚠️
src/ert/gui/tools/plugins/process_job_dialog.py 61.11% 7 Missing ⚠️
.../ert/gui/tools/plot/widgets/clearable_line_edit.py 62.50% 6 Missing ⚠️
src/ert/gui/ertwidgets/searchbox.py 60.00% 4 Missing ⚠️
src/ert/gui/simulation/view/realization.py 82.60% 4 Missing ⚠️
...t/gui/tools/plot/plot_ensemble_selection_widget.py 60.00% 4 Missing ⚠️
src/ert/gui/ertwidgets/listeditbox.py 72.72% 3 Missing ⚠️
src/ert/gui/simulation/run_dialog.py 86.95% 3 Missing ⚠️
src/ert/gui/model/snapshot.py 90.00% 2 Missing ⚠️
src/ert/gui/simulation/view/update.py 77.77% 2 Missing ⚠️
... and 10 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9586      +/-   ##
==========================================
- Coverage   91.86%   91.84%   -0.02%     
==========================================
  Files         433      433              
  Lines       26775    26798      +23     
==========================================
+ Hits        24596    24613      +17     
- Misses       2179     2185       +6     
Flag Coverage Δ
cli-tests 39.72% <0.20%> (-0.03%) ⬇️
everest-models-test 34.54% <0.20%> (-0.03%) ⬇️
gui-tests 72.11% <81.30%> (-0.01%) ⬇️
integration-test 37.17% <0.20%> (-0.02%) ⬇️
performance-tests 51.92% <61.17%> (-0.03%) ⬇️
test 40.66% <0.20%> (+0.23%) ⬆️
unit-tests 74.18% <75.20%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@JHolba
Copy link
Contributor Author

JHolba commented Dec 19, 2024

It fails locally on MacOS with python3.12, but I got it working by modifying:

@pytest.fixture
def panel_with_localization_on(qtbot: QtBot):
    def func(settings, ensemble_size):
        widget = AnalysisModuleVariablesPanel(settings, ensemble_size)
        qtbot.addWidget(widget)
        widget.show()
        check_box = widget.findChild(QCheckBox, name="localization")
        qtbot.mouseClick(check_box, Qt.MouseButton.LeftButton)
        return settings, widget

    yield func

Thank you for the help :)

Upgrade from qtpy with pyqt5 to PySide6.
Using PySide6 directly should give us better typing.
@JHolba JHolba marked this pull request as ready for review December 19, 2024 14:10
@jonathan-eq jonathan-eq self-requested a review December 20, 2024 06:57
Comment on lines +65 to +66
def keyPressEvent(self, arg__1: QKeyEvent) -> None:
if arg__1 is not None and arg__1.key() == Qt.Key.Key_Escape:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we checking if it is not None, if the parameter cannot be None?

def parent(self, child: QModelIndex | None = None) -> QObject | None:
def parent(
self, child: QModelIndex | QPersistentModelIndex | None = None
) -> QObject | QModelIndex:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need QObject here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if parent is None:
parent = QModelIndex()
parent_item = self.root if not parent.isValid() else parent.internalPointer()

if parent.column() > 0:
return 0

return len(parent_item.children)
return len(parent_item.children) # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need type: ignore?

def parent(self, child: QModelIndex | None = None) -> QObject | None:
def parent(
self, child: QModelIndex | QPersistentModelIndex | None = None
) -> QObject | QModelIndex:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this also need QObject?
For QModelIndex, is it possible to have the typing include specifically what the index is pointing to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both of the parent overloads come from inherited methods. the override must handle them both

if child is None or not child.isValid():
return QModelIndex()

parent_item = child.internalPointer().parent
parent_item = child.internalPointer().parent # type:ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another type:ignore. Maybe we dont need it?

self,
index: QModelIndex | QPersistentModelIndex,
role: int = Qt.ItemDataRole.DisplayRole,
) -> Any:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use Any?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an override. It comes from QAbstractItemModel

def _real_data(_: QModelIndex, node: RealNode, role: int) -> Any:
def _real_data(
_: QModelIndex | QPersistentModelIndex, node: RealNode, role: int
) -> Any:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another Any

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is only used by the data() method which returns any

@@ -445,7 +464,7 @@ def _fm_step_data(
if role == FileRole:
data_name = FM_STEP_COLUMNS[index.column()]
if data_name in {ids.STDOUT, ids.STDERR}:
return node.data.get(data_name, QVariant())
return node.data.get(data_name, None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is None redundant here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ids.STDOUT and ids.STDERR are both optional in the typeddict. we need the None if they are not defined

@@ -307,17 +306,18 @@ def on_snapshot_new_iteration(
) -> None:
if not parent.isValid():
index = self._snapshot_model.index(start, 0, parent)
iteration = cast(Node, index.internalPointer()).id_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we cast it to Node or IterationNode? I think the point of having Node prefixed with underscore was that it would not be imported elsewhere

)

from ert.ensemble_evaluator.state import ENSEMBLE_STATE_FAILED, REAL_STATE_TO_COLOR


class ProgressWidget(QFrame):
def __init__(self) -> None:
QWidget.__init__(self)
QFrame.__init__(self)
Copy link
Contributor

@jonathan-eq jonathan-eq Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we change this to super().__init__()?

@@ -110,5 +109,5 @@ def update_progress(self, status: dict[str, int], realization_count: int) -> Non
self.stop_waiting_progress_bar()
self.repaint_components()

def resizeEvent(self, a0: Any, event: Any = None) -> None:
def resizeEvent(self, event: Any = None) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more specific event type we can use?

@@ -127,7 +127,7 @@ def iteration(self) -> int:
def _insert_status_message(self, message: str) -> None:
item = QListWidgetItem()
item.setText(message)
item.setFlags(item.flags() & ~Qt.ItemFlags(Qt.ItemFlag.ItemIsEnabled))
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEnabled)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use item.setFlag instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no setFlag method on QListWidgetItem

@@ -131,6 +134,7 @@ def __init__(
self.setStyleSheet(f"background-color: {LIGHT_GREY}; color: black")
self.__layout.setContentsMargins(32, 47, 32, 16)
self.__layout.setSpacing(32)
self.notifier = notifier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need access to the notifier?

Copy link
Contributor

@jonathan-eq jonathan-eq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good! I have some questions, see comments :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes:maintenance Automatically categorise as maintenance change in release notes
Projects
Status: Ready for Review
Development

Successfully merging this pull request may close these issues.

3 participants