mirror of
https://github.com/ModOrganizer2/modorganizer-installer_wizard.git
synced 2026-07-27 14:07:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ca112ed0 | ||
|
|
7ef97f047c | ||
|
|
aff1cbb944 | ||
|
|
6492f84eac | ||
|
|
7b3dc82f46 | ||
|
|
76faacba19 | ||
|
|
0c8d7261fd | ||
|
|
3cf15300fb | ||
|
|
3bfd181704 | ||
|
|
bffeb8c05f | ||
|
|
f7a9c95d5b | ||
|
|
793ab6273b | ||
|
|
7e5ec2d11a | ||
|
|
ca343602ec | ||
|
|
e2202073dc | ||
|
|
fb91a1d2b6 | ||
|
|
9f71ac1517 | ||
|
|
775d764950 | ||
|
|
538a0f73e9 | ||
|
|
ecefc5eb8a | ||
|
|
b9653b8322 | ||
|
|
b39be8c295 | ||
|
|
28deb0ce25 | ||
|
|
1cf557a33c | ||
|
|
753871d62a | ||
|
|
8b49e0d3cd | ||
|
|
52e20cf676 |
@@ -8,7 +8,12 @@ src/ui/*.py
|
||||
.tox
|
||||
.vscode
|
||||
|
||||
# MO2 build files:
|
||||
vsbuild
|
||||
mob.log
|
||||
|
||||
# Generated files:
|
||||
lib
|
||||
src/lib
|
||||
installer_wizard
|
||||
installer_wizard-*.zip
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if(DEFINED DEPENDENCIES_DIR)
|
||||
include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/mo2.cmake)
|
||||
else()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake_common/mo2.cmake)
|
||||
endif()
|
||||
|
||||
project(installer_wizard LANGUAGES NONE)
|
||||
|
||||
add_subdirectory(src)
|
||||
@@ -1,163 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS><TS version="2.0">
|
||||
<context>
|
||||
<name>WizardInstallerComplete</name>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallercomplete.py" line="151"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallercomplete.py" line="152"/>
|
||||
<source>The installer script has finished. The following sub-packages and plugins will be installed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallercomplete.py" line="153"/>
|
||||
<source>Sub-Packages:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallercomplete.py" line="154"/>
|
||||
<source>Plugins:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallercomplete.py" line="155"/>
|
||||
<source>INI Tweaks:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallercomplete.py" line="156"/>
|
||||
<source>Notes:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerDialog</name>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="75"/>
|
||||
<source>BAIN Wizard Installer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="76"/>
|
||||
<source>Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="77"/>
|
||||
<source>Manual</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="78"/>
|
||||
<source>Script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="79"/>
|
||||
<source>Back</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="80"/>
|
||||
<source>Next</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerdialog.py" line="81"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerError</name>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallererror.py" line="52"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallererror.py" line="54"/>
|
||||
<source>TextLabel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerPage</name>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerpage.py" line="91"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerpage.py" line="92"/>
|
||||
<source>TextLabel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerpage.py" line="93"/>
|
||||
<source>Options:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerpage.py" line="94"/>
|
||||
<source>Description:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerRequires</name>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="113"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="114"/>
|
||||
<source><html><head/><body><p><span style=" font-weight:600;">Warning:</span> The following version requirements are not met for using this installer.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="115"/>
|
||||
<source>Requirements</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="116"/>
|
||||
<source>Graphics Extender</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="117"/>
|
||||
<source>Wrye Bash</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="118"/>
|
||||
<source>Need</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="119"/>
|
||||
<source>Game</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="122"/>
|
||||
<source>N/A</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="121"/>
|
||||
<source>Have</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/wizardinstallerrequires.py" line="123"/>
|
||||
<source>Script Extender</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
+2
-1
@@ -6,7 +6,8 @@ $target = ".\installer_wizard\"
|
||||
|
||||
Remove-Item -Recurse -Force -ErrorAction Ignore $target
|
||||
New-Item -Path $target -Type Directory | Out-Null
|
||||
Copy-Item -Recurse -Path .\src\* -Exclude "*.ui", "__pycache__" -Destination .\installer_wizard
|
||||
Copy-Item -Recurse -Path .\src\* -Exclude "*.ui" -Destination $target
|
||||
Get-ChildItem -Recurse $target -Include "__pycache__" | Remove-Item -Recurse -Force
|
||||
Copy-Item .\installer_wizard_en.ts, .\README.md, .\LICENSE $target
|
||||
|
||||
# Find the version:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Install the lib:
|
||||
pip install --target=.\src\lib --upgrade git+https://github.com/Holt59/bain-wizard-interpreter | Out-Null
|
||||
pip install --target=.\src\lib --upgrade -r .\plugin-requirements.txt | Out-Null
|
||||
|
||||
# Convert ui files:
|
||||
Get-ChildItem -Recurse -File -Include "*.ui" | ForEach-Object {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
git+https://github.com/Holt59/bain-wizard-interpreter@v0.0.3
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
black
|
||||
PyQt5
|
||||
PyQt6
|
||||
mypy
|
||||
flake8
|
||||
flake8-black
|
||||
mobase-stubs
|
||||
git+https://github.com/Holt59/bain-wizard-interpreter
|
||||
git+https://github.com/Holt59/bain-wizard-interpreter.git@v0.0.3
|
||||
|
||||
@@ -6,6 +6,12 @@ extend-ignore =
|
||||
E203,
|
||||
exclude = lib,ui
|
||||
|
||||
[isort]
|
||||
profile = black
|
||||
multi_line_output = 3
|
||||
known_mobase = mobase
|
||||
sections=FUTURE,STDLIB,THIRDPARTY,MOBASE,FIRSTPARTY,LOCALFOLDER
|
||||
|
||||
[mypy]
|
||||
warn_return_any = True
|
||||
warn_unused_configs = True
|
||||
@@ -28,18 +34,21 @@ ignore_missing_imports = True
|
||||
|
||||
[tox:tox]
|
||||
skipsdist = true
|
||||
envlist = py38-lint
|
||||
envlist = py310-lint
|
||||
|
||||
[testenv:py38-lint]
|
||||
[testenv:py310-lint]
|
||||
skip_install = true
|
||||
deps =
|
||||
git+https://github.com/TilmanK/PyQt6-stubs.git
|
||||
mobase-stubs==2.5.0.dev5
|
||||
git+https://github.com/Holt59/bain-wizard-interpreter@v0.0.3
|
||||
black
|
||||
mypy
|
||||
flake8
|
||||
flake8-black
|
||||
mobase-stubs
|
||||
git+https://github.com/Holt59/bain-wizard-interpreter
|
||||
isort
|
||||
commands =
|
||||
black src --check --diff --exclude "lib" --exclude "ui"
|
||||
isort -c src
|
||||
flake8 src
|
||||
mypy src
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
add_custom_target(installer_wizard ALL)
|
||||
mo2_configure_python(installer_wizard MODULE)
|
||||
+101
-65
@@ -1,36 +1,36 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Mapping, Optional, Tuple
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QPixmap, QKeySequence, QFontDatabase
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
import mobase
|
||||
|
||||
from .ui.wizardinstallerdialog import Ui_WizardInstallerDialog
|
||||
from .ui.wizardinstallerpage import Ui_WizardInstallerPage
|
||||
from .ui.wizardinstallerrequires import Ui_WizardInstallerRequires
|
||||
from .ui.wizardinstallercomplete import Ui_WizardInstallerComplete
|
||||
from .ui.wizardinstallererror import Ui_WizardInstallerError
|
||||
from typing import Any, Dict, List, Mapping, Optional, Set, Tuple
|
||||
|
||||
from antlr4 import ParserRuleContext
|
||||
from PyQt6 import QtWidgets
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFontDatabase, QKeySequence, QPixmap, QResizeEvent, QShortcut
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from wizard.contexts import (
|
||||
WizardInterpreterContext,
|
||||
WizardTopLevelContext,
|
||||
WizardSelectContext,
|
||||
WizardSelectOneContext,
|
||||
WizardSelectManyContext,
|
||||
WizardRequireVersionsContext,
|
||||
WizardSelectContext,
|
||||
WizardSelectManyContext,
|
||||
WizardSelectOneContext,
|
||||
WizardTerminationContext,
|
||||
WizardTopLevelContext,
|
||||
)
|
||||
from wizard.errors import WizardError
|
||||
from wizard.interpreter import WizardInterpreter
|
||||
from wizard.manager import SelectOption
|
||||
from wizard.runner import WizardRunnerState, WizardRunnerKeywordVisitor
|
||||
from wizard.runner import WizardRunnerKeywordVisitor, WizardRunnerState
|
||||
from wizard.tweaks import WizardINISetting
|
||||
from wizard.value import Plugin
|
||||
|
||||
import mobase
|
||||
|
||||
from .ui.wizardinstallercomplete import Ui_WizardInstallerComplete
|
||||
from .ui.wizardinstallerdialog import Ui_WizardInstallerDialog
|
||||
from .ui.wizardinstallererror import Ui_WizardInstallerError
|
||||
from .ui.wizardinstallerpage import Ui_WizardInstallerPage
|
||||
from .ui.wizardinstallerrequires import Ui_WizardInstallerRequires
|
||||
from .utils import make_ini_tweaks
|
||||
|
||||
WizardRunnerContext = WizardInterpreterContext[WizardRunnerState, Any]
|
||||
@@ -103,13 +103,22 @@ class WizardInstallerRequiresVersionPage(QtWidgets.QWidget):
|
||||
game = organizer.managedGame()
|
||||
|
||||
okIcon = QPixmap(":/MO/gui/checked-checkbox").scaled(
|
||||
16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
||||
16,
|
||||
16,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
noIcon = QPixmap(":/MO/gui/unchecked-checkbox").scaled(
|
||||
16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
||||
16,
|
||||
16,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
koIcon = QPixmap(":/MO/gui/indeterminate-checkbox").scaled(
|
||||
16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
||||
16,
|
||||
16,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
|
||||
self.ui.labelGame.setText(game.gameName())
|
||||
@@ -142,6 +151,7 @@ class WizardInstallerSelectPage(QtWidgets.QWidget):
|
||||
|
||||
_context: WizardSelectContext
|
||||
_images: Mapping[Path, Path]
|
||||
_currentImage: QPixmap
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -190,11 +200,11 @@ class WizardInstallerSelectPage(QtWidgets.QWidget):
|
||||
for i, option in enumerate(context.options):
|
||||
item = self.ui.optionList.item(i)
|
||||
if isinstance(context, WizardSelectManyContext):
|
||||
item.setFlags(item.flags() | Qt.ItemIsUserCheckable) # type: ignore
|
||||
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||
if option in previous_options:
|
||||
item.setCheckState(Qt.Checked)
|
||||
item.setCheckState(Qt.CheckState.Checked)
|
||||
else:
|
||||
item.setCheckState(Qt.Unchecked)
|
||||
item.setCheckState(Qt.CheckState.Unchecked)
|
||||
elif (
|
||||
isinstance(context, WizardSelectOneContext)
|
||||
and option in previous_options
|
||||
@@ -219,7 +229,7 @@ class WizardInstallerSelectPage(QtWidgets.QWidget):
|
||||
for i, option in enumerate(options):
|
||||
item = self.ui.optionList.item(i)
|
||||
item.setText(option.name)
|
||||
item.setData(Qt.UserRole, option)
|
||||
item.setData(Qt.ItemDataRole.UserRole, option)
|
||||
|
||||
# No item selected, select the first one:
|
||||
if not self.ui.optionList.currentItem():
|
||||
@@ -228,24 +238,41 @@ class WizardInstallerSelectPage(QtWidgets.QWidget):
|
||||
def onCurrentItemChanged(
|
||||
self, current: QtWidgets.QListWidgetItem, previous: QtWidgets.QListWidgetItem
|
||||
):
|
||||
option: SelectOption = current.data(Qt.UserRole)
|
||||
option: SelectOption = current.data(Qt.ItemDataRole.UserRole)
|
||||
self.ui.descriptionTextEdit.setText(option.description)
|
||||
image = option.image
|
||||
if image and Path(image) in self._images:
|
||||
target = self._images[Path(image)]
|
||||
self.ui.imageLabel.setPixmap(QPixmap(target.as_posix()))
|
||||
self._currentImage = QPixmap(target.as_posix())
|
||||
else:
|
||||
self.ui.imageLabel.setPixmap(QPixmap())
|
||||
self._currentImage = QPixmap()
|
||||
|
||||
self.ui.imageLabel.setPixmap(self.getResizedImage())
|
||||
|
||||
def getResizedImage(self) -> QPixmap:
|
||||
if self._currentImage.isNull():
|
||||
return self._currentImage
|
||||
return self._currentImage.scaled(
|
||||
self.ui.imageLabel.size(),
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
|
||||
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||
super().resizeEvent(event)
|
||||
self.ui.imageLabel.setPixmap(self.getResizedImage())
|
||||
|
||||
def selectedOptions(self) -> List[SelectOption]:
|
||||
options = []
|
||||
if isinstance(self._context, WizardSelectOneContext):
|
||||
options.append(self.ui.optionList.currentItem().data(Qt.UserRole))
|
||||
options.append(
|
||||
self.ui.optionList.currentItem().data(Qt.ItemDataRole.UserRole)
|
||||
)
|
||||
else:
|
||||
for i in range(self.ui.optionList.count()):
|
||||
item = self.ui.optionList.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
options.append(item.data(Qt.UserRole))
|
||||
if item.checkState() == Qt.CheckState.Checked:
|
||||
options.append(item.data(Qt.ItemDataRole.UserRole))
|
||||
return options
|
||||
|
||||
def selected(self) -> WizardSelectContext:
|
||||
@@ -278,29 +305,35 @@ class WizardInstallerCompletePage(QtWidgets.QWidget):
|
||||
kvisitor: WizardRunnerKeywordVisitor = context.factory.kvisitor # type: ignore
|
||||
|
||||
# The list of plugins in selected sub-packages:
|
||||
plugins: List[str] = []
|
||||
plugins: Set[Plugin] = set()
|
||||
|
||||
# SubPackages:
|
||||
for sp in kvisitor.subpackages:
|
||||
item = QtWidgets.QListWidgetItem()
|
||||
item.setText(sp.name)
|
||||
if sp.name in self.state.subpackages:
|
||||
item.setCheckState(Qt.Checked)
|
||||
item.setCheckState(Qt.CheckState.Checked)
|
||||
else:
|
||||
item.setCheckState(Qt.Unchecked)
|
||||
item.setFlags(item.flags() & ~Qt.ItemIsUserCheckable) # type: ignore
|
||||
plugins.extend(kvisitor.plugins_for(sp))
|
||||
item.setCheckState(Qt.CheckState.Unchecked)
|
||||
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsUserCheckable)
|
||||
plugins.update(kvisitor.plugins_for(sp))
|
||||
self.ui.subpackagesList.addItem(item)
|
||||
|
||||
# Switch the renamed plugins:
|
||||
for plugin in list(plugins):
|
||||
if plugin in self.state.renames:
|
||||
plugins.remove(plugin)
|
||||
plugins.add(Plugin(self.state.renames[plugin]))
|
||||
|
||||
# Plugins:
|
||||
for plugin in sorted(plugins):
|
||||
item = QtWidgets.QListWidgetItem()
|
||||
item.setText(plugin)
|
||||
item.setText(plugin.name)
|
||||
if plugin in self.state.plugins:
|
||||
item.setCheckState(Qt.Checked)
|
||||
item.setCheckState(Qt.CheckState.Checked)
|
||||
else:
|
||||
item.setCheckState(Qt.Unchecked)
|
||||
item.setFlags(item.flags() & ~Qt.ItemIsUserCheckable) # type: ignore
|
||||
item.setCheckState(Qt.CheckState.Unchecked)
|
||||
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsUserCheckable)
|
||||
self.ui.pluginsList.addItem(item)
|
||||
|
||||
# INI Tweaks:
|
||||
@@ -316,11 +349,11 @@ class WizardInstallerCompletePage(QtWidgets.QWidget):
|
||||
for file, ftweaks in tweaks.items():
|
||||
item = QtWidgets.QListWidgetItem()
|
||||
item.setText(file.replace("\\", "/"))
|
||||
item.setData(Qt.UserRole, ftweaks)
|
||||
item.setData(Qt.ItemDataRole.UserRole, ftweaks)
|
||||
self.ui.tweaksList.addItem(item)
|
||||
|
||||
self.ui.tweaksTextEdit.setFont(
|
||||
QFontDatabase.systemFont(QFontDatabase.FixedFont)
|
||||
QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
|
||||
)
|
||||
self.ui.tweaksList.setCurrentRow(0)
|
||||
|
||||
@@ -337,7 +370,7 @@ class WizardInstallerCompletePage(QtWidgets.QWidget):
|
||||
# Clear text area and create the tweaks:
|
||||
self.ui.tweaksTextEdit.clear()
|
||||
self.ui.tweaksTextEdit.appendPlainText(
|
||||
make_ini_tweaks(current.data(Qt.UserRole))
|
||||
make_ini_tweaks(current.data(Qt.ItemDataRole.UserRole))
|
||||
)
|
||||
|
||||
def subpackages(self) -> List[str]:
|
||||
@@ -349,21 +382,20 @@ class WizardInstallerCompletePage(QtWidgets.QWidget):
|
||||
sp: List[str] = []
|
||||
for i in range(self.ui.subpackagesList.count()):
|
||||
item = self.ui.subpackagesList.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
if item.checkState() == Qt.CheckState.Checked:
|
||||
sp.append(item.text())
|
||||
return sp
|
||||
|
||||
def plugins(self) -> List[str]:
|
||||
def plugins(self) -> Dict[str, bool]:
|
||||
"""
|
||||
Returns:
|
||||
The list of plugins selected in the UI (either automatically by the
|
||||
interpreter or by the user).
|
||||
"""
|
||||
sp: List[str] = []
|
||||
sp: Dict[str, bool] = {}
|
||||
for i in range(self.ui.pluginsList.count()):
|
||||
item = self.ui.pluginsList.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
sp.append(item.text())
|
||||
sp[item.text()] = item.checkState() == Qt.CheckState.Checked
|
||||
return sp
|
||||
|
||||
def tweaks(self) -> Mapping[str, List[WizardINISetting]]:
|
||||
@@ -375,7 +407,7 @@ class WizardInstallerCompletePage(QtWidgets.QWidget):
|
||||
rets = {}
|
||||
for i in range(self.ui.tweaksList.count()):
|
||||
item = self.ui.tweaksList.item(i)
|
||||
rets[item.text()] = item.data(Qt.UserRole)
|
||||
rets[item.text()] = item.data(Qt.ItemDataRole.UserRole)
|
||||
return rets
|
||||
|
||||
|
||||
@@ -396,7 +428,7 @@ class WizardInstallerCancelPage(QtWidgets.QWidget):
|
||||
)
|
||||
self.ui.iconLabel.setPixmap(
|
||||
self.style()
|
||||
.standardIcon(QtWidgets.QStyle.SP_MessageBoxWarning)
|
||||
.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_MessageBoxWarning)
|
||||
.pixmap(24, 24)
|
||||
)
|
||||
self.ui.messageEdit.setText(context.message())
|
||||
@@ -421,7 +453,7 @@ class WizardInstallerErrorPage(QtWidgets.QWidget):
|
||||
)
|
||||
self.ui.iconLabel.setPixmap(
|
||||
self.style()
|
||||
.standardIcon(QtWidgets.QStyle.SP_MessageBoxCritical)
|
||||
.standardIcon(QtWidgets.QStyle.StandardPixmap.SP_MessageBoxCritical)
|
||||
.pixmap(24, 24)
|
||||
)
|
||||
self.ui.messageEdit.setText(str(error))
|
||||
@@ -478,18 +510,21 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
self.ui = Ui_WizardInstallerDialog()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
|
||||
self.setWindowFlag(Qt.WindowType.WindowContextHelpButtonHint, False)
|
||||
self.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, True)
|
||||
|
||||
# mobase.GuessedString contains multiple names with various level of
|
||||
# "guess". Using .variants() returns the list of names, and doing str(name)
|
||||
# will return the most-likely value.
|
||||
for value in name.variants():
|
||||
self.ui.nameCombo.addItem(value)
|
||||
self.ui.nameCombo.completer().setCaseSensitivity(Qt.CaseSensitive)
|
||||
self.ui.nameCombo.completer().setCaseSensitivity(
|
||||
Qt.CaseSensitivity.CaseSensitive
|
||||
)
|
||||
self.ui.nameCombo.setCurrentIndex(self.ui.nameCombo.findText(str(name)))
|
||||
|
||||
# We need to connect the Cancel / Manual buttons. We can of course use
|
||||
# PyQt5 signal/slot syntax:
|
||||
# PyQt6 signal/slot syntax:
|
||||
self.ui.cancelBtn.clicked.connect(self.reject)
|
||||
|
||||
def manualClicked():
|
||||
@@ -501,9 +536,8 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
self.ui.prevBtn.clicked.connect(self.previousClicked)
|
||||
self.ui.nextBtn.clicked.connect(self.nextClicked)
|
||||
|
||||
QtWidgets.QShortcut(QKeySequence(Qt.Key_Backspace), self).activated.connect(
|
||||
self.previousClicked
|
||||
)
|
||||
backShortcut = QShortcut(QKeySequence(Qt.Key.Key_Backspace), self)
|
||||
backShortcut.activated.connect(self.previousClicked) # type: ignore
|
||||
|
||||
@property
|
||||
def scriptButtonClicked(self) -> pyqtSignal:
|
||||
@@ -524,7 +558,7 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
assert isinstance(widget, WizardInstallerCompletePage)
|
||||
return widget.subpackages()
|
||||
|
||||
def plugins(self):
|
||||
def plugins(self) -> Dict[str, bool]:
|
||||
"""
|
||||
Returns:
|
||||
The list of plugins to install and enable. Only valid if exec() returned
|
||||
@@ -543,7 +577,9 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
"""
|
||||
widget = self.ui.stackedWidget.currentWidget()
|
||||
assert isinstance(widget, WizardInstallerCompletePage)
|
||||
return widget.state.renames
|
||||
return {
|
||||
plugin.name: new_name for plugin, new_name in widget.state.renames.items()
|
||||
}
|
||||
|
||||
def tweaks(self) -> Mapping[str, List[WizardINISetting]]:
|
||||
"""
|
||||
@@ -612,7 +648,7 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
def _update_focus(self):
|
||||
widget = self.ui.stackedWidget.currentWidget()
|
||||
if isinstance(widget, WizardInstallerSelectPage):
|
||||
widget.ui.optionList.setFocus(True)
|
||||
widget.ui.optionList.setFocus()
|
||||
|
||||
def _update_prev_button(self):
|
||||
self.ui.prevBtn.setDisabled(self.ui.stackedWidget.currentIndex() <= 0)
|
||||
@@ -624,13 +660,13 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
|
||||
name: str = self.ui.nextBtn.text()
|
||||
if isinstance(widget, WizardInstallerSelectPage):
|
||||
name = self._tr("Next")
|
||||
name = self.tr("Next")
|
||||
elif isinstance(widget, WizardInstallerRequiresVersionPage):
|
||||
name = self._tr("Install anyway")
|
||||
name = self.tr("Install anyway")
|
||||
elif isinstance(widget, (WizardInstallerCancelPage, WizardInstallerErrorPage)):
|
||||
self.ui.nextBtn.setDisabled(True)
|
||||
else:
|
||||
name = self._tr("Install")
|
||||
name = self.tr("Install")
|
||||
|
||||
self.ui.nextBtn.setText(name)
|
||||
|
||||
@@ -683,5 +719,5 @@ class WizardInstallerDialog(QtWidgets.QDialog):
|
||||
self._update_focus()
|
||||
return super().exec()
|
||||
|
||||
def _tr(self, txt: str) -> str:
|
||||
return QtWidgets.QApplication.translate("WizardInstaller", txt)
|
||||
def tr(self, str):
|
||||
return QApplication.translate("WizardInstallerDialog", str)
|
||||
|
||||
+24
-29
@@ -1,21 +1,20 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional, List, Union
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
# MO2 ships with PyQt5, so you can use it in your plugins:
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt6 import QtWidgets
|
||||
|
||||
# MO2 ships with PyQt6, so you can use it in your plugins:
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from wizard.runner import WizardRunnerState
|
||||
|
||||
import mobase
|
||||
|
||||
from wizard.runner import WizardRunnerState
|
||||
|
||||
from .dialog import WizardInstallerDialog
|
||||
from .runner import make_interpreter
|
||||
from .utils import make_ini_tweaks, merge_ini_tweaks
|
||||
@@ -53,14 +52,17 @@ class WizardInstaller(mobase.IPluginInstallerSimple):
|
||||
def name(self):
|
||||
return "BAIN Wizard Installer"
|
||||
|
||||
def localizedName(self) -> str:
|
||||
return self.tr("BAIN Wizard Installer")
|
||||
|
||||
def author(self):
|
||||
return "Holt59"
|
||||
|
||||
def description(self):
|
||||
return self._tr("Installer for BAIN archive containing wizard scripts.")
|
||||
return self.tr("Installer for BAIN archive containing wizard scripts.")
|
||||
|
||||
def version(self):
|
||||
return mobase.VersionInfo(1, 0, 1)
|
||||
return mobase.VersionInfo(1, 0, 2)
|
||||
|
||||
def isActive(self):
|
||||
return self._organizer.pluginSetting(self.name(), "enabled")
|
||||
@@ -134,10 +136,10 @@ class WizardInstaller(mobase.IPluginInstallerSimple):
|
||||
|
||||
def _hasFomodInstaller(self) -> bool:
|
||||
# Do not consider the NCC installer.
|
||||
return bool(self._organizer.pluginSetting("Fomod Installer", "enabled"))
|
||||
return self._organizer.isPluginEnabled("Fomod Installer")
|
||||
|
||||
def _hasOmodInstaller(self) -> bool:
|
||||
return bool(self._organizer.pluginSetting("Omod Installer", "enabled"))
|
||||
return self._organizer.isPluginEnabled("Omod Installer")
|
||||
|
||||
def _getWizardArchiveBase(
|
||||
self, tree: mobase.IFileTree, data_name: str, checker: mobase.ModDataChecker
|
||||
@@ -300,7 +302,7 @@ class WizardInstaller(mobase.IPluginInstallerSimple):
|
||||
|
||||
# Note: Unlike the official installer, we do not have a "silent" setting,
|
||||
# but it is really simple to add it.
|
||||
if dialog.exec() == QtWidgets.QDialog.Accepted:
|
||||
if dialog.exec() == QtWidgets.QDialog.DialogCode.Accepted:
|
||||
|
||||
# We update the name with the user specified one:
|
||||
name.update(dialog.name(), mobase.GuessQuality.USER)
|
||||
@@ -321,21 +323,6 @@ class WizardInstaller(mobase.IPluginInstallerSimple):
|
||||
|
||||
tree.merge(entry)
|
||||
|
||||
# Move all non-selected plugins to "optional":
|
||||
optionals: List[mobase.FileTreeEntry] = []
|
||||
for entry in tree:
|
||||
if entry.isFile() and (
|
||||
entry.hasSuffix(["esl", "esp", "esm"])
|
||||
and entry.name() not in dialog.plugins()
|
||||
):
|
||||
optionals.append(entry)
|
||||
|
||||
# Do not keep empty optional folder:
|
||||
if optionals:
|
||||
optional = tree.addDirectory("optional")
|
||||
for entry in optionals:
|
||||
optional.insert(entry)
|
||||
|
||||
# Handle renames:
|
||||
for original, new in dialog.renames().items():
|
||||
# Entry should be at the root:
|
||||
@@ -347,6 +334,14 @@ class WizardInstaller(mobase.IPluginInstallerSimple):
|
||||
|
||||
tree.move(entry, new)
|
||||
|
||||
# Move not selected plugins to optional:
|
||||
for plugin, enabled in dialog.plugins().items():
|
||||
if not enabled:
|
||||
entry = tree.find(plugin)
|
||||
if not entry:
|
||||
continue # silently fail since the plugin should be disabled
|
||||
tree.addDirectory("optional").insert(entry)
|
||||
|
||||
# TODO: INI Tweaks:
|
||||
alltweaks = dialog.tweaks()
|
||||
|
||||
@@ -400,7 +395,7 @@ class WizardInstaller(mobase.IPluginInstallerSimple):
|
||||
else:
|
||||
return mobase.InstallResult.CANCELED
|
||||
|
||||
def _tr(self, str):
|
||||
def tr(self, str) -> str:
|
||||
# We need this to translate string in Python. Check the common documentation
|
||||
# for more details:
|
||||
return QApplication.translate("WizardInstaller", str)
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>WizardInstaller</name>
|
||||
<message>
|
||||
<location filename="installer.py" line="56" />
|
||||
<source>BAIN Wizard Installer</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="installer.py" line="62" />
|
||||
<source>Installer for BAIN archive containing wizard scripts.</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerComplete</name>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallercomplete.ui" line="0" />
|
||||
<source>The installer script has finished. The following sub-packages and plugins will be installed.</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallercomplete.ui" line="0" />
|
||||
<source>Sub-Packages:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallercomplete.ui" line="0" />
|
||||
<source>Plugins:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallercomplete.ui" line="0" />
|
||||
<source>INI Tweaks:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallercomplete.ui" line="0" />
|
||||
<source>Notes:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerDialog</name>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<location filename="dialog.py" line="663" />
|
||||
<source>Next</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dialog.py" line="665" />
|
||||
<source>Install anyway</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dialog.py" line="669" />
|
||||
<source>Install</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<source>BAIN Wizard Installer</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<source>Name</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<source>Manual</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<source>Script</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<source>Back</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerdialog.ui" line="0" />
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerPage</name>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerpage.ui" line="0" />
|
||||
<source>Options:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerpage.ui" line="0" />
|
||||
<source>Description:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WizardInstallerRequires</name>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source><html><head/><body><p><span style=" font-weight:600;">Warning:</span> The following version requirements are not met for using this installer.</p></body></html></source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Requirements</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Graphics Extender</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Wrye Bash</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Need</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Game</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>N/A</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Have</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui\wizardinstallerrequires.ui" line="0" />
|
||||
<source>Script Extender</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
+7
-8
@@ -1,17 +1,16 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Iterable, List, Optional
|
||||
|
||||
import mobase
|
||||
|
||||
from wizard.interpreter import WizardInterpreter
|
||||
from wizard.manager import ManagerModInterface
|
||||
from wizard.severity import SeverityContext
|
||||
from wizard.value import SubPackage, SubPackages
|
||||
from wizard.utils import make_runner_context_factory
|
||||
from wizard.value import SubPackage, SubPackages
|
||||
|
||||
import mobase
|
||||
|
||||
|
||||
class MO2SubPackage(SubPackage):
|
||||
@@ -165,11 +164,11 @@ class MO2ManagerModInterface(ManagerModInterface):
|
||||
def getPluginStatus(self, filename) -> int:
|
||||
state = self._organizer.pluginList().state(filename)
|
||||
|
||||
if state & mobase.PluginState.MISSING:
|
||||
return -1
|
||||
if state & mobase.PluginState.INACTIVE:
|
||||
if state == mobase.PluginState.ACTIVE:
|
||||
return 2
|
||||
if state == mobase.PluginState.INACTIVE:
|
||||
return 0 # Or 1?
|
||||
return 2
|
||||
return -1
|
||||
|
||||
def getFilename(self, filepath: str) -> str:
|
||||
path = self._resolve(filepath)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
@@ -45,7 +45,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -68,7 +68,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="titleLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@@ -82,6 +82,16 @@
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="markdown">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
@@ -27,7 +27,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0,1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,2,0,1">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
@@ -93,7 +93,7 @@
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@@ -136,7 +136,10 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import re
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Mapping
|
||||
|
||||
|
||||
Reference in New Issue
Block a user