PyQt6 Upgrade (#1685)

This puts Vorta on PyQt6 and starts a new main branch 0.9.

---------

Co-authored-by: real-yfprojects <real-yfprojects@users.noreply.github.com>
Co-authored-by: Manu <3916435+m3nu@users.noreply.github.com>
Co-authored-by: yfprojects <62463991+real-yfprojects@users.noreply.github.com>
This commit is contained in:
i1sm3ky 2023-04-17 15:47:01 +05:30 committed by GitHub
parent 8571ef6cb8
commit 7535f92ac8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 294 additions and 259 deletions

View File

@ -65,7 +65,8 @@ jobs:
sudo apt update && sudo apt install -y \ sudo apt update && sudo apt install -y \
xvfb libssl-dev openssl libacl1-dev libacl1 build-essential borgbackup \ xvfb libssl-dev openssl libacl1-dev libacl1 build-essential borgbackup \
libxkbcommon-x11-0 dbus-x11 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \ libxkbcommon-x11-0 dbus-x11 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 libxcb-shape0 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 libxcb-shape0 \
libegl1 libxcb-cursor0
- name: Install system dependencies (macOS) - name: Install system dependencies (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
run: | run: |

View File

@ -20,11 +20,11 @@ def create_symlink(folder: Path) -> None:
""" """
sibbling = Path(str(folder).replace("MacOS", "")) sibbling = Path(str(folder).replace("MacOS", ""))
# PyQt5/Qt/qml/QtQml/Models.2 # PyQt6/Qt/qml/QtQml/Models.2
root = str(sibbling).partition("Contents")[2].lstrip("/") root = str(sibbling).partition("Contents")[2].lstrip("/")
# ../../../../ # ../../../../
backward = "../" * (root.count("/") + 1) backward = "../" * (root.count("/") + 1)
# ../../../../Resources/PyQt5/Qt/qml/QtQml/Models.2 # ../../../../Resources/PyQt6/Qt/qml/QtQml/Models.2
good_path = f"{backward}Resources/{root}" good_path = f"{backward}Resources/{root}"
folder.symlink_to(good_path) folder.symlink_to(good_path)
@ -51,7 +51,7 @@ def fix_dll(dll: Path) -> None:
return None return None
return f"@loader_path{good_path}/{basename}" return f"@loader_path{good_path}/{basename}"
# Resources/PyQt5/Qt/qml/QtQuick/Controls.2/Fusion # Resources/PyQt6/Qt/qml/QtQuick/Controls.2/Fusion
root = str(dll.parent).partition("Contents")[2][1:] root = str(dll.parent).partition("Contents")[2][1:]
# /../../../../../../.. # /../../../../../../..
backward = "/.." * (root.count("/") + 1) backward = "/.." * (root.count("/") + 1)

View File

@ -39,14 +39,14 @@ python_requires = >=3.7
install_requires = install_requires =
platformdirs >=3.0.0, <4.0.0; sys_platform == 'darwin' # for macOS: breaking changes in 3.0.0, platformdirs >=3.0.0, <4.0.0; sys_platform == 'darwin' # for macOS: breaking changes in 3.0.0,
platformdirs >=2.6.0, <4.0.0; sys_platform != 'darwin' # for others: 2.6+ works consistently. platformdirs >=2.6.0, <4.0.0; sys_platform != 'darwin' # for others: 2.6+ works consistently.
pyqt5 pyqt6
peewee peewee
psutil psutil
setuptools setuptools
secretstorage; sys_platform != 'darwin' secretstorage; sys_platform != 'darwin'
pyobjc-core; sys_platform == 'darwin' pyobjc-core < 9.1; sys_platform == 'darwin'
pyobjc-framework-Cocoa; sys_platform == 'darwin' pyobjc-framework-Cocoa < 9.1; sys_platform == 'darwin'
pyobjc-framework-LaunchServices; sys_platform == 'darwin' pyobjc-framework-LaunchServices < 9.1; sys_platform == 'darwin'
tests_require = tests_require =
pytest pytest
pytest-qt pytest-qt
@ -100,7 +100,7 @@ commands=flake8 src tests
max_line_length = 120 max_line_length = 120
[pylint.master] [pylint.master]
extension-pkg-whitelist=PyQt5 extension-pkg-whitelist=PyQt6
load-plugins= load-plugins=
[pylint.messages control] [pylint.messages control]

View File

@ -14,7 +14,7 @@ from vorta.utils import parse_args
def main(): def main():
def exception_handler(type, value, tb): def exception_handler(type, value, tb):
from traceback import format_exception from traceback import format_exception
from PyQt5.QtWidgets import QMessageBox from PyQt6.QtWidgets import QMessageBox
logger.critical( logger.critical(
"Uncaught exception, file a report at https://github.com/borgbase/vorta/issues/new/choose", "Uncaught exception, file a report at https://github.com/borgbase/vorta/issues/new/choose",

View File

@ -1 +1 @@
__version__ = '0.8.11' __version__ = '0.9.0'

View File

@ -3,8 +3,8 @@ import os
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Tuple from typing import Any, Dict, List, Tuple
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtWidgets import QMessageBox from PyQt6.QtWidgets import QMessageBox
from vorta.borg.break_lock import BorgBreakJob from vorta.borg.break_lock import BorgBreakJob
from vorta.borg.create import BorgCreateJob from vorta.borg.create import BorgCreateJob
from vorta.borg.jobs_manager import JobsManager from vorta.borg.jobs_manager import JobsManager
@ -179,10 +179,10 @@ class VortaApp(QtSingleApplication):
def _alert_missing_borg(self): def _alert_missing_borg(self):
msg = QMessageBox() msg = QMessageBox()
msg.setIcon(QMessageBox.Critical) msg.setIcon(QMessageBox.Icon.Critical)
msg.setText(self.tr("No Borg Binary Found")) msg.setText(self.tr("No Borg Binary Found"))
msg.setInformativeText(self.tr("Vorta was unable to locate a usable Borg Backup binary.")) msg.setInformativeText(self.tr("Vorta was unable to locate a usable Borg Backup binary."))
msg.setStandardButtons(QMessageBox.Ok) msg.setStandardButtons(QMessageBox.StandardButton.Ok)
msg.exec() msg.exec()
def check_darwin_permissions(self): def check_darwin_permissions(self):
@ -201,8 +201,8 @@ class VortaApp(QtSingleApplication):
test_path = Path('~/Library/Cookies').expanduser() test_path = Path('~/Library/Cookies').expanduser()
if test_path.exists() and not os.access(test_path, os.R_OK): if test_path.exists() and not os.access(test_path, os.R_OK):
msg = QMessageBox() msg = QMessageBox()
msg.setIcon(QMessageBox.Warning) msg.setIcon(QMessageBox.Icon.Warning)
msg.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) msg.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse)
msg.setText(self.tr("Vorta needs Full Disk Access for complete Backups")) msg.setText(self.tr("Vorta needs Full Disk Access for complete Backups"))
msg.setInformativeText( msg.setInformativeText(
self.tr( self.tr(
@ -212,7 +212,7 @@ class VortaApp(QtSingleApplication):
"System Preferences > Security & Privacy</a>." "System Preferences > Security & Privacy</a>."
) )
) )
msg.setStandardButtons(QMessageBox.Ok) msg.setStandardButtons(QMessageBox.StandardButton.Ok)
msg.exec() msg.exec()
def react_to_log(self, mgs, context): def react_to_log(self, mgs, context):
@ -225,9 +225,9 @@ class VortaApp(QtSingleApplication):
repo_url = context.get('repo_url') repo_url = context.get('repo_url')
msg = QMessageBox() msg = QMessageBox()
msg.setWindowTitle(self.tr("Repository In Use")) msg.setWindowTitle(self.tr("Repository In Use"))
msg.setIcon(QMessageBox.Critical) msg.setIcon(QMessageBox.Icon.Critical)
abortButton = msg.addButton(self.tr("Abort"), QMessageBox.RejectRole) abortButton = msg.addButton(self.tr("Abort"), QMessageBox.ButtonRole.RejectRole)
msg.addButton(self.tr("Continue"), QMessageBox.AcceptRole) msg.addButton(self.tr("Continue"), QMessageBox.ButtonRole.AcceptRole)
msg.setDefaultButton(abortButton) msg.setDefaultButton(abortButton)
msg.setText(self.tr(f"The repository at {repo_url} might be in use elsewhere.")) msg.setText(self.tr(f"The repository at {repo_url} might be in use elsewhere."))
msg.setInformativeText( msg.setInformativeText(
@ -324,7 +324,7 @@ class VortaApp(QtSingleApplication):
# Create QMessageBox # Create QMessageBox
msg = QMessageBox() msg = QMessageBox()
msg.setIcon(QMessageBox.Icon.Critical) # changed for warning msg.setIcon(QMessageBox.Icon.Critical) # changed for warning
msg.setStandardButtons(QMessageBox.Ok) msg.setStandardButtons(QMessageBox.StandardButton.Ok)
msg.setWindowTitle(self.tr('Repo Check Failed')) msg.setWindowTitle(self.tr('Repo Check Failed'))
if returncode == 1: if returncode == 1:

View File

@ -11,8 +11,8 @@ from collections import namedtuple
from datetime import datetime as dt from datetime import datetime as dt
from subprocess import PIPE, Popen, TimeoutExpired from subprocess import PIPE, Popen, TimeoutExpired
from threading import Lock from threading import Lock
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
from vorta import application from vorta import application
from vorta.borg.jobs_manager import JobInterface from vorta.borg.jobs_manager import JobInterface
from vorta.i18n import trans_late, translate from vorta.i18n import trans_late, translate

View File

@ -1,5 +1,5 @@
import tempfile import tempfile
from PyQt5.QtCore import QModelIndex, Qt from PyQt6.QtCore import QModelIndex, Qt
from vorta.utils import borg_compat from vorta.utils import borg_compat
from vorta.views.extract_dialog import ExtractTree, FileData from vorta.views.extract_dialog import ExtractTree, FileData
from vorta.views.partials.treemodel import FileSystemItem, path_to_str from vorta.views.partials.treemodel import FileSystemItem, path_to_str

View File

@ -2,7 +2,7 @@ import logging
import queue import queue
import threading import threading
from abc import abstractmethod from abc import abstractmethod
from PyQt5.QtCore import QObject from PyQt6.QtCore import QObject
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ internationalisation (i18n) support code
""" """
import logging import logging
import os import os
from PyQt5.QtCore import QLocale, QTranslator from PyQt6.QtCore import QLocale, QTranslator
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,7 +1,7 @@
import logging import logging
import os import os
from PyQt5 import QtDBus from PyQt6 import QtDBus
from PyQt5.QtCore import QVariant from PyQt6.QtCore import QMetaType, QVariant
from vorta.keyring.abc import VortaKeyring from vorta.keyring.abc import VortaKeyring
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -47,9 +47,9 @@ class VortaKWallet5Keyring(VortaKeyring):
def get_result(self, method, args=[]): def get_result(self, method, args=[]):
if args: if args:
result = self.iface.callWithArgumentList(QtDBus.QDBus.AutoDetect, method, args) result = self.iface.callWithArgumentList(QtDBus.QDBus.CallMode.AutoDetect, method, args)
else: else:
result = self.iface.call(QtDBus.QDBus.AutoDetect, method) result = self.iface.call(QtDBus.QDBus.CallMode.AutoDetect, method)
return result.arguments()[0] return result.arguments()[0]
@property @property
@ -60,7 +60,7 @@ class VortaKWallet5Keyring(VortaKeyring):
def try_unlock(self): def try_unlock(self):
wallet_name = self.get_result("networkWallet") wallet_name = self.get_result("networkWallet")
wId = QVariant(0) wId = QVariant(0)
wId.convert(4) wId.convert(QMetaType(QMetaType.Type.LongLong.value))
output = self.get_result("open", args=[wallet_name, wId, 'vorta-repo']) output = self.get_result("open", args=[wallet_name, wId, 'vorta-repo'])
try: try:
self.handle = int(output) self.handle = int(output)

View File

@ -16,7 +16,7 @@ logger = logging.getLogger()
def init_logger(background=False): def init_logger(background=False):
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
logging.getLogger('peewee').setLevel(logging.INFO) logging.getLogger('peewee').setLevel(logging.INFO)
logging.getLogger('PyQt5').setLevel(logging.INFO) logging.getLogger('PyQt6').setLevel(logging.INFO)
# create logging format # create logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

View File

@ -2,8 +2,8 @@ import logging
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from typing import Any, List, Mapping, NamedTuple, Optional from typing import Any, List, Mapping, NamedTuple, Optional
from PyQt5 import QtDBus from PyQt6 import QtDBus
from PyQt5.QtCore import QObject, QVersionNumber from PyQt6.QtCore import QObject, QVersionNumber
from vorta.network_status.abc import NetworkStatusMonitor, SystemWifiInfo from vorta.network_status.abc import NetworkStatusMonitor, SystemWifiInfo
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,6 +1,6 @@
import logging import logging
import sys import sys
from PyQt5 import QtCore, QtDBus from PyQt6 import QtCore, QtDBus
from vorta.store.models import SettingsModel from vorta.store.models import SettingsModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -77,13 +77,12 @@ class DBusNotifications(VortaNotifications):
path = "/org/freedesktop/Notifications" path = "/org/freedesktop/Notifications"
interface = "org.freedesktop.Notifications" interface = "org.freedesktop.Notifications"
app_name = "vorta" app_name = "vorta"
v = QtCore.QVariant(12321) # random int to identify all notifications id_replace = QtCore.QVariant(12321)
if v.convert(QtCore.QVariant.UInt): id_replace.convert(QtCore.QMetaType(QtCore.QMetaType.Type.UInt.value))
id_replace = v
icon = "com.borgbase.Vorta-symbolic" icon = "com.borgbase.Vorta-symbolic"
title = header title = header
text = msg text = msg
actions_list = QtDBus.QDBusArgument([], QtCore.QMetaType.QStringList) actions_list = QtDBus.QDBusArgument([], QtCore.QMetaType.Type.QStringList.value)
hint = {'urgency': self.URGENCY[level]} hint = {'urgency': self.URGENCY[level]}
time = 5000 # milliseconds for display timeout time = 5000 # milliseconds for display timeout
@ -91,7 +90,9 @@ class DBusNotifications(VortaNotifications):
notify = QtDBus.QDBusInterface(item, path, interface, bus) notify = QtDBus.QDBusInterface(item, path, interface, bus)
if notify.isValid(): if notify.isValid():
x = notify.call( x = notify.call(
QtDBus.QDBus.AutoDetect, # Call arguments for Notify interface need to match exactly:
# https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html#command-notify
QtDBus.QDBus.CallMode.AutoDetect,
"Notify", "Notify",
app_name, app_name,
id_replace, id_replace,

View File

@ -1,6 +1,6 @@
from PyQt5.QtCore import QTextStream, pyqtSignal from PyQt6.QtCore import QTextStream, pyqtSignal
from PyQt5.QtNetwork import QLocalServer, QLocalSocket from PyQt6.QtNetwork import QLocalServer, QLocalSocket
from PyQt5.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
class QtSingleApplication(QApplication): class QtSingleApplication(QApplication):
@ -52,7 +52,6 @@ class QtSingleApplication(QApplication):
if self._isRunning: if self._isRunning:
# Yes, there is. # Yes, there is.
self._outStream = QTextStream(self._outSocket) self._outStream = QTextStream(self._outSocket)
self._outStream.setCodec('UTF-8')
else: else:
# No, there isn't. # No, there isn't.
self._outSocket = None self._outSocket = None
@ -84,7 +83,6 @@ class QtSingleApplication(QApplication):
if not self._inSocket: if not self._inSocket:
return return
self._inStream = QTextStream(self._inSocket) self._inStream = QTextStream(self._inSocket)
self._inStream.setCodec('UTF-8')
self._inSocket.readyRead.connect(self._onReadyRead) self._inSocket.readyRead.connect(self._onReadyRead)
def _onReadyRead(self): def _onReadyRead(self):

View File

@ -4,9 +4,9 @@ import threading
from datetime import datetime as dt from datetime import datetime as dt
from datetime import timedelta from datetime import timedelta
from typing import Dict, NamedTuple, Optional, Tuple, Union from typing import Dict, NamedTuple, Optional, Tuple, Union
from PyQt5 import QtCore, QtDBus from PyQt6 import QtCore, QtDBus
from PyQt5.QtCore import QTimer from PyQt6.QtCore import QTimer
from PyQt5.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
from vorta import application from vorta import application
from vorta.borg.check import BorgCheckJob from vorta.borg.check import BorgCheckJob
from vorta.borg.create import BorgCreateJob from vorta.borg.create import BorgCreateJob

View File

@ -1,6 +1,6 @@
import os import os
from PyQt5.QtGui import QIcon from PyQt6.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QMenu, QSystemTrayIcon from PyQt6.QtWidgets import QApplication, QMenu, QSystemTrayIcon
from vorta.store.models import BackupProfileModel from vorta.store.models import BackupProfileModel
from vorta.utils import get_asset from vorta.utils import get_asset
@ -31,8 +31,8 @@ class TrayMenu(QSystemTrayIcon):
If XDG_CURRENT_DESKTOP isn't set, always open the tray menu (macOS) If XDG_CURRENT_DESKTOP isn't set, always open the tray menu (macOS)
""" """
if reason in [ if reason in [
QSystemTrayIcon.Trigger, QSystemTrayIcon.ActivationReason.Trigger,
QSystemTrayIcon.DoubleClick, QSystemTrayIcon.ActivationReason.DoubleClick,
] and os.environ.get('XDG_CURRENT_DESKTOP'): ] and os.environ.get('XDG_CURRENT_DESKTOP'):
self.app.toggle_main_window_visibility() self.app.toggle_main_window_visibility()

View File

@ -12,17 +12,14 @@ from datetime import datetime as dt
from functools import reduce from functools import reduce
from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar from typing import Any, Callable, Iterable, List, Optional, Tuple, TypeVar
import psutil import psutil
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtCore import QFileInfo, QThread, pyqtSignal from PyQt6.QtCore import QFileInfo, QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QFileDialog, QSystemTrayIcon from PyQt6.QtWidgets import QApplication, QFileDialog, QSystemTrayIcon
from vorta.borg._compatibility import BorgCompatibility from vorta.borg._compatibility import BorgCompatibility
from vorta.i18n import trans_late from vorta.i18n import trans_late
from vorta.log import logger from vorta.log import logger
from vorta.network_status.abc import NetworkStatusMonitor from vorta.network_status.abc import NetworkStatusMonitor
QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) # enable highdpi scaling
QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) # use highdpi icons
borg_compat = BorgCompatibility() borg_compat = BorgCompatibility()
_network_status_monitor = None _network_status_monitor = None
@ -168,10 +165,10 @@ def get_dict_from_list(dataDict, mapList):
def choose_file_dialog(parent, title, want_folder=True): def choose_file_dialog(parent, title, want_folder=True):
dialog = QFileDialog(parent, title, os.path.expanduser('~')) dialog = QFileDialog(parent, title, os.path.expanduser('~'))
dialog.setFileMode(QFileDialog.Directory if want_folder else QFileDialog.ExistingFiles) dialog.setFileMode(QFileDialog.FileMode.Directory if want_folder else QFileDialog.FileMode.ExistingFiles)
dialog.setParent(parent, QtCore.Qt.Sheet) dialog.setParent(parent, QtCore.Qt.WindowType.Sheet)
if want_folder: if want_folder:
dialog.setOption(QFileDialog.ShowDirsOnly) dialog.setOption(QFileDialog.Option.ShowDirsOnly)
return dialog return dialog

View File

@ -2,18 +2,17 @@ import logging
import sys import sys
from datetime import timedelta from datetime import timedelta
from typing import Dict, Optional from typing import Dict, Optional
from PyQt5 import QtCore, uic from PyQt6 import QtCore, uic
from PyQt5.QtCore import QItemSelectionModel, QMimeData, QPoint, Qt, pyqtSlot from PyQt6.QtCore import QItemSelectionModel, QMimeData, QPoint, Qt, pyqtSlot
from PyQt5.QtGui import QDesktopServices, QKeySequence from PyQt6.QtGui import QAction, QDesktopServices, QKeySequence, QShortcut
from PyQt5.QtWidgets import ( from PyQt6.QtWidgets import (
QAction, QAbstractItemView,
QApplication, QApplication,
QHeaderView, QHeaderView,
QInputDialog, QInputDialog,
QLayout, QLayout,
QMenu, QMenu,
QMessageBox, QMessageBox,
QShortcut,
QTableView, QTableView,
QTableWidgetItem, QTableWidgetItem,
QWidget, QWidget,
@ -77,20 +76,20 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin):
header = self.archiveTable.horizontalHeader() header = self.archiveTable.horizontalHeader()
header.setVisible(True) header.setVisible(True)
header.setSectionResizeMode(0, QHeaderView.ResizeToContents) header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(2, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(3, QHeaderView.Interactive) header.setSectionResizeMode(3, QHeaderView.ResizeMode.Interactive)
header.setSectionResizeMode(4, QHeaderView.Stretch) header.setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch)
header.setStretchLastSection(True) header.setStretchLastSection(True)
if sys.platform != 'darwin': if sys.platform != 'darwin':
self._set_status('') # Set platform-specific hints. self._set_status('') # Set platform-specific hints.
self.archiveTable.setSelectionBehavior(QTableView.SelectRows) self.archiveTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.archiveTable.setEditTriggers(QTableView.NoEditTriggers) self.archiveTable.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.archiveTable.setWordWrap(False) self.archiveTable.setWordWrap(False)
self.archiveTable.setTextElideMode(QtCore.Qt.ElideLeft) self.archiveTable.setTextElideMode(QtCore.Qt.TextElideMode.ElideLeft)
self.archiveTable.setAlternatingRowColors(True) self.archiveTable.setAlternatingRowColors(True)
self.archiveTable.cellDoubleClicked.connect(self.cell_double_clicked) self.archiveTable.cellDoubleClicked.connect(self.cell_double_clicked)
self.archiveTable.setSortingEnabled(True) self.archiveTable.setSortingEnabled(True)
@ -757,7 +756,7 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin):
window = ExtractDialog(archive, model) window = ExtractDialog(archive, model)
self._toggle_all_buttons(True) self._toggle_all_buttons(True)
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
self._window = window # for testing self._window = window # for testing
window.show() window.show()
window.accepted.connect(process_result) window.accepted.connect(process_result)
@ -778,19 +777,19 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin):
QDesktopServices.openUrl(QtCore.QUrl(f'file:///{mount_point}')) QDesktopServices.openUrl(QtCore.QUrl(f'file:///{mount_point}'))
def row_of_archive(self, archive_name): def row_of_archive(self, archive_name):
items = self.archiveTable.findItems(archive_name, QtCore.Qt.MatchExactly) items = self.archiveTable.findItems(archive_name, QtCore.Qt.MatchFlag.MatchExactly)
rows = [item.row() for item in items if item.column() == 4] rows = [item.row() for item in items if item.column() == 4]
return rows[0] if rows else None return rows[0] if rows else None
def confirm_dialog(self, title, text): def confirm_dialog(self, title, text):
msg = QMessageBox() msg = QMessageBox()
msg.setIcon(QMessageBox.Information) msg.setIcon(QMessageBox.Icon.Information)
msg.setText(text) msg.setText(text)
msg.setWindowTitle(title) msg.setWindowTitle(title)
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel)
msg.button(msg.Yes).setText(self.tr("Yes")) msg.button(QMessageBox.StandardButton.Yes).setText(self.tr("Yes"))
msg.button(msg.Cancel).setText(self.tr("Cancel")) msg.button(QMessageBox.StandardButton.Cancel).setText(self.tr("Cancel"))
return msg.exec_() == QMessageBox.Yes return msg.exec() == QMessageBox.StandardButton.Yes
def delete_action(self): def delete_action(self):
# Since this function modify the UI, we can't put the whole function in a JobQUeue. # Since this function modify the UI, we can't put the whole function in a JobQUeue.
@ -835,7 +834,7 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin):
# remove rows from list and database # remove rows from list and database
for archive in archives: for archive in archives:
for entry in self.archiveTable.findItems(archive, QtCore.Qt.MatchExactly): for entry in self.archiveTable.findItems(archive, QtCore.Qt.MatchFlag.MatchExactly):
self.archiveTable.removeRow(entry.row()) self.archiveTable.removeRow(entry.row())
ArchiveModel.get(name=archive).delete_instance() ArchiveModel.get(name=archive).delete_instance()

View File

@ -5,10 +5,10 @@ import re
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import PurePath from pathlib import PurePath
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from PyQt5 import uic from PyQt6 import uic
from PyQt5.QtCore import QDateTime, QLocale, QMimeData, QModelIndex, QPoint, Qt, QThread, QUrl from PyQt6.QtCore import QDateTime, QLocale, QMimeData, QModelIndex, QPoint, Qt, QThread, QUrl
from PyQt5.QtGui import QColor, QKeySequence from PyQt6.QtGui import QColor, QKeySequence, QShortcut
from PyQt5.QtWidgets import QApplication, QHeaderView, QMenu, QShortcut, QTreeView from PyQt6.QtWidgets import QApplication, QHeaderView, QMenu, QTreeView
from vorta.store.models import SettingsModel from vorta.store.models import SettingsModel
from vorta.utils import get_asset, pretty_bytes, uses_dark_mode from vorta.utils import get_asset, pretty_bytes, uses_dark_mode
from vorta.views.partials.treemodel import ( from vorta.views.partials.treemodel import (
@ -89,9 +89,9 @@ class DiffResultDialog(DiffResultBase, DiffResultUI):
# header # header
header = self.treeView.header() header = self.treeView.header()
header.setStretchLastSection(False) # stretch only first section header.setStretchLastSection(False) # stretch only first section
header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(2, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
# signals # signals
@ -824,11 +824,11 @@ class DiffTree(FileTreeModel[DiffData]):
if role == Qt.ItemDataRole.ForegroundRole: if role == Qt.ItemDataRole.ForegroundRole:
# colour # colour
if item.data.change_type == ChangeType.ADDED: if item.data.change_type == ChangeType.ADDED:
return QColor(Qt.green) if uses_dark_mode() else QColor(Qt.darkGreen) return QColor(Qt.GlobalColor.green) if uses_dark_mode() else QColor(Qt.GlobalColor.darkGreen)
if item.data.change_type == ChangeType.MODIFIED: if item.data.change_type == ChangeType.MODIFIED:
return QColor(Qt.yellow) if uses_dark_mode() else QColor(Qt.darkYellow) return QColor(Qt.GlobalColor.yellow) if uses_dark_mode() else QColor(Qt.GlobalColor.darkYellow)
if item.data.change_type == ChangeType.REMOVED: if item.data.change_type == ChangeType.REMOVED:
return QColor(Qt.red) if uses_dark_mode() else QColor(Qt.darkRed) return QColor(Qt.GlobalColor.red) if uses_dark_mode() else QColor(Qt.GlobalColor.darkRed)
return None # no change return None # no change
if role == Qt.ItemDataRole.ToolTipRole: if role == Qt.ItemDataRole.ToolTipRole:

View File

@ -1,8 +1,9 @@
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
from PyQt5 import uic from PyQt6 import uic
from PyQt5.QtWidgets import QFileDialog, QMessageBox from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QFileDialog, QMessageBox
from vorta.keyring.abc import VortaKeyring from vorta.keyring.abc import VortaKeyring
from vorta.store.models import BackupProfileModel # noqa: F401 from vorta.store.models import BackupProfileModel # noqa: F401
from vorta.utils import get_asset from vorta.utils import get_asset
@ -31,7 +32,7 @@ class ExportWindow(ExportWindowBase, ExportWindowUI):
self.keyring = VortaKeyring.get_keyring() self.keyring = VortaKeyring.get_keyring()
profile = self.profile profile = self.profile
if profile.repo is None or self.keyring.get_password('vorta-repo', profile.repo.url) is None: if profile.repo is None or self.keyring.get_password('vorta-repo', profile.repo.url) is None:
self.storePassword.setCheckState(False) self.storePassword.setCheckState(Qt.CheckState(False))
self.storePassword.setDisabled(True) self.storePassword.setDisabled(True)
self.storePassword.setToolTip(self.tr('Disclose your borg passphrase (No passphrase set)')) self.storePassword.setToolTip(self.tr('Disclose your borg passphrase (No passphrase set)'))

View File

@ -4,11 +4,11 @@ import logging
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from pathlib import PurePath from pathlib import PurePath
from typing import Optional from typing import Optional, Union
from PyQt5 import uic from PyQt6 import uic
from PyQt5.QtCore import QDateTime, QLocale, QMimeData, QModelIndex, QPoint, Qt, QThread, QUrl from PyQt6.QtCore import QDateTime, QLocale, QMimeData, QModelIndex, QPoint, Qt, QThread, QUrl
from PyQt5.QtGui import QColor, QKeySequence from PyQt6.QtGui import QColor, QKeySequence, QShortcut
from PyQt5.QtWidgets import QApplication, QDialogButtonBox, QHeaderView, QMenu, QPushButton, QShortcut from PyQt6.QtWidgets import QApplication, QDialogButtonBox, QHeaderView, QMenu, QPushButton
from vorta.store.models import SettingsModel from vorta.store.models import SettingsModel
from vorta.utils import borg_compat, get_asset, pretty_bytes, uses_dark_mode from vorta.utils import borg_compat, get_asset, pretty_bytes, uses_dark_mode
from vorta.views.utils import get_colored_icon from vorta.views.utils import get_colored_icon
@ -72,10 +72,10 @@ class ExtractDialog(ExtractDialogBase, ExtractDialogUI):
# header # header
header = view.header() header = view.header()
header.setStretchLastSection(False) header.setStretchLastSection(False)
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(2, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(3, QHeaderView.ResizeToContents) header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
# shortcuts # shortcuts
shortcut_copy = QShortcut(QKeySequence.StandardKey.Copy, self.treeView) shortcut_copy = QShortcut(QKeySequence.StandardKey.Copy, self.treeView)
@ -281,7 +281,7 @@ class FileData:
last_modified: QDateTime last_modified: QDateTime
source_path: Optional[str] = None # only relevant for links source_path: Optional[str] = None # only relevant for links
checkstate: int = 0 # whether to extract the file (0, 1 or 2) checkstate: Qt.CheckState = Qt.CheckState.Unchecked # whether to extract the file (0, 1 or 2)
checked_children: int = 0 # number of children checked checked_children: int = 0 # number of children checked
@ -378,7 +378,7 @@ class ExtractTree(FileTreeModel[FileData]):
self, self,
section: int, section: int,
orientation: Qt.Orientation, orientation: Qt.Orientation,
role: int = Qt.ItemDataRole.DisplayRole, role: Union[int, Qt.ItemDataRole] = Qt.ItemDataRole.DisplayRole,
): ):
""" """
Get the data for the given role and section in the given header. Get the data for the given role and section in the given header.
@ -414,7 +414,7 @@ class ExtractTree(FileTreeModel[FileData]):
return None return None
def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole): def data(self, index: QModelIndex, role: Union[int, Qt.ItemDataRole] = Qt.ItemDataRole.DisplayRole):
""" """
Get the data for the given role and index. Get the data for the given role and index.
@ -467,9 +467,9 @@ class ExtractTree(FileTreeModel[FileData]):
if role == Qt.ItemDataRole.BackgroundRole and column == 3: if role == Qt.ItemDataRole.BackgroundRole and column == 3:
# health indicator # health indicator
if item.data.health: if item.data.health:
return QColor(Qt.green) if uses_dark_mode() else QColor(Qt.darkGreen) return QColor(Qt.GlobalColor.green) if uses_dark_mode() else QColor(Qt.GlobalColor.darkGreen)
else: else:
return QColor(Qt.green) if uses_dark_mode() else QColor(Qt.darkGreen) return QColor(Qt.GlobalColor.green) if uses_dark_mode() else QColor(Qt.GlobalColor.darkGreen)
if role == Qt.ItemDataRole.ToolTipRole: if role == Qt.ItemDataRole.ToolTipRole:
if column == 0: if column == 0:
@ -532,7 +532,12 @@ class ExtractTree(FileTreeModel[FileData]):
if role == Qt.ItemDataRole.CheckStateRole and column == 0: if role == Qt.ItemDataRole.CheckStateRole and column == 0:
return item.data.checkstate return item.data.checkstate
def setData(self, index: QModelIndex, value, role: int = Qt.ItemDataRole.DisplayRole) -> bool: def setData(
self,
index: QModelIndex,
value: Union[int, Qt.CheckState],
role: Union[int, Qt.ItemDataRole] = Qt.ItemDataRole.CheckStateRole,
) -> bool:
""" """
Sets the role data for the item at index to value. Sets the role data for the item at index to value.
@ -543,6 +548,13 @@ class ExtractTree(FileTreeModel[FileData]):
if role != Qt.ItemDataRole.CheckStateRole: if role != Qt.ItemDataRole.CheckStateRole:
return False return False
# convert int to enum member
# PyQt6 will pass Ints where there were IntEnums in PyQt5
if isinstance(value, int):
value = Qt.CheckState(value)
if isinstance(role, int):
role = Qt.ItemDataRole(role)
item: ExtractFileItem = index.internalPointer() item: ExtractFileItem = index.internalPointer()
if value == item.data.checkstate: if value == item.data.checkstate:
@ -616,7 +628,7 @@ class ExtractTree(FileTreeModel[FileData]):
item = index.internalPointer() item = index.internalPointer()
for i in range(number_children): for i in range(number_children):
child = index.child(i, 0) child = self.index(i, 0, index)
child_item: ExtractFileItem = child.internalPointer() child_item: ExtractFileItem = child.internalPointer()
child_item.data.checkstate = value child_item.data.checkstate = value
@ -633,8 +645,8 @@ class ExtractTree(FileTreeModel[FileData]):
self.set_checkstate_recursively(child, value) self.set_checkstate_recursively(child, value)
self.dataChanged.emit( self.dataChanged.emit(
index.child(0, 0), self.index(0, 0, index),
index.child(0, number_children - 1), self.index(0, number_children - 1, index),
(Qt.ItemDataRole.CheckStateRole,), (Qt.ItemDataRole.CheckStateRole,),
) )

View File

@ -1,5 +1,5 @@
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtWidgets import QMessageBox from PyQt6.QtWidgets import QMessageBox
from vorta.keyring.abc import VortaKeyring from vorta.keyring.abc import VortaKeyring
from vorta.profile_export import VersionException from vorta.profile_export import VersionException
from vorta.store.connection import SCHEMA_VERSION from vorta.store.connection import SCHEMA_VERSION

View File

@ -1,9 +1,9 @@
import logging import logging
from pathlib import Path from pathlib import Path
from PyQt5 import QtCore, uic from PyQt6 import QtCore, uic
from PyQt5.QtCore import QPoint from PyQt6.QtCore import QPoint
from PyQt5.QtGui import QFontMetrics, QKeySequence from PyQt6.QtGui import QFontMetrics, QKeySequence, QShortcut
from PyQt5.QtWidgets import QApplication, QCheckBox, QFileDialog, QMenu, QMessageBox, QShortcut, QToolTip from PyQt6.QtWidgets import QApplication, QCheckBox, QFileDialog, QMenu, QMessageBox, QToolTip
from vorta.profile_export import ImportFailedException, ProfileExport from vorta.profile_export import ImportFailedException, ProfileExport
from vorta.store.models import BackupProfileModel, SettingsModel from vorta.store.models import BackupProfileModel, SettingsModel
from vorta.utils import borg_compat, get_asset, get_network_status_monitor, is_system_tray_available from vorta.utils import borg_compat, get_asset, get_network_status_monitor, is_system_tray_available
@ -31,7 +31,7 @@ class MainWindow(MainWindowBase, MainWindowUI):
self.setWindowTitle('Vorta for Borg Backup') self.setWindowTitle('Vorta for Borg Backup')
self.app = parent self.app = parent
self.setWindowIcon(get_colored_icon("icon")) self.setWindowIcon(get_colored_icon("icon"))
self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint) self.setWindowFlags(QtCore.Qt.WindowType.WindowCloseButtonHint | QtCore.Qt.WindowType.WindowMinimizeButtonHint)
self.createStartBtn = LoadingButton(self.tr("Start Backup")) self.createStartBtn = LoadingButton(self.tr("Start Backup"))
self.gridLayout.addWidget(self.createStartBtn, 0, 0, 1, 1) self.gridLayout.addWidget(self.createStartBtn, 0, 0, 1, 1)
self.createStartBtn.setGif(get_asset("icons/loading")) self.createStartBtn.setGif(get_asset("icons/loading"))
@ -156,7 +156,7 @@ class MainWindow(MainWindowBase, MainWindowUI):
def profile_rename_action(self): def profile_rename_action(self):
window = EditProfileWindow(rename_existing_id=self.profileSelector.currentData()) window = EditProfileWindow(rename_existing_id=self.profileSelector.currentData())
self.window = window # For tests self.window = window # For tests
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
window.open() window.open()
window.profile_changed.connect(self.profile_add_edit_result) window.profile_changed.connect(self.profile_add_edit_result)
window.rejected.connect(lambda: self.profileSelector.setCurrentIndex(self.profileSelector.currentIndex())) window.rejected.connect(lambda: self.profileSelector.setCurrentIndex(self.profileSelector.currentIndex()))
@ -171,11 +171,11 @@ class MainWindow(MainWindowBase, MainWindowUI):
self, self,
self.tr("Confirm deletion"), self.tr("Confirm deletion"),
msg, msg,
QMessageBox.Yes | QMessageBox.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.No, QMessageBox.StandardButton.No,
) )
if reply == QMessageBox.Yes: if reply == QMessageBox.StandardButton.Yes:
to_delete.delete_instance(recursive=True) to_delete.delete_instance(recursive=True)
self.app.scheduler.remove_job(to_delete_id) # Remove pending jobs self.app.scheduler.remove_job(to_delete_id) # Remove pending jobs
self.profileSelector.removeItem(self.profileSelector.currentIndex()) self.profileSelector.removeItem(self.profileSelector.currentIndex())
@ -189,7 +189,7 @@ class MainWindow(MainWindowBase, MainWindowUI):
def profile_add_action(self): def profile_add_action(self):
window = AddProfileWindow() window = AddProfileWindow()
self.window = window # For tests self.window = window # For tests
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
window.open() window.open()
window.profile_changed.connect(self.profile_add_edit_result) window.profile_changed.connect(self.profile_add_edit_result)
window.rejected.connect(lambda: self.profileSelector.setCurrentIndex(self.profileSelector.currentIndex())) window.rejected.connect(lambda: self.profileSelector.setCurrentIndex(self.profileSelector.currentIndex()))
@ -201,7 +201,7 @@ class MainWindow(MainWindowBase, MainWindowUI):
""" """
window = ExportWindow(profile=self.current_profile.refresh()) window = ExportWindow(profile=self.current_profile.refresh())
self.window = window self.window = window
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
window.show() window.show()
def profile_import_action(self): def profile_import_action(self):
@ -236,7 +236,7 @@ class MainWindow(MainWindowBase, MainWindowUI):
return return
window = ImportWindow(profile_export=profile_export) window = ImportWindow(profile_export=profile_export)
self.window = window self.window = window
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
window.profile_imported.connect(profile_imported_event) window.profile_imported.connect(profile_imported_event)
window.show() window.show()
@ -280,13 +280,13 @@ class MainWindow(MainWindowBase, MainWindowUI):
if not is_system_tray_available(): if not is_system_tray_available():
if SettingsModel.get(key="enable_background_question").value: if SettingsModel.get(key="enable_background_question").value:
msg = QMessageBox() msg = QMessageBox()
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
msg.setParent(self, QtCore.Qt.Sheet) msg.setParent(self, QtCore.Qt.WindowType.Sheet)
msg.setText(self.tr("Should Vorta continue to run in the background?")) msg.setText(self.tr("Should Vorta continue to run in the background?"))
msg.button(QMessageBox.Yes).clicked.connect( msg.button(QMessageBox.StandardButton.Yes).clicked.connect(
lambda: self.miscTab.save_setting("disable_background_state", True) lambda: self.miscTab.save_setting("disable_background_state", True)
) )
msg.button(QMessageBox.No).clicked.connect( msg.button(QMessageBox.StandardButton.No).clicked.connect(
lambda: ( lambda: (
self.miscTab.save_setting("disable_background_state", False), self.miscTab.save_setting("disable_background_state", False),
self.app.quit(), self.app.quit(),

View File

@ -1,7 +1,7 @@
import logging import logging
from PyQt5 import uic from PyQt6 import uic
from PyQt5.QtCore import Qt from PyQt6.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QCheckBox, QFormLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem from PyQt6.QtWidgets import QApplication, QCheckBox, QFormLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem
from vorta._version import __version__ from vorta._version import __version__
from vorta.config import LOG_DIR from vorta.config import LOG_DIR
from vorta.i18n import translate from vorta.i18n import translate
@ -87,7 +87,7 @@ class MiscTab(MiscTabBase, MiscTabUI, BackupProfileMixin):
# create widget # create widget
cb = QCheckBox(translate('settings', setting.label)) cb = QCheckBox(translate('settings', setting.label))
cb.setToolTip(setting.tooltip) cb.setToolTip(setting.tooltip)
cb.setCheckState(setting.value) cb.setCheckState(Qt.CheckState(setting.value))
cb.setTristate(False) cb.setTristate(False)
cb.stateChanged.connect(lambda v, key=setting.key: self.save_setting(key, v)) cb.stateChanged.connect(lambda v, key=setting.key: self.save_setting(key, v))

View File

@ -2,7 +2,7 @@
Adapted from https://stackoverflow.com/questions/53618971/how-to-make-a-qpushbutton-a-loading-button Adapted from https://stackoverflow.com/questions/53618971/how-to-make-a-qpushbutton-a-loading-button
""" """
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt6 import QtCore, QtGui, QtWidgets
class LoadingButton(QtWidgets.QPushButton): class LoadingButton(QtWidgets.QPushButton):

View File

@ -1,7 +1,7 @@
from typing import Optional from typing import Optional
from PyQt5.QtCore import QCoreApplication, QEvent, QSize, Qt from PyQt6.QtCore import QCoreApplication, QEvent, QSize, Qt
from PyQt5.QtGui import QHelpEvent, QIcon, QMouseEvent, QPaintEvent from PyQt6.QtGui import QHelpEvent, QIcon, QMouseEvent, QPaintEvent
from PyQt5.QtWidgets import QSizePolicy, QStyle, QStylePainter, QToolTip, QWidget from PyQt6.QtWidgets import QSizePolicy, QStyle, QStylePainter, QToolTip, QWidget
class ToolTipButton(QWidget): class ToolTipButton(QWidget):
@ -24,7 +24,7 @@ class ToolTipButton(QWidget):
""" """
super().__init__(parent) super().__init__(parent)
self.setCursor(Qt.CursorShape.WhatsThisCursor) self.setCursor(Qt.CursorShape.WhatsThisCursor)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
self.setMouseTracking(True) self.setMouseTracking(True)
self._icon = icon or QIcon() self._icon = icon or QIcon()
@ -98,8 +98,10 @@ class ToolTipButton(QWidget):
https://doc.qt.io/qt-5/qwidget.html#mouseMoveEvent https://doc.qt.io/qt-5/qwidget.html#mouseMoveEvent
""" """
super().mouseMoveEvent(event) super().mouseMoveEvent(event)
QToolTip.showText(event.globalPos(), self.toolTip(), self) QToolTip.showText(event.globalPosition().toPoint(), self.toolTip(), self)
QCoreApplication.postEvent(self, QHelpEvent(QEvent.Type.ToolTip, event.pos(), event.globalPos())) QCoreApplication.postEvent(
self, QHelpEvent(QEvent.Type.ToolTip, event.position().toPoint(), event.globalPosition().toPoint())
)
def setIcon(self, icon: QIcon): def setIcon(self, icon: QIcon):
""" """

View File

@ -9,7 +9,7 @@ import os.path as osp
from functools import reduce from functools import reduce
from pathlib import PurePath from pathlib import PurePath
from typing import Generic, List, Optional, Sequence, Tuple, TypeVar, Union, overload from typing import Generic, List, Optional, Sequence, Tuple, TypeVar, Union, overload
from PyQt5.QtCore import QAbstractItemModel, QModelIndex, QObject, QSortFilterProxyModel, Qt, pyqtSignal from PyQt6.QtCore import QAbstractItemModel, QModelIndex, QObject, QSortFilterProxyModel, Qt, pyqtSignal
#: A representation of a path #: A representation of a path
Path = Tuple[str, ...] Path = Tuple[str, ...]
@ -837,7 +837,7 @@ class FileTreeModel(QAbstractItemModel, Generic[T]):
row, item = parent_item._parent.get(parent_item.subpath) row, item = parent_item._parent.get(parent_item.subpath)
return self.createIndex(row, 0, parent_item) return self.createIndex(row, 0, parent_item)
def flags(self, index: QModelIndex) -> Qt.ItemFlags: def flags(self, index: QModelIndex) -> Qt.ItemFlag:
""" """
Returns the item flags for the given index. Returns the item flags for the given index.

View File

@ -1,5 +1,5 @@
from PyQt5 import QtCore, uic from PyQt6 import QtCore, uic
from PyQt5.QtWidgets import QDialogButtonBox from PyQt6.QtWidgets import QDialogButtonBox
from vorta.i18n import trans_late, translate from vorta.i18n import trans_late, translate
from vorta.store.models import BackupProfileModel from vorta.store.models import BackupProfileModel
from vorta.utils import get_asset from vorta.utils import get_asset
@ -14,20 +14,20 @@ class AddProfileWindow(AddProfileBase, AddProfileUI):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
self.edited_profile = None self.edited_profile = None
self.buttonBox.rejected.connect(self.close) self.buttonBox.rejected.connect(self.close)
self.buttonBox.accepted.connect(self.save) self.buttonBox.accepted.connect(self.save)
self.profileNameField.textChanged.connect(self.button_validation) self.profileNameField.textChanged.connect(self.button_validation)
self.buttonBox.button(QDialogButtonBox.Save).setText(self.tr("Save")) self.buttonBox.button(QDialogButtonBox.StandardButton.Save).setText(self.tr("Save"))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(self.tr("Cancel")) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(self.tr("Cancel"))
self.name_blank = trans_late('AddProfileWindow', 'Please enter a profile name.') self.name_blank = trans_late('AddProfileWindow', 'Please enter a profile name.')
self.name_exists = trans_late('AddProfileWindow', 'A profile with this name already exists.') self.name_exists = trans_late('AddProfileWindow', 'A profile with this name already exists.')
# Call validate to set inital messages # Call validate to set inital messages
self.buttonBox.button(QDialogButtonBox.Save).setEnabled(self.validate()) self.buttonBox.button(QDialogButtonBox.StandardButton.Save).setEnabled(self.validate())
def _set_status(self, text): def _set_status(self, text):
self.errorText.setText(text) self.errorText.setText(text)
@ -40,7 +40,7 @@ class AddProfileWindow(AddProfileBase, AddProfileUI):
self.accept() self.accept()
def button_validation(self): def button_validation(self):
self.buttonBox.button(QDialogButtonBox.Save).setEnabled(self.validate()) self.buttonBox.button(QDialogButtonBox.StandardButton.Save).setEnabled(self.validate())
def validate(self): def validate(self):
name = self.profileNameField.text() name = self.profileNameField.text()

View File

@ -1,6 +1,7 @@
import re import re
from PyQt5 import QtCore, uic from PyQt6 import QtCore, uic
from PyQt5.QtWidgets import QAction, QApplication, QDialogButtonBox, QLineEdit from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QApplication, QDialogButtonBox, QLineEdit
from vorta.borg.info_repo import BorgInfoRepoJob from vorta.borg.info_repo import BorgInfoRepoJob
from vorta.borg.init import BorgInitJob from vorta.borg.init import BorgInitJob
from vorta.i18n import translate from vorta.i18n import translate
@ -19,7 +20,7 @@ class AddRepoWindow(AddRepoBase, AddRepoUI):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
self.result = None self.result = None
self.is_remote_repo = True self.is_remote_repo = True
@ -41,7 +42,7 @@ class AddRepoWindow(AddRepoBase, AddRepoUI):
self.showHideAction.setCheckable(True) self.showHideAction.setCheckable(True)
self.showHideAction.toggled.connect(self.set_visibility) self.showHideAction.toggled.connect(self.set_visibility)
self.passwordLineEdit.addAction(self.showHideAction, QLineEdit.TrailingPosition) self.passwordLineEdit.addAction(self.showHideAction, QLineEdit.ActionPosition.TrailingPosition)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
@ -103,7 +104,7 @@ class AddRepoWindow(AddRepoBase, AddRepoUI):
self.confirmLineEdit.setText(password) self.confirmLineEdit.setText(password)
def set_visibility(self, visible): def set_visibility(self, visible):
visibility = QLineEdit.Normal if visible else QLineEdit.Password visibility = QLineEdit.EchoMode.Normal if visible else QLineEdit.EchoMode.Password
self.passwordLineEdit.setEchoMode(visibility) self.passwordLineEdit.setEchoMode(visibility)
self.confirmLineEdit.setEchoMode(visibility) self.confirmLineEdit.setEchoMode(visibility)
@ -224,7 +225,7 @@ class ExistingRepoWindow(AddRepoWindow):
del self.confirmLabel del self.confirmLabel
def set_visibility(self, visible): def set_visibility(self, visible):
visibility = QLineEdit.Normal if visible else QLineEdit.Password visibility = QLineEdit.EchoMode.Normal if visible else QLineEdit.EchoMode.Password
self.passwordLineEdit.setEchoMode(visibility) self.passwordLineEdit.setEchoMode(visibility)
if visible: if visible:

View File

@ -1,8 +1,8 @@
import os import os
from pathlib import PurePath from pathlib import PurePath
from PyQt5 import QtCore, uic from PyQt6 import QtCore, uic
from PyQt5.QtCore import QMimeData, QUrl from PyQt6.QtCore import QMimeData, QUrl
from PyQt5.QtWidgets import QApplication, QLayout, QMenu, QMessageBox from PyQt6.QtWidgets import QApplication, QLayout, QMenu, QMessageBox
from vorta.store.models import ArchiveModel, BackupProfileMixin, RepoModel from vorta.store.models import ArchiveModel, BackupProfileMixin, RepoModel
from vorta.utils import borg_compat, get_asset, get_private_keys, pretty_bytes from vorta.utils import borg_compat, get_asset, get_private_keys, pretty_bytes
from .repo_add_dialog import AddRepoWindow, ExistingRepoWindow from .repo_add_dialog import AddRepoWindow, ExistingRepoWindow
@ -192,15 +192,15 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin):
"""Open a dialog to create an ssh key.""" """Open a dialog to create an ssh key."""
ssh_add_window = SSHAddWindow() ssh_add_window = SSHAddWindow()
self._window = ssh_add_window # For tests self._window = ssh_add_window # For tests
ssh_add_window.setParent(self, QtCore.Qt.Sheet) ssh_add_window.setParent(self, QtCore.Qt.WindowType.Sheet)
ssh_add_window.accepted.connect(self.init_ssh) ssh_add_window.accepted.connect(self.init_ssh)
# ssh_add_window.rejected.connect(lambda: self.sshComboBox.setCurrentIndex(0)) # ssh_add_window.rejected.connect(lambda: self.sshComboBox.setCurrentIndex(0))
ssh_add_window.open() ssh_add_window.open()
def ssh_copy_to_clipboard_action(self): def ssh_copy_to_clipboard_action(self):
msg = QMessageBox() msg = QMessageBox()
msg.setStandardButtons(QMessageBox.Ok) msg.setStandardButtons(QMessageBox.StandardButton.Ok)
msg.setParent(self, QtCore.Qt.Sheet) msg.setParent(self, QtCore.Qt.WindowType.Sheet)
index = self.sshComboBox.currentIndex() index = self.sshComboBox.currentIndex()
if index > 0: if index > 0:
@ -234,7 +234,7 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin):
"""Open a dialog to create a new repo and add it to vorta.""" """Open a dialog to create a new repo and add it to vorta."""
window = AddRepoWindow() window = AddRepoWindow()
self._window = window # For tests self._window = window # For tests
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
window.added_repo.connect(self.process_new_repo) window.added_repo.connect(self.process_new_repo)
# window.rejected.connect(lambda: self.repoSelector.setCurrentIndex(0)) # window.rejected.connect(lambda: self.repoSelector.setCurrentIndex(0))
window.open() window.open()
@ -243,7 +243,7 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin):
"""Open a dialog to add a existing repo to vorta.""" """Open a dialog to add a existing repo to vorta."""
window = ExistingRepoWindow() window = ExistingRepoWindow()
self._window = window # For tests self._window = window # For tests
window.setParent(self, QtCore.Qt.Sheet) window.setParent(self, QtCore.Qt.WindowType.Sheet)
window.added_repo.connect(self.process_new_repo) window.added_repo.connect(self.process_new_repo)
# window.rejected.connect(lambda: self.repoSelector.setCurrentIndex(0)) # window.rejected.connect(lambda: self.repoSelector.setCurrentIndex(0))
window.open() window.open()
@ -271,8 +271,8 @@ class RepoTab(RepoBase, RepoUI, BackupProfileMixin):
self.init_repo_stats() self.init_repo_stats()
msg = QMessageBox() msg = QMessageBox()
msg.setStandardButtons(QMessageBox.Ok) msg.setStandardButtons(QMessageBox.StandardButton.Ok)
msg.setParent(self, QtCore.Qt.Sheet) msg.setParent(self, QtCore.Qt.WindowType.Sheet)
selected_repo_id = self.repoSelector.currentData() selected_repo_id = self.repoSelector.currentData()
selected_repo_index = self.repoSelector.currentIndex() selected_repo_index = self.repoSelector.currentIndex()

View File

@ -1,6 +1,6 @@
from PyQt5 import QtCore, uic from PyQt6 import QtCore, uic
from PyQt5.QtCore import QDateTime, QLocale from PyQt6.QtCore import QDateTime, QLocale
from PyQt5.QtWidgets import QApplication, QHeaderView, QListWidgetItem, QTableView, QTableWidgetItem from PyQt6.QtWidgets import QAbstractItemView, QApplication, QHeaderView, QListWidgetItem, QTableWidgetItem
from vorta import application from vorta import application
from vorta.i18n import get_locale from vorta.i18n import get_locale
from vorta.scheduler import ScheduleStatusType from vorta.scheduler import ScheduleStatusType
@ -37,10 +37,10 @@ class ScheduleTab(ScheduleBase, ScheduleUI, BackupProfileMixin):
self.logTableWidget.setAlternatingRowColors(True) self.logTableWidget.setAlternatingRowColors(True)
header = self.logTableWidget.horizontalHeader() header = self.logTableWidget.horizontalHeader()
header.setVisible(True) header.setVisible(True)
[header.setSectionResizeMode(i, QHeaderView.ResizeToContents) for i in range(5)] [header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents) for i in range(5)]
header.setSectionResizeMode(3, QHeaderView.Stretch) header.setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
self.logTableWidget.setSelectionBehavior(QTableView.SelectRows) self.logTableWidget.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.logTableWidget.setEditTriggers(QTableView.NoEditTriggers) self.logTableWidget.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
# Scheduler intervals we know # Scheduler intervals we know
self.scheduleIntervalUnit.addItem(self.tr('Minutes'), 'minutes') self.scheduleIntervalUnit.addItem(self.tr('Minutes'), 'minutes')
@ -138,17 +138,19 @@ class ScheduleTab(ScheduleBase, ScheduleUI, BackupProfileMixin):
self.scheduleFixedTime.setTime(QtCore.QTime(profile.schedule_fixed_hour, profile.schedule_fixed_minute)) self.scheduleFixedTime.setTime(QtCore.QTime(profile.schedule_fixed_hour, profile.schedule_fixed_minute))
# Set borg-check options # Set borg-check options
self.validationCheckBox.setCheckState(QtCore.Qt.Checked if profile.validation_on else QtCore.Qt.Unchecked) self.validationCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.validation_on else QtCore.Qt.CheckState.Unchecked
)
self.validationWeeksCount.setValue(profile.validation_weeks) self.validationWeeksCount.setValue(profile.validation_weeks)
# Other checkbox options # Other checkbox options
self.pruneCheckBox.setCheckState(QtCore.Qt.Checked if profile.prune_on else QtCore.Qt.Unchecked) self.pruneCheckBox.setCheckState(
QtCore.Qt.CheckState.Checked if profile.prune_on else QtCore.Qt.CheckState.Unchecked
)
self.missedBackupsCheckBox.setCheckState( self.missedBackupsCheckBox.setCheckState(
QtCore.Qt.Checked if profile.schedule_make_up_missed else QtCore.Qt.Unchecked QtCore.Qt.CheckState.Checked if profile.schedule_make_up_missed else QtCore.Qt.CheckState.Unchecked
)
self.meteredNetworksCheckBox.setChecked(
QtCore.Qt.Unchecked if profile.dont_run_on_metered_networks else QtCore.Qt.Checked
) )
self.meteredNetworksCheckBox.setChecked(False if profile.dont_run_on_metered_networks else True)
self.preBackupCmdLineEdit.setText(profile.pre_backup_cmd) self.preBackupCmdLineEdit.setText(profile.pre_backup_cmd)
self.postBackupCmdLineEdit.setText(profile.post_backup_cmd) self.postBackupCmdLineEdit.setText(profile.post_backup_cmd)
@ -183,11 +185,11 @@ class ScheduleTab(ScheduleBase, ScheduleUI, BackupProfileMixin):
for wifi in get_sorted_wifis(self.profile()): for wifi in get_sorted_wifis(self.profile()):
item = QListWidgetItem() item = QListWidgetItem()
item.setText(wifi.ssid) item.setText(wifi.ssid)
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) item.setFlags(item.flags() | QtCore.Qt.ItemFlag.ItemIsUserCheckable)
if wifi.allowed: if wifi.allowed:
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
else: else:
item.setCheckState(QtCore.Qt.Unchecked) item.setCheckState(QtCore.Qt.CheckState.Unchecked)
self.wifiListWidget.addItem(item) self.wifiListWidget.addItem(item)
self.wifiListWidget.itemChanged.connect(self.save_wifi_item) self.wifiListWidget.itemChanged.connect(self.save_wifi_item)

