Visitar URL original
Route GUI Qt imports through qtpy, keeping PySide6 preferred by C-Achard · Pull Request #3490 · DeepLabCut/DeepLabCut · GitHub
Skip to content
Draft
Show file tree
Hide file tree
Changes from 14 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
21 changes: 7 additions & 14 deletions deeplabcut/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,16 @@

def main():
try:
import_module("PySide6")

lite = False
except ModuleNotFoundError:
lite = True
import_module("deeplabcut.gui")
except ImportError as err:
print(err)
return
Comment thread
C-Achard marked this conversation as resolved.
Resolved

# if module is executed directly (i.e. `python -m deeplabcut.__init__`) launch straight into the GUI
if not lite:
print("Starting GUI...")
from deeplabcut.gui.launch_script import launch_dlc
print("Starting GUI...")
from deeplabcut.gui.launch_script import launch_dlc

launch_dlc()
else:
print(
"You installed DLC lite, thus GUI's cannot be used. If you need GUI support please: pip install"
"'deeplabcut[gui]''"
)
launch_dlc()


if __name__ == "__main__":
Expand Down
70 changes: 68 additions & 2 deletions deeplabcut/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,75 @@
#

import os
import sys
from importlib.util import find_spec
from pathlib import Path

os.environ["QT_API"] = "pyside6"
import qtpy # Necessary unused import to properly store the env variable
# Selection precedence:
# 1. Explicit, valid, user-set QT_API always wins;
# 2. Otherwise an already imported binding wins
# 3. Otherwise PySide6. qtpy's own default order (pyqt5, pyside2, pyqt6,
# pyside6) puts PySide6 LAST, so an environment that also has PyQt6
# (napari[all], for instance) would silently demote without this;
# 4. Otherwise leave QT_API unset and let qtpy autodetect.
_MODULE_TO_QT_API = (
("PyQt5", "pyqt5"),
("PySide2", "pyside2"),
("PyQt6", "pyqt6"),
("PySide6", "pyside6"),
)
_VALID_QT_APIS = tuple(api for _, api in _MODULE_TO_QT_API)

_INSTALL_DOCS_URL = "https://deeplabcut.github.io/DeepLabCut/docs/installation.html"


def _no_binding_message() -> str:
if find_spec("PySide6") is None:
cause = (
"The DeepLabCut GUI could not start: the graphics libraries it needs are not installed.\n\n"
"Install the GUI dependencies with\n"
" pip install 'deeplabcut[gui]'"
)
else:
cause = (
"The DeepLabCut GUI could not start: PySide6 is installed but could not be loaded.\n\n"
"The installation is likely incomplete. Try\n"
" pip install --force-reinstall pyside6"
)

return (
f"{cause}\n\n"
f"Python currently in use:\n {sys.executable}\n\n"
"If that is not the environment you expected, activate the correct conda environment "
"(for example `conda activate DEEPLABCUT`) and try again.\n\n"
f"Installation help: {_INSTALL_DOCS_URL}"
)


_user_qt_api = (os.environ.get("QT_API") or "").lower()
if _user_qt_api and _user_qt_api not in _VALID_QT_APIS:
raise ValueError(
f"QT_API is set to an unsupported value {_user_qt_api!r}. Valid values are: {', '.join(_VALID_QT_APIS)}."
)

if not _user_qt_api:
_already_loaded = next((api for module, api in _MODULE_TO_QT_API if module in sys.modules), None)
if _already_loaded is not None:
os.environ["QT_API"] = _already_loaded
elif find_spec("PySide6") is not None:
# find_spec does not import PySide6.
# Let qtpy import so version checks and
# its QT_API write-back still run
os.environ["QT_API"] = "pyside6"

try:
import qtpy # noqa: F401 imported side effect: binding selection
except ImportError as err:
raise ImportError(_no_binding_message()) from err

# qtpy rewrites QT_API to the binding it actually loaded, but be explicit:
# matplotlib's backends/qt_compat.py also reads QT_API and must not end up on a
# different binding. It expects the lowercase form, which is `qtpy.API`.
os.environ["QT_API"] = qtpy.API

