From f009e046c0c10880810498ec00d33127c084f29f Mon Sep 17 00:00:00 2001 From: introkun Date: Mon, 25 Mar 2024 23:24:07 -0300 Subject: [PATCH 1/7] update to qt6 --- .editorconfig | 1 - .pylintrc | 2 +- README.md | 6 +- autoformat | 10 +-- gui/app.py | 88 +++++++++++++--------- gui/header.py | 41 +++++++---- gui/panes/dirlist.py | 129 ++++++++++++++++++-------------- gui/panes/files.py | 11 ++- gui/panes/filters.py | 150 +++++++++++++++++++++++++------------- gui/panes/format.py | 58 ++++++++------- gui/panes/options.py | 24 ++++-- gui/progress.py | 11 +-- gui/table_model.py | 46 +++++++----- gui/tabs.py | 5 +- nut_gui.py | 29 ++++---- requirements.txt | 4 +- requirements_dev.txt | 18 ++--- tests-gui/gui_app_test.py | 22 +++--- 18 files changed, 393 insertions(+), 262 deletions(-) diff --git a/.editorconfig b/.editorconfig index 797a73e8f..572685422 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,7 +13,6 @@ insert_final_newline = true [*.{js,py}] charset = utf-8 -# 4 space indentation [*.py] indent_style = tab indent_size = 4 diff --git a/.pylintrc b/.pylintrc index 25eee16f1..eac0e6b9a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -3,7 +3,7 @@ # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. -extension-pkg-whitelist=PyQt5 +extension-pkg-whitelist=PyQt6 # Specify a score threshold to be exceeded before program exits with error. fail-under=90 diff --git a/README.md b/README.md index 6dd5f5c63..31d76ae63 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ NUT has the ability to interact with Google Drive. For this to work, you will ne
Details -* Install Python 3.9+ from your preferred package manager, along with the `libusb`, `python3-pip` & `python3-pyqt5` packages +* Install Python 3.9+ from your preferred package manager, along with the `libusb`, `python3-pip` & `python3-pyqt6` packages * Install `curl` with the openssl backend - install `libssl-dev` (ie, `apt install libssl-dev libcurl4-openssl-dev`) * Clone this repository to desired directory and change your working directory to the cloned repository * Install the PIP modules with the following command `pip3 install -r requirements.txt`. If you previously tried installing pycurl and get the error `libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)`, uninstall it, make sure to follow step 2 again (installing curl with the openssl backend), and `pip3 install pycurl --no-cache-dir` @@ -77,12 +77,12 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="27e2", GROUP="plug
Details -* Install Python 3.9 and PyQt5 via Homebrew (`brew install python@3.9 pyqt@5`) +* Install Python 3.9 and PyQt6 via Homebrew (`brew install python@3.9 pyqt@6`) * Install pyenv: [pyenv](https://github.com/pyenv/pyenv) + [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv) (`brew install pyenv pyenv-virtualenv` and follow install directions) * Install `libusb` (`brew install libusb`) * Install `curl` with the openssl backend (`brew uninstall --ignore-dependencies curl && brew install curl`) * Install Python 3.9.7 with pyenv and set it as the default (`pyenv install 3.9.7 && pyenv global 3.9.7`) -* Load the system python's site-packages via pyenv's python. This is required to use PyQT5 from Homebrew. +* Load the system python's site-packages via pyenv's python. This is required to use PyQT6 from Homebrew. - Get the Homebrew Python site-packages path (via `brew info python@3.9`). - Add it to the load path of your pyenv's Python install (cd `pyenv root`). - To do this, go to the site packages directory of your pyenv install (ie. `$HOME/.pyenv/versions/3.9.7/lib/python3.9/site-packages`) and create an file named `homebrew.pth` containing the path for Homebrew Python's site packages directory (ie. `/opt/homebrew/lib/python3.9/site-packages`) diff --git a/autoformat b/autoformat index a8131f816..b4887a7e2 100755 --- a/autoformat +++ b/autoformat @@ -3,9 +3,9 @@ # TODO: migrate to tasks.py (invoke autoformat) # delete python cache -find . -type f -name "*.py[co]" -delete -find . -type d -name "__pycache__" -delete +find ./ -type f -name "*.py[co]" -delete +find ./ -type d -name "__pycache__" -delete # execute autoformatters -find . -iname '*.py' -exec autopep8 --global-config setup.cfg --aggressive -i "{}" \; -find . -iname '*.py' -exec autoflake --in-place --remove-all-unused-imports --remove-unused-variables "{}" \; -find . -iname '*.py' -exec sed -i 's/ /\t/g' "{}" \; +find ./ -iname '*.py' -exec autopep8 --global-config setup.cfg --aggressive -i "{}" \; +find ./ -iname '*.py' -exec autoflake --in-place --remove-all-unused-imports --remove-unused-variables "{}" \; +find ./ -iname '*.py' -exec sed -i 's/ /\t/g' "{}" \; diff --git a/gui/app.py b/gui/app.py index cff2c5dc4..6cf7e44df 100644 --- a/gui/app.py +++ b/gui/app.py @@ -2,8 +2,9 @@ import os import webbrowser -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtWidgets import (QWidget, QDesktopWidget, QVBoxLayout, QMessageBox) +from PyQt6.QtGui import QGuiApplication +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QMessageBox import gui.tabs import gui.panes.files @@ -20,10 +21,11 @@ import Fs.driver.init from translator import tr + class App(QWidget): def __init__(self): super().__init__() - self.title = 'NUT 3.3' + self.title = "NUT 3.3" self.needsRefresh = False self.isInitialized = False self.initUI() @@ -34,7 +36,7 @@ def refresh(self): def initUI(self): self.isInitialized = False self.setWindowTitle(self.title) - screen = QDesktopWidget().screenGeometry() + screen = QGuiApplication.primaryScreen().geometry() left = int(screen.width() / 4) top = int(screen.height() / 4) width = int(screen.width() / 2) @@ -47,15 +49,29 @@ def initUI(self): layout.addLayout(self.header.layout) self.files = gui.panes.files.Files() - self.tabs = gui.tabs.Tabs({ - tr('main.grid.files'): self.files, - tr('main.grid.filters'): gui.panes.filters.Filters(), - tr('main.grid.save_paths'): gui.panes.format.Format(), - tr('main.grid.local_scan_paths'): gui.panes.dirlist.DirList(Config.paths.scan, self.saveScanPaths, rowType=gui.panes.dirlist.DirectoryLocal), - tr('main.grid.remote_pull_paths'): gui.panes.dirlist.DirList(Config.pullUrls, self.savePullUrls, rowType=gui.panes.dirlist.DirectoryNetwork), - tr('main.grid.users'): gui.panes.dirlist.DirList(list(Users.users.values()), self.saveUsers, rowType=gui.panes.dirlist.User), # rowType - tr('main.grid.options'): gui.panes.options.Options() - }) + self.tabs = gui.tabs.Tabs( + { + tr("main.grid.files"): self.files, + tr("main.grid.filters"): gui.panes.filters.Filters(), + tr("main.grid.save_paths"): gui.panes.format.Format(), + tr("main.grid.local_scan_paths"): gui.panes.dirlist.DirList( + Config.paths.scan, + self.saveScanPaths, + rowType=gui.panes.dirlist.DirectoryLocal, + ), + tr("main.grid.remote_pull_paths"): gui.panes.dirlist.DirList( + Config.pullUrls, + self.savePullUrls, + rowType=gui.panes.dirlist.DirectoryNetwork, + ), + tr("main.grid.users"): gui.panes.dirlist.DirList( + list(Users.users.values()), + self.saveUsers, + rowType=gui.panes.dirlist.User, + ), # rowType + tr("main.grid.options"): gui.panes.options.Options(), + } + ) layout.addWidget(self.tabs) self.progress = Progress(self) @@ -120,9 +136,12 @@ def on_compress(): nut.compressAll(Config.compression.level) def on_organize(self): - answer = QMessageBox.question(self, tr('main.top_menu.organize'), - tr('main.dialog.organize_confirmation'), - QMessageBox.Yes | QMessageBox.No) + answer = QMessageBox.question( + self, + tr("main.top_menu.organize"), + tr("main.dialog.organize_confirmation"), + QMessageBox.Yes | QMessageBox.No, + ) if answer == QMessageBox.Yes: nut.organize() @@ -145,49 +164,50 @@ def on_titledb(): def on_gdrive(self): if Config.getGdriveCredentialsFile() is None: webbrowser.open_new_tab( - 'https://developers.google.com/drive/api/v3/quickstart/go', + "https://developers.google.com/drive/api/v3/quickstart/go", ) QMessageBox.information( self, - 'Google Drive OAuth Setup', - "You require a credentials.json file to set up Google Drive " + - "OAuth. This file can be obtained from " + - "https://developers.google.com/drive/api/v3/quickstart/go , " + - "click on the blue button that says 'Enable the Drive API' " + - "and save the credentials.json to t his application's " + - "directory.", + "Google Drive OAuth Setup", + "You require a credentials.json file to set up Google Drive " + + "OAuth. This file can be obtained from " + + "https://developers.google.com/drive/api/v3/quickstart/go , " + + "click on the blue button that says 'Enable the Drive API' " + + "and save the credentials.json to t his application's " + + "directory.", ) else: buttonReply = QMessageBox.question( self, - 'Google Drive OAuth Setup', + "Google Drive OAuth Setup", "Do you you want to setup GDrive OAuth?", - QMessageBox.Yes | QMessageBox.No, QMessageBox.No, + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No, ) if buttonReply == QMessageBox.Yes: try: - os.unlink('gdrive.token') + os.unlink("gdrive.token") except OSError: pass try: - os.unlink('token.pickle') + os.unlink("token.pickle") except OSError: pass Fs.driver.gdrive.getGdriveToken(None, None) QMessageBox.information( self, - 'Google Drive OAuth Setup', - "OAuth has completed. Please copy gdrive.token and " + - "credentials.json to your Nintendo Switch's " + - "sdmc:/switch/tinfoil/ and/or sdmc:/switch/sx/ " + - "directories." + "Google Drive OAuth Setup", + "OAuth has completed. Please copy gdrive.token and " + + "credentials.json to your Nintendo Switch's " + + "sdmc:/switch/tinfoil/ and/or sdmc:/switch/sx/ " + + "directories.", ) @staticmethod def closeEvent(event): del event # TODO: implement a graceful shutdown of other threads - os._exit(0) # pylint: disable=protected-access + os._exit(0) # pylint: disable=protected-access diff --git a/gui/header.py b/gui/header.py index e771b9646..73099a237 100644 --- a/gui/header.py +++ b/gui/header.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- import socket -from PyQt5.QtCore import (Qt, QTimer) -from PyQt5.QtWidgets import (QVBoxLayout, QHBoxLayout, QPushButton, QLabel) +from PyQt6.QtCore import Qt, QTimer +from PyQt6.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton, QLabel from nut import Config, Users, Usb from translator import tr + def _get_ip_address(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: @@ -17,28 +18,36 @@ def _get_ip_address(): except OSError: return None + def _create_button(app, parent, text, max_width, handler): widget = QPushButton(text, app) widget.setMaximumWidth(max_width) widget.clicked.connect(handler) - widget.setFocusPolicy(Qt.StrongFocus) + widget.setFocusPolicy(Qt.FocusPolicy.StrongFocus) parent.addWidget(widget) return widget -class Header: # pylint: disable=too-many-instance-attributes,too-few-public-methods + +class Header: # pylint: disable=too-many-instance-attributes,too-few-public-methods def __init__(self, app): self.layout = QVBoxLayout() top = QHBoxLayout() bottom = QHBoxLayout() - self.scan = _create_button(app, top, tr('main.top_menu.scan'), 100, app.on_scan) - _create_button(app, top, tr('main.top_menu.organize'), 200, app.on_organize) - self.pull = _create_button(app, top, tr('main.top_menu.pull'), 100, app.on_pull) - self.titledb = _create_button(app, top, tr('main.top_menu.update_titledb'), 200, app.on_titledb) - _create_button(app, top, tr('main.top_menu.decompress_nsz'), 200, app.on_decompress) - _create_button(app, top, tr('main.top_menu.compress_nsp'), 200, app.on_compress) - self.gdrive = _create_button(app, top, tr('main.top_menu.setup_gdrive'), 200, app.on_gdrive) + self.scan = _create_button(app, top, tr("main.top_menu.scan"), 100, app.on_scan) + _create_button(app, top, tr("main.top_menu.organize"), 200, app.on_organize) + self.pull = _create_button(app, top, tr("main.top_menu.pull"), 100, app.on_pull) + self.titledb = _create_button( + app, top, tr("main.top_menu.update_titledb"), 200, app.on_titledb + ) + _create_button( + app, top, tr("main.top_menu.decompress_nsz"), 200, app.on_decompress + ) + _create_button(app, top, tr("main.top_menu.compress_nsp"), 200, app.on_compress) + self.gdrive = _create_button( + app, top, tr("main.top_menu.setup_gdrive"), 200, app.on_gdrive + ) top.addStretch() @@ -46,21 +55,21 @@ def __init__(self, app): if ipAddr: self.serverInfo = QLabel( - f"{tr('main.status.ip')}: {ipAddr} {tr('main.status.port')}: {Config.server.port} " + - f"{tr('main.status.user')}: {Users.first().id} {tr('main.status.password')}: " + - f"{Users.first().password}" + f"{tr('main.status.ip')}: {ipAddr} {tr('main.status.port')}: {Config.server.port} " + + f"{tr('main.status.user')}: {Users.first().id} {tr('main.status.password')}: " + + f"{Users.first().password}" ) else: self.serverInfo = QLabel("Offline") self.serverInfo.setMinimumWidth(200) - self.serverInfo.setAlignment(Qt.AlignCenter) + self.serverInfo.setAlignment(Qt.AlignmentFlag.AlignCenter) bottom.addWidget(self.serverInfo) bottom.addStretch() self.usbStatus = QLabel("USB: " + tr("usb.status." + Usb.status)) self.usbStatus.setMinimumWidth(50) - self.usbStatus.setAlignment(Qt.AlignCenter) + self.usbStatus.setAlignment(Qt.AlignmentFlag.AlignCenter) bottom.addWidget(self.usbStatus) self.timer = QTimer() diff --git a/gui/panes/dirlist.py b/gui/panes/dirlist.py index 2f43b488b..d1e70138b 100644 --- a/gui/panes/dirlist.py +++ b/gui/panes/dirlist.py @@ -1,10 +1,23 @@ # -*- coding: utf-8 -*- import urllib.parse -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import (QComboBox, QDialog, QDialogButtonBox, QFileDialog, - QFormLayout, QHBoxLayout, QLabel, QLineEdit, - QListWidget, QPushButton, QVBoxLayout, QWidget, QScrollArea, QFrame) +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import ( + QComboBox, + QDialog, + QDialogButtonBox, + QFileDialog, + QFormLayout, + QHBoxLayout, + QLabel, + QLineEdit, + QListWidget, + QPushButton, + QVBoxLayout, + QWidget, + QScrollArea, + QFrame, +) import Fs.driver from nut import Users @@ -12,8 +25,8 @@ class Edit(QLineEdit): - """Edit UI control - """ + """Edit UI control""" + def __init__(self, parent): super().__init__() self.parent = parent @@ -29,9 +42,10 @@ def focusOutEvent(self, event): super().focusOutEvent(event) + class FolderPicker(QDialog): - """FolderPicker UI control - """ + """FolderPicker UI control""" + def __init__(self, url): super().__init__() self.setWindowTitle("Directory Picker") @@ -62,18 +76,18 @@ def onSelect(self, item): def refreshList(self): self.list.clear() - for d in Fs.driver.openDir(self.url).ls(): + for d in Fs.driver.openDir(self.url).ls(): if d.isFile(): continue self.list.addItem(d.baseName()) - def save(self): pass -class GdrivePicker(QDialog): # pylint: disable=too-many-instance-attributes - """GdrivePicker UI control - """ + +class GdrivePicker(QDialog): # pylint: disable=too-many-instance-attributes + """GdrivePicker UI control""" + def __init__(self): super().__init__() self.setWindowTitle("Directory Picker") @@ -82,30 +96,30 @@ def __init__(self): settings = QFormLayout() schemes = QComboBox(self) - schemes.addItem('') - schemes.addItem('ftp') - schemes.addItem('ftps') - schemes.addItem('gdrive') - schemes.addItem('http') - schemes.addItem('https') - schemes.addItem('sftp') - settings.addRow(QLabel('Scheme'), schemes) + schemes.addItem("") + schemes.addItem("ftp") + schemes.addItem("ftps") + schemes.addItem("gdrive") + schemes.addItem("http") + schemes.addItem("https") + schemes.addItem("sftp") + settings.addRow(QLabel("Scheme"), schemes) self.schemes = schemes self.username = Edit(self) - settings.addRow(QLabel('Username'), self.username) + settings.addRow(QLabel("Username"), self.username) self.password = Edit(self) - settings.addRow(QLabel('Password'), self.password) + settings.addRow(QLabel("Password"), self.password) self.host = Edit(self) - settings.addRow(QLabel('Host'), self.host) + settings.addRow(QLabel("Host"), self.host) self.port = Edit(self) - settings.addRow(QLabel('Port'), self.port) + settings.addRow(QLabel("Port"), self.port) self.path = Edit(self) - settings.addRow(QLabel('Path'), self.path) + settings.addRow(QLabel("Path"), self.path) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.setUrl) @@ -126,38 +140,41 @@ def setUrl(self): if not scheme: return - if scheme == 'gdrive': - self.url = 'gdrive:/' + if scheme == "gdrive": + self.url = "gdrive:/" else: if not self.host.getValue(): return - self.url = scheme + '://' + self.url = scheme + "://" if self.username.getValue(): - self.url += urllib.parse.quote(self.username.getValue(), safe = '') + self.url += urllib.parse.quote(self.username.getValue(), safe="") if self.password.getValue(): - self.url += ':' + urllib.parse.quote(self.password.getValue(), safe = '') + self.url += ":" + urllib.parse.quote( + self.password.getValue(), safe="" + ) - self.url += '@' + self.url += "@" self.url += self.host.getValue() if self.port.getValue(): - self.url += ':' + self.port.getValue() + self.url += ":" + self.port.getValue() - self.url += '/' + self.url += "/" if self.path.getValue(): self.url = Fs.driver.join(self.url, self.path.getValue()) self.accept() - except BaseException: # pylint: disable=broad-except + except BaseException: # pylint: disable=broad-except self.reject() + class User(QWidget): - """User UI control - """ + """User UI control""" + def __init__(self, parent): super().__init__() self.parent = parent @@ -185,17 +202,17 @@ def setValue(self, user): class DirectoryLocal(QWidget): - """DirectoryLocal UI control - """ + """DirectoryLocal UI control""" + def __init__(self, parent): super().__init__() self.parent = parent layout = QHBoxLayout(self) - self.dirBtn = QPushButton(tr('dirlist.browse')) + self.dirBtn = QPushButton(tr("dirlist.browse")) self.dirBtn.setFixedWidth(70) self.dirBtn.clicked.connect(self.on_browse) - self.dirBtn.setFocusPolicy(Qt.StrongFocus) + self.dirBtn.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.edit = Edit(self) @@ -207,8 +224,9 @@ def save(self): self.parent.save() def on_browse(self): - value = QFileDialog.getExistingDirectory(self, tr('Select Directory'),\ - self.getValue(), QFileDialog.ShowDirsOnly) + value = QFileDialog.getExistingDirectory( + self, tr("Select Directory"), self.getValue(), QFileDialog.ShowDirsOnly + ) if value: self.setValue(value) @@ -225,15 +243,16 @@ def focusOutEvent(self, event): super().focusOutEvent(event) + class DirectoryNetwork(QWidget): - """DirectoryNetwork UI control - """ + """DirectoryNetwork UI control""" + def __init__(self, parent): super().__init__() self.parent = parent layout = QHBoxLayout(self) - self.dirBtn = QPushButton('browse') + self.dirBtn = QPushButton("browse") self.dirBtn.setFixedWidth(70) self.dirBtn.clicked.connect(self.on_browse) @@ -270,9 +289,10 @@ def focusOutEvent(self, event): super().focusOutEvent(event) + class Row(QWidget): - """Row UI control - """ + """Row UI control""" + def __init__(self, parent, value, rowType=DirectoryLocal): super().__init__() self.parent = parent @@ -282,10 +302,10 @@ def __init__(self, parent, value, rowType=DirectoryLocal): if value is not None: self.control.setValue(value) - self.remove = QPushButton('X') + self.remove = QPushButton("X") self.remove.setFixedWidth(50) self.remove.clicked.connect(self.on_remove) - self.remove.setFocusPolicy(Qt.StrongFocus) + self.remove.setFocusPolicy(Qt.FocusPolicy.StrongFocus) layout.addWidget(self.control) layout.addWidget(self.remove) @@ -302,20 +322,21 @@ def on_remove(self): def save(self): self.parent.save() + class DirList(QWidget): - """DirList UI control - """ + """DirList UI control""" + def __init__(self, values, onChange=None, rowType=DirectoryLocal): super().__init__() self.rowType = rowType self.scroll = QScrollArea(self) self.scroll.setWidgetResizable(True) - self.scroll.setFrameShape(QFrame.NoFrame) + self.scroll.setFrameShape(QFrame.Shape.NoFrame) layout = QVBoxLayout(self.scroll) self.list = QVBoxLayout() - self.button = QPushButton('Add') + self.button = QPushButton("Add") self.button.clicked.connect(self.on_click) diff --git a/gui/panes/files.py b/gui/panes/files.py index 0db1f269e..e429b9796 100644 --- a/gui/panes/files.py +++ b/gui/panes/files.py @@ -1,7 +1,6 @@ - -from PyQt5 import QtWidgets -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QTableView +from PyQt6 import QtWidgets +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import QTableView from nut import Nsps from gui.table_model import TableModel @@ -14,10 +13,10 @@ def __init__(self): self.setModel(self.model) header = self.horizontalHeader() - header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch) self.setSortingEnabled(True) - self.sortByColumn(0, Qt.AscendingOrder) + self.sortByColumn(0, Qt.SortOrder.AscendingOrder) self.refresh() diff --git a/gui/panes/filters.py b/gui/panes/filters.py index 0d2cfd5e6..47c90e8ac 100644 --- a/gui/panes/filters.py +++ b/gui/panes/filters.py @@ -1,7 +1,17 @@ # -*- coding: utf-8 -*- -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import (QCheckBox, QGridLayout, QGroupBox, QHBoxLayout, - QLabel, QSizePolicy, QVBoxLayout, QWidget, QScrollArea, QFrame) +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import ( + QCheckBox, + QGridLayout, + QGroupBox, + QHBoxLayout, + QLabel, + QSizePolicy, + QVBoxLayout, + QWidget, + QScrollArea, + QFrame, +) from qt_range_slider import QtRangeSlider import humanize @@ -10,10 +20,9 @@ from translator import tr - class ConfCheckbox(QCheckBox): - """ConfCheckbox - """ + """ConfCheckbox""" + def __init__(self, text, conf): super().__init__(text) self.conf = conf @@ -29,23 +38,24 @@ def onStateChanged(self, state): def get(self): try: j = Config - for path in self.conf.split('.'): + for path in self.conf.split("."): j = getattr(j, path) return j - except BaseException: # pylint: disable=broad-except + except BaseException: # pylint: disable=broad-except return None def set(self, value): j = Config - paths = self.conf.split('.') + paths = self.conf.split(".") last = paths.pop() for path in paths: j = getattr(j, path) setattr(j, last, value) + class RegionEntry(QWidget): - """RegionEntry - """ + """RegionEntry""" + def __init__(self, region): super().__init__() self.region = region.upper() @@ -67,8 +77,8 @@ def onStateChanged(self, state): class Region(QWidget): - """Region - """ + """Region""" + def __init__(self): super().__init__() @@ -86,10 +96,11 @@ def __init__(self): layout.addWidget(RegionEntry(region), i // width, i % width) i += 1 + class Filters(QWidget): - """Filters - """ - def __init__(self): # pylint: disable=too-many-locals + """Filters""" + + def __init__(self): # pylint: disable=too-many-locals super().__init__() self.MIN_FILE_SIZE = 0 @@ -100,17 +111,17 @@ def __init__(self): # pylint: disable=too-many-locals self.scroll = QScrollArea(self) self.scroll.setWidgetResizable(True) - self.scroll.setFrameShape(QFrame.NoFrame) + self.scroll.setFrameShape(QFrame.Shape.NoFrame) layout = QVBoxLayout(self.scroll) - typesGroup = QGroupBox(tr('filters.types.group')) + typesGroup = QGroupBox(tr("filters.types.group")) Filters._createTypesGroup(layout, typesGroup) Filters._createRegionGroup(layout) - sizeFilterGroup = QGroupBox(tr('filters.size.group')) + sizeFilterGroup = QGroupBox(tr("filters.size.group")) sizeFilterLayout = QHBoxLayout(sizeFilterGroup) minFileSizeFilter = self.MIN_FILE_SIZE @@ -120,19 +131,38 @@ def __init__(self): # pylint: disable=too-many-locals if Config.download.fileSizeMax is not None: maxFileSizeFilter = Config.download.fileSizeMax - filterMinSizeLabel = Filters._createLeftLabel(sizeFilterLayout, minFileSizeFilter) - sizeFilter = self._createRangeSlider(sizeFilterLayout, self.MIN_FILE_SIZE, self.MAX_FILE_SIZE, - minFileSizeFilter, maxFileSizeFilter) - filterMaxSizeLabel = Filters._createRightLabel(sizeFilterLayout, sizeFilter.get_right_thumb_value()) - - sizeFilter.left_thumb_value_changed.connect((lambda x: \ - Filters._on_thumb_value_changed(filterMinSizeLabel, x, "fileSizeMin"))) - sizeFilter.right_thumb_value_changed.connect((lambda x: \ - Filters._on_thumb_value_changed(filterMaxSizeLabel, x, "fileSizeMax"))) + filterMinSizeLabel = Filters._createLeftLabel( + sizeFilterLayout, minFileSizeFilter + ) + sizeFilter = self._createRangeSlider( + sizeFilterLayout, + self.MIN_FILE_SIZE, + self.MAX_FILE_SIZE, + minFileSizeFilter, + maxFileSizeFilter, + ) + filterMaxSizeLabel = Filters._createRightLabel( + sizeFilterLayout, sizeFilter.get_right_thumb_value() + ) + + sizeFilter.left_thumb_value_changed.connect( + ( + lambda x: Filters._on_thumb_value_changed( + filterMinSizeLabel, x, "fileSizeMin" + ) + ) + ) + sizeFilter.right_thumb_value_changed.connect( + ( + lambda x: Filters._on_thumb_value_changed( + filterMaxSizeLabel, x, "fileSizeMax" + ) + ) + ) layout.addWidget(sizeFilterGroup) - rankFilterGroup = QGroupBox(tr('filters.rank.group')) + rankFilterGroup = QGroupBox(tr("filters.rank.group")) rankFilterLayout = QHBoxLayout(rankFilterGroup) minRankFilter = self.MIN_RANK @@ -142,16 +172,30 @@ def __init__(self): # pylint: disable=too-many-locals if Config.download.rankMax is not None: maxRankFilter = Config.download.rankMax - filterMinRankLabel = Filters._createLeftLabel(rankFilterLayout, minRankFilter, isSize=False) - rankFilter = self._createRangeSlider(rankFilterLayout, self.MIN_RANK, self.MAX_RANK, - minRankFilter, maxRankFilter) - filterMaxRankLabel = Filters._createRightLabel(rankFilterLayout, rankFilter.get_right_thumb_value(), - isSize=False) - - rankFilter.left_thumb_value_changed.connect((lambda x: \ - Filters._on_thumb_value_changed(filterMinRankLabel, x, "rankMin", isSize=False))) - rankFilter.right_thumb_value_changed.connect((lambda x: \ - Filters._on_thumb_value_changed(filterMaxRankLabel, x, "rankMax", isSize=False))) + filterMinRankLabel = Filters._createLeftLabel( + rankFilterLayout, minRankFilter, isSize=False + ) + rankFilter = self._createRangeSlider( + rankFilterLayout, self.MIN_RANK, self.MAX_RANK, minRankFilter, maxRankFilter + ) + filterMaxRankLabel = Filters._createRightLabel( + rankFilterLayout, rankFilter.get_right_thumb_value(), isSize=False + ) + + rankFilter.left_thumb_value_changed.connect( + ( + lambda x: Filters._on_thumb_value_changed( + filterMinRankLabel, x, "rankMin", isSize=False + ) + ) + ) + rankFilter.right_thumb_value_changed.connect( + ( + lambda x: Filters._on_thumb_value_changed( + filterMaxRankLabel, x, "rankMax", isSize=False + ) + ) + ) layout.addWidget(rankFilterGroup) @@ -171,7 +215,7 @@ def _on_thumb_value_changed(label, value, config_param, isSize=True): @staticmethod def _createRegionGroup(layout): - region = QGroupBox('REGION') + region = QGroupBox("REGION") regionLayout = QHBoxLayout(region) regionLayout.addWidget(Region()) layout.addWidget(region) @@ -180,29 +224,35 @@ def _createRegionGroup(layout): def _createTypesGroup(layout, typesGroup): typesLayout = QHBoxLayout(typesGroup) - typesLayout.addWidget(ConfCheckbox(tr('filters.types.base'), 'download.base')) + typesLayout.addWidget(ConfCheckbox(tr("filters.types.base"), "download.base")) typesLayout.addStretch() - typesLayout.addWidget(ConfCheckbox(tr('filters.types.dlc'), 'download.DLC')) + typesLayout.addWidget(ConfCheckbox(tr("filters.types.dlc"), "download.DLC")) typesLayout.addStretch() - typesLayout.addWidget(ConfCheckbox(tr('filters.types.update'), 'download.update')) + typesLayout.addWidget( + ConfCheckbox(tr("filters.types.update"), "download.update") + ) typesLayout.addStretch() - typesLayout.addWidget(ConfCheckbox(tr('filters.types.demo'), 'download.demo')) + typesLayout.addWidget(ConfCheckbox(tr("filters.types.demo"), "download.demo")) layout.addWidget(typesGroup) @staticmethod def _createLeftLabel(layout, value, isSize=True): - return Filters._createLabel(layout, value, Qt.AlignRight, isSize) + return Filters._createLabel(layout, value, Qt.AlignmentFlag.AlignRight, isSize) @staticmethod def _createRightLabel(layout, value, isSize=True): - return Filters._createLabel(layout, value, Qt.AlignLeft, isSize) - - def _createRangeSlider(self, layout, defaultMinValue, defaultMaxValue, minValue, maxValue): # pylint: disable=too-many-arguments - rangeSlider = QtRangeSlider(self, defaultMinValue, defaultMaxValue, minValue, maxValue) + return Filters._createLabel(layout, value, Qt.AlignmentFlag.AlignLeft, isSize) + + def _createRangeSlider( + self, layout, defaultMinValue, defaultMaxValue, minValue, maxValue + ): # pylint: disable=too-many-arguments + rangeSlider = QtRangeSlider( + self, defaultMinValue, defaultMaxValue, minValue, maxValue + ) layout.addWidget(rangeSlider) return rangeSlider @@ -210,7 +260,7 @@ def _createRangeSlider(self, layout, defaultMinValue, defaultMaxValue, minValue, def _createLabel(layout, value, alignment, isSize=True): label = QLabel(f"{humanize.naturalsize(value, True) if isSize else value}") label.setFixedWidth(80) - label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) label.setAlignment(alignment) layout.addWidget(label) diff --git a/gui/panes/format.py b/gui/panes/format.py index b949e0394..2461c845b 100644 --- a/gui/panes/format.py +++ b/gui/panes/format.py @@ -1,31 +1,37 @@ # -*- coding: utf-8 -*- -from PyQt5.QtWidgets import (QFormLayout, QGroupBox, - QLabel, QLineEdit, - QScrollArea, QVBoxLayout, - QWidget, QFrame) +from PyQt6.QtWidgets import ( + QFormLayout, + QGroupBox, + QLabel, + QLineEdit, + QScrollArea, + QVBoxLayout, + QWidget, + QFrame, +) from nut import Config class Edit(QLineEdit): - """Edit class - """ + """Edit class""" + def __init__(self, id_, type_): super().__init__() self.id = id_ self.type = type_ if type_: - self.key = type_.lower() + 'Title' + id_ + self.key = type_.lower() + "Title" + id_ else: - self.key = 'title' + id_ + self.key = "title" + id_ self.setText(getattr(Config.paths, self.key)) # self.textChanged.connect(self.onChange) def focusOutEvent(self, event): print(f"Edit focusOutEvent: {event}") - current = getattr(Config.paths, self.key) or '' + current = getattr(Config.paths, self.key) or "" new = self.text() if current != new: @@ -35,38 +41,40 @@ def focusOutEvent(self, event): super().focusOutEvent(event) def onChange(self): - print('changed: ' + self.id) + print("changed: " + self.id) + class Row(QGroupBox): - """Row class - """ - def __init__(self, type_=''): - super().__init__((type_ or 'nsp').upper()) + """Row class""" + + def __init__(self, type_=""): + super().__init__((type_ or "nsp").upper()) layout = QFormLayout(self) - layout.addRow(QLabel('Base'), Edit('Base', type_)) - layout.addRow(QLabel('DLC'), Edit('DLC', type_)) - layout.addRow(QLabel('Update'), Edit('Update', type_)) - layout.addRow(QLabel('Demo'), Edit('Demo', type_)) - layout.addRow(QLabel('Demo Update'), Edit('DemoUpdate', type_)) + layout.addRow(QLabel("Base"), Edit("Base", type_)) + layout.addRow(QLabel("DLC"), Edit("DLC", type_)) + layout.addRow(QLabel("Update"), Edit("Update", type_)) + layout.addRow(QLabel("Demo"), Edit("Demo", type_)) + layout.addRow(QLabel("Demo Update"), Edit("DemoUpdate", type_)) # self.layout.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) + class Format(QWidget): - """Format class - """ + """Format class""" + def __init__(self): super().__init__() self.scroll = QScrollArea(self) self.scroll.setWidgetResizable(True) - self.scroll.setFrameShape(QFrame.NoFrame) + self.scroll.setFrameShape(QFrame.Shape.NoFrame) layout = QVBoxLayout(self.scroll) - layout.addWidget(Row('')) - layout.addWidget(Row('nsz')) - layout.addWidget(Row('xci')) + layout.addWidget(Row("")) + layout.addWidget(Row("nsz")) + layout.addWidget(Row("xci")) # layout.addWidget(Row('nsx')) widget = QWidget() diff --git a/gui/panes/options.py b/gui/panes/options.py index d5d656981..d2e50991b 100644 --- a/gui/panes/options.py +++ b/gui/panes/options.py @@ -1,17 +1,27 @@ # -*- coding: utf-8 -*- -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QFormLayout, QLabel, QHBoxLayout, QSlider, QGroupBox -from PyQt5.QtCore import Qt +from PyQt6.QtWidgets import ( + QWidget, + QVBoxLayout, + QFormLayout, + QLabel, + QHBoxLayout, + QSlider, + QGroupBox, +) +from PyQt6.QtCore import Qt from nut import Config + def _init_slider(slider, min_value, max_value, value): slider.setMinimum(min_value) slider.setMaximum(max_value) slider.setValue(value) slider.valueChanged.connect(slider.save) + class Threads(QSlider): def __init__(self, parent): - super().__init__(Qt.Horizontal) + super().__init__(Qt.Orientation.Horizontal) self.parent = parent _init_slider(self, 1, 8, Config.threads) @@ -22,9 +32,10 @@ def save(self): if self.parent: self.parent.save() + class Compress(QSlider): def __init__(self, parent): - super().__init__(Qt.Horizontal) + super().__init__(Qt.Orientation.Horizontal) self.parent = parent _init_slider(self, 0, 22, Config.compression.level) @@ -35,6 +46,7 @@ def save(self): if self.parent: self.parent.save() + class SliderControl(QWidget): def __init__(self, _type): super().__init__() @@ -58,12 +70,12 @@ def __init__(self): layout.addLayout(serverGroup) - group = QGroupBox('THREADS') + group = QGroupBox("THREADS") groupLayout = QHBoxLayout(group) groupLayout.addWidget(SliderControl(_type=Threads)) layout.addWidget(group) - group = QGroupBox('COMPRESSION LEVEL') + group = QGroupBox("COMPRESSION LEVEL") groupLayout = QHBoxLayout(group) groupLayout.addWidget(SliderControl(_type=Compress)) layout.addWidget(group) diff --git a/gui/progress.py b/gui/progress.py index 0489ecf96..9ae9b808b 100644 --- a/gui/progress.py +++ b/gui/progress.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- import time -from PyQt5.QtCore import Qt, pyqtSlot, QTimer -from PyQt5.QtWidgets import (QProgressBar, QLabel, QHBoxLayout) +from PyQt6.QtCore import Qt, pyqtSlot, QTimer +from PyQt6.QtWidgets import QProgressBar, QLabel, QHBoxLayout from nut import Status def _format_speed(n): - return str(round(n / 1000 / 1000, 1)) + 'MB/s' + return str(round(n / 1000 / 1000, 1)) + "MB/s" + class Progress: def __init__(self, app): @@ -31,8 +32,8 @@ def __init__(self, app): def resetStatus(self): self.progress.setValue(0) - self.text.setText('') - self.speed.setText('') + self.text.setText("") + self.speed.setText("") def tick(self): for i in Status.lst: diff --git a/gui/table_model.py b/gui/table_model.py index 2914df87c..4dc4631b8 100644 --- a/gui/table_model.py +++ b/gui/table_model.py @@ -2,51 +2,53 @@ import os from enum import Enum -from PyQt5 import QtCore -from PyQt5.QtCore import QAbstractTableModel, Qt +from PyQt6 import QtCore +from PyQt6.QtCore import QAbstractTableModel, Qt import humanize from nut import Print + class Column(Enum): FILENAME = 0 TITLE_ID = 1 TITLE_TYPE = 2 FILE_SIZE = 3 + class SortDirection(Enum): ASC = 0 DESC = 1 + class TableModel(QAbstractTableModel): def __init__(self, parent=None): del parent super().__init__() self.datatable = [] self.column_count = 4 - self.headers = [ - "File", "Title ID", "Type", "Size" - ] + self.headers = ["File", "Title ID", "Type", "Size"] self.sort_column = 0 self.sort_order = SortDirection.ASC def update(self, dataIn): - Print.debug('TableModel update start') + Print.debug("TableModel update start") self.datatable = [] for value in dataIn.values(): new_item = {} new_item[Column.FILENAME] = os.path.basename(value.path) new_item[Column.TITLE_ID] = str(value.titleId) - titleType = "UPD" if value.isUpdate() else "DLC" if value.isDLC() \ - else "BASE" + titleType = ( + "UPD" if value.isUpdate() else "DLC" if value.isDLC() else "BASE" + ) new_item[Column.TITLE_TYPE] = titleType new_item[Column.FILE_SIZE] = value.fileSize self.datatable.append(new_item) self._sort() - Print.debug('TableModel update finished') + Print.debug("TableModel update finished") def rowCount(self, parent=QtCore.QModelIndex()): del parent @@ -56,8 +58,8 @@ def columnCount(self, parent=QtCore.QModelIndex()): del parent return self.column_count - def data(self, index, role=Qt.DisplayRole): - if role == Qt.DisplayRole: + def data(self, index, role=Qt.ItemDataRole.DisplayRole): + if role == Qt.ItemDataRole.DisplayRole: i = index.row() j = index.column() row = self.datatable[i] @@ -69,24 +71,30 @@ def data(self, index, role=Qt.DisplayRole): # pylint: disable=no-self-use def flags(self, index): del index - return Qt.ItemIsEnabled + return Qt.ItemFlag.ItemIsEnabled - def headerData(self, section, orientation, role=Qt.DisplayRole): - if role != Qt.DisplayRole: + def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole): + if role != Qt.ItemDataRole.DisplayRole: return QtCore.QVariant() - if orientation == Qt.Horizontal: + if orientation == Qt.Orientation.Horizontal: return self.headers[section] return str(section) def _sort(self): self.layoutAboutToBeChanged.emit() - self.datatable.sort(key=lambda item: item[Column(self.sort_column)], \ - reverse=self.sort_order != SortDirection.ASC) + self.datatable.sort( + key=lambda item: item[Column(self.sort_column)], + reverse=self.sort_order != SortDirection.ASC, + ) self.layoutChanged.emit() - def sort(self, column, order=Qt.AscendingOrder): + def sort(self, column, order=Qt.SortOrder.AscendingOrder): self.sort_column = column - self.sort_order = SortDirection.ASC if order == Qt.AscendingOrder else SortDirection.DESC + self.sort_order = ( + SortDirection.ASC + if order == Qt.SortOrder.AscendingOrder + else SortDirection.DESC + ) self._sort() def setRowCount(self, row_count): diff --git a/gui/tabs.py b/gui/tabs.py index c8f947906..abdcd367d 100644 --- a/gui/tabs.py +++ b/gui/tabs.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -from PyQt5.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QSizePolicy -from PyQt5.QtCore import Qt +from PyQt6.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QSizePolicy +from PyQt6.QtCore import Qt + class Tabs(QWidget): def __init__(self, tabs): diff --git a/nut_gui.py b/nut_gui.py index 4d6d3432c..c71a6429d 100644 --- a/nut_gui.py +++ b/nut_gui.py @@ -5,8 +5,8 @@ import urllib3 -from PyQt5.QtGui import (QIcon) -from PyQt5.QtWidgets import (QApplication) +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import QApplication import nut import Server @@ -20,24 +20,27 @@ def usbThread(): Usb.daemon() + def nutThread(): Server.run() + def initThread(app): - print('initThread start') + print("initThread start") nut.scan() app.refresh() - print('initThread finish') + print("initThread finish") + def run(): urllib3.disable_warnings() - print(r' ,;:;;,') - print(r' ;;;;;') - print(r' .=\', ;:;;:,') + print(r" ,;:;;,") + print(r" ;;;;;") + print(r" .=\', ;:;;:,") print(r' /_\', "=. \';:;:;') - print(r' @=:__, \,;:;:\'') - print(r' _(\.= ;:;;\'') + print(r" @=:__, \,;:;:\'") + print(r" _(\.= ;:;;\'") print(r' `"_( _/="`') print(r' `"\'') @@ -47,7 +50,7 @@ def run(): Hook.init() app = QApplication(sys.argv) - app.setWindowIcon(QIcon('images/logo.jpg')) + app.setWindowIcon(QIcon("images/logo.jpg")) ex = App() threads = [] @@ -58,12 +61,10 @@ def run(): for t in threads: t.start() - sys.exit(app.exec_()) - - print('fin') + sys.exit(app.exec()) -if __name__ == '__main__': +if __name__ == "__main__": run() Hook.call("exit") diff --git a/requirements.txt b/requirements.txt index 1c7bc2b20..92dfaa4f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ colorama~=0.4.4 beautifulsoup4~=4.10.0 urllib3~=1.26.7 pyusb~=1.2.1 -pyqt5~=5.15.6 +pyqt6~=6.6.1 google-api-python-client~=1.12.10 google-auth-oauthlib~=0.4.6 pycryptodome~=3.14.1 @@ -18,7 +18,7 @@ asn1~=2.4.2 filelock~=3.4.2 Unidecode~=1.3.2 pycurl~=7.44.1; sys_platform != 'win32' -qt-range-slider~=0.2.7 +qt-range-slider~=0.4.2 watchdog~=2.1.6 certifi>=2021.5.30 humanize~=3.14 diff --git a/requirements_dev.txt b/requirements_dev.txt index 041ddd1bc..9c9b6bb61 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,9 +1,9 @@ -pytest~=6.2.5 -pyfakefs~=4.5.4 -pylint~=2.12.2 -invoke~=1.6.0 -coverage~=6.3 -coveralls~=3.3.1 -rope~=0.22.0 -pre-commit~=2.17.0 -pylint-exit~=1.2.0 +pytest +pyfakefs +pylint +invoke +coverage +coveralls +rope +pre-commit +pylint-exit diff --git a/tests-gui/gui_app_test.py b/tests-gui/gui_app_test.py index 45bf41a67..211a9ffef 100644 --- a/tests-gui/gui_app_test.py +++ b/tests-gui/gui_app_test.py @@ -3,9 +3,9 @@ import unittest import logging -from PyQt5.QtWidgets import (QApplication, QPushButton, QLineEdit, QSlider) -from PyQt5.QtTest import QTest -from PyQt5.QtCore import QEvent +from PyQt6.QtWidgets import QApplication, QPushButton, QLineEdit, QSlider +from PyQt6.QtTest import QTest +from PyQt6.QtCore import QEvent from gui.app import App from gui.panes.options import Threads, Compress @@ -17,15 +17,17 @@ USERS_TAB_INDEX = 5 OPTIONS_TAB_INDEX = 6 + def _find_button_by_text(widget, text): for button in widget.findChildren(QPushButton): if button.text() == text: return button return None + class GuiAppTest(unittest.TestCase): - """Tests for gui/app.py - """ + """Tests for gui/app.py""" + def setUp(self): self.app = QApplication(sys.argv) self.form = App() @@ -47,13 +49,13 @@ def tearDown(self): Users.export() def test_run(self): - self.assertEqual(self.form.title, 'NUT 3.3') + self.assertEqual(self.form.title, "NUT 3.3") self.form.header.scan.click() - self.form.tabs.tabs.setCurrentIndex(0) # files - self.form.tabs.tabs.setCurrentIndex(1) # filters - self.form.tabs.tabs.setCurrentIndex(2) # save paths + self.form.tabs.tabs.setCurrentIndex(0) # files + self.form.tabs.tabs.setCurrentIndex(1) # filters + self.form.tabs.tabs.setCurrentIndex(2) # save paths self.form.tabs.tabs.setCurrentIndex(LOCAL_SCAN_PATHS_TAB_INDEX) - self.form.tabs.tabs.setCurrentIndex(4) # remote scan paths + self.form.tabs.tabs.setCurrentIndex(4) # remote scan paths self.form.tabs.tabs.setCurrentIndex(USERS_TAB_INDEX) self.form.tabs.tabs.setCurrentIndex(OPTIONS_TAB_INDEX) From 0db16705192eaa1f408f87a683dd101ad3883cef Mon Sep 17 00:00:00 2001 From: introkun Date: Mon, 25 Mar 2024 23:33:22 -0300 Subject: [PATCH 2/7] fixed linter --- gui/app.py | 1 + gui/table_model.py | 1 - nut/Config.py | 4 ++-- nut/Hex.py | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gui/app.py b/gui/app.py index 6cf7e44df..fe94348ba 100644 --- a/gui/app.py +++ b/gui/app.py @@ -209,5 +209,6 @@ def on_gdrive(self): @staticmethod def closeEvent(event): del event + # pylint: disable=fixme # TODO: implement a graceful shutdown of other threads os._exit(0) # pylint: disable=protected-access diff --git a/gui/table_model.py b/gui/table_model.py index 4dc4631b8..1fe3b9a74 100644 --- a/gui/table_model.py +++ b/gui/table_model.py @@ -68,7 +68,6 @@ def data(self, index, role=Qt.ItemDataRole.DisplayRole): return f"{row[Column(j)]}" return QtCore.QVariant() - # pylint: disable=no-self-use def flags(self, index): del index return Qt.ItemFlag.ItemIsEnabled diff --git a/nut/Config.py b/nut/Config.py index eaf809f81..b397f5545 100644 --- a/nut/Config.py +++ b/nut/Config.py @@ -3,11 +3,11 @@ import json import os import time -import collections +from collections.abc import Mapping from binascii import unhexlify as uhx from nut import Print from nut.config_impl.download import Download -from collections.abc import Mapping + threads = 1 jsonOutput = False diff --git a/nut/Hex.py b/nut/Hex.py index a2d18452b..f2655e13d 100644 --- a/nut/Hex.py +++ b/nut/Hex.py @@ -36,5 +36,6 @@ def dump(data, size=16): print('|', asciiFormat.format(ascii_), '|') index += size + # pylint: disable=consider-using-min-builtin if bytesRead - index < size: size = bytesRead - index From c198eb92a87dbae7d6ac3a9c82810796399a4f23 Mon Sep 17 00:00:00 2001 From: introkun Date: Mon, 25 Mar 2024 23:39:56 -0300 Subject: [PATCH 3/7] update CI --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 55a58a3ae..77c14f154 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,25 +14,25 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, macos-11] + os: [ubuntu-latest, macos-latest] python-version: [3.10.0] # include: # - os: windows-latest # python-version: 3.9.7 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Cache Qt id: cache-qt - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ../Qt key: ${{ runner.os }}-QtCache - name: Install Qt - uses: jurplel/install-qt-action@v2 + uses: jurplel/install-qt-action@v3 with: cached: ${{ steps.cache-qt.outputs.cache-hit }} - name: Install Linux dependencies From 34fbaeab68d4847d9e1a8a747dec75a7b15a11bf Mon Sep 17 00:00:00 2001 From: introkun Date: Mon, 25 Mar 2024 23:41:54 -0300 Subject: [PATCH 4/7] update python on CI to 3.12.2 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 77c14f154..6a1553112 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - python-version: [3.10.0] + python-version: [3.12.2] # include: # - os: windows-latest # python-version: 3.9.7 From fac80da04548128813c5e34a8f80af82c9bee996 Mon Sep 17 00:00:00 2001 From: introkun Date: Mon, 25 Mar 2024 23:51:17 -0300 Subject: [PATCH 5/7] fixed test --- tests/nut_users_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/nut_users_test.py b/tests/nut_users_test.py index 6038d2ce6..98d0c3f02 100644 --- a/tests/nut_users_test.py +++ b/tests/nut_users_test.py @@ -166,9 +166,9 @@ def test_user_set_is_admin(self): def test_user_get_require_auth(self): user = Users.User() - self.assertIs(user.getRequireAuth(), 'True', 'All users require Auth by default') + self.assertIs(user.getRequireAuth(), str(True), 'All users require Auth by default') user.setRequireAuth(False) - self.assertIs(user.getRequireAuth(), 'False') + self.assertIs(user.getRequireAuth(), str(False)) if __name__ == "__main__": unittest.main() From 732e1dbc7ce7cd3d9fc5372fc88c09944128010a Mon Sep 17 00:00:00 2001 From: introkun Date: Mon, 25 Mar 2024 23:56:54 -0300 Subject: [PATCH 6/7] removed deprecated coverage annotate --- tasks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks.py b/tasks.py index 1b2615716..2dc036810 100644 --- a/tasks.py +++ b/tasks.py @@ -16,7 +16,6 @@ def coverage(c, details=False, gui=False): if gui: c.run("coverage run -a -m unittest tests-gui.gui_app_test") if details: - c.run("coverage annotate") c.run("coverage report", pty=True) @task From b4006b0a8d538351cca1c81044f8fc2b1659b588 Mon Sep 17 00:00:00 2001 From: introkun Date: Tue, 26 Mar 2024 00:17:46 -0300 Subject: [PATCH 7/7] disabled gui tests --- tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index 2dc036810..28fcf414a 100644 --- a/tasks.py +++ b/tasks.py @@ -13,8 +13,8 @@ def coverage(c, details=False, gui=False): c.run("coverage erase") c.run("coverage run -m pytest --ignore tests-gui") - if gui: - c.run("coverage run -a -m unittest tests-gui.gui_app_test") + # if gui: + # c.run("coverage run -a -m unittest tests-gui.gui_app_test") if details: c.run("coverage report", pty=True)