View File

@ -1,9 +1,10 @@
import logging import logging
import os import os
from pathlib import PurePath from pathlib import PurePath
from PyQt5 import QtCore, QtGui, uic from PyQt6 import QtCore, QtGui, uic
from PyQt5.QtCore import QFileInfo, QMimeData, QPoint, Qt, QUrl, pyqtSlot from PyQt6.QtCore import QFileInfo, QMimeData, QPoint, Qt, QUrl, pyqtSlot
from PyQt5.QtWidgets import QApplication, QHeaderView, QMenu, QMessageBox, QShortcut, QTableWidgetItem from PyQt6.QtGui import QShortcut
from PyQt6.QtWidgets import QApplication, QHeaderView, QMenu, QMessageBox, QTableWidgetItem
from vorta.store.models import BackupProfileMixin, SettingsModel, SourceFileModel from vorta.store.models import BackupProfileMixin, SettingsModel, SourceFileModel
from vorta.utils import FilePathInfoAsync, choose_file_dialog, get_asset, pretty_bytes, sort_sizes from vorta.utils import FilePathInfoAsync, choose_file_dialog, get_asset, pretty_bytes, sort_sizes
from vorta.views.utils import get_colored_icon from vorta.views.utils import get_colored_icon
@ -23,7 +24,7 @@ class SourceColumn:
class SizeItem(QTableWidgetItem): class SizeItem(QTableWidgetItem):
def __init__(self, s): def __init__(self, s):
super().__init__(s) super().__init__(s)
self.setTextAlignment(Qt.AlignVCenter + Qt.AlignRight) self.setTextAlignment(Qt.AlignmentFlag.AlignVCenter + Qt.AlignmentFlag.AlignRight)
def __lt__(self, other): def __lt__(self, other):
if other.text() == '': if other.text() == '':
@ -63,9 +64,9 @@ class SourceTab(SourceBase, SourceUI, BackupProfileMixin):
header.setVisible(True) header.setVisible(True)
header.setSortIndicatorShown(1) header.setSortIndicatorShown(1)
header.setSectionResizeMode(SourceColumn.Path, QHeaderView.Stretch) header.setSectionResizeMode(SourceColumn.Path, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(SourceColumn.Size, QHeaderView.ResizeToContents) header.setSectionResizeMode(SourceColumn.Size, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(SourceColumn.FilesCount, QHeaderView.ResizeToContents) header.setSectionResizeMode(SourceColumn.FilesCount, QHeaderView.ResizeMode.ResizeToContents)
self.sourceFilesWidget.setSortingEnabled(True) self.sourceFilesWidget.setSortingEnabled(True)
self.sourceFilesWidget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.sourceFilesWidget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
@ -139,7 +140,7 @@ class SourceTab(SourceBase, SourceUI, BackupProfileMixin):
sorting = self.sourceFilesWidget.isSortingEnabled() sorting = self.sourceFilesWidget.isSortingEnabled()
self.sourceFilesWidget.setSortingEnabled(False) self.sourceFilesWidget.setSortingEnabled(False)
items = self.sourceFilesWidget.findItems(path, QtCore.Qt.MatchExactly) items = self.sourceFilesWidget.findItems(path, QtCore.Qt.MatchFlag.MatchExactly)
# Conversion int->str->int needed because QT limits int to 32-bit # Conversion int->str->int needed because QT limits int to 32-bit
data_size = int(data_size) data_size = int(data_size)
files_count = int(files_count) files_count = int(files_count)
@ -250,7 +251,7 @@ class SourceTab(SourceBase, SourceUI, BackupProfileMixin):
sourcetab_sort_order = int(SettingsModel.get(key='sourcetab_sort_order').str_value) sourcetab_sort_order = int(SettingsModel.get(key='sourcetab_sort_order').str_value)
# Sort items as per settings # Sort items as per settings
self.sourceFilesWidget.sortItems(sourcetab_sort_column, sourcetab_sort_order) self.sourceFilesWidget.sortItems(sourcetab_sort_column, Qt.SortOrder(sourcetab_sort_order))
self.excludePatternsField.appendPlainText(profile.exclude_patterns) self.excludePatternsField.appendPlainText(profile.exclude_patterns)
self.excludeIfPresentField.appendPlainText(profile.exclude_if_present) self.excludeIfPresentField.appendPlainText(profile.exclude_if_present)
@ -262,7 +263,7 @@ class SourceTab(SourceBase, SourceUI, BackupProfileMixin):
SettingsModel.update({SettingsModel.str_value: str(column)}).where( SettingsModel.update({SettingsModel.str_value: str(column)}).where(
SettingsModel.key == 'sourcetab_sort_column' SettingsModel.key == 'sourcetab_sort_column'
).execute() ).execute()
SettingsModel.update({SettingsModel.str_value: str(order)}).where( SettingsModel.update({SettingsModel.str_value: str(order.value)}).where(
SettingsModel.key == 'sourcetab_sort_order' SettingsModel.key == 'sourcetab_sort_order'
).execute() ).execute()

View File

@ -1,7 +1,7 @@
import os import os
from PyQt5 import uic from PyQt6 import uic
from PyQt5.QtCore import QProcess, Qt from PyQt6.QtCore import QProcess, Qt
from PyQt5.QtWidgets import QApplication, QDialogButtonBox from PyQt6.QtWidgets import QApplication, QDialogButtonBox
from ..utils import get_asset from ..utils import get_asset
uifile = get_asset('UI/sshadd.ui') uifile = get_asset('UI/sshadd.ui')
@ -12,7 +12,7 @@ class SSHAddWindow(SSHAddBase, SSHAddUI):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setupUi(self) self.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
# dialogButtonBox # dialogButtonBox
self.generateButton = self.buttonBox.button(QDialogButtonBox.StandardButton.Ok) self.generateButton = self.buttonBox.button(QDialogButtonBox.StandardButton.Ok)

View File

@ -1,4 +1,4 @@
from PyQt5.QtGui import QIcon, QImage, QPixmap from PyQt6.QtGui import QIcon, QImage, QPixmap
from vorta.utils import get_asset, uses_dark_mode from vorta.utils import get_asset, uses_dark_mode

View File

@ -46,6 +46,12 @@ def qapp(tmpdir_factory):
mock_db = SqliteDatabase(str(tmp_db)) mock_db = SqliteDatabase(str(tmp_db))
vorta.store.connection.init_db(mock_db) vorta.store.connection.init_db(mock_db)
# Needs to be disabled before calling VortaApp()
if sys.platform == 'darwin':
cfg = vorta.store.models.SettingsModel.get(key='check_full_disk_access')
cfg.value = False
cfg.save()
from vorta.application import VortaApp from vorta.application import VortaApp
VortaApp.set_borg_details_action = MagicMock() # Can't use pytest-mock in session scope VortaApp.set_borg_details_action = MagicMock() # Can't use pytest-mock in session scope

View File

@ -1,7 +1,7 @@
from collections import namedtuple from collections import namedtuple
import psutil import psutil
import pytest import pytest
from PyQt5 import QtCore from PyQt6 import QtCore
import vorta.borg import vorta.borg
import vorta.utils import vorta.utils
import vorta.views.archive_tab import vorta.views.archive_tab
@ -58,7 +58,7 @@ def test_repo_prune(qapp, qtbot, mocker, borg_json_output):
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result) mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
qtbot.mouseClick(tab.bPrune, QtCore.Qt.LeftButton) qtbot.mouseClick(tab.bPrune, QtCore.Qt.MouseButton.LeftButton)
qtbot.waitUntil(lambda: 'Refreshing archives done.' in main.progressText.text(), **pytest._wait_defaults) qtbot.waitUntil(lambda: 'Refreshing archives done.' in main.progressText.text(), **pytest._wait_defaults)
@ -73,7 +73,7 @@ def test_repo_compact(qapp, qtbot, mocker, borg_json_output):
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result) mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
qtbot.mouseClick(tab.compactButton, QtCore.Qt.LeftButton) qtbot.mouseClick(tab.compactButton, QtCore.Qt.MouseButton.LeftButton)
qtbot.waitUntil( qtbot.waitUntil(
lambda: 'compaction freed about 56.00 kB repository space' in main.logText.text(), **pytest._wait_defaults lambda: 'compaction freed about 56.00 kB repository space' in main.logText.text(), **pytest._wait_defaults
@ -91,7 +91,7 @@ def test_check(qapp, mocker, borg_json_output, qtbot):
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result) mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
qtbot.mouseClick(tab.bCheck, QtCore.Qt.LeftButton) qtbot.mouseClick(tab.bCheck, QtCore.Qt.MouseButton.LeftButton)
success_text = 'INFO: Archive consistency check complete' success_text = 'INFO: Archive consistency check complete'
qtbot.waitUntil(lambda: success_text in main.logText.text(), **pytest._wait_defaults) qtbot.waitUntil(lambda: success_text in main.logText.text(), **pytest._wait_defaults)

View File

@ -1,6 +1,6 @@
from pathlib import PurePath from pathlib import PurePath
import pytest import pytest
from PyQt5.QtCore import QDateTime, QItemSelectionModel, Qt from PyQt6.QtCore import QDateTime, QItemSelectionModel, Qt
import vorta.borg import vorta.borg
import vorta.utils import vorta.utils
import vorta.views.archive_tab import vorta.views.archive_tab

View File

@ -1,4 +1,4 @@
from PyQt5.QtCore import QModelIndex, Qt from PyQt6.QtCore import QModelIndex, Qt
import vorta.borg import vorta.borg
from vorta.views.extract_dialog import ExtractTree, FileData, FileType, parse_json_lines from vorta.views.extract_dialog import ExtractTree, FileData, FileType, parse_json_lines
from vorta.views.partials.treemodel import FileSystemItem from vorta.views.partials.treemodel import FileSystemItem
@ -92,12 +92,12 @@ def test_selection():
c: FileSystemItem[FileData] = ic.internalPointer() c: FileSystemItem[FileData] = ic.internalPointer()
select(model, ic) select(model, ic)
assert c.data.checkstate == 2 assert c.data.checkstate == Qt.CheckState(2)
assert c.data.checked_children == 0 assert c.data.checked_children == 0
# Test deselect # Test deselect
deselect(model, ic) deselect(model, ic)
assert c.data.checkstate == 0 assert c.data.checkstate == Qt.CheckState(0)
assert c.data.checked_children == 0 assert c.data.checked_children == 0
# Test select parent as well as children # Test select parent as well as children
@ -121,19 +121,19 @@ def test_selection():
iab = model.indexPath(("a", "b")) iab = model.indexPath(("a", "b"))
deselect(model, iab) deselect(model, iab)
assert a.data.checkstate == 1 assert a.data.checkstate == Qt.CheckState(1)
assert aa.data.checkstate == 2 assert aa.data.checkstate == Qt.CheckState(2)
assert ab.data.checkstate == 0 assert ab.data.checkstate == Qt.CheckState(0)
assert abc.data.checkstate == 0 assert abc.data.checkstate == Qt.CheckState(0)
assert a.data.checked_children == 1 assert a.data.checked_children == 1
assert ab.data.checked_children == 0 assert ab.data.checked_children == 0
# Test deselect item and children # Test deselect item and children
deselect(model, ia) deselect(model, ia)
assert a.data.checkstate == 0 assert a.data.checkstate == Qt.CheckState(0)
assert aa.data.checkstate == 0 assert aa.data.checkstate == Qt.CheckState(0)
assert ab.data.checkstate == 0 assert ab.data.checkstate == Qt.CheckState(0)
assert a.data.checked_children == 0 assert a.data.checked_children == 0
assert aa.data.checked_children == 0 assert aa.data.checked_children == 0
@ -146,9 +146,9 @@ def test_selection():
select(model, iab) select(model, iab)
select(model, iaac) select(model, iaac)
assert a.data.checkstate == 1 assert a.data.checkstate == Qt.CheckState(1)
assert aa.data.checkstate == 1 assert aa.data.checkstate == Qt.CheckState(1)
assert ab.data.checkstate == 2 assert ab.data.checkstate == Qt.CheckState(2)
assert a.data.checked_children == 2 assert a.data.checked_children == 2
assert ab.data.checked_children == 2 assert ab.data.checked_children == 2
@ -159,21 +159,21 @@ def test_selection():
deselect(model, iaa) deselect(model, iaa)
deselect(model, iab) deselect(model, iab)
assert a.data.checkstate == 0 assert a.data.checkstate == Qt.CheckState(0)
assert a.data.checked_children == 0 assert a.data.checked_children == 0
# Test select child with deselected parent # Test select child with deselected parent
select(model, iaac) select(model, iaac)
assert a.data.checkstate == 1 assert a.data.checkstate == Qt.CheckState(1)
assert ab.data.checkstate == 0 assert ab.data.checkstate == Qt.CheckState(0)
assert aa.data.checkstate == 1 assert aa.data.checkstate == Qt.CheckState(1)
assert a.data.checked_children == 1 assert a.data.checked_children == 1
assert ab.data.checked_children == 0 assert ab.data.checked_children == 0
assert aa.data.checked_children == 1 assert aa.data.checked_children == 1
select(model, iaa) select(model, iaa)
assert a.data.checkstate == 1 assert a.data.checkstate == Qt.CheckState(1)
select(model, iab) select(model, iab)
assert a.data.checkstate == 1 assert a.data.checkstate == Qt.CheckState(1)

View File

@ -1,8 +1,8 @@
import os import os
from pathlib import Path from pathlib import Path
import pytest import pytest
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtWidgets import QDialogButtonBox, QFileDialog, QMessageBox from PyQt6.QtWidgets import QDialogButtonBox, QFileDialog, QMessageBox
from vorta.store.models import BackupProfileModel, SourceFileModel from vorta.store.models import BackupProfileModel, SourceFileModel
from vorta.views.import_window import ImportWindow from vorta.views.import_window import ImportWindow
@ -18,7 +18,9 @@ def test_import_success(qapp, qtbot, rootdir, monkeypatch):
import_dialog: ImportWindow = main.window import_dialog: ImportWindow = main.window
import_dialog.overwriteExistingSettings.setChecked(True) import_dialog.overwriteExistingSettings.setChecked(True)
qtbot.mouseClick(import_dialog.buttonBox.button(QDialogButtonBox.Ok), QtCore.Qt.LeftButton) qtbot.mouseClick(
import_dialog.buttonBox.button(QDialogButtonBox.StandardButton.Ok), QtCore.Qt.MouseButton.LeftButton
)
qtbot.waitSignal(import_dialog.profile_imported, **pytest._wait_defaults) qtbot.waitSignal(import_dialog.profile_imported, **pytest._wait_defaults)
restored_profile = BackupProfileModel.get_or_none(name="Test Profile Restoration") restored_profile = BackupProfileModel.get_or_none(name="Test Profile Restoration")
@ -81,7 +83,9 @@ def test_export_success(qapp, qtbot, tmpdir, monkeypatch):
main.profile_export_action() main.profile_export_action()
export_dialog = main.window export_dialog = main.window
qtbot.mouseClick(export_dialog.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) qtbot.mouseClick(
export_dialog.buttonBox.button(QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton
)
qtbot.waitUntil(lambda: os.path.isfile(FILE_PATH)) qtbot.waitUntil(lambda: os.path.isfile(FILE_PATH))
assert os.path.isfile(FILE_PATH) assert os.path.isfile(FILE_PATH)
@ -107,7 +111,9 @@ def test_export_fail_unwritable(qapp, qtbot, tmpdir, monkeypatch):
main.profile_export_action() main.profile_export_action()
export_dialog = main.window export_dialog = main.window
qtbot.mouseClick(export_dialog.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) qtbot.mouseClick(
export_dialog.buttonBox.button(QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton
)
assert 'could not be created' in alert_message assert 'could not be created' in alert_message
assert not os.path.isfile(FILE_PATH) assert not os.path.isfile(FILE_PATH)

View File

@ -1,5 +1,5 @@
import pytest import pytest
from PyQt5 import QtCore from PyQt6 import QtCore
import vorta.application import vorta.application
import vorta.borg.borg_job import vorta.borg.borg_job
@ -12,7 +12,7 @@ def test_create_perm_error(qapp, borg_json_output, mocker, qtbot):
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result) mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
qtbot.mouseClick(main.createStartBtn, QtCore.Qt.LeftButton) qtbot.mouseClick(main.createStartBtn, QtCore.Qt.MouseButton.LeftButton)
qtbot.waitUntil(lambda: hasattr(qapp, '_msg'), **pytest._wait_defaults) qtbot.waitUntil(lambda: hasattr(qapp, '_msg'), **pytest._wait_defaults)
assert qapp._msg.text().startswith("You do not have permission") assert qapp._msg.text().startswith("You do not have permission")
@ -28,7 +28,7 @@ def test_create_lock(qapp, borg_json_output, mocker, qtbot):
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result) mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
qtbot.mouseClick(main.createStartBtn, QtCore.Qt.LeftButton) qtbot.mouseClick(main.createStartBtn, QtCore.Qt.MouseButton.LeftButton)
qtbot.waitUntil(lambda: hasattr(qapp, '_msg'), **pytest._wait_defaults) qtbot.waitUntil(lambda: hasattr(qapp, '_msg'), **pytest._wait_defaults)
assert "The repository at" in qapp._msg.text() assert "The repository at" in qapp._msg.text()

View File

@ -3,8 +3,8 @@ import sys
from pathlib import Path from pathlib import Path
from unittest.mock import Mock from unittest.mock import Mock
import pytest import pytest
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtWidgets import QCheckBox, QFormLayout from PyQt6.QtWidgets import QCheckBox, QFormLayout
import vorta.store.models import vorta.store.models
@ -79,5 +79,7 @@ def _click_toggle_setting(setting, qapp, qtbot):
if checkbox.text() == setting: if checkbox.text() == setting:
# Have to use pos to click checkbox correctly # Have to use pos to click checkbox correctly
# https://stackoverflow.com/questions/19418125/pysides-qtest-not-checking-box/24070484#24070484 # https://stackoverflow.com/questions/19418125/pysides-qtest-not-checking-box/24070484#24070484
qtbot.mouseClick(checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2, int(checkbox.height() / 2))) qtbot.mouseClick(
checkbox, QtCore.Qt.MouseButton.LeftButton, pos=QtCore.QPoint(2, int(checkbox.height() / 2))
)
break break

