Skip to content

Commit

Permalink
Exposing existing filters in nut_gui (#281)
Browse files Browse the repository at this point in the history
* refactoring: extracted Download from Config
* added tests for nut.Config.Download
* switched to external library for slider
* added qt mirror
* read file size filters from config
* updated gitignore
* refactored filters
* fixed linting
* save min/max size filters to config
  • Loading branch information
introkun authored Jan 31, 2021
1 parent 102bf3f commit d05ed5f
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 67 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
uses: jurplel/install-qt-action@v2
with:
setup-python: 'false'
mirror: 'http://mirrors.ocf.berkeley.edu/qt/'
- name: Install Win dependencies
if: startsWith(matrix.os, 'windows')
run: |
Expand All @@ -58,7 +59,7 @@ jobs:
env:
QT_QPA_PLATFORM: offscreen
run: |
invoke coverage
invoke coverage --gui
- name: Lint
run: |
invoke lint
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ keys.txt
_NSPOUT

nut.lock

# config file to switch between conda environments
environment.yml
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ valid-metaclass-classmethod-first-arg=cls
[DESIGN]

# Maximum number of arguments for function / method.
max-args=5
max-args=6

# Maximum number of attributes for a class (see R0902).
max-attributes=12
Expand Down
124 changes: 106 additions & 18 deletions gui/panes/filters.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import os
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QWidget, QAction, QTabWidget, QBoxLayout, QVBoxLayout, QTableWidget, QTableWidgetItem, QFormLayout, QLabel, QLineEdit, QHBoxLayout, QCheckBox, QGroupBox, QGridLayout
from PyQt5.QtGui import QIcon
from PyQt5 import QtWidgets
from PyQt5.QtCore import pyqtSlot, QRect
from nut import Nsps, Config
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QCheckBox, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QSizePolicy, QVBoxLayout, QWidget)
from qt_range_slider import QtRangeSlider

from nut import Config
from translator import tr


# pylint: disable=fixme
# TODO: move to a separate module
def _format_size(num, suffix='B'):
if num is None:
return ''
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
if abs(num) < 1024.0:
return "%3.1f %s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f %s%s" % (num, 'Yi', suffix)

class ConfCheckbox(QCheckBox):
"""ConfCheckbox
"""
def __init__(self, text, conf):
super().__init__(text)
self.conf = conf
Expand All @@ -25,7 +39,7 @@ def get(self):
for path in self.conf.split('.'):
j = getattr(j, path)
return j
except BaseException as e:
except BaseException: # pylint: disable=broad-except
return None

def set(self, value):
Expand All @@ -37,6 +51,8 @@ def set(self, value):
setattr(j, last, value)

class RegionEntry(QWidget):
"""RegionEntry
"""
def __init__(self, region):
super().__init__()
self.region = region.upper()
Expand All @@ -58,13 +74,16 @@ def onStateChanged(self, state):


class Region(QWidget):
"""Region
"""
def __init__(self):
super().__init__()

layout = QGridLayout(self)

regions = []
for region, languages in Config.regionLanguages().items():
del languages
regions.append(region)

regions.sort()
Expand All @@ -76,31 +95,100 @@ def __init__(self):
i += 1

class Filters(QWidget):
"""Filters
"""
def __init__(self):
super().__init__()

self.MIN_FILE_SIZE = 0
self.MAX_FILE_SIZE = 30 * 1024**3

layout = QVBoxLayout(self)

types = QGroupBox(tr('filters.types.group'))
typesGroup = QGroupBox(tr('filters.types.group'))

Filters._createTypesGroup(layout, typesGroup)

Filters._createRegionGroup(layout)

testGroup = QHBoxLayout(types)
sizeFilterGroup = QGroupBox(tr('filters.size.group'))
sizeFilterLayout = QHBoxLayout(sizeFilterGroup)

testGroup.addWidget(ConfCheckbox(tr('filters.types.base'), 'download.base'))
testGroup.addStretch()
minFileSizeFilter = 0
if Config.download.fileSizeMax is not None:
minFileSizeFilter = Config.download.fileSizeMin
maxFileSizeFilter = self.MAX_FILE_SIZE
if Config.download.fileSizeMax is not None:
maxFileSizeFilter = Config.download.fileSizeMax

testGroup.addWidget(ConfCheckbox(tr('filters.types.dlc'), 'download.DLC'))
testGroup.addStretch()
filterMinSizeLabel = Filters._createLeftLabel(sizeFilterLayout, minFileSizeFilter)

testGroup.addWidget(ConfCheckbox(tr('filters.types.update'), 'download.update'))
testGroup.addStretch()
rangeSlider = self._createRangeSlider(sizeFilterLayout, minFileSizeFilter, maxFileSizeFilter)

testGroup.addWidget(ConfCheckbox(tr('filters.types.demo'), 'download.demo'))
filterMaxSizeLabel = Filters._createRightLabel(sizeFilterLayout, rangeSlider.get_right_thumb_value())

layout.addWidget(types)
layout.addWidget(sizeFilterGroup)

rangeSlider.left_thumb_value_changed.connect((lambda x: \
Filters._on_left_thumb_value_changed(filterMinSizeLabel, x)))
rangeSlider.right_thumb_value_changed.connect((lambda x: \
Filters._on_right_thumb_value_changed(filterMaxSizeLabel, x)))

@staticmethod
def _on_left_thumb_value_changed(label, value):
label.setText(_format_size(value))
Config.download.fileSizeMin = value
Config.save()

@staticmethod
def _on_right_thumb_value_changed(label, value):
label.setText(_format_size(value))
Config.download.fileSizeMax = value
Config.save()

