diff --git a/Makefile b/Makefile index c5eb4ad..c1b2b8c 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ clean-build: ## remove build artifacts test: environment ## run tests quickly with the default Python pip install -U pytest pytest-cov - pytest --cov tests + pytest --cov=torrentfileQt tests coverage: test ## gather coverage data pip install -U coverage diff --git a/README.md b/README.md index 99133b7..32c1636 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,8 @@ TorrentFileQt is a Graphical User Interface for [TorrentFile CLI](https://github ------- +![InfoTabScreenShot.png](./assets/CheckTabScreenShot.png) + +------- + ![torrentfile.png](./assets/torrentfile.png) diff --git a/assets/CheckTabScreenShot.png b/assets/CheckTabScreenShot.png new file mode 100644 index 0000000..5612e49 Binary files /dev/null and b/assets/CheckTabScreenShot.png differ diff --git a/assets/CreateTabScreenShot.png b/assets/CreateTabScreenShot.png index 2770236..2af10de 100644 Binary files a/assets/CreateTabScreenShot.png and b/assets/CreateTabScreenShot.png differ diff --git a/assets/InfoTabScreenShot.png b/assets/InfoTabScreenShot.png index 1b0fb69..99b80b3 100644 Binary files a/assets/InfoTabScreenShot.png and b/assets/InfoTabScreenShot.png differ diff --git a/assets/torrentfile.png b/assets/torrentfile.png index cb26e6d..14f7fa5 100644 Binary files a/assets/torrentfile.png and b/assets/torrentfile.png differ diff --git a/coverage.xml b/coverage.xml index b48079a..b0ded77 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,14 +1,14 @@ - + C:\Users\asp\Documents\Code\repos\torrentfileQt - + - + @@ -23,8 +23,8 @@ - - + + @@ -69,23 +69,22 @@ + - - - - + + - - + + - + @@ -97,7 +96,6 @@ - @@ -105,40 +103,40 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -153,7 +151,7 @@ - + @@ -167,7 +165,7 @@ - + @@ -181,7 +179,7 @@ - + @@ -195,7 +193,43 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -206,7 +240,7 @@ - + @@ -214,35 +248,38 @@ - + - + - + - + + - + + + - + - + - + @@ -251,53 +288,51 @@ + - + - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - + + + + + + + @@ -305,81 +340,82 @@ - - - - - - + + + + + + + - + - - - - - - + + + + + + + - - - + + + - - - - - + + + + - + - - - - - - - + + + + + + + + - - - - - - + + + + + + - - + - - - - - - - - - - - - - + + + + + + + + + + + - + + + - - - - + + + + + + - - - + + - @@ -389,95 +425,87 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + + - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - + - - - - - diff --git a/package.json b/package.json index d2a6cce..452939b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "torrentfileqt", "displayName": "TorrentfileQt", - "version": "1.1.1", + "version": "1.2.0", "description": "GUI torrentfile creator.", "repository": { "type": "git", diff --git a/tests/context.py b/tests/context.py index 0eeb466..1a9392b 100644 --- a/tests/context.py +++ b/tests/context.py @@ -42,7 +42,7 @@ def rmpath(path): shutil.rmtree(path) -def testDir(func): +def tstDir(func): """Create root testing directory partial function. Args: @@ -81,15 +81,15 @@ def fill(path, exp=25): return path -@testDir -def testfile(val=20): +@tstDir +def tstfile(val=20): root = os.path.join(TESTDIR, "file1") fill(root, exp=val) return root -@testDir -def testdir(): +@tstDir +def tstdir(): root = ROOT dir1 = os.path.join(root, "dir1") file1 = os.path.join(root, "file1") diff --git a/tests/test_gui.py b/tests/test_gui.py index 5676bcd..8ee9de5 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -18,12 +18,14 @@ ############################################################################## import os - import pytest +from pathlib import Path + from PyQt6.QtWidgets import QApplication, QMainWindow, QStatusBar -from torrentfile import TorrentFile, TorrentFileV2, TorrentFileHybrid -from tests.context import testdir, testfile, rmpath + +from torrentfile import TorrentFile, TorrentFileV2, TorrentFileHybrid +from tests.context import tstdir, tstfile, rmpath from torrentfileQt.window import alt_start, TabWidget from torrentfileQt import qss @@ -36,17 +38,15 @@ def wind(): @pytest.fixture(scope="module") -def tdir(wind): - root = testdir() - yield root, wind - rmpath(root) +def tdir(): + root = tstdir() + return root @pytest.fixture(scope="module", params=list(range(14, 28))) def tfile(request): - path = testfile(val=request.param) - yield path - rmpath(path) + path = tstfile(val=request.param) + return path @pytest.fixture(scope="module", params=[TorrentFile, TorrentFileV2, TorrentFileHybrid]) @@ -62,8 +62,8 @@ def ttorrent1(tfile, request): } torrent = request.param(**args) outfile, _ = torrent.write() - yield outfile - rmpath(outfile) + return outfile + @pytest.fixture(scope="module", params=[TorrentFile, TorrentFileV2, TorrentFileHybrid]) @@ -72,8 +72,7 @@ def ttorrent2(tfile, request): args = {"path": path} torrent = request.param(**args) outfile, _ = torrent.write() - yield outfile - rmpath(outfile) + return outfile def test_window1(wind): @@ -131,8 +130,8 @@ def test_info_tab_select2(wind, ttorrent2): assert infotab.nameEdit.text() != "" -def test_create_tab_dir(tdir): - root, wind = tdir +def test_create_tab_dir(tdir, wind): + root = tdir createtab = wind.central.createWidget button = createtab.browse_dir_button button.browse(path=root) @@ -196,19 +195,48 @@ def test_create_tab_file_hybrid(wind, tfile): assert os.path.exists(torfile) -# def test_check_tab(wind, ttorrent1): -# checktab = wind.central.checkWidget -# testdir = os.path.dirname(ttorrent1) -# checktab.browseButton1.browse(path=testdir) -# checktab.browseButton2.browse(path=testdir) -# checktab.checkButton.click() -# assert checktab.fileInput.text() != "" - - -# def test_check_tab2(wind, ttorrent2): -# checktab = wind.central.checkWidget -# testdir = os.path.dirname(ttorrent2) -# checktab.browseButton1.browse(path=testdir) -# checktab.browseButton2.browse(path=testdir) -# checktab.checkButton.click() -# assert checktab.fileInput.text() != "" +def test_check_tab(wind, ttorrent1): + checktab = wind.central.checkWidget + testdir = os.path.dirname(ttorrent1) + checktab.browseButton1.browse(path=testdir) + checktab.browseButton2.browse(path=testdir) + assert checktab.searchInput.text() != "" + + +def test_check_tab2(wind, ttorrent2): + checktab = wind.central.checkWidget + testdir = os.path.dirname(ttorrent2) + checktab.browseButton1.browse(path=testdir) + checktab.browseButton2.browse(path=testdir) + assert checktab.fileInput.text() != "" + + +def test_check_tab3(wind, ttorrent1): + root = ttorrent1 + checktab = wind.central.checkWidget + tree_widget = checktab.treeWidget + tree_widget.setRoot(root) + path = Path(root).resolve() + parent = path.parent.parent + for path in parent.iterdir(): + tree_widget.callback(True, str(path), 10, 10) + tree_widget.performAction() + assert tree_widget.invisibleRootItem() is not None + + +def test_check_tab4(wind, ttorrent1): + root = ttorrent1 + checktab = wind.central.checkWidget + tree_widget = checktab.treeWidget + tree_widget.setRoot(root) + tree_widget.clear_data() + assert tree_widget.invisibleRootItem() is not None + + +def test_check_tab5(wind, ttorrent2): + checktab = wind.central.checkWidget + testdir = os.path.dirname(ttorrent2) + checktab.browseButton1.browse(path=testdir) + checktab.browseButton2.browse(path=testdir) + checktab.checkButton.click() + assert checktab.textEdit.toPlainText() != "" diff --git a/torrentfileQt/checkTab.py b/torrentfileQt/checkTab.py index 142dd4c..f4ec67e 100644 --- a/torrentfileQt/checkTab.py +++ b/torrentfileQt/checkTab.py @@ -18,13 +18,13 @@ ############################################################################## import os -import asyncio from threading import Thread from collections.abc import Sequence -from PyQt6.QtCore import Qt, pyqtSignal, QThread +from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtGui import QIcon from PyQt6.QtWidgets import ( + QWidget, QFileDialog, QFormLayout, QHBoxLayout, @@ -34,23 +34,18 @@ QToolButton, QTreeWidget, QTreeWidgetItem, - QPlainTextEdit, - QProgressBar, - QWidget, ) from torrentfile.progress import CheckerClass from torrentfileQt.qss import ( - pushButtonSheet, - toolButtonSheet, - treeSheet, logTextEditSheet, pushButtonSheet, toolButtonSheet, treeSheet, headerSheet, ) + from torrentfileQt.widgets import Label, LineEdit @@ -117,43 +112,54 @@ class CheckButton(QPushButton): def __init__(self, text, parent=None): """Construct the CheckButton Widget.""" super().__init__(text, parent=parent) + self.widget = parent self.pressed.connect(self.submit) self.setCursor(Qt.CursorShape.PointingHandCursor) self.setStyleSheet(pushButtonSheet) self.torrent = None self.folder = None + + @property + def textEdit(self): + return self.widget.textEdit + + @property + def treeWidget(self): + return self.widget.treeWidget + + def submit(self): - self.widget = self.parent() - self.textEdit = self.widget.textEdit - self.treeWidget = self.widget.treeWidget + self.treeWidget.clear() + self.textEdit.clear() func1 = self.textEdit.callback func2 = self.treeWidget.callback CheckerClass.register_callbacks(func1, func2) self.torrent = self.widget.fileInput.text() self.folder = self.widget.searchInput.text() self.widget.treeWidget.setRoot(self.folder) - # thread = Thread(group=None, target=self.re_check_torrent) - # thread.run() - self.re_check_torrent() + thread = Thread(group=None, target=self.re_check_torrent) + thread.run() + # self.re_check_torrent() def re_check_torrent(self): checker = CheckerClass(self.torrent, self.folder) msg = f"Torrent Contents are {checker.result}% completely downloaded." self.textEdit.insertPlainText(msg) - last_path = self.treeWidget.paths[-1] - value = self.treeWidget.itemWidgets[last_path] - widget = value["widget"] - children = value["children"] - tally = total = 0 - for child in children: - tally += child.val - total += 100 - percent = int((tally / total) * 100) - self.treeWidget.removeItemWidget(widget, 2) - progressbar = Progress(parent=None) - progressbar.setValue(percent) - self.treeWidget.setItemWidget(widget, 2, progressbar) + if self.treeWidget.paths: + last_path = self.treeWidget.paths[-1] + value = self.treeWidget.itemWidgets[last_path] + widget = value["widget"] + children = value["children"] + tally = total = 0 + for child in children: + tally += child.val + total += 100 + percent = int((tally / total) * 100) + self.treeWidget.removeItemWidget(widget, 2) + progressbar = Progress(parent=None) + progressbar.setValue(percent) + self.treeWidget.setItemWidget(widget, 2, progressbar) class BrowseTorrents(QToolButton): @@ -192,7 +198,7 @@ def browse(self, path=None): return if isinstance(path, Sequence): path = path[0] - path = os.path.realpath(path) + path = os.path.normpath(path) self.parent().fileInput.clear() self.parent().fileInput.setText(path) @@ -227,6 +233,7 @@ def browse(self, path=None): path = QFileDialog.getExistingDirectory(parent=self, caption=caption) if not path: return + path = os.path.normpath(path) self.parent().searchInput.clear() self.parent().searchInput.setText(path) @@ -240,7 +247,9 @@ class TreePieceItem(QTreeWidgetItem): def __init__(self, type=0, tree=None): super().__init__(type=type) self.val = None - self.setChildIndicatorPolicy(self.ChildIndicatorPolicy.ShowIndicator) + self.setChildIndicatorPolicy( + self.ChildIndicatorPolicy.DontShowIndicatorWhenChildless + ) self.data_role = Qt.ItemDataRole.UserRole self.tree = tree @@ -270,11 +279,6 @@ def __init__(self, parent=None): super().__init__(parent=parent) self.setRange(0, 100) self.setValue(0) - # self.setStyleSheet(progr6essbarSheet) - - -class CallbackThread(QThread): - callbackActivated = pyqtSignal() class TreeWidget(QTreeWidget): @@ -285,6 +289,7 @@ class TreeWidget(QTreeWidget): Args: parent(`QWidget`, default=None) """ + callback_activated = pyqtSignal() def __init__(self, parent=None): super().__init__(parent=parent) @@ -300,25 +305,36 @@ def __init__(self, parent=None): self.itemWidgets = {} self.paths = [] self.item_tree = {"widget" : self.invisibleRootItem()} + self.callback_activated.connect(self.performAction) + + def clear_data(self): + self.clear() + self.item_tree = {"widget" : self.invisibleRootItem()} + self.itemWidgets = {} + self.paths = [] + self.root = None + + def performAction(self): + if self.paths: + last_path = self.paths[-1] + value = self.itemWidgets[last_path] + widget = value["widget"] + children = value["children"] + tally = total = 0 + for child in children: + tally += child.val + total += 100 + percent = int((tally / total) * 100) + self.removeItemWidget(widget, 2) + progressbar = Progress(parent=None) + progressbar.setValue(percent) + self.setItemWidget(widget, 2, progressbar) + def callback(self, response, path, size, total): if path.startswith(self.root): path = path.strip(self.root) if path not in self.itemWidgets: - if self.paths: - last_path = self.paths[-1] - value = self.itemWidgets[last_path] - widget = value["widget"] - children = value["children"] - tally = total = 0 - for child in children: - tally += child.val - total += 100 - percent = int((tally / total) * 100) - self.removeItemWidget(widget, 2) - progressbar = Progress(parent=None) - progressbar.setValue(percent) - self.setItemWidget(widget, 2, progressbar) temp, partials = path, [] while True: root, base = os.path.split(temp) @@ -326,6 +342,7 @@ def callback(self, response, path, size, total): break partials.insert(0,base) temp = root + self.callback_activated.emit() item_tree = self.item_tree for i, partial in enumerate(partials): if partial in item_tree: @@ -355,32 +372,6 @@ def callback(self, response, path, size, total): def setRoot(self, root): self.root = os.path.split(root)[0] - def add_top_item(self, path): - item = TreePieceItem(type=0, tree=self) - item.set_top(path, "./assets/file.png") - self.itemWidgets[path] = {"widget": item, "children": []} - self.addTopLevelItem(item) - progressbar = Progress() - self.setItemWidget(item, 2, progressbar) - self.total += 1 - - def callback2(self, response, path, size, total): - if path.startswith(self.root): - path = path.strip(self.root) - if self.total is None: - self.total = total - if path not in self.itemWidgets: - self.add_top_item(path) - item = self.itemWidgets[path]["widget"] - children = self.itemWidgets[path]["children"] - item2 = TreePieceItem(type=0, tree=self) - item.addChild(item2) - amount = 0 if not response else 100 - item2.set_val(amount) - children.append(item2) - self.window.update() - self.window.repaint() - class LogTextEdit(QPlainTextEdit): @@ -395,6 +386,9 @@ def __init__(self, parent=None): self.setFont(font) self.setStyleSheet(logTextEditSheet) + def clear_data(self): + self.clear() + def callback(self, msg): self.insertPlainText(msg) self.insertPlainText("\n\n") diff --git a/torrentfileQt/qss.py b/torrentfileQt/qss.py index b5b9ff2..dc6a122 100644 --- a/torrentfileQt/qss.py +++ b/torrentfileQt/qss.py @@ -51,15 +51,15 @@ color: white; } QTreeWidget QProgressBar { - border: 4px solid #e57e22; - border-radius: 12px; + border: 2px solid #e57e22; + border-radius: 6px; text-align: center; - margin: 3px; } QTreeWidget QProgressBar::chunk { - background-color: #66ccff; - width: 10px; + background-color: #164caf; + width: 25px; margin: 1px; + border-radius: 30px; } """ diff --git a/torrentfileQt/window.py b/torrentfileQt/window.py index 8b81779..0593a37 100644 --- a/torrentfileQt/window.py +++ b/torrentfileQt/window.py @@ -64,7 +64,7 @@ def __init__(self, parent=None, app=None): self.setMenuBar(self.menubar) self.setStatusBar(self.statusbar) self.setStyleSheet(mainWindowSheet) - self.resize(600, 600) + self.resize(600, 700) self._setupUI() def _setupUI(self):