View File

@ -1,6 +1,6 @@
import sys import sys
import pytest import pytest
from PyQt5 import QtDBus from PyQt6 import QtDBus
import vorta.borg import vorta.borg
import vorta.notifications import vorta.notifications

View File

@ -1,17 +1,19 @@
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt5.QtWidgets import QDialogButtonBox from PyQt6.QtWidgets import QDialogButtonBox
from vorta.store.models import BackupProfileModel from vorta.store.models import BackupProfileModel
def test_profile_add(qapp, qtbot): def test_profile_add(qapp, qtbot):
main = qapp.main_window main = qapp.main_window
qtbot.mouseClick(main.profileAddButton, QtCore.Qt.LeftButton) qtbot.mouseClick(main.profileAddButton, QtCore.Qt.MouseButton.LeftButton)
add_profile_window = main.window add_profile_window = main.window
qtbot.addWidget(add_profile_window) # qtbot.addWidget(add_profile_window)
qtbot.keyClicks(add_profile_window.profileNameField, 'Test Profile') qtbot.keyClicks(add_profile_window.profileNameField, 'Test Profile')
qtbot.mouseClick(add_profile_window.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) qtbot.mouseClick(
add_profile_window.buttonBox.button(QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton
)
assert BackupProfileModel.get_or_none(name='Test Profile') is not None assert BackupProfileModel.get_or_none(name='Test Profile') is not None
assert main.profileSelector.currentText() == 'Test Profile' assert main.profileSelector.currentText() == 'Test Profile'
@ -19,14 +21,16 @@ def test_profile_add(qapp, qtbot):
def test_profile_edit(qapp, qtbot): def test_profile_edit(qapp, qtbot):
main = qapp.main_window main = qapp.main_window
qtbot.mouseClick(main.profileRenameButton, QtCore.Qt.LeftButton) qtbot.mouseClick(main.profileRenameButton, QtCore.Qt.MouseButton.LeftButton)
edit_profile_window = main.window edit_profile_window = main.window
qtbot.addWidget(edit_profile_window) # qtbot.addWidget(edit_profile_window)
edit_profile_window.profileNameField.setText("") edit_profile_window.profileNameField.setText("")
qtbot.keyClicks(edit_profile_window.profileNameField, 'Test Profile') qtbot.keyClicks(edit_profile_window.profileNameField, 'Test Profile')
qtbot.mouseClick(edit_profile_window.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) qtbot.mouseClick(
edit_profile_window.buttonBox.button(QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton
)
assert BackupProfileModel.get_or_none(name='Default') is None assert BackupProfileModel.get_or_none(name='Default') is None
assert BackupProfileModel.get_or_none(name='Test Profile') is not None assert BackupProfileModel.get_or_none(name='Test Profile') is not None

View File

@ -1,7 +1,8 @@
import os import os
import uuid import uuid
import pytest import pytest
from PyQt5 import QtCore from PyQt6 import QtCore
from PyQt6.QtWidgets import QMessageBox
import vorta.borg.borg_job import vorta.borg.borg_job
from vorta.keyring.abc import VortaKeyring from vorta.keyring.abc import VortaKeyring
from vorta.store.models import ArchiveModel, EventLogModel, RepoModel from vorta.store.models import ArchiveModel, EventLogModel, RepoModel
@ -20,7 +21,7 @@ def test_repo_add_failures(qapp, qtbot, mocker, borg_json_output):
qtbot.keyClicks(add_repo_window.passwordLineEdit, LONG_PASSWORD) qtbot.keyClicks(add_repo_window.passwordLineEdit, LONG_PASSWORD)
qtbot.keyClicks(add_repo_window.confirmLineEdit, LONG_PASSWORD) qtbot.keyClicks(add_repo_window.confirmLineEdit, LONG_PASSWORD)
qtbot.keyClicks(add_repo_window.repoURL, 'aaa') qtbot.keyClicks(add_repo_window.repoURL, 'aaa')
qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton) qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.MouseButton.LeftButton)
assert add_repo_window.errorText.text().startswith('Please enter a valid') assert add_repo_window.errorText.text().startswith('Please enter a valid')
add_repo_window.passwordLineEdit.clear() add_repo_window.passwordLineEdit.clear()
@ -28,34 +29,35 @@ def test_repo_add_failures(qapp, qtbot, mocker, borg_json_output):
qtbot.keyClicks(add_repo_window.passwordLineEdit, SHORT_PASSWORD) qtbot.keyClicks(add_repo_window.passwordLineEdit, SHORT_PASSWORD)
qtbot.keyClicks(add_repo_window.confirmLineEdit, SHORT_PASSWORD) qtbot.keyClicks(add_repo_window.confirmLineEdit, SHORT_PASSWORD)
qtbot.keyClicks(add_repo_window.repoURL, 'bbb.com:repo') qtbot.keyClicks(add_repo_window.repoURL, 'bbb.com:repo')
qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton) qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.MouseButton.LeftButton)
assert add_repo_window.passwordLabel.text() == 'Passwords must be greater than 8 characters long.' assert add_repo_window.passwordLabel.text() == 'Passwords must be greater than 8 characters long.'
add_repo_window.passwordLineEdit.clear() add_repo_window.passwordLineEdit.clear()
add_repo_window.confirmLineEdit.clear() add_repo_window.confirmLineEdit.clear()
qtbot.keyClicks(add_repo_window.passwordLineEdit, SHORT_PASSWORD + "1") qtbot.keyClicks(add_repo_window.passwordLineEdit, SHORT_PASSWORD + "1")
qtbot.keyClicks(add_repo_window.confirmLineEdit, SHORT_PASSWORD) qtbot.keyClicks(add_repo_window.confirmLineEdit, SHORT_PASSWORD)
qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton) qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.MouseButton.LeftButton)
assert add_repo_window.passwordLabel.text() == 'Passwords must be identical and greater than 8 characters long.' assert add_repo_window.passwordLabel.text() == 'Passwords must be identical and greater than 8 characters long.'
add_repo_window.passwordLineEdit.clear() add_repo_window.passwordLineEdit.clear()
add_repo_window.confirmLineEdit.clear() add_repo_window.confirmLineEdit.clear()
qtbot.keyClicks(add_repo_window.passwordLineEdit, LONG_PASSWORD) qtbot.keyClicks(add_repo_window.passwordLineEdit, LONG_PASSWORD)
qtbot.keyClicks(add_repo_window.confirmLineEdit, SHORT_PASSWORD) qtbot.keyClicks(add_repo_window.confirmLineEdit, SHORT_PASSWORD)
qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton) qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.MouseButton.LeftButton)
assert add_repo_window.passwordLabel.text() == 'Passwords must be identical.' assert add_repo_window.passwordLabel.text() == 'Passwords must be identical.'
def test_repo_unlink(qapp, qtbot): def test_repo_unlink(qapp, qtbot, monkeypatch):
main = qapp.main_window main = qapp.main_window
tab = main.repoTab tab = main.repoTab
monkeypatch.setattr(QMessageBox, "show", lambda *args: True)
main.tabWidget.setCurrentIndex(0) main.tabWidget.setCurrentIndex(0)
qtbot.mouseClick(tab.repoRemoveToolbutton, QtCore.Qt.LeftButton) qtbot.mouseClick(tab.repoRemoveToolbutton, QtCore.Qt.MouseButton.LeftButton)
qtbot.waitUntil(lambda: tab.repoSelector.count() == 1, **pytest._wait_defaults) qtbot.waitUntil(lambda: tab.repoSelector.count() == 1, **pytest._wait_defaults)
assert RepoModel.select().count() == 0 assert RepoModel.select().count() == 0
qtbot.mouseClick(main.createStartBtn, QtCore.Qt.LeftButton) qtbot.mouseClick(main.createStartBtn, QtCore.Qt.MouseButton.LeftButton)
# -1 is the repo id in this test # -1 is the repo id in this test
qtbot.waitUntil(lambda: 'Select a backup repository first.' in main.progressText.text(), **pytest._wait_defaults) qtbot.waitUntil(lambda: 'Select a backup repository first.' in main.progressText.text(), **pytest._wait_defaults)
assert 'Select a backup repository first.' in main.progressText.text() assert 'Select a backup repository first.' in main.progressText.text()
@ -105,7 +107,7 @@ def test_repo_add_success(qapp, qtbot, mocker, borg_json_output):
def test_ssh_dialog(qapp, qtbot, tmpdir): def test_ssh_dialog(qapp, qtbot, tmpdir):
main = qapp.main_window main = qapp.main_window
qtbot.mouseClick(main.repoTab.bAddSSHKey, QtCore.Qt.LeftButton) qtbot.mouseClick(main.repoTab.bAddSSHKey, QtCore.Qt.MouseButton.LeftButton)
ssh_dialog = main.repoTab._window ssh_dialog = main.repoTab._window
ssh_dir = tmpdir ssh_dir = tmpdir
@ -135,7 +137,7 @@ def test_create(qapp, borg_json_output, mocker, qtbot):
popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result) mocker.patch.object(vorta.borg.borg_job, 'Popen', return_value=popen_result)
qtbot.mouseClick(main.createStartBtn, QtCore.Qt.LeftButton) qtbot.mouseClick(main.createStartBtn, QtCore.Qt.MouseButton.LeftButton)
qtbot.waitUntil(lambda: 'Backup finished.' in main.progressText.text(), **pytest._wait_defaults) qtbot.waitUntil(lambda: 'Backup finished.' in main.progressText.text(), **pytest._wait_defaults)
qtbot.waitUntil(lambda: main.createStartBtn.isEnabled(), **pytest._wait_defaults) qtbot.waitUntil(lambda: main.createStartBtn.isEnabled(), **pytest._wait_defaults)
assert EventLogModel.select().count() == 1 assert EventLogModel.select().count() == 1