BASE_DIR = Path(__file__).parent
4 changes: 2 additions & 2 deletions deeplabcut/gui/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

from pathlib import Path

from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QTimer, Slot
from qtpy import QtWidgets
from qtpy.QtCore import Qt, QTimer, Slot

from deeplabcut.core.config import read_config_as_dict
from deeplabcut.gui.dlc_params import DLCParams
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/gui/config_file_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from collections.abc import Callable
from pathlib import Path

from PySide6 import QtCore, QtWidgets
from qtpy import QtCore, QtWidgets


class ConfigFileMonitor(QtCore.QObject):
Expand Down
8 changes: 4 additions & 4 deletions deeplabcut/gui/dialogs/debug_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

from collections.abc import Callable, Iterable

from PySide6 import QtGui
from PySide6.QtCore import Qt
from PySide6.QtGui import QAction, QFontDatabase, QKeySequence, QTextCursor
from PySide6.QtWidgets import (
from qtpy import QtGui
from qtpy.QtCore import Qt
from qtpy.QtGui import QAction, QFontDatabase, QKeySequence, QTextCursor
from qtpy.QtWidgets import (
QApplication,
QDialog,
QHBoxLayout,
Expand Down
7 changes: 3 additions & 4 deletions deeplabcut/gui/displays/selected_shuffle_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@

from pathlib import Path

import PySide6.QtCore as QtCore
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QSizePolicy
from qtpy import QtCore, QtWidgets
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QSizePolicy

from deeplabcut.core.engine import Engine
from deeplabcut.utils import auxiliaryfunctions
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/displays/shuffle_metadata_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

from pathlib import Path

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

import deeplabcut.generate_training_dataset.metadata as metadata

Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/gui/gui_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from importlib.resources import files
from pathlib import Path

from PySide6.QtGui import QIcon, QPixmap
from qtpy.QtGui import QIcon, QPixmap

ASSETS_DIR = files("deeplabcut.gui").joinpath("assets")

Expand Down
17 changes: 8 additions & 9 deletions deeplabcut/gui/launch_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@
import logging
import sys

import PySide6.QtWidgets as QtWidgets
import qdarkstyle
from PySide6.QtCore import Qt
from qdarkstyle.dark.palette import DarkPalette
from qtpy import QtWidgets
from qtpy.QtCore import Qt

from deeplabcut.gui.gui_assets import get_style_qss, icon_from_resource, pixmap_from_resource

Expand All @@ -39,13 +40,11 @@ def launch_dlc():
splash = QtWidgets.QSplashScreen(pixmap)
splash.show()

app.setStyleSheet(get_style_qss()) # this gets overridden immediately?
app.setStyleSheet(get_style_qss())
try:
dark_stylesheet = qdarkstyle.load_stylesheet_pyside6()
except Exception as e:
logger.warning(f"Could not load qdarkstyle stylesheet for PySide6: {e}. Falling back to PySide2 stylesheet.")
dark_stylesheet = qdarkstyle.load_stylesheet_pyside2()
app.setStyleSheet(dark_stylesheet)
app.setStyleSheet(qdarkstyle.load_stylesheet(palette=DarkPalette))
except Exception:
logger.warning("Could not load the qdarkstyle stylesheet; keeping the bundled style.qss.", exc_info=True)

# Set up a logger and add an stdout handler.
# A single logger can have many handlers:
Expand All @@ -67,7 +66,7 @@ def launch_dlc():
window.receiver.start()
window.showMaximized()
splash.finish(window)
sys.exit(app.exec_())
sys.exit(app.exec())
Comment thread
C-Achard marked this conversation as resolved.
Resolved


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/analyze_videos.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from functools import partial
from pathlib import Path

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

import deeplabcut
from deeplabcut.core.config import ProjectConfig
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/create_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from datetime import datetime
from pathlib import Path

from PySide6 import QtCore, QtWidgets
from PySide6.QtGui import QBrush, QColor, QDesktopServices, QPainter, QPen
from qtpy import QtCore, QtWidgets
from qtpy.QtGui import QBrush, QColor, QDesktopServices, QPainter, QPen

from deeplabcut.create_project import create_new_project, create_new_project_3d
from deeplabcut.gui.dlc_params import DLCParams
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/create_training_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
from pathlib import Path

import dlclibrary
from PySide6 import QtWidgets
from PySide6.QtCore import Qt, Slot
from qtpy import QtWidgets
from qtpy.QtCore import Qt, Slot

import deeplabcut
import deeplabcut.compat as compat
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/create_videos.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

import deeplabcut
from deeplabcut.gui.components import (
Expand Down
6 changes: 3 additions & 3 deletions deeplabcut/gui/tabs/evaluate_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
from pathlib import Path

import matplotlib.image as mpimg
from matplotlib.backends.backend_qt5agg import (
from matplotlib.backends.backend_qtagg import (
FigureCanvasQTAgg as FigureCanvas,
)
from matplotlib.figure import Figure
from PySide6 import QtWidgets
from PySide6.QtCore import Qt, Slot
from qtpy import QtWidgets
from qtpy.QtCore import Qt, Slot

import deeplabcut
from deeplabcut.core.engine import Engine
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/extract_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from functools import partial
from pathlib import Path

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

from deeplabcut.generate_training_dataset import extract_frames
from deeplabcut.gui.components import (
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/extract_outlier_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
#
# Licensed under GNU Lesser General Public License v3.0
#
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

import deeplabcut
from deeplabcut.gui.components import (
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/label_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

from pathlib import Path

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

from deeplabcut.generate_training_dataset import check_labels
from deeplabcut.gui.components import DefaultTab
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/manage_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import os
from pathlib import Path

from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
from qtpy.QtCore import Qt
from qtpy.QtWidgets import (
QFileDialog,
QLabel,
QLineEdit,
Expand Down
6 changes: 3 additions & 3 deletions deeplabcut/gui/tabs/modelzoo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
from pathlib import Path

import dlclibrary
from PySide6 import QtWidgets
from PySide6.QtCore import QRegularExpression, QSize, Qt, QTimer, Signal, Slot
from PySide6.QtGui import QRegularExpressionValidator
from qtpy import QtWidgets
from qtpy.QtCore import QRegularExpression, QSize, Qt, QTimer, Signal, Slot
from qtpy.QtGui import QRegularExpressionValidator

import deeplabcut
from deeplabcut.core.engine import Engine
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/gui/tabs/open_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import os
from pathlib import Path

from PySide6 import QtCore, QtWidgets
from qtpy import QtCore, QtWidgets

from deeplabcut.gui.gui_assets import icon_from_resource

Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/refine_tracklets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
#
from pathlib import Path

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

import deeplabcut
from deeplabcut.core import trackingutils
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/train_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

from dataclasses import dataclass

from PySide6 import QtWidgets
from PySide6.QtCore import Qt, Slot
from qtpy import QtWidgets
from qtpy.QtCore import Qt, Slot

import deeplabcut.compat as compat
from deeplabcut.core.engine import Engine
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/unsupervised_id_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
#
from functools import partial

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

import deeplabcut
from deeplabcut.gui.components import (
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tabs/video_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
#
import time

from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from qtpy import QtWidgets
from qtpy.QtCore import Qt

from deeplabcut.gui.components import (
DefaultTab,
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/gui/tracklet_toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import pandas as pd
from matplotlib.path import Path as MPLPath
from matplotlib.widgets import Button, CheckButtons, LassoSelector, Slider, TextBox
from PySide6.QtCore import QMutex
from PySide6.QtWidgets import QMessageBox
from qtpy.QtCore import QMutex
from qtpy.QtWidgets import QMessageBox

from deeplabcut.gui.utils import move_to_separate_thread
from deeplabcut.refine_training_dataset.tracklets import TrackletManager
Expand Down
Loading
Loading