@staticmethod
def _createRegionGroup(layout):
region = QGroupBox('REGION')
regionLayout = QHBoxLayout(region)
regionLayout.addWidget(Region())
layout.addWidget(region)

layout.addStretch()
@staticmethod
def _createTypesGroup(layout, typesGroup):
typesLayout = QHBoxLayout(typesGroup)

typesLayout.addWidget(ConfCheckbox(tr('filters.types.base'), 'download.base'))
typesLayout.addStretch()

typesLayout.addWidget(ConfCheckbox(tr('filters.types.dlc'), 'download.DLC'))
typesLayout.addStretch()

typesLayout.addWidget(ConfCheckbox(tr('filters.types.update'), 'download.update'))
typesLayout.addStretch()

typesLayout.addWidget(ConfCheckbox(tr('filters.types.demo'), 'download.demo'))

layout.addWidget(typesGroup)

@staticmethod
def _createLeftLabel(layout, value):
return Filters._createLabel(layout, value, Qt.AlignRight)

@staticmethod
def _createRightLabel(layout, value):
return Filters._createLabel(layout, value, Qt.AlignLeft)

def _createRangeSlider(self, layout, minValue, maxValue):
rangeSlider = QtRangeSlider(self, self.MIN_FILE_SIZE, self.MAX_FILE_SIZE, minValue, maxValue)
layout.addWidget(rangeSlider)
return rangeSlider

@staticmethod
def _createLabel(layout, value, alignment):
label = QLabel(f"{_format_size(value)}")
label.setFixedWidth(80)
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
label.setAlignment(alignment)

layout.addWidget(label)
return label
46 changes: 2 additions & 44 deletions nut/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from nut import Print

from nut.config_impl.download import Download

threads = 1
jsonOutput = False
isRunning = True
Expand Down Expand Up @@ -694,50 +696,6 @@ def loadTitleBlacklist():
paths = Paths()
server = Server()

class Download: # pylint: disable=too-many-instance-attributes
"""Download-releate settings
"""

def __init__(self):
self.base = True
self.demo = False
self.DLC = True
self.update = False
self.sansTitleKey = False
self.deltas = False
self.regions = []
self.rankMin = None
self.rankMax = None
self.fileSizeMax = None
self.fileSizeMin = None
self.ratingMin = None
self.ratingMax = None
self.releaseDateMin = None
self.releaseDateMax = None

def addRegion(self, region_):
region_ = region_.upper()
if region_ not in self.regions:
self.regions.append(region_)

def removeRegion(self, region_):
region_ = region_.upper()
if region_ not in self.regions:
return

self.regions.remove(region_)

def hasRegion(self, regions, default=True):
if not self.regions or len(self.regions) == 0 or regions is None:
return default

for a in self.regions:
for b in regions:
if a == b:
return True

return False


class DAuthToken:
"""DAuthToken
Expand Down
Empty file added nut/config_impl/__init__.py
Empty file.
43 changes: 43 additions & 0 deletions nut/config_impl/download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
class Download: # pylint: disable=too-many-instance-attributes
"""Download-releate settings
"""

def __init__(self):
self.base = True
self.demo = False
self.DLC = True
self.update = False
self.sansTitleKey = False
self.deltas = False
self.regions = []
self.rankMin = None
self.rankMax = None
self.fileSizeMax = None
self.fileSizeMin = None
self.ratingMin = None
self.ratingMax = None
self.releaseDateMin = None
self.releaseDateMax = None

def addRegion(self, region_):
region_ = region_.upper()
if region_ not in self.regions:
self.regions.append(region_)

def removeRegion(self, region_):
region_ = region_.upper()
if region_ not in self.regions:
return

self.regions.remove(region_)

def hasRegion(self, regions, default=True):
if not self.regions or len(self.regions) == 0 or regions is None:
return default

for a in self.regions:
for b in regions:
if a == b:
return True

return False
2 changes: 2 additions & 0 deletions public_html/translate.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@
"filters.types.demo": "Demo",
"filters.types.group": "TYPES",
"filters.region.group": "REGION",
"filters.size.group": "File Size",
"dirlist.browse": "Browse"
},
"ar": {
Expand Down Expand Up @@ -9557,6 +9558,7 @@
"filters.types.demo": "Демо",
"filters.types.group": "Типы",
"filters.region.group": "Регион",
"filters.size.group": "Размер файла",
"dirlist.browse": "Обзор"
},
"sv": {
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ asn1~=2.4.1
filelock~=3.0.12
Unidecode~=1.1.1
pycurl~=7.43.0
qt-range-slider~=0.2.3
1 change: 1 addition & 0 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pylint~=2.6.0
invoke~=1.4.0
coverage~=5.3
coveralls~=2.2.0
rope~=0.18.0
7 changes: 4 additions & 3 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
py = "python3"

@task
def coverage(c, details=False):
def coverage(c, details=False, gui=False):
c.run("coverage erase")
c.run("coverage run -m pytest --ignore tests-gui")
c.run("coverage run -m pytest --ignore tests")
if gui:
c.run("coverage run -m pytest --ignore tests")
if details:
c.run("coverage html && coverage annotate")
c.run("coverage report", pty=True)
Expand All @@ -25,7 +26,7 @@ def test(c):
@task
def lint(c):
run_arg = "pylint -j 4 nut/Config.py tests/ nut/Nsps.py nut/Hex.py nut_gui.py \
gui/panes/dirlist.py Fs/driver/http.py"
gui/panes/dirlist.py gui/panes/filters.py Fs/driver/http.py nut/config_impl/download.py"
if os.name == 'nt': # Windows
c.run(run_arg)
else:
Expand Down
Loading

0 comments on commit d05ed5f

Please sign in to comment.