View File

@ -2,7 +2,7 @@ from datetime import datetime as dt
from datetime import timedelta from datetime import timedelta
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
from PyQt5 import QtCore from PyQt6 import QtCore
import vorta.scheduler import vorta.scheduler
from vorta.application import VortaApp from vorta.application import VortaApp
from vorta.store.models import BackupProfileModel, EventLogModel from vorta.store.models import BackupProfileModel, EventLogModel
@ -31,17 +31,17 @@ def test_schedule_tab(qapp: VortaApp, qtbot, clockmock):
qapp.scheduler.schedule_changed.connect(lambda *args: tab.draw_next_scheduled_backup()) qapp.scheduler.schedule_changed.connect(lambda *args: tab.draw_next_scheduled_backup())
# Test # Test
qtbot.mouseClick(tab.scheduleOffRadio, QtCore.Qt.LeftButton) qtbot.mouseClick(tab.scheduleOffRadio, QtCore.Qt.MouseButton.LeftButton)
assert tab.nextBackupDateTimeLabel.text() == 'None scheduled' assert tab.nextBackupDateTimeLabel.text() == 'None scheduled'
tab.scheduleIntervalCount.setValue(5) tab.scheduleIntervalCount.setValue(5)
qtbot.mouseClick(tab.scheduleIntervalRadio, QtCore.Qt.LeftButton) qtbot.mouseClick(tab.scheduleIntervalRadio, QtCore.Qt.MouseButton.LeftButton)
assert "None" not in tab.nextBackupDateTimeLabel.text() assert "None" not in tab.nextBackupDateTimeLabel.text()
tab.scheduleFixedTime.setTime(QtCore.QTime(23, 59)) tab.scheduleFixedTime.setTime(QtCore.QTime(23, 59))
# Clicking currently broken for this button on github.com only # Clicking currently broken for this button on github.com only
# qtbot.mouseClick(tab.scheduleFixedRadio, QtCore.Qt.LeftButton) # qtbot.mouseClick(tab.scheduleFixedRadio, QtCore.Qt.MouseButton.LeftButton)
# Workaround for github # Workaround for github
tab.scheduleFixedRadio.setChecked(True) tab.scheduleFixedRadio.setChecked(True)

View File

@ -1,6 +1,6 @@
from pathlib import PurePath from pathlib import PurePath
import pytest import pytest
from PyQt5.QtCore import QModelIndex from PyQt6.QtCore import QModelIndex
from vorta.views.partials.treemodel import FileSystemItem, FileTreeModel from vorta.views.partials.treemodel import FileSystemItem, FileTreeModel