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

GUI PYTHON: replace pyqt6 library by pyside6 #681

Merged
merged 4 commits into from
Oct 17, 2024
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ generate_stub_files.py

# GUI
/gui/utils.py
/gui/*resource*.py
/gui/**/ui_*.py

# NSIS
Expand Down
14 changes: 5 additions & 9 deletions CONTRIBUTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
- mypy
- *(nanobind)*
- scikit-build-core
- pyqt6
- pyside6
- pytest
- numpy
- pandas
Expand All @@ -41,11 +41,7 @@
- build
- twine

**note**: to install `pyqt6`, you have to do it via `pip`:
```bash
pip install pyqt6
```
To install `larray`, you have to run:
**note**: To install `larray`, you have to run:
```bash
conda install -c larray-project larray
```
Expand Down Expand Up @@ -103,7 +99,7 @@ where `<target>` is one the item in the list below:
- `iode_c_api` -> Core API of IODE (in pure C).
- `iode_cpp_api` -> C++ classes that wrap IODE C structure (used in the GUI Qt part).
- `iode_gui` -> Graphical user interface (GUI) based on Qt (C++)
- `ui_to_py` -> Generates Python GUI scripts from Qt Designer files *.ui
- `iode_gui_python` -> Generates Python GUI scripts from Qt Designer files *.ui + Qt resource file *.qrc
- `nsis` -> Builds a Windows Installer for the users.
- `test_c_api` -> Builds the tests for the C API (based on Google Test).
- `test_cpp_api` -> Builds the tests for the C++ classes (based on Google Test).
Expand Down Expand Up @@ -259,8 +255,8 @@ root_dir_iode> pip install <project_name_from_pyproject.toml>
```

# GUI (Python)
Before to work on any issue related to the GUI (Python), you have to build the CMake target `ui_to_py`.
This target will generate the Python scripts from the Qt Designer files *.ui.
Before to work on any issue related to the GUI (Python), you have to build the CMake target `iode_gui_python`.
This target will generate the Python scripts from the Qt Designer files *.ui and the Qt resource files *.qrc.

# Working On An Issue

Expand Down
41 changes: 29 additions & 12 deletions gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ if(SKBUILD)
else()

# Find PyUIC6
find_program(PYUIC6_EXECUTABLE pyuic6)
find_program(PYUIC6_EXECUTABLE pyside6-uic)
# Find PyRCC6
find_program(PYRCC6_EXECUTABLE pyside6-rcc)

set(RESOURCE_FILENAME "iode_resource")
set(RC_RESOURCE_FILE "${IODE_RESOUCES_DIR}/${RESOURCE_FILENAME}.rc")
set(QRC_RESOURCE_FILE "${IODE_RESOUCES_DIR}/${RESOURCE_FILENAME}.qrc")

set(PY_RESOURCE_FILENAME "${RESOURCE_FILENAME}_rc")
set(PY_RESOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${PY_RESOURCE_FILENAME}.py")

# Check if PyUIC6 and PyRCC6 are found
if(PYUIC6_EXECUTABLE AND PYRCC6_EXECUTABLE)
cmake_print_variables(PYUIC6_EXECUTABLE)
cmake_print_variables(PYRCC6_EXECUTABLE)

# Check if PyUIC6 is found
if(PYUIC6_EXECUTABLE)
# Get all .ui files in the current directory and subdirectories
file(GLOB_RECURSE UI_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.ui")

Expand All @@ -30,16 +42,23 @@ if(PYUIC6_EXECUTABLE)
# Add the .py file to the list of sources
list(APPEND GENERATED_FILES_FROM_UI ${PY_FILE})
endforeach()

# remove previously generated Python file if it exists
file(REMOVE ${GENERATED_FILES_FROM_UI})

# See Qt Resource System
add_custom_command(
OUTPUT ${PY_RESOURCE_FILE}
COMMAND ${PYRCC6_EXECUTABLE} -o ${PY_RESOURCE_FILE} ${QRC_RESOURCE_FILE}
DEPENDS ${QRC_RESOURCE_FILE}
)

# remove previously generated Python files if they exist
file(REMOVE ${GENERATED_FILES_FROM_UI} ${PY_RESOURCE_FILE})

# Create a custom target that depends on all the .py files
add_custom_target(ui_to_py ALL DEPENDS ${GENERATED_FILES_FROM_UI})
add_custom_target(iode_gui_python ALL DEPENDS ${GENERATED_FILES_FROM_UI} ${PY_RESOURCE_FILE})

message(STATUS "New target ui_to_py enabled")
message(STATUS "New target iode_gui_python enabled")
else()
message(WARNING "pyuic6 not found. Please install PyQt6.")
message(WARNING "pyside6-uic or pyside6-rcc not found. Please install PyQt6.")
endif()

configure_file(
Expand Down Expand Up @@ -248,9 +267,7 @@ set(PROJECT_SOURCES ${GUI_UTILS} ${GUI_SETTINGS} ${GUI_PRINT} ${GUI_TEXT_EDI
${GUI_MAIN_WIDGETS} ${GUI_MENUS} ${GUI_MAIN}
)

set(APP_ICON_WINDOWS "${IODE_RESOUCES_DIR}/iode.rc")

qt_add_executable(${IODE_GUI_TARGET} ${IODE_RESOUCES_DIR}/iode_resource.qrc ${APP_ICON_WINDOWS} MANUAL_FINALIZATION ${PROJECT_SOURCES})
qt_add_executable(${IODE_GUI_TARGET} ${QRC_RESOURCE_FILE} ${RC_RESOURCE_FILE} MANUAL_FINALIZATION ${PROJECT_SOURCES})
target_link_libraries(${IODE_GUI_TARGET} PRIVATE Qt6::Core Qt6::Widgets Qt6::Gui Qt6::Charts Qt6::PrintSupport iode_cpp_api)
# By calling target_include_directories, we make sure that the absolute path to
# the gui directory is automatically added as an include path to the ${IODE_GUI_TARGET} target.
Expand Down
12 changes: 6 additions & 6 deletions gui/abstract_main_window.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from PyQt6.QtCore import Qt, QSettings, pyqtSlot
from PyQt6.QtWidgets import QMainWindow, QTextEdit, QLineEdit, QDialog
from PySide6.QtCore import Qt, QSettings, Slot
from PySide6.QtWidgets import QMainWindow, QTextEdit, QLineEdit, QDialog

from text_edit.completer import IodeCompleter
from plot.plot import PlotDialog
Expand Down Expand Up @@ -40,17 +40,17 @@ def output(self) -> QTextEdit:
def iode_command(self) -> QLineEdit:
raise NotImplementedError()

@pyqtSlot(QDialog)
@Slot(QDialog)
def append_dialog(self, dialog: QDialog):
dialog.open()
self.dialogs.append(dialog)

@pyqtSlot(PlotDialog)
@Slot(PlotDialog)
def append_plot(self, dialog: PlotDialog):
dialog.plot()
self.dialogs.append(dialog)

@pyqtSlot()
@pyqtSlot(IodeTypes)
@Slot()
@Slot(IodeTypes)
def update_tab_and_completer(self, iode_type: IodeTypes = None):
raise NotImplementedError()
6 changes: 3 additions & 3 deletions gui/iode_objs/delegates/base_delegate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from PyQt6.QtWidgets import (QStyledItemDelegate, QLineEdit, QPlainTextEdit,
from PySide6.QtWidgets import (QStyledItemDelegate, QLineEdit, QPlainTextEdit,
QWidget, QStyleOptionViewItem)
from PyQt6.QtCore import Qt, QEvent, QObject, QModelIndex, QAbstractItemModel
from PyQt6.QtGui import QKeyEvent
from PySide6.QtCore import Qt, QEvent, QObject, QModelIndex, QAbstractItemModel
from PySide6.QtGui import QKeyEvent

from util.double_validator import DoubleValidator
from iode import IodeTypes
Expand Down
6 changes: 3 additions & 3 deletions gui/iode_objs/delegates/scalars_delegate.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from base_delegate import BaseDelegate
from util.double_validator import IodeDoubleValidator

from PyQt6.QtCore import QModelIndex
from PyQt6.QtWidgets import QLineEdit, QWidget, QStyleOptionViewItem
from PyQt6.QtGui import QDoubleValidator
from PySide6.QtCore import QModelIndex
from PySide6.QtWidgets import QLineEdit, QWidget, QStyleOptionViewItem
from PySide6.QtGui import QDoubleValidator

from iode import IodeTypes

Expand Down
4 changes: 2 additions & 2 deletions gui/iode_objs/delegates/variables_delegate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from base_delegate import BaseDelegate
from util.double_validator import IodeDoubleValidator

from PyQt6.QtCore import QModelIndex
from PyQt6.QtWidgets import QLineEdit, QWidget, QStyleOptionViewItem
from PySide6.QtCore import QModelIndex
from PySide6.QtWidgets import QLineEdit, QWidget, QStyleOptionViewItem

from iode import IodeTypes

Expand Down
2 changes: 1 addition & 1 deletion gui/iode_objs/edit/edit_vars_sample.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PyQt6.QtWidgets import QDialog
from PySide6.QtWidgets import QDialog


class EditIodeSampleDialog(QDialog):
Expand Down
6 changes: 3 additions & 3 deletions gui/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
from PyQt6.QtWidgets import QApplication, QSplashScreen
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import QLocale
from PySide6.QtWidgets import QApplication, QSplashScreen
from PySide6.QtGui import QPixmap
from PySide6.QtCore import QLocale

from utils import ORGANIZATION_NAME
from main_window import MainWindow
Expand Down
6 changes: 3 additions & 3 deletions gui/main_widgets/file_explorer/file_delegate.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import List
from pathlib import Path

from PyQt6.QtWidgets import QWidget, QStyledItemDelegate, QLineEdit, QStyleOptionViewItem, QMessageBox
from PyQt6.QtCore import QModelIndex, Qt, QObject, QAbstractItemModel
from PyQt6.QtGui import QPainter, QPalette, QColor
from PySide6.QtWidgets import QWidget, QStyledItemDelegate, QLineEdit, QStyleOptionViewItem, QMessageBox
from PySide6.QtCore import QModelIndex, Qt, QObject, QAbstractItemModel
from PySide6.QtGui import QPainter, QPalette, QColor


class FileDelegate(QStyledItemDelegate):
Expand Down
46 changes: 23 additions & 23 deletions gui/main_widgets/file_explorer/file_explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from typing import List, Tuple
from pathlib import Path

from PyQt6.QtCore import (QModelIndex, QPersistentModelIndex, QDir, QFile, QFileInfo,
QPoint, Qt, QProcess, QUrl, pyqtSignal, pyqtSlot)
from PyQt6.QtGui import (QAction, QKeySequence, QShortcut, QFileSystemModel,
QDesktopServices, QCloseEvent, QDragMoveEvent, QDropEvent)
from PyQt6.QtWidgets import QTreeView, QMenu, QMessageBox, QApplication
from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, QDir, QFile, QFileInfo,
QPoint, Qt, QProcess, QUrl, Signal, Slot)
from PySide6.QtGui import (QAction, QKeySequence, QShortcut, QDesktopServices,
QCloseEvent, QDragMoveEvent, QDropEvent)
from PySide6.QtWidgets import QTreeView, QMenu, QFileSystemModel, QMessageBox, QApplication

from settings import ProjectSettings
from main_widgets.file_explorer.file_explorer_proxy import FileExplorerProxyModel
Expand Down Expand Up @@ -40,7 +40,7 @@ class IodeFileExplorer(QTreeView):
- It is possible to move files and/or directories inside the project tree via drag and drop.
"""

fileMoved = pyqtSignal(str, str)
fileMoved = Signal(str, str)

def __init__(self, parent=None):
super().__init__(parent)
Expand Down Expand Up @@ -418,7 +418,7 @@ def update_project_dir(self, project_dir: QDir, onStartup: bool=False):
self.show()

# override method of QTreeView
@pyqtSlot(QDragMoveEvent)
@Slot(QDragMoveEvent)
def dragMoveEvent(self, event: QDragMoveEvent):
try:
# Check if the event has text data and if the mouse is over the widget
Expand All @@ -434,7 +434,7 @@ def dragMoveEvent(self, event: QDragMoveEvent):
event.ignore()

# override method of QTreeView
@pyqtSlot(QDropEvent)
@Slot(QDropEvent)
def dropEvent(self, event: QDropEvent):
# Check if the event has text data
if event.mimeData().hasText():
Expand Down Expand Up @@ -470,7 +470,7 @@ def dropEvent(self, event: QDropEvent):
event.ignore()

# override method of QTreeView
@pyqtSlot(QPoint)
@Slot(QPoint)
def onCustomContextMenu(self, point: QPoint):
"""
Pops up either directory or file context menu depending on the item selected.
Expand All @@ -493,7 +493,7 @@ def onCustomContextMenu(self, point: QPoint):

context_menu_current.exec(global_point)

@pyqtSlot(QModelIndex)
@Slot(QModelIndex)
def _open_file(self, index: QModelIndex):
"""
Function called when the user double clicked on a item in the tree (= file explorer) view.
Expand All @@ -514,7 +514,7 @@ def _open_file(self, index: QModelIndex):
QMessageBox.warning(self, "WARNING", "An error occurred while trying to "
f"open a file: {str(e)}")

@pyqtSlot(str, bool)
@Slot(str, bool)
def file_content_modified(self, filepath: str, modified: bool):
"""
Set item in color when a user modifies the content of the associated database
Expand All @@ -534,7 +534,7 @@ def file_content_modified(self, filepath: str, modified: bool):

self.viewport().repaint()

@pyqtSlot()
@Slot()
def create_file(self):
"""
Create a new file.
Expand All @@ -556,7 +556,7 @@ def create_file(self):
QMessageBox.warning(self, "WARNING", "An error occurred while trying to "
f"create a file: {str(e)}")

@pyqtSlot()
@Slot()
def create_dir(self):
"""
Create a new sub directory.
Expand All @@ -576,7 +576,7 @@ def create_dir(self):
QMessageBox.warning(self, "WARNING", "An error occurred while trying to "
f"create a directory: {str(e)}")

@pyqtSlot()
@Slot()
def absolute_path(self):
"""
Copy the absolute path of the selected path to the selected file.
Expand All @@ -587,7 +587,7 @@ def absolute_path(self):
clipboard.setText(abs_path)
self._cleanup_slot()

@pyqtSlot()
@Slot()
def relative_path(self):
"""
Copy the relative path of the selected path to the selected file.
Expand All @@ -598,7 +598,7 @@ def relative_path(self):
clipboard.setText(rel_path)
self._cleanup_slot()

@pyqtSlot()
@Slot()
def reveal_in_folder(self):
"""
Open an OS file explorer and highlight the selected file.
Expand Down Expand Up @@ -626,7 +626,7 @@ def reveal_in_folder(self):
self.selectionModel().clearSelection()
self._cleanup_slot()

@pyqtSlot()
@Slot()
def cut(self):
"""
Cut file or directory.
Expand All @@ -639,7 +639,7 @@ def cut(self):
QMessageBox.warning(self, "WARNING", "An error occurred while trying to cut "
f"and paste file or directory: {str(e)}")

@pyqtSlot()
@Slot()
def copy(self):
"""
Copy file or directory.
Expand All @@ -652,7 +652,7 @@ def copy(self):
QMessageBox.warning(self, "WARNING", "An error occurred while trying to "
f"copy/paste file or directory: {str(e)}")

@pyqtSlot()
@Slot()
def paste(self):
"""
Paste file or directory.
Expand Down Expand Up @@ -700,7 +700,7 @@ def paste(self):
except Exception as e:
QMessageBox.warning(self, "WARNING", f"An error occurred while pasting file or directory: {str(e)}")

@pyqtSlot()
@Slot()
def rename(self):
"""
Rename file or directory.
Expand All @@ -722,7 +722,7 @@ def rename(self):
except Exception as e:
QMessageBox.warning(self, "WARNING", f"An error occurred while renaming file or directory: {str(e)}")

@pyqtSlot()
@Slot()
def remove(self):
"""
Delete file or directory.
Expand Down Expand Up @@ -775,7 +775,7 @@ def remove(self):
except Exception as e:
QMessageBox.warning(self, "WARNING", f"An error occurred while removing file or directory: {str(e)}")

@pyqtSlot(bool)
@Slot(bool)
def open_files(self, force_as_text: bool = False):
"""
Function called when the user double clicks on a item in the tree (= file explorer) view.
Expand All @@ -794,7 +794,7 @@ def open_files(self, force_as_text: bool = False):
except Exception as e:
QMessageBox.warning(self, "WARNING", f"An error occurred while opening file(s): {str(e)}")

@pyqtSlot()
@Slot()
def cancel(self):
"""
Deselects all items + clear Clipboard.
Expand Down
2 changes: 1 addition & 1 deletion gui/main_widgets/file_explorer/file_explorer_proxy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PyQt6.QtCore import QPersistentModelIndex, QSortFilterProxyModel, QModelIndex, QFileInfo
from PySide6.QtCore import QPersistentModelIndex, QSortFilterProxyModel, QModelIndex, QFileInfo


class FileExplorerProxyModel(QSortFilterProxyModel):
Expand Down
Loading
Loading