diff --git a/.gitignore b/.gitignore index 7d36037..6d29b55 100644 --- a/.gitignore +++ b/.gitignore @@ -198,3 +198,5 @@ compile_commands.json python_browser.py .vscode + +PyQt6-stubs diff --git a/common.py b/common.py index 84872bb..f8e4091 100644 --- a/common.py +++ b/common.py @@ -1,11 +1,15 @@ import os import re -import sys -import glob -from PyQt5 import QtGui -from PyQt5.QtCore import qDebug -from PyQt5.QtCore import qWarning +try: + from PyQt5 import QtGui +except: + from PyQt6 import QtGui + +try: + from PyQt5.QtCore import qDebug, qWarning +except: + from PyQt6.QtCore import qDebug, qWarning red = QtGui.QColor(255, 170, 170) green = QtGui.QColor(205, 222, 135) @@ -129,7 +133,7 @@ def readLines(filename): def getModByName(organizer, name): - return organizer.getMod(name) + return organizer.modList().getMod(name) def getModStateByName(organizer, name): diff --git a/link_deploy.py b/link_deploy.py index f4d1a0b..02ff5e7 100644 --- a/link_deploy.py +++ b/link_deploy.py @@ -1,16 +1,7 @@ -from ast import Mod import os -import glob -import json -import traceback -import inspect import datetime -import signal import functools -import traceback -import threading -import subprocess import multiprocessing import concurrent.futures @@ -19,21 +10,46 @@ import pathlib from . import common as Dc -import PyQt5.QtGui as QtGui -import PyQt5.QtCore as QtCore -import PyQt5.QtWidgets as QtWidgets - -from PyQt5.QtCore import Qt -from PyQt5.QtCore import QThread -from PyQt5.QtCore import QRunnable -from PyQt5.QtCore import QThreadPool -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import qDebug -from PyQt5.QtCore import qWarning -from PyQt5.QtCore import qCritical -from PyQt5.QtCore import QCoreApplication - -from PyQt5.QtWidgets import QTreeWidgetItem +try: + import PyQt5.QtGui as QtGui +except: + import PyQt6.QtGui as QtGui + +try: + import PyQt5.QtWidgets as QtWidgets + + QFramePanel = QtWidgets.QFrame.Panel + QFrameSunken = QtWidgets.QFrame.Sunken +except: + import PyQt6.QtWidgets as QtWidgets + + QFramePanel = QtWidgets.QFrame.Shape.Panel + QFrameSunken = QtWidgets.QFrame.Shadow.Sunken + +try: + from PyQt5.QtCore import ( + Qt, + QThread, + pyqtSignal, + qWarning, + QCoreApplication, + ) + + qtBlack = Qt.black + qtUserRole = Qt.UserRole + qtWindowContextHelpButtonHint = Qt.WindowContextHelpButtonHint +except: + from PyQt6.QtCore import ( + Qt, + QThread, + pyqtSignal, + qWarning, + QCoreApplication, + ) + + qtBlack = Qt.GlobalColor.black + qtUserRole = Qt.ItemDataRole.UserRole + qtWindowContextHelpButtonHint = Qt.WindowType.WindowContextHelpButtonHint def is_relative_to(from_path, to_path): @@ -282,7 +298,7 @@ def __init__(self, organizer: mobase.IOrganizer, parent=None): self.resize(800, 800) self.setWindowIcon(QtGui.QIcon(":/deorder/link_deploy")) - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) + self.setWindowFlags(self.windowFlags() & ~qtWindowContextHelpButtonHint) # Vertical Layout verticalLayout = QtWidgets.QVBoxLayout() @@ -314,9 +330,7 @@ def __init__(self, organizer: mobase.IOrganizer, parent=None): # Vertical Layout -> Target Label self.targetDirLabel = QtWidgets.QLabel(self) - self.targetDirLabel.setFrameStyle( - QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken - ) + self.targetDirLabel.setFrameStyle(QFramePanel | QFrameSunken) self.targetDirLabel.setText( self.__tr("Deployment dir") + ":\n" + self.__targetDir ) @@ -325,7 +339,7 @@ def __init__(self, organizer: mobase.IOrganizer, parent=None): # Vertical Layout -> Original Label # self.originalDirLabel = QtWidgets.QLabel(self) - # self.originalDirLabel.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken) + # self.originalDirLabel.setFrameStyle(QFramePanel | QFrameSunken) # self.originalDirLabel.setText(self.__tr("Original dir") + ":\n" + self.__originalDir) # verticalLayout.addWidget(self.originalDirLabel) @@ -345,7 +359,7 @@ def __init__(self, organizer: mobase.IOrganizer, parent=None): # Vertical Layout -> Status Label self.statusLabel = QtWidgets.QLabel(self) - self.statusLabel.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken) + self.statusLabel.setFrameStyle(QFramePanel | QFrameSunken) self.statusLabel.setText("...") verticalLayout.addWidget(self.statusLabel) @@ -379,7 +393,7 @@ def _deploy(self): entries = [] root = self.mappingList.invisibleRootItem() for item_index in range(root.childCount()): - entry = root.child(item_index).data(0, Qt.UserRole) + entry = root.child(item_index).data(0, qtUserRole) entry["item_index"] = item_index entries.append(entry) self.__deploy_worker = DeployWorker( @@ -403,7 +417,7 @@ def set_item_status_callback(result): item.setBackground(0, Dc.green) if status == "failed": item.setBackground(0, Dc.red) - item.setForeground(0, Qt.black) + item.setForeground(0, qtBlack) self.statusLabel.setText( self.__tr("{} {}").format(filepath, self.__tr(status)) ) @@ -426,7 +440,7 @@ def _refreshList(self): def add_item_callback(filepath): item = QtWidgets.QTreeWidgetItem(self.mappingList, [filepath, "..."]) - item.setData(0, Qt.UserRole, {"filepath": str(filepath)}) + item.setData(0, qtUserRole, {"filepath": str(filepath)}) self.mappingList.addTopLevelItem(item) def finished_callback(): @@ -474,14 +488,18 @@ def init(self, organizer): from . import resources # noqa self.__organizer = organizer - return True - - def isActive(self): - return bool(self.__organizer.pluginSetting(self.NAME, "enabled")) + return bool(self.__organizer.pluginSetting(self.NAME, "agree")) def settings(self): return [ - mobase.PluginSetting("enabled", self.__tr("Enable plugin"), True), + mobase.PluginSetting("enabled", self.__tr("Enable plugin"), False), + mobase.PluginSetting( + "agree", + self.__tr( + "I agree that this plugin is experimental and may cause data loss." + ), + False, + ), mobase.PluginSetting( "symlink", self.__tr("Use symlinks/softlinks instead of hardlinks"), @@ -492,7 +510,7 @@ def settings(self): def display(self): self.__window = PluginWindow(self.__organizer, self) self.__window.setWindowTitle(self.NAME) - self.__window.exec_() + self.__window.exec() def icon(self): return QtGui.QIcon(":/deorder/link_deploy") diff --git a/merge_plugins_hide.py b/merge_plugins_hide.py index 607881a..a6c1224 100644 --- a/merge_plugins_hide.py +++ b/merge_plugins_hide.py @@ -6,15 +6,42 @@ import mobase from . import common as Dc -import PyQt5.QtGui as QtGui -import PyQt5.QtCore as QtCore -import PyQt5.QtWidgets as QtWidgets +try: + import PyQt5.QtGui as QtGui +except: + import PyQt6.QtGui as QtGui -from PyQt5.QtCore import Qt -from PyQt5.QtCore import qDebug -from PyQt5.QtCore import qWarning -from PyQt5.QtCore import qCritical -from PyQt5.QtCore import QCoreApplication + QAction = QtGui.QAction + +try: + import PyQt5.QtWidgets as QtWidgets + + QAction = QtWidgets.QAction + QAbstractItemViewExtendedSelection = QtWidgets.QAbstractItemView.ExtendedSelection +except: + import PyQt6.QtWidgets as QtWidgets + + QAbstractItemViewExtendedSelection = ( + QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection + ) + +try: + from PyQt5.QtCore import Qt, qDebug, qWarning, qCritical, QCoreApplication + + qtBlack = Qt.black + qtUserRole = Qt.UserRole + qtScrollBarAlwaysOff = Qt.ScrollBarAlwaysOff + qtCustomContextMenu = Qt.CustomContextMenu + qtWindowContextHelpButtonHint = Qt.WindowContextHelpButtonHint + +except: + from PyQt6.QtCore import Qt, qDebug, qWarning, qCritical, QCoreApplication + + qtBlack = Qt.GlobalColor.black + qtUserRole = Qt.ItemDataRole.UserRole + qtScrollBarAlwaysOff = Qt.ScrollBarPolicy.ScrollBarAlwaysOff + qtCustomContextMenu = Qt.ContextMenuPolicy.CustomContextMenu + qtWindowContextHelpButtonHint = Qt.WindowType.WindowContextHelpButtonHint class PluginWindow(QtWidgets.QDialog): @@ -35,7 +62,7 @@ def __init__(self, organizer, parent=None): self.resize(500, 500) self.setWindowIcon(QtGui.QIcon(":/deorder/merge_plugins_hide")) - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) + self.setWindowFlags(self.windowFlags() & ~qtWindowContextHelpButtonHint) # Vertical Layout verticalLayout = QtWidgets.QVBoxLayout() @@ -50,12 +77,10 @@ def __init__(self, organizer, parent=None): self.mergedModList.headerItem().setText(0, self.__tr("Merge name")) self.mergedModList.headerItem().setText(1, self.__tr("Plugins state")) - self.mergedModList.setContextMenuPolicy(Qt.CustomContextMenu) - self.mergedModList.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.mergedModList.setContextMenuPolicy(qtCustomContextMenu) + self.mergedModList.setHorizontalScrollBarPolicy(qtScrollBarAlwaysOff) self.mergedModList.customContextMenuRequested.connect(self.openMergedModMenu) - self.mergedModList.setSelectionMode( - QtWidgets.QAbstractItemView.ExtendedSelection - ) + self.mergedModList.setSelectionMode(QAbstractItemViewExtendedSelection) verticalLayout.addWidget(self.mergedModList) @@ -305,10 +330,10 @@ def refreshMergedModList(self): for x in range(2): if color: item.setBackground(x, color) - item.setForeground(x, Qt.black) + item.setForeground(x, qtBlack) item.setData( x, - Qt.UserRole, + qtUserRole, {"modName": modName, "modPluginsState": modPluginsState}, ) self.mergedModList.addTopLevelItem(item) @@ -319,7 +344,7 @@ def openMergedModMenu(self, position): if selectedItems: menu = QtWidgets.QMenu() - selectedItemsData = [item.data(0, Qt.UserRole) for item in selectedItems] + selectedItemsData = [item.data(0, qtUserRole) for item in selectedItems] selectedModsWithEnabled = [ selectedItemData["modName"] for selectedItemData in selectedItemsData @@ -331,7 +356,7 @@ def openMergedModMenu(self, position): if (selectedItemData["modPluginsState"] in Dc.SomeModPluginsActive) ] - enableAction = QtWidgets.QAction( + enableAction = QAction( QtGui.QIcon(":/MO/gui/active"), self.__tr("&Enable plugins"), self ) enableAction.setEnabled(False) @@ -339,7 +364,7 @@ def openMergedModMenu(self, position): if selectedModsWithEnabled: enableAction.setEnabled(True) - disableAction = QtWidgets.QAction( + disableAction = QAction( QtGui.QIcon(":/MO/gui/inactive"), self.__tr("&Disable plugins"), self ) disableAction.setEnabled(False) @@ -347,7 +372,7 @@ def openMergedModMenu(self, position): if selectedModsWithDisabled: disableAction.setEnabled(True) - action = menu.exec_(self.mergedModList.mapToGlobal(position)) + action = menu.exec(self.mergedModList.mapToGlobal(position)) # Catch and log exceptional side-effects try: @@ -451,9 +476,6 @@ def init(self, organizer): self.__organizer = organizer return True - def isActive(self): - return bool(self.__organizer.pluginSetting(self.NAME, "enabled")) - def settings(self): return [ mobase.PluginSetting("enabled", self.__tr("Enable this plugin"), True), @@ -473,7 +495,7 @@ def settings(self): def display(self): self.__window = PluginWindow(self.__organizer, self) self.__window.setWindowTitle(self.NAME) - self.__window.exec_() + self.__window.exec() # Refresh Mod Organizer mod list to reflect changes where files were changed # outside MO2 @@ -481,7 +503,7 @@ def display(self): "mohidden", "optional", ]: - self.__organizer.refreshModList() + self.__organizer.refresh() def icon(self): return QtGui.QIcon(":/deorder/merge_plugins_hide") diff --git a/requirements.txt b/requirements.txt index c2a03bb..e37998e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -black==22.3.0 +black==22.10.0 click==8.1.3 -colorama==0.4.4 +colorama==0.4.6 mobase-stubs==2.5.0.dev10 mypy-extensions==0.4.3 -pathspec==0.9.0 +pathspec==0.10.1 platformdirs==2.5.2 PyQt5-stubs==5.15.6.0 tomli==2.0.1 diff --git a/resources.py b/resources.py index 0e2f79f..27e7770 100644 --- a/resources.py +++ b/resources.py @@ -6,7 +6,10 @@ # # WARNING! All changes made in this file will be lost! -from PyQt5 import QtCore +try: + from PyQt5 import QtCore +except: + from PyQt6 import QtCore qt_resource_data = b"\ \x00\x00\x1d\xe2\ @@ -1334,7 +1337,7 @@ \x00\x00\x01\x78\x5c\x56\xac\xf3\ " -qt_version = [int(v) for v in QtCore.qVersion().split('.')] +qt_version = [int(v) for v in QtCore.qVersion().split(".")] if qt_version < [5, 8, 0]: rcc_version = 1 qt_resource_struct = qt_resource_struct_v1 @@ -1342,10 +1345,17 @@ rcc_version = 2 qt_resource_struct = qt_resource_struct_v2 + def qInitResources(): - QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + QtCore.qRegisterResourceData( + rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data + ) + def qCleanupResources(): - QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + QtCore.qUnregisterResourceData( + rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data + ) + qInitResources() diff --git a/sync_mod_order.py b/sync_mod_order.py index e7ce07c..9c661fc 100644 --- a/sync_mod_order.py +++ b/sync_mod_order.py @@ -6,13 +6,45 @@ import mobase from . import common as Dc -import PyQt5.QtGui as QtGui -import PyQt5.QtCore as QtCore -import PyQt5.QtWidgets as QtWidgets +try: + import PyQt5.QtGui as QtGui +except: + import PyQt6.QtGui as QtGui -from PyQt5.QtCore import Qt -from PyQt5.QtCore import qDebug, qCritical -from PyQt5.QtCore import QCoreApplication + QAction = QtGui.QAction + + +try: + import PyQt5.QtWidgets as QtWidgets + + QAction = QtWidgets.QAction +except: + import PyQt6.QtWidgets as QtWidgets + +try: + from PyQt5.QtCore import Qt + + qtUserRole = Qt.UserRole + qtScrollBarAlwaysOff = Qt.ScrollBarAlwaysOff + qtCustomContextMenu = Qt.CustomContextMenu + qtWindowContextHelpButtonHint = Qt.WindowContextHelpButtonHint +except: + from PyQt6.QtCore import Qt + + qtUserRole = Qt.ItemDataRole.UserRole + qtScrollBarAlwaysOff = Qt.ScrollBarPolicy.ScrollBarAlwaysOff + qtCustomContextMenu = Qt.ContextMenuPolicy.CustomContextMenu + qtWindowContextHelpButtonHint = Qt.WindowType.WindowContextHelpButtonHint + +try: + from PyQt5.QtCore import qDebug, qCritical +except: + from PyQt6.QtCore import qDebug, qCritical + +try: + from PyQt5.QtCore import QCoreApplication +except: + from PyQt6.QtCore import QCoreApplication class PluginWindow(QtWidgets.QDialog): @@ -28,7 +60,7 @@ def __init__(self, organizer, parent=None): self.resize(500, 500) self.setWindowIcon(QtGui.QIcon(":/deorder/sync_mod_order")) - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) + self.setWindowFlags(self.windowFlags() & ~qtWindowContextHelpButtonHint) # Vertical Layout verticalLayout = QtWidgets.QVBoxLayout() @@ -42,8 +74,8 @@ def __init__(self, organizer, parent=None): self.profileList.header().setVisible(True) self.profileList.headerItem().setText(0, self.__tr("Profile name")) - self.profileList.setContextMenuPolicy(Qt.CustomContextMenu) - self.profileList.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.profileList.setContextMenuPolicy(qtCustomContextMenu) + self.profileList.setHorizontalScrollBarPolicy(qtScrollBarAlwaysOff) self.profileList.customContextMenuRequested.connect(self.openProfileMenu) verticalLayout.addWidget(self.profileList) @@ -110,7 +142,7 @@ def refreshProfileList(self): self.profileList.clear() for profileName in sorted(self.__profileInfo): item = QtWidgets.QTreeWidgetItem(self.profileList, [profileName]) - item.setData(0, Qt.UserRole, {"profileName": profileName}) + item.setData(0, qtUserRole, {"profileName": profileName}) self.profileList.addTopLevelItem(item) self.profileList.resizeColumnToContents(0) @@ -119,13 +151,13 @@ def openProfileMenu(self, position): if selectedItems: menu = QtWidgets.QMenu() - selectedItemsData = [item.data(0, Qt.UserRole) for item in selectedItems] + selectedItemsData = [item.data(0, qtUserRole) for item in selectedItems] selectedProfiles = [ selectedItemData["profileName"] for selectedItemData in selectedItemsData ] - syncAction = QtWidgets.QAction( + syncAction = QAction( QtGui.QIcon(":/MO/gui/next"), self.__tr("&Sync current profile mod order to"), self, @@ -133,7 +165,7 @@ def openProfileMenu(self, position): syncAction.setEnabled(True) menu.addAction(syncAction) - action = menu.exec_(self.profileList.mapToGlobal(position)) + action = menu.exec(self.profileList.mapToGlobal(position)) try: if action == syncAction: @@ -198,19 +230,16 @@ def init(self, organizer): self.__organizer = organizer return True - def isActive(self): - return bool(self.__organizer.pluginSetting(self.NAME, "enabled")) - def settings(self): return [mobase.PluginSetting("enabled", self.__tr("Enable this plugin"), True)] def display(self): self.__window = PluginWindow(self.__organizer) self.__window.setWindowTitle(self.NAME) - self.__window.exec_() + self.__window.exec() # Refresh Mod Organizer mod list to reflect changes - self.__organizer.refreshModList() + self.__organizer.refresh() def icon(self):