From dd4b7254d9e1c989d8538d505ad56430cec371c5 Mon Sep 17 00:00:00 2001 From: Yaronzz Date: Thu, 30 Dec 2021 11:47:36 +0800 Subject: [PATCH] Reset directory --- TIDALDL-PY/setup-gui.py | 16 + TIDALDL-PY/tidal_gui/__init__.py | 33 + TIDALDL-PY/tidal_gui/control/__init__.py | 10 + TIDALDL-PY/tidal_gui/control/checkBox.py | 19 + TIDALDL-PY/tidal_gui/control/comboBox.py | 27 + .../tidal_gui/control/framelessWidget.py | 159 ++++ TIDALDL-PY/tidal_gui/control/label.py | 20 + TIDALDL-PY/tidal_gui/control/layout.py | 25 + TIDALDL-PY/tidal_gui/control/line.py | 20 + TIDALDL-PY/tidal_gui/control/lineEdit.py | 23 + TIDALDL-PY/tidal_gui/control/listWidget.py | 30 + TIDALDL-PY/tidal_gui/control/pushButton.py | 32 + TIDALDL-PY/tidal_gui/control/scrollWidget.py | 28 + TIDALDL-PY/tidal_gui/control/tableView.py | 48 ++ TIDALDL-PY/tidal_gui/control/tableWidget.py | 95 +++ TIDALDL-PY/tidal_gui/downloader.py | 38 + TIDALDL-PY/tidal_gui/resource/__init__.py | 4 + TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg | 1 + .../tidal_gui/resource/svg - 原/__init__.py | 4 + .../resource/svg - 原/buymeacoffee.svg | 1 + .../tidal_gui/resource/svg - 原/check.svg | 5 + .../tidal_gui/resource/svg - 原/down.svg | 6 + .../tidal_gui/resource/svg - 原/downHover.svg | 6 + .../tidal_gui/resource/svg - 原/github.svg | 1 + .../tidal_gui/resource/svg - 原/left.svg | 1 + .../resource/svg - 原/leftTab/download.svg | 1 + .../svg - 原/leftTab/downloadHover.svg | 1 + .../resource/svg - 原/leftTab/info.svg | 1 + .../resource/svg - 原/leftTab/search.svg | 1 + .../resource/svg - 原/leftTab/settings.svg | 1 + .../tidal_gui/resource/svg - 原/paypal.svg | 1 + .../tidal_gui/resource/svg - 原/right.svg | 1 + .../tidal_gui/resource/svg - 原/search.svg | 1 + .../tidal_gui/resource/svg - 原/search2.svg | 1 + .../resource/svg - 原/taskItem/cancel.svg | 1 + .../resource/svg - 原/taskItem/delete.svg | 1 + .../resource/svg - 原/taskItem/expand.svg | 1 + .../resource/svg - 原/taskItem/open.svg | 1 + .../resource/svg - 原/taskItem/retry.svg | 1 + .../resource/svg - 原/taskTab/complete.svg | 1 + .../resource/svg - 原/taskTab/download.svg | 1 + .../resource/svg - 原/taskTab/error.svg | 1 + .../tidal_gui/resource/svg - 原/upHover.svg | 1 + .../resource/svg - 原/windows/close.svg | 5 + .../resource/svg - 原/windows/closeHover.svg | 5 + .../resource/svg - 原/windows/max.svg | 5 + .../resource/svg - 原/windows/min.svg | 4 + TIDALDL-PY/tidal_gui/resource/svg/V.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/__init__.py | 4 + .../tidal_gui/resource/svg/buymeacoffee.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/check.svg | 5 + TIDALDL-PY/tidal_gui/resource/svg/down.svg | 6 + .../tidal_gui/resource/svg/downHover.svg | 6 + TIDALDL-PY/tidal_gui/resource/svg/github.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/left.svg | 1 + .../resource/svg/leftTab/download.svg | 1 + .../resource/svg/leftTab/downloadHover.svg | 1 + .../tidal_gui/resource/svg/leftTab/info.svg | 1 + .../tidal_gui/resource/svg/leftTab/search.svg | 1 + .../resource/svg/leftTab/settings.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/paypal.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/right.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/search.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/search2.svg | 1 + .../resource/svg/taskItem/cancel.svg | 1 + .../resource/svg/taskItem/delete.svg | 1 + .../resource/svg/taskItem/expand.svg | 1 + .../tidal_gui/resource/svg/taskItem/open.svg | 1 + .../tidal_gui/resource/svg/taskItem/retry.svg | 1 + .../resource/svg/taskTab/complete.svg | 1 + .../resource/svg/taskTab/download.svg | 1 + .../tidal_gui/resource/svg/taskTab/error.svg | 1 + TIDALDL-PY/tidal_gui/resource/svg/upHover.svg | 1 + .../tidal_gui/resource/svg/windows/close.svg | 5 + .../resource/svg/windows/closeHover.svg | 5 + .../tidal_gui/resource/svg/windows/max.svg | 5 + .../tidal_gui/resource/svg/windows/min.svg | 4 + .../tidal_gui/resource/themeDefault.qss | 774 ++++++++++++++++++ TIDALDL-PY/tidal_gui/style.py | 63 ++ TIDALDL-PY/tidal_gui/theme.py | 63 ++ TIDALDL-PY/tidal_gui/tidalImp.py | 165 ++++ TIDALDL-PY/tidal_gui/view/__init__.py | 10 + TIDALDL-PY/tidal_gui/view/aboutView.py | 74 ++ TIDALDL-PY/tidal_gui/view/downloadItemView.py | 78 ++ TIDALDL-PY/tidal_gui/view/loginView.py | 156 ++++ TIDALDL-PY/tidal_gui/view/mainView.py | 124 +++ TIDALDL-PY/tidal_gui/view/searchView.py | 225 +++++ TIDALDL-PY/tidal_gui/view/settingsView.py | 104 +++ TIDALDL-PY/tidal_gui/view/taskItemView.py | 98 +++ TIDALDL-PY/tidal_gui/view/taskView.py | 87 ++ TIDALDL-PY/tidal_gui/viewModel/__init__.py | 11 + TIDALDL-PY/tidal_gui/viewModel/aboutModel.py | 22 + .../tidal_gui/viewModel/downloadItemModel.py | 140 ++++ TIDALDL-PY/tidal_gui/viewModel/loginModel.py | 73 ++ TIDALDL-PY/tidal_gui/viewModel/mainModel.py | 55 ++ TIDALDL-PY/tidal_gui/viewModel/searchModel.py | 152 ++++ .../tidal_gui/viewModel/settingsModel.py | 31 + .../tidal_gui/viewModel/taskItemModel.py | 107 +++ TIDALDL-PY/tidal_gui/viewModel/taskModel.py | 71 ++ TIDALDL-PY/tidal_gui/viewModel/viewModel.py | 26 + 100 files changed, 3460 insertions(+) create mode 100644 TIDALDL-PY/setup-gui.py create mode 100644 TIDALDL-PY/tidal_gui/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/control/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/control/checkBox.py create mode 100644 TIDALDL-PY/tidal_gui/control/comboBox.py create mode 100644 TIDALDL-PY/tidal_gui/control/framelessWidget.py create mode 100644 TIDALDL-PY/tidal_gui/control/label.py create mode 100644 TIDALDL-PY/tidal_gui/control/layout.py create mode 100644 TIDALDL-PY/tidal_gui/control/line.py create mode 100644 TIDALDL-PY/tidal_gui/control/lineEdit.py create mode 100644 TIDALDL-PY/tidal_gui/control/listWidget.py create mode 100644 TIDALDL-PY/tidal_gui/control/pushButton.py create mode 100644 TIDALDL-PY/tidal_gui/control/scrollWidget.py create mode 100644 TIDALDL-PY/tidal_gui/control/tableView.py create mode 100644 TIDALDL-PY/tidal_gui/control/tableWidget.py create mode 100644 TIDALDL-PY/tidal_gui/downloader.py create mode 100644 TIDALDL-PY/tidal_gui/resource/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/check.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/windows/close.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/windows/closeHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/windows/max.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg - 原/windows/min.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/V.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/check.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/down.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/downHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/github.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/left.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/paypal.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/right.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/search.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/search2.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/upHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/windows/close.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/windows/closeHover.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/windows/max.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/svg/windows/min.svg create mode 100644 TIDALDL-PY/tidal_gui/resource/themeDefault.qss create mode 100644 TIDALDL-PY/tidal_gui/style.py create mode 100644 TIDALDL-PY/tidal_gui/theme.py create mode 100644 TIDALDL-PY/tidal_gui/tidalImp.py create mode 100644 TIDALDL-PY/tidal_gui/view/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/view/aboutView.py create mode 100644 TIDALDL-PY/tidal_gui/view/downloadItemView.py create mode 100644 TIDALDL-PY/tidal_gui/view/loginView.py create mode 100644 TIDALDL-PY/tidal_gui/view/mainView.py create mode 100644 TIDALDL-PY/tidal_gui/view/searchView.py create mode 100644 TIDALDL-PY/tidal_gui/view/settingsView.py create mode 100644 TIDALDL-PY/tidal_gui/view/taskItemView.py create mode 100644 TIDALDL-PY/tidal_gui/view/taskView.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/__init__.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/aboutModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/loginModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/mainModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/searchModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/settingsModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/taskModel.py create mode 100644 TIDALDL-PY/tidal_gui/viewModel/viewModel.py diff --git a/TIDALDL-PY/setup-gui.py b/TIDALDL-PY/setup-gui.py new file mode 100644 index 0000000..60d14b3 --- /dev/null +++ b/TIDALDL-PY/setup-gui.py @@ -0,0 +1,16 @@ +from setuptools import setup, find_packages +setup( + name = 'tidal-gui', + version = '1.0.0.4', + license = "Apache2", + description = "Tidal Music Downloader.", + + author = 'YaronH', + author_email = "yaronhuang@foxmail.com", + + packages = find_packages(), + include_package_data = True, + platforms = "any", + install_requires=["aigpy", "PyQt5", "requests>=2.22.0", "pycryptodome", "pydub", "prettytable", "lyricsgenius"], + entry_points={'console_scripts': [ 'tidal-gui = tidal_gui:main', ]} +) diff --git a/TIDALDL-PY/tidal_gui/__init__.py b/TIDALDL-PY/tidal_gui/__init__.py new file mode 100644 index 0000000..014e003 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/__init__.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : __init__.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import sys + +from PyQt5.QtWidgets import QApplication + +from tidal_gui import theme +from tidal_gui.viewModel.mainModel import MainModel + + +def main(): + qss = theme.getThemeQssContent() + app = QApplication(sys.argv) + app.setStyleSheet(qss) + + mainView = MainModel() + mainView.show() + + app.exec_() + mainView.uninit() + + sys.exit() + +if __name__ == '__main__': + main() diff --git a/TIDALDL-PY/tidal_gui/control/__init__.py b/TIDALDL-PY/tidal_gui/control/__init__.py new file mode 100644 index 0000000..5d66b0c --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/__init__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : __init__.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" diff --git a/TIDALDL-PY/tidal_gui/control/checkBox.py b/TIDALDL-PY/tidal_gui/control/checkBox.py new file mode 100644 index 0000000..8066429 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/checkBox.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : checkBox.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" + +from PyQt5.QtWidgets import QCheckBox + + +class CheckBox(QCheckBox): + def __init__(self, text: str = "", checked: bool = False): + super(CheckBox, self).__init__() + self.setChecked(checked) + self.setText(text) diff --git a/TIDALDL-PY/tidal_gui/control/comboBox.py b/TIDALDL-PY/tidal_gui/control/comboBox.py new file mode 100644 index 0000000..793c66b --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/comboBox.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : comboBox.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtWidgets import QComboBox, QListView +from PyQt5.QtCore import Qt + + +class ComboBox(QComboBox): + def __init__(self, items: list, width: int=200): + super(ComboBox, self).__init__() + self.setItems(items) + self.setFixedWidth(width) + self.setView(QListView()) + # remove shadow + self.view().window().setWindowFlags(Qt.Popup | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint) + self.view().window().setAttribute(Qt.WA_TranslucentBackground) + + def setItems(self, items): + for item in items: + self.addItem(str(item)) diff --git a/TIDALDL-PY/tidal_gui/control/framelessWidget.py b/TIDALDL-PY/tidal_gui/control/framelessWidget.py new file mode 100644 index 0000000..d97cea6 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/framelessWidget.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : framelessWidget.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import ctypes.wintypes +from ctypes.wintypes import POINT +import typing + +import PyQt5 +import win32api +import win32con +from PyQt5.QtCore import Qt, QPoint +from PyQt5.QtGui import QMouseEvent +from PyQt5.QtWidgets import QWidget, QGridLayout, QHBoxLayout + +from tidal_gui.control.pushButton import PushButton +from tidal_gui.style import ButtonStyle + + +class FramelessWidget(QWidget): + def __init__(self): + super(FramelessWidget, self).__init__() + self.setWindowFlags(Qt.FramelessWindowHint) + self.BorderWidth = 5 + self.borderWidget = QWidget() + self.borderWidget.setObjectName("widgetMain") + self.borderWidget.setStyleSheet("QWidget#widgetMain{border: 1px solid #000000;};") + + self.contentGrid = QGridLayout() + self.contentGrid.setContentsMargins(1, 1, 1, 1) + + self.windowBtnGrid = self.__createWindowsButtonLayout__() + self.enableMove = True + self.validMoveWidget = None + self.clickPos = None + + self.grid = QGridLayout() + self.grid.setSpacing(0) + self.grid.setContentsMargins(0, 0, 0, 0) + self.grid.addWidget(self.borderWidget, 0, 0) + self.grid.addLayout(self.contentGrid, 0, 0) + self.setLayout(self.grid) + + def __showMaxWindows__(self): + if self.windowState() == Qt.WindowMaximized: + self.showNormal() + else: + self.showMaximized() + + def __createWindowsButtonLayout__(self): + self.closeBtn = PushButton('', ButtonStyle.CloseWindow) + self.maxBtn = PushButton('', ButtonStyle.MaxWindow) + self.minBtn = PushButton('', ButtonStyle.MinWindow) + + self.closeBtn.clicked.connect(self.close) + self.minBtn.clicked.connect(self.showMinimized) + self.maxBtn.clicked.connect(self.__showMaxWindows__) + + layout = QHBoxLayout() + layout.setSpacing(0) + layout.setContentsMargins(1, 1, 1, 1) + layout.addWidget(self.minBtn) + layout.addWidget(self.maxBtn) + layout.addWidget(self.closeBtn) + return layout + + def __clickInValidMoveWidget__(self, x=-1, y=-1) -> bool: + if self.validMoveWidget is None: + return True + if x == -1 and y == -1: + x = self.clickPos.x() + y = self.clickPos.y() + + pos = self.validMoveWidget.pos() + if x < pos.x() or x > pos.x() + self.validMoveWidget.width(): + return False + if y < pos.y() or y > pos.y() + self.validMoveWidget.height(): + return False + return True + + def nativeEvent(self, eventType, message): + retVal, result = super(FramelessWidget, self).nativeEvent(eventType, message) + if eventType == "windows_generic_MSG": + msg = ctypes.wintypes.MSG.from_address(message.__int__()) + if msg.message != win32con.WM_NCHITTEST: + return retVal, result + + # 获取鼠标移动经过时的坐标 + x = win32api.LOWORD(msg.lParam) - self.frameGeometry().x() + y = win32api.HIWORD(msg.lParam) - self.frameGeometry().y() + + w, h = self.width(), self.height() + lx = x < self.BorderWidth + rx = x > w - self.BorderWidth + ty = y < self.BorderWidth + by = y > h - self.BorderWidth + if (lx and ty):# 左上角 + return True, win32con.HTTOPLEFT + elif (rx and by):# 右下角 + return True, win32con.HTBOTTOMRIGHT + elif (rx and ty):# 右上角 + return True, win32con.HTTOPRIGHT + elif (lx and by):# 左下角 + return True, win32con.HTBOTTOMLEFT + elif ty:# 上 + return True, win32con.HTTOP + elif by:# 下 + return True, win32con.HTBOTTOM + elif lx:# 左 + return True, win32con.HTLEFT + elif rx:# 右 + return True, win32con.HTRIGHT + return retVal, result + + def mousePressEvent(self, e: QMouseEvent): + if e.button() == Qt.LeftButton: + self.clickPos = e.pos() + + def mouseReleaseEvent(self, e: QMouseEvent): + if e.button() == Qt.LeftButton: + self.clickPos = QPoint(-1, -1) + + def mouseMoveEvent(self, e: QMouseEvent): + if not self.enableMove: + return + if Qt.LeftButton & e.buttons(): + if self.__clickInValidMoveWidget__() and self.clickPos: + self.move(e.pos() + self.pos() - self.clickPos) + + def mouseDoubleClickEvent(self, e: QMouseEvent): + if self.maxBtn.isHidden(): + return + if Qt.LeftButton & e.buttons(): + if self.__clickInValidMoveWidget__(e.x(), e.y()): + self.__showMaxWindows__() + + def getGrid(self): + return self.contentGrid + + def disableMove(self): + self.enableMove = False + + def setValidMoveWidget(self, widget): + self.validMoveWidget = widget + + def setWindowButton(self, showClose=True, showMin=True, showMax=True): + if not showMax: + self.maxBtn.hide() + if not showMin: + self.minBtn.hide() + if not showClose: + self.closeBtn.hide() + self.grid.addLayout(self.windowBtnGrid, 0, 0, Qt.AlignTop | Qt.AlignRight) diff --git a/TIDALDL-PY/tidal_gui/control/label.py b/TIDALDL-PY/tidal_gui/control/label.py new file mode 100644 index 0000000..6656370 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/label.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : label.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtWidgets import QLabel + +from tidal_gui.style import LabelStyle + + +class Label(QLabel): + def __init__(self, text: str = "", style: LabelStyle = LabelStyle.Default): + super(Label, self).__init__() + self.setText(text) + self.setObjectName(style.name + "Label") diff --git a/TIDALDL-PY/tidal_gui/control/layout.py b/TIDALDL-PY/tidal_gui/control/layout.py new file mode 100644 index 0000000..4b38808 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/layout.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : layout.py +@Date : 2021/8/13 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout + + +def createHBoxLayout(widgets): + layout = QHBoxLayout() + for item in widgets: + layout.addWidget(item) + return layout + + +def createVBoxLayout(widgets): + layout = QVBoxLayout() + for item in widgets: + layout.addWidget(item) + return layout diff --git a/TIDALDL-PY/tidal_gui/control/line.py b/TIDALDL-PY/tidal_gui/control/line.py new file mode 100644 index 0000000..3573f86 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/line.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : line.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtWidgets import QFrame + + +class Line(QFrame): + def __init__(self, shape: str = 'V'): + super(Line, self).__init__() + self.setFrameShape(QFrame.VLine if shape == 'V' else QFrame.HLine) + self.setFrameShadow(QFrame.Sunken) + + self.setObjectName('VLineQFrame' if shape == 'V' else 'HLineQFrame') diff --git a/TIDALDL-PY/tidal_gui/control/lineEdit.py b/TIDALDL-PY/tidal_gui/control/lineEdit.py new file mode 100644 index 0000000..a7ba787 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/lineEdit.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : lineEdit.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QLineEdit, QAction + + +class LineEdit(QLineEdit): + def __init__(self, placeholderText: str = "", iconUrl: str = ''): + super(LineEdit, self).__init__() + self.setPlaceholderText(placeholderText) + + if iconUrl != '': + action = QAction(self) + action.setIcon(QIcon(iconUrl)) + self.addAction(action, QLineEdit.LeadingPosition) diff --git a/TIDALDL-PY/tidal_gui/control/listWidget.py b/TIDALDL-PY/tidal_gui/control/listWidget.py new file mode 100644 index 0000000..4ad62c1 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/listWidget.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : listWidget.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtCore import QSize +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QWidget + +from tidal_gui.style import ListWidgetStyle + + +class ListWidget(QListWidget): + def __init__(self, style: ListWidgetStyle = ListWidgetStyle.Default): + super(ListWidget, self).__init__() + self.setObjectName(style.name + "ListWidget") + + def addIConTextItem(self, iconUrl: str, text: str): + self.addItem(QListWidgetItem(QIcon(iconUrl), text)) + + def addWidgetItem(self, widget: QWidget): + item = QListWidgetItem(self) + # item.setSizeHint(QSize(widget.width(), widget.height())) + self.addItem(item) + self.setItemWidget(item, widget) diff --git a/TIDALDL-PY/tidal_gui/control/pushButton.py b/TIDALDL-PY/tidal_gui/control/pushButton.py new file mode 100644 index 0000000..475a48c --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/pushButton.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : pushButton.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QPushButton + +from tidal_gui.style import ButtonStyle + + +class PushButton(QPushButton): + def __init__(self, + text: str = '', + style: ButtonStyle = ButtonStyle.Default, + width=0, + iconUrl=''): + super(PushButton, self).__init__() + self.setText(text) + self.setObjectName(style.name + "PushButton") + + if width > 0: + self.setFixedWidth(width) + + if iconUrl != '': + self.setIcon(QIcon(iconUrl)) + diff --git a/TIDALDL-PY/tidal_gui/control/scrollWidget.py b/TIDALDL-PY/tidal_gui/control/scrollWidget.py new file mode 100644 index 0000000..c135522 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/scrollWidget.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : scrollWidget.py +@Date : 2021/10/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" + +from PyQt5.QtCore import QSize, Qt +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QWidget, QScrollArea, QVBoxLayout, QPushButton + + +class ScrollWidget(QScrollArea): + def __init__(self): + super(ScrollWidget, self).__init__() + self.setWidgetResizable(True) + + self._widget = QWidget() + self._layout = QVBoxLayout(self._widget) + self.setWidget(self._widget) + + def addWidgetItem(self, widget: QWidget): + self._layout.addWidget(widget, 0, Qt.AlignTop) + pass diff --git a/TIDALDL-PY/tidal_gui/control/tableView.py b/TIDALDL-PY/tidal_gui/control/tableView.py new file mode 100644 index 0000000..05f9349 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/tableView.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : tableView.py +@Date : 2021/9/10 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QStandardItemModel, QStandardItem +from PyQt5.QtWidgets import QTableView, QTableWidgetItem, QAbstractItemView + + +class TableView(QTableView): + def __init__(self, columnNames: list, rowCount: int = 20): + super(TableView, self).__init__() + + self._model = QStandardItemModel() + self._model.setColumnCount(len(columnNames)) + self._model.setRowCount(rowCount) + + for index, name in enumerate(columnNames): + self._model.setHeaderData(index, Qt.Horizontal, name) + + self.setModel(self._model) + # self.setHorizontalHeaderItem(index, QTableWidgetItem(name)) + # for index in range(0, rowCount): + # self.setRowHeight(index, 50) + + self.setShowGrid(False) + self.verticalHeader().setVisible(False) + self.setSelectionBehavior(QAbstractItemView.SelectRows) + self.setSelectionMode(QAbstractItemView.SingleSelection) + + self.horizontalHeader().setStretchLastSection(True) + self.setEditTriggers(QAbstractItemView.NoEditTriggers) + + self.setFocusPolicy(Qt.NoFocus) + + def addItem(self, rowIdx: int, colIdx: int, text: str): + item = QStandardItem(text) + item.setTextAlignment(Qt.AlignCenter) + self._model.setItem(rowIdx, colIdx, item) + # + # def addWidgetItem(self, rowIdx: int, colIdx: int, widget): + # self.setCellWidget(rowIdx, colIdx, widget) diff --git a/TIDALDL-PY/tidal_gui/control/tableWidget.py b/TIDALDL-PY/tidal_gui/control/tableWidget.py new file mode 100644 index 0000000..ed17a43 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/control/tableWidget.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : tableWidget.py +@Date : 2021/8/18 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import aigpy +import requests +import threading +from PyQt5.QtCore import Qt, QUrl +from PyQt5.QtGui import QIcon, QPixmap +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply +from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QAbstractItemView, QLabel +from tidal_gui.control.label import Label, LabelStyle + + +class TableWidget(QTableWidget): + def __init__(self, columnNames: list, rowCount: int = 20): + super(TableWidget, self).__init__() + self.setColumnCount(len(columnNames)) + self.setRowCount(rowCount) + + self._lock = threading.Lock() + self._netManager = QNetworkAccessManager() + + self.columnAligns = [] + + for index, name in enumerate(columnNames): + item = QTableWidgetItem(name) + + align = Qt.AlignLeft | Qt.AlignVCenter + if name == '#' or name == ' ': + align = Qt.AlignCenter + + self.columnAligns.append(align) + item.setTextAlignment(align) + self.setHorizontalHeaderItem(index, item) + + for index in range(0, rowCount): + self.setRowHeight(index, 50) + + self.setShowGrid(False) + self.verticalHeader().setVisible(False) + self.setSelectionBehavior(QAbstractItemView.SelectRows) + self.setSelectionMode(QAbstractItemView.SingleSelection) + + self.horizontalHeader().setStretchLastSection(True) + self.setEditTriggers(QAbstractItemView.NoEditTriggers) + + self.setFocusPolicy(Qt.NoFocus) + + def changeRowCount(self, rows: int): + if rows != self.rowCount(): + self.setRowCount(rows) + for index in range(0, rows): + self.setRowHeight(index, 50) + + def addItem(self, rowIdx: int, colIdx: int, text): + if isinstance(text, str): + item = QTableWidgetItem(text) + item.setTextAlignment(self.columnAligns[colIdx]) + self.setItem(rowIdx, colIdx, item) + elif isinstance(text, QUrl): + self.__addPicItem__(rowIdx, colIdx, text) + + def __picDownload__(self, rowIdx, colIdx): + reply = self.sender() + data = reply.readAll() + if data.size() <= 0: + return + + pic = QPixmap() + pic.loadFromData(data) + + self._lock.acquire() + self.cellWidget(rowIdx, colIdx).setPixmap(pic.scaled(32, 32)) + self._lock.release() + + reply.deleteLater() + + def __addPicItem__(self, rowIdx: int, colIdx: int, url: QUrl): + icon = Label('', LabelStyle.Icon) + icon.setAlignment(Qt.AlignCenter) + + self.setCellWidget(rowIdx, colIdx, icon) + + reply = self._netManager.get(QNetworkRequest(url)) + reply.finished.connect(lambda: self.__picDownload__(rowIdx, colIdx)) + + def addWidgetItem(self, rowIdx: int, colIdx: int, widget): + self.setCellWidget(rowIdx, colIdx, widget) diff --git a/TIDALDL-PY/tidal_gui/downloader.py b/TIDALDL-PY/tidal_gui/downloader.py new file mode 100644 index 0000000..3613520 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/downloader.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : downloader.py +@Date : 2021/09/15 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.Qt import QThread +from queue import Queue +from tidal_gui.viewModel.taskModel import TaskModel +import time + +class DownloaderImp(QThread): + def __init__(self): + super(DownloaderImp, self).__init__() + self._taskModel = None + + def run(self): + print('DownloadImp start...') + while not self.isInterruptionRequested(): + if self._taskModel is not None: + item = self._taskModel.getWaitDownloadItem() + if item is not None: + item.download() + time.sleep(1) + print('DownloadImp stop...') + + def setTaskModel(self, model: TaskModel): + self._taskModel = model + + def stop(self): + self.requestInterruption() + self.wait() + +downloadImp = DownloaderImp() diff --git a/TIDALDL-PY/tidal_gui/resource/__init__.py b/TIDALDL-PY/tidal_gui/resource/__init__.py new file mode 100644 index 0000000..fd40910 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/__init__.py @@ -0,0 +1,4 @@ + + + + diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg new file mode 100644 index 0000000..0a1576c --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/__init__.py b/TIDALDL-PY/tidal_gui/resource/svg - 原/__init__.py new file mode 100644 index 0000000..fd40910 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/__init__.py @@ -0,0 +1,4 @@ + + + + diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg new file mode 100644 index 0000000..9a8bd88 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/check.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/check.svg new file mode 100644 index 0000000..3bfad7e --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/check.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg new file mode 100644 index 0000000..a171c81 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg new file mode 100644 index 0000000..24cbb35 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg new file mode 100644 index 0000000..2d1cea6 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg new file mode 100644 index 0000000..e931f47 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg new file mode 100644 index 0000000..be5235f --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg new file mode 100644 index 0000000..3eeb232 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg new file mode 100644 index 0000000..b428605 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg new file mode 100644 index 0000000..0c1900f --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg new file mode 100644 index 0000000..dee79cf --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg new file mode 100644 index 0000000..a46caba --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg new file mode 100644 index 0000000..96bf9b1 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg new file mode 100644 index 0000000..a1cffbe --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg new file mode 100644 index 0000000..2235c0d --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg new file mode 100644 index 0000000..2f1c840 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg new file mode 100644 index 0000000..45176f6 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg new file mode 100644 index 0000000..922d4eb --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg new file mode 100644 index 0000000..6a01693 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg new file mode 100644 index 0000000..42d25cd --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg new file mode 100644 index 0000000..9397de9 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg new file mode 100644 index 0000000..0e9d285 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg new file mode 100644 index 0000000..a2bc642 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg new file mode 100644 index 0000000..9fff595 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/close.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/close.svg new file mode 100644 index 0000000..d06198c --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/close.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/closeHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/closeHover.svg new file mode 100644 index 0000000..0cb0e59 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/closeHover.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/max.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/max.svg new file mode 100644 index 0000000..016e62b --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/max.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/min.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/min.svg new file mode 100644 index 0000000..a180f19 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/windows/min.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/V.svg b/TIDALDL-PY/tidal_gui/resource/svg/V.svg new file mode 100644 index 0000000..0a1576c --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/V.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/__init__.py b/TIDALDL-PY/tidal_gui/resource/svg/__init__.py new file mode 100644 index 0000000..fd40910 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/__init__.py @@ -0,0 +1,4 @@ + + + + diff --git a/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg b/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg new file mode 100644 index 0000000..9a8bd88 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/check.svg b/TIDALDL-PY/tidal_gui/resource/svg/check.svg new file mode 100644 index 0000000..3bfad7e --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/check.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/down.svg b/TIDALDL-PY/tidal_gui/resource/svg/down.svg new file mode 100644 index 0000000..a171c81 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/down.svg @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg new file mode 100644 index 0000000..24cbb35 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/github.svg b/TIDALDL-PY/tidal_gui/resource/svg/github.svg new file mode 100644 index 0000000..2d1cea6 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/left.svg b/TIDALDL-PY/tidal_gui/resource/svg/left.svg new file mode 100644 index 0000000..e931f47 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg new file mode 100644 index 0000000..be5235f --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg new file mode 100644 index 0000000..3eeb232 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg new file mode 100644 index 0000000..b428605 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg new file mode 100644 index 0000000..0c1900f --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg new file mode 100644 index 0000000..dee79cf --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg b/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg new file mode 100644 index 0000000..a46caba --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/right.svg b/TIDALDL-PY/tidal_gui/resource/svg/right.svg new file mode 100644 index 0000000..96bf9b1 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/search.svg b/TIDALDL-PY/tidal_gui/resource/svg/search.svg new file mode 100644 index 0000000..a1cffbe --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/search2.svg b/TIDALDL-PY/tidal_gui/resource/svg/search2.svg new file mode 100644 index 0000000..2235c0d --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/search2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg new file mode 100644 index 0000000..2f1c840 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg new file mode 100644 index 0000000..45176f6 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg new file mode 100644 index 0000000..922d4eb --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg new file mode 100644 index 0000000..6a01693 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg new file mode 100644 index 0000000..42d25cd --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg new file mode 100644 index 0000000..9397de9 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg new file mode 100644 index 0000000..0e9d285 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg new file mode 100644 index 0000000..a2bc642 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg new file mode 100644 index 0000000..9fff595 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/windows/close.svg b/TIDALDL-PY/tidal_gui/resource/svg/windows/close.svg new file mode 100644 index 0000000..6220a5d --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/windows/close.svg @@ -0,0 +1,5 @@ + + + diff --git a/TIDALDL-PY/tidal_gui/resource/svg/windows/closeHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/windows/closeHover.svg new file mode 100644 index 0000000..0cb0e59 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/windows/closeHover.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/windows/max.svg b/TIDALDL-PY/tidal_gui/resource/svg/windows/max.svg new file mode 100644 index 0000000..016e62b --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/windows/max.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/svg/windows/min.svg b/TIDALDL-PY/tidal_gui/resource/svg/windows/min.svg new file mode 100644 index 0000000..a180f19 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/svg/windows/min.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/resource/themeDefault.qss b/TIDALDL-PY/tidal_gui/resource/themeDefault.qss new file mode 100644 index 0000000..6784334 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/resource/themeDefault.qss @@ -0,0 +1,774 @@ +:root { + --Font_Family: Microsoft YaHei UI; + --Font_Size: 12px; + --Font_Size_TabHeader: 12px; + --Font_Size_TableHeader: 12px; + --Font_Size_PageTitle: 15px; + --Font_Size_HugeTitle: 30px; + --Font_Size_MsgLabel: 15px; + + --Color_Default: #ffffff; + --Color_DefaultHover: #f0f0f0; + --Color_DefaultPressed: #ececec; + --Color_DefaultText: #212121; + + --Color_Primary: #326cf3; + --Color_PrimaryHover: #477bf4; + --Color_PrimaryPressed: #84a7f8; + --Color_PrimaryText: #ffffff; + + --Color_Success: #2db84d; + --Color_SuccessHover: #42bf5f; + --Color_SuccessPressed: #81d494; + + --Color_Danger: #db3340; + --Color_DangerHover: #df4853; + --Color_DangerPressed: #e9858c; + + --Color_Warning: #e9af20; + --Color_WarningHover: #ebb737; + --Color_WarningPressed: #f2cf79; + + --Color_Info: #00bcd4; + --Color_InfoHover: #1ac3d8; + --Color_InfoPressed: #66d7e5; + + --Color_Border: #cbcbcb; + --Color_WindowsIcon: #cbcbcb; + --Color_Background: #eeeeee; + --Color_Shadow: #AA000000; + + --Color_Transparent: transparent; + + --Size_InputPaddingLR: 8px; + --Size_InputPaddingTB: 6px; + + --Size_HeaderHeight: 45px; + --Size_ItemHeight: 18px; + --Size_ProgressHeight: 26px; + --Size_ControlHeight: 18px; + + --Size_ControlPaddingLR: 8px; + --Size_ControlPaddingTB: 6px; + + --Size_IconSize: 24px; + --Size_CornerRadius: 4px; + + --Size_CalendarWidth: 287px; + --Size_CalendarHeight: 300px; +} + +/* #QSS_START */ + +* { + font-family: var(--Font_Family); + font-size: var(--Font_Size); + + background: var(--Color_Default); + color: var(--Color_DefaultText); +} + +/* Widget */ + +QWidget#MainViewLeftWidget { + background: #000000; +} + +QWidget#TestWidget { + background: #000000; +} + +QWidget#BaseWidget { + background: var(--Color_Default); +} + +QWidget#TaskItemView { + background: var(--Color_Default); + border-style: solid; + border-width: 1px; + border-color: var(--Color_Border); + border-radius: var(--Size_CornerRadius); +} + +QWidget#AboutWidget { + +} + + + +/* PushButton Style */ + +QPushButton { + border-style: solid; + border-width: 1px; + border-color: var(--Color_DefaultPressed); + padding-left: var(--Size_InputPaddingLR); + padding-right: var(--Size_InputPaddingLR); + padding-top: var(--Size_InputPaddingTB); + padding-bottom: var(--Size_InputPaddingTB); + border-radius: var(--Size_CornerRadius); + color: var(--Color_DefaultText); + background: var(--Color_Default); +} + +QPushButton::hover { + color: var(--Color_DefaultText); + background: var(--Color_DefaultHover); +} + +QPushButton::pressed { + color: var(--Color_DefaultText); + background: var(--Color_DefaultPressed); +} + + +QPushButton#PrimaryPushButton { + border-color: var(--Color_PrimaryPressed); + color: var(--Color_PrimaryText); + background: var(--Color_Primary); +} + +QPushButton#PrimaryPushButton::hover { + background: var(--Color_PrimaryHover); +} + +QPushButton#PrimaryPushButton::pressed { + background: var(--Color_PrimaryPressed); +} + +QPushButton#SuccessPushButton { + border-color: var(--Color_SuccessPressed); + color: var(--Color_PrimaryText); + background: var(--Color_Success); +} + +QPushButton#SuccessPushButton::hover { + background: var(--Color_SuccessHover); +} + +QPushButton#SuccessPushButton::pressed { + background: var(--Color_SuccessPressed); +} + +QPushButton#DangerPushButton { + border-color: var(--Color_DangerPressed); + color: var(--Color_PrimaryText); + background: var(--Color_Danger); +} + +QPushButton#DangerPushButton::hover { + background: var(--Color_DangerHover); +} + +QPushButton#DangerPushButton::pressed { + background: var(--Color_DangerPressed); +} + +QPushButton#WarningPushButton { + border-color: var(--Color_WarningPressed); + color: var(--Color_PrimaryText); + background: var(--Color_Warning); +} + +QPushButton#WarningPushButton::hover { + background: var(--Color_WarningHover); +} + +QPushButton#WarningPushButton::pressed { + background: var(--Color_WarningPressed); +} + +QPushButton#InfoPushButton { + border-color: var(--Color_InfoPressed); + color: var(--Color_PrimaryText); + background: var(--Color_Info); +} + +QPushButton#InfoPushButton::hover { + background: var(--Color_InfoHover); +} + +QPushButton#InfoPushButton::pressed { + background: var(--Color_InfoPressed); +} + +/* Software Buton */ + +QPushButton#SoftwareIconPushButton { + border-style: none; + border-radius: 0px; + height: 26; + width: 26; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/V.svg) +} +QPushButton#SoftwareIconPushButton::hover { + image: url($RESOURCE_PATH$/svg/V.svg) +} +QPushButton#SoftwareIconPushButton::pressed { + image: url($RESOURCE_PATH$/svg/V.svg) +} + + +/* Windows Buton */ +QPushButton#CloseWindowPushButton { + border-style: none; + border-radius: 0px; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/windows/close.svg) +} + +QPushButton#CloseWindowPushButton::hover { + background: #ff1a1a; + image: url($RESOURCE_PATH$/svg/windows/closeHover.svg) +} + +QPushButton#CloseWindowPushButton::pressed { + background: #ff6666; + image: url($RESOURCE_PATH$/svg/windows/closeHover.svg) +} + +QPushButton#MaxWindowPushButton { + border-style: none; + border-radius: 0px; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/windows/max.svg) +} + +QPushButton#MaxWindowPushButton::hover { + background: var(--Color_DefaultHover); +} + +QPushButton#MaxWindowPushButton::pressed { + background: var(--Color_DefaultPressed); +} + +QPushButton#MinWindowPushButton { + border-style: none; + border-radius: 0px; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/windows/min.svg) +} + +QPushButton#MinWindowPushButton::hover { + background: var(--Color_DefaultHover); +} + +QPushButton#MinWindowPushButton::pressed { + background: var(--Color_DefaultPressed); +} + +/* Task item tool button */ +QPushButton#TaskRetryPushButton { + border-style: none; + background: var(--Color_Default); + padding-left: 4px; + padding-right: 4px; + image: url($RESOURCE_PATH$/svg/taskItem/retry.svg) +} +QPushButton#TaskRetryPushButton::hover { + background: var(--Color_DefaultHover); +} +QPushButton#TaskRetryPushButton::pressed { + background: var(--Color_DefaultPressed); +} + +QPushButton#TaskCancelPushButton { + border-style: none; + background: var(--Color_Default); + padding-left: 4px; + padding-right: 4px; + image: url($RESOURCE_PATH$/svg/taskItem/cancel.svg) +} +QPushButton#TaskCancelPushButton::hover { + background: var(--Color_DefaultHover); +} +QPushButton#TaskCancelPushButton::pressed { + background: var(--Color_DefaultPressed); +} + +QPushButton#TaskDeletePushButton { + border-style: none; + background: var(--Color_Default); + padding-left: 4px; + padding-right: 4px; + image: url($RESOURCE_PATH$/svg/taskItem/delete.svg) +} +QPushButton#TaskDeletePushButton::hover { + background: var(--Color_DefaultHover); +} +QPushButton#TaskDeletePushButton::pressed { + background: var(--Color_DefaultPressed); +} + +QPushButton#TaskOpenPushButton { + border-style: none; + background: var(--Color_Default); + padding-left: 4px; + padding-right: 4px; + image: url($RESOURCE_PATH$/svg/taskItem/open.svg) +} +QPushButton#TaskOpenPushButton::hover { + background: var(--Color_DefaultHover); +} +QPushButton#TaskOpenPushButton::pressed { + background: var(--Color_DefaultPressed); +} + +QPushButton#TaskExpandPushButton { + border-style: none; + background: var(--Color_Default); + padding-left: 4px; + padding-right: 4px; + image: url($RESOURCE_PATH$/svg/taskItem/expand.svg) +} +QPushButton#TaskExpandPushButton::hover { + background: var(--Color_DefaultHover); +} +QPushButton#TaskExpandPushButton::pressed { + background: var(--Color_DefaultPressed); +} + +/* MainTab Icon Button */ +QPushButton#SearchIconPushButton { + border-style: none; + border-radius: 0px; + height: 26; + width: 26; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/leftTab/search.svg) +} +QPushButton#SearchIconPushButton::hover { + image: url($RESOURCE_PATH$/svg/leftTab/search.svg) +} +QPushButton#SearchIconPushButton::pressed { + image: url($RESOURCE_PATH$/svg/leftTab/search.svg) +} + +QPushButton#TaskIconPushButton { + border-style: none; + border-radius: 0px; + height: 26; + width: 26; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/leftTab/download.svg) +} +QPushButton#TaskIconPushButton::hover { + image: url($RESOURCE_PATH$/svg/leftTab/downloadHover.svg) +} +QPushButton#TaskIconPushButton::pressed { + image: url($RESOURCE_PATH$/svg/leftTab/downloadHover.svg) +} + +QPushButton#SettingsIconPushButton { + border-style: none; + border-radius: 0px; + height: 26; + width: 26; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/leftTab/settings.svg) +} +QPushButton#SettingsIconPushButton::hover { + image: url($RESOURCE_PATH$/svg/leftTab/settings.svg) +} +QPushButton#SettingsIconPushButton::pressed { + image: url($RESOURCE_PATH$/svg/leftTab/settings.svg) +} + +QPushButton#AboutIconPushButton { + border-style: none; + border-radius: 0px; + height: 26; + width: 26; + background: var(--Color_Transparent); + image: url($RESOURCE_PATH$/svg/leftTab/info.svg) +} +QPushButton#AboutIconPushButton::hover { + image: url($RESOURCE_PATH$/svg/leftTab/info.svg) +} +QPushButton#AboutIconPushButton::pressed { + image: url($RESOURCE_PATH$/svg/leftTab/info.svg) +} + + +QPushButton#PrePagePushButton { + border-style: none; + border-radius: 0px; + border-width: 1px; + border-color: var(--Color_Border); + height: 18; + width: 18; + background: var(--Color_DefaultHover); + image: url($RESOURCE_PATH$/svg/left.svg) +} +QPushButton#PrePagePushButton::hover { + border-style: solid; + image: url($RESOURCE_PATH$/svg/left.svg) +} +QPushButton#PrePagePushButton::pressed { + image: url($RESOURCE_PATH$/svg/left.svg) +} + +QPushButton#NextPagePushButton { + border-style: none; + border-radius: 0px; + border-width: 1px; + border-color: var(--Color_Border); + height: 18; + width: 18; + background: var(--Color_DefaultHover); + image: url($RESOURCE_PATH$/svg/right.svg) +} +QPushButton#NextPagePushButton::hover { + border-style: solid; + image: url($RESOURCE_PATH$/svg/right.svg) +} +QPushButton#NextPagePushButton::pressed { + image: url($RESOURCE_PATH$/svg/right.svg) +} +/* LineEdit Style */ + +QLineEdit { + border-style: solid; + border-width: 1px; + border-color: var(--Color_Border); + padding-left: var(--Size_InputPaddingLR); + padding-right: var(--Size_InputPaddingLR); + padding-top: var(--Size_InputPaddingTB); + padding-bottom: var(--Size_InputPaddingTB); + border-radius: var(--Size_CornerRadius); + min-height: var(--Size_ControlHeight); + color: var(--Color_DefaultText); + background: var(--Color_Default); +} + +QLineEdit::hover { + border-color: var(--Color_Primary); +} + +QLineEdit::focus { + border-color: var(--Color_Primary); +} + +QLineEdit::disabled { + background: var(--Color_DefaultHover); + border-style: none; +} + +/* CheckBox Style */ + +QCheckBox::indicator { + width: 14px; + height: 14px; + padding: 2px; + + border: 1px solid var(--Color_Border); + border-radius: 2px; +} + +QCheckBox::indicator::unchecked:hover { + border: 1px solid var(--Color_PrimaryHover); +} + +QCheckBox::indicator::checked { + background: var(--Color_Primary); + image: url($RESOURCE_PATH$/svg/check.svg); + border: 1px solid var(--Color_Primary); +} + +/* TabWidget */ + +QTabWidget::pane { + border:none; + border-top: 1px solid var(--Color_Border); +} +QTabBar::tab { + border:none; + padding-left: 8px; + padding-right: 8px; + padding-bottom: 3px; + font-size: var(--Font_Size_TabHeader); + font-weight: bold; + min-width: 60; +} +QTabBar::tab:selected { + color: var(--Color_Primary); + border-color: var(--Color_Primary); + border-style: solid; + border-bottom-width: 3px; +} + +/* Label */ +QLabel#PageTitleLabel { + font-size: var(--Font_Size_PageTitle); + font-weight: bold; +} + +QLabel#PageSubTitleLabel { + font-size: var(--Font_Size_PageTitle); +} + +QLabel#HugeTitleLabel { + font-size: var(--Font_Size_HugeTitle); + font-weight: bold; +} + +QLabel#LogoBottomLabel { + font-size: var(--Font_Size_MsgLabel); + color: #ffffff; +} + +QLabel#SearchErrLabel { + font-size: var(--Font_Size); + color: var(--Color_Danger); + max-height: var(--Font_Size_MsgLabel); +} + +QLabel#IconLabel { + background: var(--Color_Transparent); +} + +QLabel#BoldLabel { + font-weight: bold; +} + +/* QFrame */ +QFrame#VLineQFrame +{ + border-top-style: none; + border-bottom-style: none; + border-right-style: none; + border-left: 1px solid var(--Color_Border); +} + +QFrame#HLineQFrame +{ + border-top: 1px solid var(--Color_Border); + border-bottom-style: none; + border-right-style: none; + border-left-style: none; +} + +/* QComboBox */ +QComboBox { + border-style: solid; + border-width: 1px; + border-color: var(--Color_Border); + padding-left: var(--Size_InputPaddingLR); + padding-right: var(--Size_InputPaddingLR); + padding-top: var(--Size_InputPaddingTB); + padding-bottom: var(--Size_InputPaddingTB); + border-radius: var(--Size_CornerRadius); + min-height: var(--Size_ControlHeight); + color: var(--Color_DefaultText); + background: var(--Color_Default); +} + +QComboBox::hover { + border-color: var(--Color_Primary); +} + +QComboBox::focus { + border-color: var(--Color_Primary); +} + +QComboBox::drop-down { + subcontrol-position:center right; + margin-right: 15px; + border-left-style: none; +} +QComboBox::down-arrow { + image: url($RESOURCE_PATH$/svg/down.svg); + width : 24px; + height : 24px; +} +QComboBox::down-arrow:hover { + image: url($RESOURCE_PATH$/svg/downHover.svg); +} + +QComboBox::down-arrow:on { + image: url($RESOURCE_PATH$/svg/upHover.svg); +} + +QComboBox QAbstractItemView { + margin-top:5px; + border:1px solid var(--Color_Primary); + outline:none; +} + +QComboBox QAbstractItemView::item { + height: var(--Size_ItemHeight); + + padding-top: var(--Size_InputPaddingTB); + padding-bottom: var(--Size_InputPaddingTB); +} + +QComboBox QAbstractItemView::item:selected{ + color: var(--Color_PrimaryText); + background: var(--Color_Primary); +} + + +/* QListWidget */ +QListWidget#TaskTabListWidget { + border-style: none; + background: var(--Color_Default); + max-width: 150px; + outline:none; +} + +QListWidget#TaskTabListWidget::item +{ + height:32px; + padding-left: var(--Size_ControlPaddingLR); + border: none; + border-radius: var(--Size_CornerRadius); + margin-top:5px; +} + +QListWidget#TaskTabListWidget::item:hover +{ + background: var(--Color_DefaultHover); + color: var(--Color_DefaultText); +} + +QListWidget#TaskTabListWidget::item:selected +{ + background: var(--Color_Primary); + color: var(--Color_PrimaryText); +} + + +QListWidget#TaskContentListWidget { + border-style: none; + background: var(--Color_Default); + outline:none; +} + +QListWidget#TaskContentListWidget::item +{ + border-style: solid; + border-width: 1px; + border-radius: var(--Size_CornerRadius); + border-color: var(--Color_Border); + + min-height:80px; + margin-top:5px; + background: var(--Color_Default); +} + +QListWidget#TaskContentListWidget::item:hover +{ + background: var(--Color_Default); +} + +QListWidget#TaskContentListWidget::item:selected +{ + background: var(--Color_Default); +} + + + +QListWidget#DownloadItemsListWidget { + border-left: none; + border-right: none; + border-top: 1px solid var(--Color_Border); + border-bottom: none; + background: var(--Color_Default); + outline:none; +} + +QListWidget#DownloadItemsListWidget::item +{ + border-style: none; + min-height:45px; + margin-top:2px; + background: var(--Color_Default); +} + +QListWidget#DownloadItemsListWidget::item:hover +{ + background: var(--Color_Default); +} + +QListWidget#DownloadItemsListWidget::item:selected +{ + background: var(--Color_Default); +} + +QListWidget QScrollBar::vertical{ + background:transparent; + width: 4px; + border-radius:6px; +} +QListWidget QScrollBar::handle{ + background: lightgray; + border-radius:6px; +} +QListWidget QScrollBar::handle:hover{background:gray;} +QListWidget QScrollBar::sub-line{background:transparent;} +QListWidget QScrollBar::add-line{background:transparent;} + +/* QTableWidget */ +QTableWidget { + border-left: none; + border-right: none; + border-top: none; + border-bottom: 1px solid var(--Color_Border); + + text-align: center; + color: var(--Color_DefaultText); + background: var(--Color_DefaultHover); + padding:5px; + +} + +QTableWidget::item{ + margin-top: 5px; + background: var(--Color_Default); +} +QTableWidget::item:selected{ + color: var(--Color_PrimaryText); + background: var(--Color_Primary); +} + + +QTableWidget QHeaderView::section { + color: var(--Color_DefaultText); + background: var(--Color_DefaultHover); + text-align: center; + height: var(--Size_HeaderHeight); + border: none; + font-size: var(--Font_Size_TableHeader); +} + +QTableWidget QScrollBar::vertical{ + background:transparent; + width: 4px; + border-radius:6px; +} +QTableWidget QScrollBar::handle{ + background: lightgray; + border-radius:6px; +} +QTableWidget QScrollBar::handle:hover{background:gray;} +QTableWidget QScrollBar::sub-line{background:transparent;} +QTableWidget QScrollBar::add-line{background:transparent;} + +/* QProgressBar */ +QProgressBar { + border:none; + background:rgb(230, 230, 230); + border-radius:0px; + text-align:center; + color:gray; +} +QProgressBar::chunk { + background:rgb(71, 137, 250); + border-radius:0px; +} + +/* QScrollArea */ +QScrollArea { + border: none; +} \ No newline at end of file diff --git a/TIDALDL-PY/tidal_gui/style.py b/TIDALDL-PY/tidal_gui/style.py new file mode 100644 index 0000000..50475d2 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/style.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +''' +@File : enum.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +''' + +from enum import Enum + + +class ButtonStyle(Enum): + Default = 0, + Primary = 1, + Success = 2, + Danger = 3, + Warning = 4, + Info = 5, + + CloseWindow = 6, + MaxWindow = 7, + MinWindow = 8, + + SearchIcon = 9, + TaskIcon = 10, + SettingsIcon = 11, + AboutIcon = 12, + + SoftwareIcon = 13 + + PrePage = 14 + NextPage = 15 + + TaskRetry = 16, + TaskCancel = 17, + TaskDelete = 18, + TaskOpen = 19, + TaskExpand = 20, + + +class LabelStyle(Enum): + Default = 0, + PageTitle = 1, + PageSubTitle = 2, + HugeTitle = 3, + LogoBottom = 4, + SearchErr = 5, + Icon = 6, + Bold = 7, + + +class ThemeStyle(Enum): + Default = 0, + Dark = 1, + + +class ListWidgetStyle(Enum): + Default = 0, + TaskTab = 1, + DownloadItems = 2, diff --git a/TIDALDL-PY/tidal_gui/theme.py b/TIDALDL-PY/tidal_gui/theme.py new file mode 100644 index 0000000..0aa09b2 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/theme.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : qssParse.py +@Date : 2021/05/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" + +import os +import aigpy +from tidal_gui.style import ThemeStyle + +_RESOURCE_PATH = './resource' +if os.path.isdir(_RESOURCE_PATH): + _RESOURCE_PATH = os.path.abspath(_RESOURCE_PATH).replace('\\', '/') +else: + _RESOURCE_PATH = aigpy.path.getDirName(__file__).replace('\\', '/') + "resource" + + + +def __getParam__(line: str): + key = aigpy.string.getSub(line, "--", ":") + value = aigpy.string.getSub(line, ":", ";") + return key, value + + +def __parseParamsList__(content: str) -> dict: + globalStr = aigpy.string.getSub(content, ":root", "}") + lines = globalStr.split("\n") + + array = {} + for line in lines: + key, value = __getParam__(line) + if key == "" or value == "": + continue + array[key] = value + return array + + +def __parseQss__(content: str, params: dict) -> str: + content = aigpy.string.getSub(content, "/* #QSS_START */") + for key in params: + content = content.replace("var(--" + key + ")", params[key]) + + content = content.replace("$RESOURCE_PATH$", _RESOURCE_PATH) + return content + + +def __getQss__(filePath: str) -> str: + content = aigpy.file.getContent(filePath) + params = __parseParamsList__(content) + qss = __parseQss__(content, params) + return qss + +def getResourcePath(): + return _RESOURCE_PATH + +def getThemeQssContent(style: ThemeStyle = ThemeStyle.Default): + name = "theme" + style.name + ".qss" + return __getQss__(_RESOURCE_PATH + '/' + name) diff --git a/TIDALDL-PY/tidal_gui/tidalImp.py b/TIDALDL-PY/tidal_gui/tidalImp.py new file mode 100644 index 0000000..b706f04 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/tidalImp.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : tidalImp.py +@Date : 2021/9/2 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import time + +import requests +import tidal_dl.model +import tidal_dl.enums +from aigpy.stringHelper import isNull +from tidal_dl import TokenSettings, TOKEN, TidalAPI, CONF +from tidal_dl.util import getAlbumPath, getPlaylistPath, getTrackPath + + +class TidalImp(TidalAPI): + def __init__(self): + super(TidalImp, self).__init__() + + def loginByConfig(self): + if isNull(TOKEN.accessToken): + return False + + msg, check = self.verifyAccessToken(TOKEN.accessToken) + if check: + self.key.countryCode = TOKEN.countryCode + self.key.userId = TOKEN.userid + self.key.accessToken = TOKEN.accessToken + return True + + msg, check = self.refreshAccessToken(TOKEN.refreshToken) + if check: + TOKEN.userid = self.key.userId + TOKEN.countryCode = self.key.countryCode + TOKEN.accessToken = self.key.accessToken + TOKEN.expiresAfter = time.time() + int(self.key.expiresIn) + TokenSettings.save(TOKEN) + return True + else: + tmp = TokenSettings() # clears saved tokens + TokenSettings.save(tmp) + return False + + def loginByWeb(self): + start = time.time() + elapsed = 0 + while elapsed < self.key.authCheckTimeout: + elapsed = time.time() - start + msg, check = self.checkAuthStatus() + if not check: + if msg == "pending": + time.sleep(self.key.authCheckInterval + 1) + continue + return False + if check: + TOKEN.userid = self.key.userId + TOKEN.countryCode = self.key.countryCode + TOKEN.accessToken = self.key.accessToken + TOKEN.refreshToken = self.key.refreshToken + TOKEN.expiresAfter = time.time() + int(self.key.expiresIn) + TokenSettings.save(TOKEN) + return True + return False + + @staticmethod + def getArtistsNames(artists): # : list[tidal_dl.model.Artist] + ret = [] + for item in artists: + ret.append(item.name) + return ','.join(ret) + + @staticmethod + def getDurationString(seconds: int): + m, s = divmod(seconds, 60) + h, m = divmod(m, 60) + return "%02d:%02d:%02d" % (h, m, s) + + def getCoverData(self, sid, width="320", height="320"): + url = self.getCoverUrl(sid, width, height) + try: + respond = requests.get(url) + return respond.content + except: + return '' + + @staticmethod + def getAudioQualityList(): + return map(lambda quality: quality.name, tidal_dl.enums.AudioQuality) + + @staticmethod + def getVideoQualityList(): + return map(lambda quality: quality.name, tidal_dl.enums.VideoQuality) + + def getBasePath(self, model): + if isinstance(model, tidal_dl.model.Album): + return getAlbumPath(CONF, model) + if isinstance(model, tidal_dl.model.Playlist): + return getPlaylistPath(CONF, model) + if isinstance(model, tidal_dl.model.Track): + return getAlbumPath(CONF, model.album) + if isinstance(model, tidal_dl.model.Video): + return CONF.downloadPath + '/Video/' + return './' + + def getConfig(self): + return CONF + + # def getTackPath(self, basePath, track, stream, album=None, playlist=None): + # # number + # number = __getIndexStr__(track.trackNumber) + # if playlist is not None and CONF.usePlaylistFolder: + # number = __getIndexStr__(track.trackNumberOnPlaylist) + # # artist + # artist = aigpy.path.replaceLimitChar(__getArtistsString__(track.artists), '-') + # # title + # title = track.title + # if not aigpy.string.isNull(track.version): + # title += ' (' + track.version + ')' + # title = aigpy.path.replaceLimitChar(title, '-') + # # get explicit + # explicit = "(Explicit)" if CONF.addExplicitTag and track.explicit else '' + # # album and addyear + # albumname = aigpy.path.replaceLimitChar(album.title, '-') + # year = "" + # if album.releaseDate is not None: + # year = aigpy.string.getSubOnlyEnd(album.releaseDate, '-') + # # extension + # extension = __getExtension__(stream.url) + # retpath = CONF.trackFileFormat + # if retpath is None or len(retpath) <= 0: + # retpath = Settings.getDefaultTrackFileFormat() + # retpath = retpath.replace(R"{TrackNumber}", number) + # retpath = retpath.replace(R"{ArtistName}", artist.strip()) + # retpath = retpath.replace(R"{TrackTitle}", title) + # retpath = retpath.replace(R"{ExplicitFlag}", explicit) + # retpath = retpath.replace(R"{AlbumYear}", year) + # retpath = retpath.replace(R"{AlbumTitle}", albumname.strip()) + # retpath = retpath.strip() + # return basePath + retpath + extension + + # def getVideoPath(self, basePath, video): + # # hyphen + # hyphen = ' - ' if CONF.addHyphen else ' ' + # # get number + # number = '' + # if CONF.useTrackNumber: + # number = __getIndexStr__(video.trackNumber) + hyphen + # # get artist + # artist = '' + # if CONF.artistBeforeTitle: + # artist = aigpy.path.replaceLimitChar(__getArtistsString__(video.artists), '-') + hyphen + # # get explicit + # explicit = "(Explicit)" if CONF.addExplicitTag and video.explicit else '' + # # title + # title = aigpy.path.replaceLimitChar(video.title, '-') + # # extension + # extension = ".mp4" + # return basePath + number + artist.strip() + title + explicit + extension + +tidalImp = TidalImp() diff --git a/TIDALDL-PY/tidal_gui/view/__init__.py b/TIDALDL-PY/tidal_gui/view/__init__.py new file mode 100644 index 0000000..33becbf --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/__init__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : __init__.py +@Date : 2021/05/11 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" diff --git a/TIDALDL-PY/tidal_gui/view/aboutView.py b/TIDALDL-PY/tidal_gui/view/aboutView.py new file mode 100644 index 0000000..ec8b3f4 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/aboutView.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : aboutView.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QGridLayout, QHBoxLayout, QLabel + +from tidal_gui.control.label import Label +from tidal_gui.control.pushButton import PushButton +from tidal_gui.style import LabelStyle, ButtonStyle +from tidal_gui.theme import getResourcePath + + +class AboutView(QWidget): + def __init__(self): + super(AboutView, self).__init__() + self.__initView__() + + def __initView__(self): + grid = QGridLayout(self) + grid.addWidget(self.__initLogo__(), 0, 0, Qt.AlignLeft) + grid.addLayout(self.__initContent__(), 0, 1) + + def __initLogo__(self): + path = getResourcePath() + "/svg/V.svg" + self._logo = QLabel() + self._logo.setPixmap(QPixmap(path)) + return self._logo + + def __initButton__(self): + path = getResourcePath() + "/svg/" + + self._feedbackBtn = PushButton('Feedback', ButtonStyle.Default, iconUrl=path + 'github.svg') + self._buymeacoffeeBtn = PushButton('Buymeacoffee', ButtonStyle.Info, iconUrl=path + 'buymeacoffee.svg') + self._paypalBtn = PushButton('Paypal', ButtonStyle.Primary, iconUrl=path + 'paypal.svg') + + layout = QHBoxLayout() + layout.addWidget(self._feedbackBtn) + layout.addWidget(self._buymeacoffeeBtn) + layout.addWidget(self._paypalBtn) + return layout + + def __initContent__(self): + self._titleLabel = Label('', LabelStyle.HugeTitle) + self._authorLabel = Label('') + self._versionLabel = Label('') + self._lastVersionLabel = Label('') + + layout = QVBoxLayout() + layout.addWidget(self._titleLabel) + layout.addWidget(self._authorLabel) + layout.addWidget(self._versionLabel) + layout.addWidget(self._lastVersionLabel) + layout.addLayout(self.__initButton__()) + return layout + + def setTitle(self, text: str): + self._titleLabel.setText(text) + + def setAuthor(self, text: str): + self._authorLabel.setText('MADE WITH ♥ BY ' + text) + + def setVersion(self, text: str): + self._versionLabel.setText('VERSION ' + text) + + def setLastVersion(self, text: str): + self._lastVersionLabel.setText('LAST-VERSION ' + text) diff --git a/TIDALDL-PY/tidal_gui/view/downloadItemView.py b/TIDALDL-PY/tidal_gui/view/downloadItemView.py new file mode 100644 index 0000000..3b492df --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/downloadItemView.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : downloadItemView.py +@Date : 2021/10/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QWidget, QHBoxLayout, QTableWidget, QVBoxLayout, QGridLayout, QProgressBar +from PyQt5.QtCore import Qt + +from tidal_gui.control.label import Label +from tidal_gui.control.layout import createHBoxLayout, createVBoxLayout +from tidal_gui.control.pushButton import PushButton +from tidal_gui.style import LabelStyle, ButtonStyle + + +class DownloadItemView(QWidget): + def __init__(self): + super(DownloadItemView, self).__init__() + self.__initView__() + + def __initView__(self): + self._indexLabel = Label('1') + self._titleLabel = Label('title', LabelStyle.Bold) + self._ownLabel = Label('own') + self._ownLabel.setMaximumWidth(200) + self._actionLabel = Label('') + self._actionLabel.setFixedWidth(80) + self._errLabel = Label('') + self._sizeLabel = Label('/') + self._speedLabel = Label('') + + self._progress = QProgressBar() + self._progress.setTextVisible(False) + self._progress.setFixedHeight(3) + self._progress.setFixedWidth(300) + + grid = QGridLayout(self) + grid.addWidget(self._indexLabel, 0, 0, Qt.AlignLeft | Qt.AlignVCenter) + grid.addWidget(self._titleLabel, 0, 1, Qt.AlignLeft | Qt.AlignVCenter) + grid.addWidget(self._ownLabel, 0, 2, Qt.AlignRight | Qt.AlignVCenter) + grid.addWidget(self._progress, 0, 3, Qt.AlignRight | Qt.AlignVCenter) + grid.addWidget(self._actionLabel, 0, 4, Qt.AlignRight | Qt.AlignVCenter) + grid.addWidget(self._errLabel, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) + + grid.setColumnStretch(1, 1) + + layout = QHBoxLayout() + layout.setSpacing(30) + layout.addWidget(self._sizeLabel) + layout.addWidget(self._speedLabel) + + grid.addLayout(layout, 1, 3, Qt.AlignLeft | Qt.AlignVCenter) + + def setLabel(self, index, title, own): + self._indexLabel.setText(str(index)) + self._titleLabel.setText(title) + self._ownLabel.setText(own) + + def setErrmsg(self, msg): + self._errLabel.setText(msg) + + def setAction(self, msg): + self._actionLabel.setText(msg) + + def setProgress(self, value): + self._progress.setValue(value) + pass + + def setSize(self, curSize: str, totalSize: str): + self._sizeLabel.setText(f'{curSize}/{totalSize}') + + def setSpeed(self, speed: str): + self._speedLabel.setText(speed) diff --git a/TIDALDL-PY/tidal_gui/view/loginView.py b/TIDALDL-PY/tidal_gui/view/loginView.py new file mode 100644 index 0000000..182d317 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/loginView.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : loginView.py +@Date : 2021/04/30 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import * + +from tidal_gui.control.checkBox import CheckBox +from tidal_gui.control.framelessWidget import FramelessWidget +from tidal_gui.control.label import Label +from tidal_gui.control.layout import createHBoxLayout +from tidal_gui.control.lineEdit import LineEdit +from tidal_gui.control.pushButton import PushButton +from tidal_gui.style import ButtonStyle, LabelStyle +from tidal_gui.theme import getResourcePath + + +class LoginView(FramelessWidget): + viewWidth = 650 + viewHeight = 400 + logoWidth = 300 + + def __init__(self): + super(LoginView, self).__init__() + self.setFixedSize(self.viewWidth, self.viewHeight) + self.__initView__() + self.setWindowButton(True, False, False) + + def __initAccountTab__(self): + self._deviceCodeEdit = LineEdit() + self._confirmBtn = PushButton("LOGIN", ButtonStyle.Primary) + + grid = QGridLayout() + grid.setSpacing(15) + grid.setRowStretch(0, 1) + + grid.addLayout(createHBoxLayout([Label("DeviceCode"), self._deviceCodeEdit]), 1, 0, 1, 2) + grid.setRowStretch(3, 1) + grid.addWidget(self._confirmBtn, 5, 0, 1, 2) + grid.setRowStretch(6, 1) + + widget = QWidget() + widget.setLayout(grid) + return widget + + def __initProxyTab__(self): + self._enableProxyCheck = CheckBox('') + self._proxyHostEdit = LineEdit() + self._proxyPortEdit = LineEdit() + self._proxyUserEdit = LineEdit() + self._proxyPwdEdit = LineEdit() + + grid = QGridLayout() + grid.setSpacing(8) + grid.setRowStretch(0, 1) + grid.addWidget(Label("HttpProxy"), 1, 0) + grid.addWidget(self._enableProxyCheck, 1, 1) + grid.addWidget(Label("Host"), 2, 0) + grid.addWidget(self._proxyHostEdit, 2, 1) + grid.addWidget(Label("Port"), 3, 0) + grid.addWidget(self._proxyPortEdit, 3, 1) + grid.addWidget(Label("UserName"), 4, 0) + grid.addWidget(self._proxyUserEdit, 4, 1) + grid.addWidget(Label("Password"), 5, 0) + grid.addWidget(self._proxyPwdEdit, 5, 1) + grid.setRowStretch(6, 1) + + widget = QWidget() + widget.setLayout(grid) + return widget + + def __initIconWidget__(self): + self._icon = Label('') + self._icon.setStyleSheet("QLabel{background-color:rgb(0,0,0);}") + + self._icon.setPixmap(QPixmap(getResourcePath() + "/svg/V.svg")) + self._icon.setAlignment(Qt.AlignCenter) + + self._iconLabel = Label('', LabelStyle.LogoBottom) + self._iconLabel.setAlignment(Qt.AlignCenter) + self._iconLabel.hide() + + layout = QVBoxLayout() + layout.setSpacing(15) + layout.setContentsMargins(0, 0, 0, 0) + layout.addStretch(1) + layout.addWidget(self._icon) + layout.addWidget(self._iconLabel) + layout.addStretch(1) + + widget = QWidget() + widget.setStyleSheet("QWidget{background-color:rgb(0,0,0);}") + widget.setLayout(layout) + return widget + + def __initView__(self): + iconWidget = self.__initIconWidget__() + + self._tab = QTabWidget(self) + self._tab.addTab(self.__initAccountTab__(), "LOGIN") + self._tab.addTab(self.__initProxyTab__(), "PROXY") + self._tab.setFixedWidth(self.viewWidth - self.logoWidth - 60) + self._tab.hide() + + grid = self.getGrid() + grid.setSpacing(15) + grid.setContentsMargins(0, 0, 0, 0) + grid.addWidget(iconWidget) + grid.addWidget(self._tab, 0, 1, Qt.AlignCenter) + + self.setValidMoveWidget(iconWidget) + + def showEnterView(self): + self._tab.show() + + def hideEnterView(self): + self._tab.hide() + + def setDeviceCode(self, text): + self._deviceCodeEdit.setText(text) + + def enableHttpProxy(self) -> bool: + return self._enableProxyCheck.isChecked() + + def getProxyInfo(self) -> dict: + infos = {'host': self._proxyHostEdit.text(), 'port': self._proxyPortEdit.text(), + 'username': self._proxyUserEdit.text(), 'password': self._proxyPwdEdit.text()} + return infos + + def connectConfirmButton(self, func): + self._confirmBtn.clicked.connect(func) + + def enableConfirmButton(self, enable): + self._confirmBtn.setEnabled(enable) + + def setMsg(self, text): + if len(text) <= 0: + self._iconLabel.hide() + else: + self._iconLabel.setText(text) + self._iconLabel.show() + + def showErrMessage(self, text: str): + qmb = QMessageBox(self) + qmb.setWindowTitle('Error') + qmb.setIcon(QMessageBox.Warning) + qmb.setText(text) + qmb.addButton(QPushButton('OK', qmb), QMessageBox.YesRole) + qmb.open() diff --git a/TIDALDL-PY/tidal_gui/view/mainView.py b/TIDALDL-PY/tidal_gui/view/mainView.py new file mode 100644 index 0000000..0c6e9c4 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/mainView.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : main.py +@Date : 2021/05/11 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from enum import IntEnum + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QVBoxLayout, QWidget, QGridLayout + +from tidal_gui.control.framelessWidget import FramelessWidget +from tidal_gui.control.pushButton import PushButton +from tidal_gui.style import ButtonStyle + + +class PageType(IntEnum): + Search = 0, + Task = 1, + Settings = 2, + About = 3, + Max = 4, + + +class MainView(FramelessWidget): + viewWidth = 1200 + viewHeight = 700 + + def __init__(self): + super(MainView, self).__init__() + self.setMinimumHeight(self.viewHeight) + self.setMinimumWidth(self.viewWidth) + self.__initPages__() + self.__initView__() + self.setWindowButton(True, True, True) + + def __initPages__(self): + self._pages = [] + for index in range(0, PageType.Max): + self._pages.append(None) + + def __initView__(self): + leftTab = self.__initLeftTab__() + content = self.__initContent__() + moveWgt = self.__initMoveHead__() + + grid = self.getGrid() + grid.addWidget(leftTab, 0, 0, 2, 1, Qt.AlignLeft) + grid.addWidget(moveWgt, 0, 1, Qt.AlignTop) + grid.addLayout(content, 1, 1) + + self.setValidMoveWidget(moveWgt) + + def __initLeftTab__(self): + self._icon = PushButton("", ButtonStyle.SoftwareIcon) + self._searchBtn = PushButton("", ButtonStyle.SearchIcon) + self._taskBtn = PushButton("", ButtonStyle.TaskIcon) + self._settingsBtn = PushButton("", ButtonStyle.SettingsIcon) + self._aboutBtn = PushButton("", ButtonStyle.AboutIcon) + + self._searchBtn.clicked.connect(lambda: self.showPage(PageType.Search)) + self._taskBtn.clicked.connect(lambda: self.showPage(PageType.Task)) + self._settingsBtn.clicked.connect(lambda: self.showPage(PageType.Settings)) + self._aboutBtn.clicked.connect(lambda: self.showPage(PageType.About)) + + layout = QVBoxLayout() + layout.setSpacing(15) + layout.setContentsMargins(15, 45, 15, 20) + layout.addWidget(self._icon) + layout.addWidget(self._searchBtn) + layout.addWidget(self._taskBtn) + layout.addStretch(1) + layout.addWidget(self._settingsBtn) + layout.addWidget(self._aboutBtn) + + widget = QWidget() + widget.setLayout(layout) + widget.setObjectName("MainViewLeftWidget") + return widget + + def __initMoveHead__(self) -> QWidget: + self._moveWidget = QWidget() + self._moveWidget.setFixedHeight(30) + self._moveWidget.setObjectName("BaseWidget") + return self._moveWidget + + def __initContent__(self) -> QGridLayout: + self._searchView = None + self._settingsView = None + self._aboutView = None + self._taskView = None + self._contentLayout = QGridLayout() + self._contentLayout.setContentsMargins(10, 0, 10, 10) + return self._contentLayout + + def showPage(self, pageType: PageType = PageType.Search): + for index in range(0, PageType.Max): + if index == pageType: + self._pages[index].show() + else: + self._pages[index].hide() + + def __setContentPage__(self, view, pageType: PageType): + self._pages[pageType] = view + self._pages[pageType].hide() + self._contentLayout.addWidget(view, 0, 0) + + def setSearchView(self, view): + self.__setContentPage__(view, PageType.Search) + + def setTaskView(self, view): + self.__setContentPage__(view, PageType.Task) + + def setSettingsView(self, view): + self.__setContentPage__(view, PageType.Settings) + + def setAboutView(self, view: QWidget): + # self.__setContentPage__(view, PageType.About) + self._pages[PageType.About] = view + self._pages[PageType.About].hide() diff --git a/TIDALDL-PY/tidal_gui/view/searchView.py b/TIDALDL-PY/tidal_gui/view/searchView.py new file mode 100644 index 0000000..64a12a1 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/searchView.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : searchView.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import threading +import tidal_dl.model +from PyQt5.QtCore import Qt, QUrl +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QTabWidget +from tidal_dl import Type + +from tidal_gui.control.comboBox import ComboBox +from tidal_gui.control.label import Label +from tidal_gui.control.layout import createHBoxLayout, createVBoxLayout +from tidal_gui.control.lineEdit import LineEdit +from tidal_gui.control.pushButton import PushButton +from tidal_gui.control.tableView import TableView +from tidal_gui.control.tableWidget import TableWidget +from tidal_gui.style import ButtonStyle, LabelStyle +from tidal_gui.theme import getResourcePath +from tidal_gui.tidalImp import tidalImp + + +class SearchView(QWidget): + def __init__(self): + super(SearchView, self).__init__() + self._rowCount = 20 + self._table = {} + self._lock = threading.Lock() + self.__initView__() + self._searchEdit.setFocus() + + def __initView__(self): + grid = QVBoxLayout(self) + grid.addLayout(self.__initHead__(), Qt.AlignTop) + grid.addLayout(self.__initContent__()) + grid.addLayout(self.__initTail__(), Qt.AlignBottom) + + def __initHead__(self): + self._searchEdit = LineEdit(iconUrl=getResourcePath() + "/svg/search2.svg") + self._searchErrLabel = Label('', LabelStyle.SearchErr) + self._searchErrLabel.hide() + layout = QVBoxLayout() + layout.addWidget(self._searchEdit) + layout.addWidget(self._searchErrLabel) + layout.setContentsMargins(0, 0, 0, 5) + return layout + + def __initTail__(self): + self._trackQualityComboBox = ComboBox([], 150) + self._videoQualityComboBox = ComboBox([], 150) + + self._prePageBtn = PushButton('', ButtonStyle.PrePage) + self._nextPageBtn = PushButton('', ButtonStyle.NextPage) + + self._pageIndexEdit = LineEdit('') + self._pageIndexEdit.setAlignment(Qt.AlignCenter) + self._pageIndexEdit.setEnabled(False) + + self._downloadBtn = PushButton('Download', ButtonStyle.Primary) + + layout = QHBoxLayout() + layout.addWidget(Label('Track:')) + layout.addWidget(self._trackQualityComboBox) + layout.addWidget(Label('Video:')) + layout.addWidget(self._videoQualityComboBox) + layout.addStretch(1) + layout.addWidget(self._prePageBtn) + layout.addWidget(self._pageIndexEdit) + layout.addWidget(self._nextPageBtn) + layout.addWidget(self._downloadBtn) + return layout + + def __initContent__(self): + self._tabWidget = QTabWidget(self) + self._tabWidget.addTab(self.__initTable__(Type.Album), "ALBUM") + self._tabWidget.addTab(self.__initTable__(Type.Track), "TRACK") + self._tabWidget.addTab(self.__initTable__(Type.Video), "VIDEO") + self._tabWidget.addTab(self.__initTable__(Type.Playlist), "PLAYLIST") + + layout = QVBoxLayout() + layout.addWidget(self._tabWidget) + return layout + + def __initTable__(self, stype: Type): + columnHeads = [] + columnWidths = [] + if stype == Type.Album: + columnHeads = ['#', ' ', ' ', 'Title', 'Artists', 'Release', 'Duration'] + columnWidths = [50, 60, 60, 400, 200, 200] + elif stype == Type.Track: + columnHeads = ['#', ' ', 'Title', 'Album', 'Artists', 'Duration'] + columnWidths = [50, 60, 400, 200, 200] + elif stype == Type.Video: + columnHeads = ['#', ' ', ' ', 'Title', 'Artists', 'Duration'] + columnWidths = [50, 60, 60, 400, 200, 200] + elif stype == Type.Playlist: + columnHeads = ['#', ' ', 'Title', 'Artist', 'Duration'] + columnWidths = [50, 60, 400, 200, 200] + + self._table[stype] = TableWidget(columnHeads, self._rowCount) + for index, width in enumerate(columnWidths): + self._table[stype].setColumnWidth(index, width) + + return self._table[stype] + + def __clearTableRowItems__(self, stype: Type, fromIndex: int): + endIndex = self._rowCount - 1 + columnNum = self._table[stype].columnCount() + while fromIndex <= endIndex: + for colIdx in range(0, columnNum): + self._table[stype].addItem(fromIndex, colIdx, '') + fromIndex += 1 + + def setTableItems(self, stype: Type, indexOffset: int, result: tidal_dl.model.SearchResult): + if stype == Type.Album: + items = result.albums.items + datas = [] + for index, item in enumerate(items): + rowData = [str(index + 1 + indexOffset), + QUrl(tidalImp.getCoverUrl(item.cover)), + tidalImp.getFlag(item, Type.Album, True), + item.title, + item.artists[0].name, + str(item.releaseDate), + tidalImp.getDurationString(item.duration)] + datas.append(rowData) + + elif stype == Type.Track: + items = result.tracks.items + datas = [] + for index, item in enumerate(items): + rowData = [str(index + 1 + indexOffset), + tidalImp.getFlag(item, Type.Track, True), + item.title, + item.album.title, + item.artists[0].name, + tidalImp.getDurationString(item.duration)] + datas.append(rowData) + + elif stype == Type.Video: + items = result.videos.items + datas = [] + for index, item in enumerate(items): + rowData = [str(index + 1 + indexOffset), + QUrl(tidalImp.getCoverUrl(item.imageID)), + tidalImp.getFlag(item, Type.Video, True), + item.title, + item.artists[0].name, + tidalImp.getDurationString(item.duration)] + datas.append(rowData) + + elif stype == Type.Playlist: + items = result.playlists.items + datas = [] + for index, item in enumerate(items): + rowData = [str(index + 1 + indexOffset), + QUrl(tidalImp.getCoverUrl(item.squareImage)), + item.title, + '', + tidalImp.getDurationString(item.duration)] + datas.append(rowData) + + for index, rowData in enumerate(datas): + for colIdx, obj in enumerate(rowData): + self._table[stype].addItem(index, colIdx, obj) + + self.__clearTableRowItems__(stype, len(items)) + self._table[stype].viewport().update() + + def getSearchText(self): + return self._searchEdit.text() + + def setSearchErrmsg(self, text: str): + self._searchErrLabel.setText(text) + if text != '': + self._searchErrLabel.show() + else: + self._searchErrLabel.hide() + + def setPageIndex(self, index, sum): + self._pageIndexEdit.setText(str(index) + '/' + str(sum)) + + def getPageIndex(self) -> (int, int): + nums = self._pageIndexEdit.text().split('/') + return int(nums[0]), int(nums[1]) + + def getSelectedTabIndex(self): + return self._tabWidget.currentIndex() + + def getSelectedTableIndex(self, stype: Type): + array = self._table[stype].selectedIndexes() + if len(array) <= 0: + return -1 + return array[0].row() + + def setTrackQualityItems(self, items: list): + self._trackQualityComboBox.setItems(items) + + def setVideoQualityItems(self, items: list): + self._videoQualityComboBox.setItems(items) + + def getTrackQualityText(self): + return self._trackQualityComboBox.currentText() + + def getVideoQualityText(self): + return self._videoQualityComboBox.currentText() + + def connectButton(self, name: str, func): + if name == 'search': + self._searchEdit.returnPressed.connect(func) + elif name == 'prePage': + self._prePageBtn.clicked.connect(func) + elif name == 'nextPage': + self._nextPageBtn.clicked.connect(func) + elif name == 'download': + self._downloadBtn.clicked.connect(func) + + def connectTab(self, func): + self._tabWidget.currentChanged.connect(func) diff --git a/TIDALDL-PY/tidal_gui/view/settingsView.py b/TIDALDL-PY/tidal_gui/view/settingsView.py new file mode 100644 index 0000000..4b43ba3 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/settingsView.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : settingsView.py +@Date : 2021/05/11 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QGridLayout + +from tidal_gui.control.checkBox import CheckBox +from tidal_gui.control.comboBox import ComboBox +from tidal_gui.control.label import Label +from tidal_gui.control.line import Line +from tidal_gui.control.lineEdit import LineEdit +from tidal_gui.control.pushButton import PushButton +from tidal_gui.style import LabelStyle, ButtonStyle, ThemeStyle + + +class SettingsView(QWidget): + def __init__(self): + super(SettingsView, self).__init__() + self.__initView__() + + def __initView__(self): + grid = QVBoxLayout(self) + grid.addLayout(self.__initHeader__()) + grid.addLayout(self.__initContent__()) + grid.addLayout(self.__initToolButton__()) + + def __initHeader__(self): + self._header = Label("SETTINGS", LabelStyle.PageTitle) + layout = QVBoxLayout() + layout.addWidget(self._header) + layout.addWidget(Line('H')) + layout.setSpacing(10) + layout.setContentsMargins(0, 0, 0, 10) + return layout + + def __initToolButton__(self) -> QHBoxLayout: + self._logoutBtn = PushButton('Logout', ButtonStyle.Danger, 100) + self._cancelBtn = PushButton('Cancel', ButtonStyle.Default, 100) + self._confirmBtn = PushButton('OK', ButtonStyle.Primary, 100) + + layout = QHBoxLayout() + layout.addWidget(self._logoutBtn) + layout.addStretch(1) + layout.addWidget(self._cancelBtn) + layout.addWidget(self._confirmBtn) + return layout + + def __addContent__(self, control, desc=''): + rowIdx = self._contentLayout.rowCount() + if desc != '': + self._contentLayout.addWidget(Label(desc), rowIdx, 0, Qt.AlignRight) + self._contentLayout.addWidget(control, rowIdx, 1) + + def __initContent__(self): + self._contentLayout = QGridLayout() + self._contentLayout.setSpacing(15) + + self._pathEdit = LineEdit() + self._threadNumComboBox = ComboBox([1, 5, 10, 20]) + self._searchNumComboBox = ComboBox([10, 20, 30, 40, 50]) + self.__addContent__(self._pathEdit, 'Path:') + self.__addContent__(self._threadNumComboBox, 'ThreadNum:') + self.__addContent__(self._searchNumComboBox, 'SearchNum:') + + self._contentLayout.setRowStretch(self._contentLayout.rowCount(), 1) + + self._albumFolderFormatEdit = LineEdit() + self._trackFileFormatEdit = LineEdit() + self._videoFileFormatEdit = LineEdit() + self.__addContent__(self._albumFolderFormatEdit, 'AlbumFolderFormat:') + self.__addContent__(self._trackFileFormatEdit, 'TrackFileFormat:') + self.__addContent__(self._videoFileFormatEdit, 'VideoFileFormat:') + + self._saveCoverCheck = CheckBox('Download album cover') + self._skipExistCheck = CheckBox('Skip exist file when downloading') + self._convertMp4ToM4a = CheckBox('Convert mp4 to m4a') + self.__addContent__(self._saveCoverCheck) + self.__addContent__(self._skipExistCheck) + self.__addContent__(self._convertMp4ToM4a) + + self._contentLayout.setRowStretch(self._contentLayout.rowCount(), 1) + + self._themeComboBox = ComboBox(list(map(lambda x: x.name, ThemeStyle))) + self._languageComboBox = ComboBox(['Default']) + self.__addContent__(self._themeComboBox, 'Theme:') + self.__addContent__(self._languageComboBox, 'Language:') + + self._contentLayout.setRowStretch(self._contentLayout.rowCount(), 10) + return self._contentLayout + + def connectButton(self, name: str, func): + if name == "Logout": + self._logoutBtn.clicked.connect(func) + elif name == "Cancel": + self._cancelBtn.clicked.connect(func) + elif name == "OK": + self._confirmBtn.clicked.connect(func) diff --git a/TIDALDL-PY/tidal_gui/view/taskItemView.py b/TIDALDL-PY/tidal_gui/view/taskItemView.py new file mode 100644 index 0000000..96cba3b --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/taskItemView.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : taskItemView.py +@Date : 2021/9/14 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QWidget, QHBoxLayout, QTableWidget, QVBoxLayout, QListView, QGridLayout +from PyQt5.QtCore import Qt, QPoint + +from tidal_gui.control.label import Label +from tidal_gui.control.layout import createHBoxLayout, createVBoxLayout +from tidal_gui.control.pushButton import PushButton +from tidal_gui.control.listWidget import ListWidget +from tidal_gui.style import LabelStyle, ButtonStyle, ListWidgetStyle + + +class TaskItemView(QWidget): + def __init__(self): + super().__init__() + self.__initView__() + self.setObjectName('TaskItemView') + self.setAttribute(Qt.WA_StyledBackground) + + def __initView__(self): + layout = QVBoxLayout() + layout.addLayout(self.__initHead__(), Qt.AlignTop) + layout.addWidget(self.__initList__(), Qt.AlignTop) + + self.setLayout(layout) + + def __initHead__(self): + self._btnRetry = PushButton('', ButtonStyle.TaskRetry) + self._btnCancel = PushButton('', ButtonStyle.TaskCancel) + self._btnDelete = PushButton('', ButtonStyle.TaskDelete) + self._btnOpen = PushButton('', ButtonStyle.TaskOpen) + self._btnExpand = PushButton('', ButtonStyle.TaskExpand) + self._btnExpand.clicked.connect(self.__expandClick__) + btnLayout = createHBoxLayout([self._btnRetry, self._btnCancel, + self._btnDelete, self._btnOpen, + self._btnExpand]) + + self._titleLabel = Label('', LabelStyle.PageTitle) + self._descLabel = Label() + self._errLabel = Label() + self._errLabel.hide() + labelLayout = createVBoxLayout([self._titleLabel, self._descLabel, self._errLabel]) + + self._picLabel = Label('', LabelStyle.Icon) + self._picLabel.setMinimumHeight(64) + headLayout = QHBoxLayout() + headLayout.addWidget(self._picLabel) + headLayout.addLayout(labelLayout) + headLayout.addStretch(1) + headLayout.addLayout(btnLayout) + + return headLayout + + def __initList__(self): + self._list = ListWidget(ListWidgetStyle.DownloadItems) + self._list.setResizeMode(QListView.Adjust) + return self._list + + def setLabel(self, title, desc): + self._titleLabel.setText(title) + self._descLabel.setText(desc) + + def setErrmsg(self, msg): + self._errLabel.setText(msg) + + def setPic(self, data): + pic = QPixmap() + pic.loadFromData(data) + self._picLabel.setPixmap(pic.scaled(64, 64)) + + def addListItem(self, view): + self._list.addWidgetItem(view) + + def __expandClick__(self): + if self._list.isHidden(): + self._list.show() + else: + self._list.hide() + + def connectButton(self, name: str, func): + if name == 'retry': + self._btnRetry.clicked.connect(func) + elif name == 'cancel': + self._btnCancel.clicked.connect(func) + elif name == 'delete': + self._btnDelete.clicked.connect(func) + elif name == 'open': + self._btnOpen.clicked.connect(func) + diff --git a/TIDALDL-PY/tidal_gui/view/taskView.py b/TIDALDL-PY/tidal_gui/view/taskView.py new file mode 100644 index 0000000..6fc9eb6 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/view/taskView.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : taskView.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from enum import Enum +from PyQt5.QtCore import Qt, QSize +from PyQt5.QtWidgets import QWidget, QGridLayout, QListWidgetItem, QListView + +from tidal_gui.control.label import Label +from tidal_gui.control.line import Line +from tidal_gui.control.listWidget import ListWidget +from tidal_gui.control.scrollWidget import ScrollWidget +from tidal_gui.style import LabelStyle, ListWidgetStyle +from tidal_gui.theme import getResourcePath +from tidal_gui.view.taskItemView import TaskItemView + + +class TaskListType(Enum): + Download = 0, + Complete = 1, + Error = 2, + + +class TaskView(QWidget): + def __init__(self): + super(TaskView, self).__init__() + self._listMap = {} + self._pageMap = {} + + for item in map(lambda typeItem: typeItem.name, TaskListType): + self._listMap[item] = ScrollWidget() + self._pageMap[item] = QWidget() + + self.__initView__() + self._listTab.setCurrentRow(0) + self._pageMap[TaskListType.Download.name].show() + + def __initView__(self): + grid = QGridLayout(self) + grid.addLayout(self.__initLefTab__(), 0, 0, Qt.AlignLeft) + for item in map(lambda typeItem: typeItem.name, TaskListType): + grid.addWidget(self.__createContent__(item), 0, 1) + + def __initLefTab__(self): + self._listTab = ListWidget(ListWidgetStyle.TaskTab) + self._listTab.setIconSize(QSize(20, 20)) + + iconPath = getResourcePath() + "/svg/taskTab/" + self._listTab.addIConTextItem(iconPath + 'download.svg', TaskListType.Download.name) + self._listTab.addIConTextItem(iconPath + 'complete.svg', TaskListType.Complete.name) + self._listTab.addIConTextItem(iconPath + 'error.svg', TaskListType.Error.name) + + self._listTab.itemClicked.connect(self.__tabItemChanged__) + + layout = QGridLayout() + layout.addWidget(Label("TASK LIST", LabelStyle.PageTitle), 0, 0, Qt.AlignLeft) + layout.addWidget(self._listTab, 1, 0, Qt.AlignLeft) + layout.addWidget(Line('V'), 0, 1, 2, 1, Qt.AlignLeft) + return layout + + def __createContent__(self, typeStr: str): + layout = QGridLayout() + layout.setContentsMargins(0, 0, 10, 10) + layout.setSpacing(10) + layout.addWidget(Label(typeStr, LabelStyle.PageSubTitle), 0, 0, Qt.AlignTop) + layout.addWidget(Line('H'), 1, 0, Qt.AlignTop) + layout.addWidget(self._listMap[typeStr], 2, 0) + + self._pageMap[typeStr].setLayout(layout) + self._pageMap[typeStr].hide() + return self._pageMap[typeStr] + + def __tabItemChanged__(self, item: QListWidgetItem): + for name in self._listMap: + if name == item.text(): + self._pageMap[name].show() + else: + self._pageMap[name].hide() + + def addItemView(self, stype: TaskListType, view): + self._listMap[stype.name].addWidgetItem(view) diff --git a/TIDALDL-PY/tidal_gui/viewModel/__init__.py b/TIDALDL-PY/tidal_gui/viewModel/__init__.py new file mode 100644 index 0000000..a4ecc95 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : __init__.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" + diff --git a/TIDALDL-PY/tidal_gui/viewModel/aboutModel.py b/TIDALDL-PY/tidal_gui/viewModel/aboutModel.py new file mode 100644 index 0000000..b258138 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/aboutModel.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : aboutModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from tidal_gui.view.aboutView import AboutView +from tidal_gui.viewModel.viewModel import ViewModel + + +class AboutModel(ViewModel): + def __init__(self): + super(AboutModel, self).__init__() + self.view = AboutView() + self.view.setTitle('TIDAL-GUI') + self.view.setAuthor('Yaronzz') + self.view.setVersion('1.0.0.1') + self.view.setLastVersion('1.0.0.1') diff --git a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py new file mode 100644 index 0000000..7688aab --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : downloadItemModel.py +@Date : 2021/10/08 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" + +import _thread +import time +from enum import Enum +import aigpy.stringHelper +from tidal_dl import Type +from tidal_dl.model import Album, Track, Video, Playlist + +from tidal_gui.tidalImp import tidalImp +from tidal_gui.view.downloadItemView import DownloadItemView +from tidal_gui.viewModel.viewModel import ViewModel + +class DownloadStatus(Enum): + Wait = 0, + Running = 1, + Finish = 2, + Error = 3, + Cancel = 4, + +_endStatus_ = [DownloadStatus.Finish, DownloadStatus.Error, DownloadStatus.Cancel] + +class DownloadItemModel(ViewModel): + def __init__(self, index, data, basePath): + super(DownloadItemModel, self).__init__() + self.view = DownloadItemView() + self.data = data + self.basePath = basePath + self.isTrack = isinstance(data, Track) + self.__setStatus__(DownloadStatus.Wait) + + if self.isTrack: + self.__initTrack__(index) + else: + self.__initVideo__(index) + + def __setStatus__(self, status: DownloadStatus, desc: str = ''): + self.status = status + if desc == '': + self.view.setAction(status.name) + else: + self.view.setAction(status.name + '-' + desc) + + def __initTrack__(self, index): + title = self.data.title + own = self.data.album.title + self.view.setLabel(index, title, own) + + def __initVideo__(self, index): + title = self.data.title + own = tidalImp.getArtistsNames(self.data.artists) + self.view.setLabel(index, title, own) + + def isInWait(self): + return self.status == DownloadStatus.Wait + + def stopDownload(self): + if self.status not in _endStatus_: + self.__setStatus__(DownloadStatus.Cancel) + + def retry(self): + self.__setStatus__(DownloadStatus.Wait) + + def download(self): + self.__setStatus__(DownloadStatus.Running) + + # if self.isTrack: + # if not self.__dlTrack__(index): + # return + # else: + # if not self.__dlVideo__(index): + # return + + self.view.setProgress(100) + self.__setStatus__(DownloadStatus.Finish) + + def __dlTrack__(self): + try: + track = self.data + conf = tidalImp.getConfig() + + msg, stream = tidalImp.getStreamUrl(track.id, conf.audioQuality) + if not aigpy.string.isNull(msg) or stream is None: + self.__setStatus__(DownloadStatus.Error) + return False + + tidalImp.getBasePath(model) + path = tidalImp.getTackPath(self.basePath, track, stream) + + # check exist + if conf.checkExist and __isNeedDownload__(path, stream.url) == False: + return True + + tool = aigpy.download.DownloadTool(path + '.part', [stream.url]) + check, err = tool.start(conf.showProgress) + + if not check: + Printf.err("Download failed! " + aigpy.path.getFileName(path) + ' (' + str(err) + ')') + return + # encrypted -> decrypt and remove encrypted file + if aigpy.string.isNull(stream.encryptionKey): + os.replace(path + '.part', path) + else: + key, nonce = decrypt_security_token(stream.encryptionKey) + decrypt_file(path + '.part', path, key, nonce) + os.remove(path + '.part') + + path = __convertToM4a__(path, stream.codec) + + # contributors + msg, contributors = API.getTrackContributors(track.id) + msg, tidalLyrics = API.getLyrics(track.id) + + lyrics = '' if tidalLyrics is None else tidalLyrics.subtitles + if conf.addLyrics and lyrics == '': + lyrics = __getLyrics__(track.title, __getArtistsString__(track.artists), conf.lyricsServerProxy) + + if conf.lyricFile: + if tidalLyrics is None: + Printf.info(f'Failed to get lyrics from tidal!"{track.title}"') + else: + lrcPath = path.rsplit(".", 1)[0] + '.lrc' + aigpy.fileHelper.write(lrcPath, tidalLyrics.subtitles, 'w') + + __setMetaData__(track, album, path, contributors, lyrics) + Printf.success(aigpy.path.getFileName(path)) + except Exception as e: + Printf.err("Download failed! " + track.title + ' (' + str(e) + ')') + + def __dlVideo__(self): + pass diff --git a/TIDALDL-PY/tidal_gui/viewModel/loginModel.py b/TIDALDL-PY/tidal_gui/viewModel/loginModel.py new file mode 100644 index 0000000..9e6058a --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/loginModel.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : loginModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import _thread +import time +import webbrowser + +from PyQt5.QtCore import QTimer, pyqtSignal, QObject + +from tidal_gui.tidalImp import tidalImp +from tidal_gui.view.loginView import LoginView +from tidal_gui.viewModel.viewModel import ViewModel + +class LoginModel(ViewModel): + SIGNAL_LOGIN_SUCCESS = pyqtSignal() + + def __init__(self): + super(LoginModel, self).__init__() + self.view = LoginView() + self.view.connectConfirmButton(self.__openWeb__) + self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__) + + def __refresh__(self, stype: str, text: str): + if stype == "userCode": + self.view.setDeviceCode(text) + self.view.enableConfirmButton(True) + self.view.setMsg(' ') + self.view.showEnterView() + elif stype == "showMsg": + self.view.hideEnterView() + self.view.setMsg(text) + + def login(self, useConfig = True): + self.SIGNAL_REFRESH_VIEW.emit('showMsg', "LOGIN...") + + def __thread_login__(model: LoginModel, useConfig: bool): + if useConfig and tidalImp.loginByConfig(): + model.SIGNAL_LOGIN_SUCCESS.emit() + return + model.getDeviceCode() + + _thread.start_new_thread(__thread_login__, (self, useConfig)) + + def getDeviceCode(self): + self.SIGNAL_REFRESH_VIEW.emit('showMsg', "GET DEVICE-CODE...") + + def __thread_getCode__(model: LoginModel): + msg, check = tidalImp.getDeviceCode() + if check: + model.SIGNAL_REFRESH_VIEW.emit('userCode', tidalImp.key.userCode) + else: + model.SIGNAL_REFRESH_VIEW.emit('showMsg', msg) + + _thread.start_new_thread(__thread_getCode__, (self,)) + + def __openWeb__(self): + self.view.enableConfirmButton(False) + webbrowser.open('http://link.tidal.com/' + tidalImp.key.userCode, new=0, autoraise=True) + + def __thread_waitLogin__(model: LoginModel): + if tidalImp.loginByWeb(): + model.SIGNAL_LOGIN_SUCCESS.emit() + else: + model.getDeviceCode() + + _thread.start_new_thread(__thread_waitLogin__, (self,)) diff --git a/TIDALDL-PY/tidal_gui/viewModel/mainModel.py b/TIDALDL-PY/tidal_gui/viewModel/mainModel.py new file mode 100644 index 0000000..e6bd947 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/mainModel.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : mainModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from tidal_gui.downloader import downloadImp +from tidal_gui.view.mainView import MainView +from tidal_gui.viewModel.aboutModel import AboutModel +from tidal_gui.viewModel.loginModel import LoginModel +from tidal_gui.viewModel.searchModel import SearchModel +from tidal_gui.viewModel.settingsModel import SettingsModel +from tidal_gui.viewModel.taskModel import TaskModel +from tidal_gui.viewModel.viewModel import ViewModel + + +class MainModel(ViewModel): + def __init__(self): + super(MainModel, self).__init__() + self.loginModel = LoginModel() + self.searchModel = SearchModel() + self.taskModel = TaskModel() + self.settingsModel = SettingsModel(self) + self.aboutModel = AboutModel() + + self.loginModel.SIGNAL_LOGIN_SUCCESS.connect(self.__loginSuccess__) + self.searchModel.SIGNAL_ADD_TASKITEM.connect(self.taskModel.addTaskItem) + + self.view = MainView() + self.view.setSearchView(self.searchModel.view) + self.view.setTaskView(self.taskModel.view) + self.view.setSettingsView(self.settingsModel.view) + self.view.setAboutView(self.aboutModel.view) + + self.view.showPage() + + downloadImp.setTaskModel(self.taskModel) + downloadImp.start() + + def uninit(self): + self.taskModel.stopDownloadItem() + downloadImp.stop() + + def show(self, relogin:bool = False): + self.view.hide() + self.loginModel.login(bool(1-relogin)) + self.loginModel.show() + + def __loginSuccess__(self): + self.loginModel.hide() + self.view.show() diff --git a/TIDALDL-PY/tidal_gui/viewModel/searchModel.py b/TIDALDL-PY/tidal_gui/viewModel/searchModel.py new file mode 100644 index 0000000..9e35ee0 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/searchModel.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : searchModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import _thread +import threading + +import aigpy.stringHelper +import tidal_dl +from PyQt5.QtCore import pyqtSignal +from aigpy.modelHelper import ModelBase +from tidal_dl import Type +from tidal_dl.model import Album, SearchResult + +from tidal_gui.tidalImp import tidalImp +from tidal_gui.view.searchView import SearchView +from tidal_gui.viewModel.viewModel import ViewModel + + +class SearchModel(ViewModel): + SIGNAL_ADD_TASKITEM = pyqtSignal(ModelBase) + + def __init__(self): + super(SearchModel, self).__init__() + self._lock = threading.Lock() + self._resultData = None + + self.view = SearchView() + self.view.setPageIndex(1, 1) + self.view.setTrackQualityItems(tidalImp.getAudioQualityList()) + self.view.setVideoQualityItems(tidalImp.getVideoQualityList()) + self.view.connectButton('search', self.__search__) + self.view.connectButton('prePage', self.__searchPre__) + self.view.connectButton('nextPage', self.__searchNext__) + self.view.connectButton('download', self.__download__) + self.view.connectTab(lambda: self.__search__(0)) + self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__) + + def __getSumByResult__(self, stype: Type): + if stype == Type.Album: + return self._resultData.albums.totalNumberOfItems + elif stype == Type.Artist: + return self._resultData.artists.totalNumberOfItems + elif stype == Type.Track: + return self._resultData.tracks.totalNumberOfItems + elif stype == Type.Video: + return self._resultData.videos.totalNumberOfItems + elif stype == Type.Playlist: + return self._resultData.playlists.totalNumberOfItems + return 0 + + def __refresh__(self, stype: str, data): + if stype == 'setPageIndex': + self.view.setPageIndex(data[0], data[1]) + elif stype == 'setTableItems': + self.view.setTableItems(data[0], data[1], data[2]) + elif stype == 'setSearchErrmsg': + self.view.setSearchErrmsg(data) + + def __startThread__(self, index: int): + def __thread_search__(model: SearchModel, index: int): + typeIndex = model.view.getSelectedTabIndex() + searchText = model.view.getSearchText() + + if aigpy.stringHelper.isNull(searchText): + model._lock.release() + return + + # search + limit = 20 + offset = (index - 1) * limit + stype = tidal_dl.Type(typeIndex) + msg, model._resultData = tidalImp.search(searchText, stype, offset, limit) + + if not aigpy.stringHelper.isNull(msg): + model.SIGNAL_REFRESH_VIEW.emit('setSearchErrmsg', msg) + model._lock.release() + return + + # get page index + total = model.__getSumByResult__(stype) + if total <= 0: + model.SIGNAL_REFRESH_VIEW.emit('setSearchErrmsg', 'Search results are empty...') + model._lock.release() + return + + maxIdx = total // limit + (1 if total % limit > 0 else 0) + if index > maxIdx: + model._lock.release() + return + + # set view + model.SIGNAL_REFRESH_VIEW.emit('setPageIndex', (index, maxIdx)) + model.SIGNAL_REFRESH_VIEW.emit('setTableItems', (stype, offset, model._resultData)) + model._lock.release() + + _thread.start_new_thread(__thread_search__, (self, index)) + + def __search__(self, num: int = 0): + if not self._lock.acquire(False): + return + + self.view.setSearchErrmsg('') + + if aigpy.string.isNull(self.view.getSearchText()): + self._lock.release() + return + + index = 1 + if num != 0: + curIdx, curSum = self.view.getPageIndex() + if curIdx + num > 1: + index = curIdx + num + + self.__startThread__(index) + + def __searchNext__(self): + self.__search__(1) + + def __searchPre__(self): + self.__search__(-1) + + def __download__(self): + if self._resultData is None: + self.view.setSearchErrmsg('Please search first...') + return + + typeIndex = self.view.getSelectedTabIndex() + stype = tidal_dl.Type(typeIndex) + + items = [] + if stype == Type.Album: + items = self._resultData.albums.items + elif stype == Type.Track: + items = self._resultData.tracks.items + elif stype == Type.Video: + items = self._resultData.videos.items + elif stype == Type.Playlist: + items = self._resultData.playlists.items + + index = self.view.getSelectedTableIndex(stype) + if index < 0 or index >= len(items): + self.view.setSearchErrmsg('Please select one item...') + return + + self.SIGNAL_ADD_TASKITEM.emit(items[index]) diff --git a/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py b/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py new file mode 100644 index 0000000..6e7d912 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : settingsModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from tidal_gui.view.settingsView import SettingsView +from tidal_gui.viewModel.viewModel import ViewModel + + +class SettingsModel(ViewModel): + def __init__(self, parent): + super(SettingsModel, self).__init__() + self._parent = parent + self.view = SettingsView() + self.view.connectButton('Logout', self.__logout__) + self.view.connectButton('Cancel', self.__cancel__) + self.view.connectButton('OK', self.__ok__) + + def __logout__(self): + self._parent.show(True) + + def __cancel__(self): + pass + + def __ok__(self): + pass diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py new file mode 100644 index 0000000..a70e3d4 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : taskItemModel.py +@Date : 2021/9/14 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +import _thread +import os + +import aigpy.stringHelper +from tidal_dl import Type +from tidal_dl.model import Album, Track, Video, Playlist + +from tidal_gui.tidalImp import tidalImp +from tidal_gui.view.taskItemView import TaskItemView +from tidal_gui.viewModel.viewModel import ViewModel +from tidal_gui.viewModel.downloadItemModel import DownloadItemModel +from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QWidget, QScrollArea, QVBoxLayout, QPushButton +import time + + +class TaskItemModel(ViewModel): + def __init__(self, data): + super(TaskItemModel, self).__init__() + self.view = TaskItemView() + self.data = data + self.downloadModelList = [] + self.path = tidalImp.getBasePath(data) + + aigpy.path.mkdirs(self.path) + + if isinstance(data, Album): + self.__initAlbum__(data) + elif isinstance(data, Track): + self.__initTrack__(data) + elif isinstance(data, Video): + self.__initVideo__(data) + elif isinstance(data, Playlist): + self.__initPlaylist__(data) + + self.view.connectButton('retry', self.__btnFuncRetry__) + self.view.connectButton('cancel', self.__btnFuncCancel__) + self.view.connectButton('delete', self.__btnFuncDelete__) + self.view.connectButton('open', self.__btnFuncOpen__) + + self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__) + + def __refresh__(self, stype: str, text: str): + if stype == "setPic": + self.view.setPic(text) + elif stype == "addListItem": + for index, item in enumerate(text): + downItem = DownloadItemModel(index + 1, item, self.path) + self.view.addListItem(downItem.view) + self.downloadModelList.append(downItem) + + def __btnFuncRetry__(self): + for item in self.downloadModelList: + item.retry() + + def __btnFuncCancel__(self): + for item in self.downloadModelList: + item.stopDownload() + + def __btnFuncDelete__(self): + for item in self.downloadModelList: + item.stopDownload() + + def __btnFuncOpen__(self): + if os.path.exists(self.path): + os.startfile(self.path) + + def __initAlbum__(self, data: Album): + title = data.title + desc = f"by {tidalImp.getArtistsNames(data.artists)} " \ + f"{tidalImp.getDurationString(data.duration)} " \ + f"Track-{data.numberOfTracks} " \ + f"Video-{data.numberOfVideos}" + self.view.setLabel(title, desc) + + def __thread_func__(model: TaskItemModel, album: Album): + cover = tidalImp.getCoverData(album.cover, '1280', '1280') + model.SIGNAL_REFRESH_VIEW.emit('setPic', cover) + + msg, tracks, videos = tidalImp.getItems(album.id, Type.Album) + if not aigpy.stringHelper.isNull(msg): + model.view.setErrmsg(msg) + return + + model.SIGNAL_REFRESH_VIEW.emit('addListItem', tracks + videos) + print('__initAlbum__') + time.sleep(1) + + _thread.start_new_thread(__thread_func__, (self, data)) + + def __initTrack__(self, data: Track): + pass + + def __initVideo__(self, data: Video): + pass + + def __initPlaylist__(self, data: Playlist): + pass diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskModel.py new file mode 100644 index 0000000..e83e3c3 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/taskModel.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : taskModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtWidgets import QPushButton, QCheckBox, QWidget +from tidal_dl import Type +from tidal_dl.model import Album, Artist + +from tidal_gui.control.layout import createHBoxLayout +from tidal_gui.view.taskView import TaskView, TaskListType +from tidal_gui.viewModel.taskItemModel import TaskItemModel +from tidal_gui.viewModel.viewModel import ViewModel +from tidal_gui.viewModel.downloadItemModel import DownloadItemModel + + +class TaskModel(ViewModel): + def __init__(self): + super(TaskModel, self).__init__() + self.view = TaskView() + + self._listMap = {} + for item in map(lambda typeItem: typeItem.name, TaskListType): + self._listMap[item] = [] + + self.test() + + def addTaskItem(self, data): + item = TaskItemModel(data) + self._listMap[TaskListType.Download.name].append(item) + self.view.addItemView(TaskListType.Download, item.view) + + def getWaitDownloadItem(self) -> DownloadItemModel: + for item in self._listMap[TaskListType.Download.name]: + for downItem in item.downloadModelList: + if downItem.isInWait(): + return downItem + return None + + def stopDownloadItem(self): + for item in self._listMap[TaskListType.Download.name]: + for downItem in item.downloadModelList: + downItem.stopDownload() + + def test(self): + ar = Artist() + ar.name = 'yaron' + ar.id = 110 + album = Album() + album.artist = None + album.artists = [ar] + album.audioModes = ['STEREO'] + album.audioQuality = 'LOSSLESS' + album.cover = '8203ff9a-47e3-49a0-9b44-a6dbbe9c9469' + album.duration = 1140 + album.explicit = False + album.id = 177748204 + album.numberOfTracks = 10 + album.numberOfVideos = 0 + album.numberOfVolumes = 1 + album.releaseDate = '2021-03-16' + album.title = 'Love' + album.type = 'ALBUM' + album.version = None + + self.addTaskItem(album) diff --git a/TIDALDL-PY/tidal_gui/viewModel/viewModel.py b/TIDALDL-PY/tidal_gui/viewModel/viewModel.py new file mode 100644 index 0000000..356d6e9 --- /dev/null +++ b/TIDALDL-PY/tidal_gui/viewModel/viewModel.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +@File : viewModel.py +@Date : 2021/8/17 +@Author : Yaronzz +@Version : 1.0 +@Contact : yaronhuang@foxmail.com +@Desc : +""" +from PyQt5.QtCore import QObject +from PyQt5.QtCore import QTimer, pyqtSignal + + +class ViewModel(QObject): + SIGNAL_REFRESH_VIEW = pyqtSignal(str, object) + + def __init__(self): + super(ViewModel, self).__init__() + self.view = None + + def show(self): + self.view.show() + + def hide(self): + self.view.hide()