Compare commits

...

14 Commits

Author SHA1 Message Date
KeksNino 62c6afe350 disable library tab (may be added again in the future) 2026-02-03 17:41:59 +01:00
KeksNino 1aaf56a4dc fix: window transparency on linux/macos 2026-02-03 17:04:09 +01:00
Vxrtrauter 47b643d68b revert all workflow changes 2026-02-02 02:31:00 +01:00
Vxrtrauter de1c8577d6 test new workflow 2026-02-02 02:26:41 +01:00
Vxrtrauter c835b8375c test new workflow 2026-02-02 02:23:52 +01:00
Vxrtrauter e7291e8e99 revert workflow changes 2026-02-02 02:23:22 +01:00
Vxrtrauter a4efe9185a update workflows 2026-02-02 02:18:33 +01:00
Vxrtrauter 86b4f023ac update QApplication instance handling (switch to pyqtdarktheme-fork) 2026-02-02 00:50:32 +01:00
KeksNino 006da55aa0 fix: pause/resume button showing up twice 2026-02-01 20:28:24 +01:00
KeksNino 214a389ad3 fix: if statement to disable windows transparency 2026-02-01 20:14:49 +01:00
KeksNino d4005d7e86 fix: disable transparency on windows as it is broken 2026-01-31 19:17:30 +01:00
KeksNino 08a45cb422 feat: add qt window transparency 2026-01-31 18:46:40 +01:00
Vxrtrauter c49726fe42 feat: improve cursor styles for buttons and combo box in GUI 2026-01-31 07:51:00 +01:00
Vxrtrauter ecc117dedc feat: enhance GUI layout and styling for better usability 2026-01-31 07:36:40 +01:00
6 changed files with 101 additions and 41 deletions
+15
View File
@@ -60,6 +60,21 @@ def settings_dialog(self):
autoresume_layout.addWidget(autoresume_checkbox) autoresume_layout.addWidget(autoresume_checkbox)
dialog.layout().addWidget(autoresume_container) dialog.layout().addWidget(autoresume_container)
# transparent window checkbox
transparent_window_container = QWidget()
transparent_window_layout = QHBoxLayout()
transparent_window_checkbox = QCheckBox()
transparent_window_container.setLayout(transparent_window_layout)
transparent_window_layout.addWidget(QLabel("Window Transparency (requires restart) (Linux/MacOS only): "))
transparent_window_layout.addStretch()
transparent_window_checkbox.setChecked(state.window_transparency)
transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked))
transparent_window_layout.addWidget(transparent_window_checkbox)
dialog.layout().addWidget(transparent_window_container)
################## ##################
# SERVER SETTING # # SERVER SETTING #
################## ##################
+58 -27
View File
@@ -1,12 +1,12 @@
from ctypes import alignment
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent, QSize
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QLineEdit, QLineEdit,
QTableView, QTableView,
QWidget, QWidget,
QVBoxLayout, QVBoxLayout,
QListWidget, QListWidget,
QToolBar,
QLabel, QLabel,
QHBoxLayout, QHBoxLayout,
QComboBox, QComboBox,
@@ -16,13 +16,10 @@ from PySide6.QtWidgets import (
QMessageBox, QMessageBox,
QTableWidget, QTableWidget,
QTextEdit, QTextEdit,
QStyle,
QStyleOptionButton,
QStyledItemDelegate, QStyledItemDelegate,
QApplication,
) )
from PySide6.QtGui import QIcon, QAction, QCloseEvent, QImage, QPixmap from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap
import darkdetect import darkdetect
import threading import threading
import platform import platform
@@ -138,6 +135,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter
self.dlbutton = QtWidgets.QPushButton("Download") self.dlbutton = QtWidgets.QPushButton("Download")
self.dlbutton.setCursor(Qt.PointingHandCursor)
self.libraryList = QListWidget() self.libraryList = QListWidget()
self.emptyResults = QLabel("No Results") self.emptyResults = QLabel("No Results")
@@ -161,6 +159,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.qtablewidget.verticalHeader().setVisible(False) self.qtablewidget.verticalHeader().setVisible(False)
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"]) self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
header = self.qtablewidget.horizontalHeader() header = self.qtablewidget.horizontalHeader()
header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(0, QHeaderView.Stretch)
header.setSectionResizeMode(1, QHeaderView.Fixed) header.setSectionResizeMode(1, QHeaderView.Fixed)
@@ -207,8 +207,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
status = magnetdl.status() status = magnetdl.status()
if col == 0: if col == 0:
is_paused = magnetdl.status().paused pass
return "▶︎" if is_paused else "⏸︎"
elif col == 1: elif col == 1:
return status.name if status.has_metadata else "Fetching metadata..." return status.name if status.has_metadata else "Fetching metadata..."
elif col == 2: elif col == 2:
@@ -300,8 +299,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
def setEditorData(self, editor, index): def setEditorData(self, editor, index):
button = editor.findChild(QtWidgets.QPushButton) button = editor.findChild(QtWidgets.QPushButton)
magnet_link = list(state.active_downloads.keys())[index.row()] magnet_link = list(state.active_downloads.keys())[index.row()]
magnetdl = state.active_downloads[magnet_link] magnetdl = state.active_downloads[magnet_link]
status = magnetdl.status() status = magnetdl.status()
@@ -312,8 +309,6 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
else: else:
button.setText("▶︎" if status.paused else "⏸︎") button.setText("▶︎" if status.paused else "⏸︎")
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
magnet_link = list(state.active_downloads.keys())[index.row()] magnet_link = list(state.active_downloads.keys())[index.row()]
magnetdl = state.active_downloads[magnet_link] magnetdl = state.active_downloads[magnet_link]
@@ -366,13 +361,24 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
# Tabs # Tabs
self.tabs = QTabWidget() self.tabs = QTabWidget()
self.tabs = QTabWidget()
self.tabs.setStyleSheet("""
QTabBar::tab {
font-size: 11px;
padding: 2px 8px;
height: 25px;
}
QTabWidget::pane {
margin-top: -20px;
}
""")
self.horizontal_layout = QHBoxLayout() self.horizontal_layout = QHBoxLayout()
self.horizontal_layout.addWidget(self.emptyResults, stretch=3) self.horizontal_layout.addWidget(self.emptyResults, stretch=3)
self.horizontal_layout.addWidget(self.qtablewidget) self.horizontal_layout.addWidget(self.qtablewidget)
self.tab1 = create_tab("Search", self.searchbar, self.qtablewidget, self.tabs, self.dlbutton, self.horizontal_layout) self.tab1 = create_tab("Search", self.searchbar, self.qtablewidget, self.tabs, self.dlbutton, self.horizontal_layout)
self.tab2 = create_tab("Library", self.emptyLibrary, self.libraryList, self.tabs, None, None) # self.tab2 = create_tab("Library", self.emptyLibrary, self.libraryList, self.tabs, None, None)
self.tab3 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None) self.tab3 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None)
if state.image_path is not None and os.path.exists(state.image_path): if state.image_path is not None and os.path.exists(state.image_path):
@@ -389,32 +395,57 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
y = self.height() - self.overlay_label.height() - offset_y y = self.height() - self.overlay_label.height() - offset_y
self.overlay_label.move(x, y) self.overlay_label.move(x, y)
# temporarily disabled # temporarily disabled
# state.image_changed.connect(self.update_image_overlay) # state.image_changed.connect(self.update_image_overlay)
self.corner_widget = QWidget()
self.corner_layout = QHBoxLayout(self.corner_widget)
self.corner_layout.setContentsMargins(0, 0, 0, 0)
self.tracker_list = QComboBox() self.tracker_list = QComboBox()
self.tracker_list.addItems(["rutracker", "uztracker", "m0nkrus"]) self.tracker_list.addItems(["rutracker", "uztracker", "m0nkrus"])
self.tracker_list.setFixedSize(90, 30) self.tracker_list.setCursor(Qt.CursorShape.PointingHandCursor)
self.tracker_list.activated.connect(self.set_tracker) self.tracker_list.activated.connect(self.set_tracker)
self.corner_layout.addWidget(self.tracker_list)
topLayout = QHBoxLayout() self.settings_btn = QtWidgets.QToolButton()
self.settings_btn.setIconSize(QSize(21, 21))
self.settings_btn.setStyleSheet("background: transparent;")
if darkdetect.isDark(): if darkdetect.isDark():
settings_button = QtWidgets.QPushButton(QIcon(get_asset_path("settings_white.png")), "") self.settings_btn.setIcon(QIcon(get_asset_path("settings_white.png")))
else: else:
settings_button = QtWidgets.QPushButton(QIcon(get_asset_path("settings_black.png")), "") self.settings_btn.setIcon(QIcon(get_asset_path("settings_black.png")))
self.settings_btn.setToolTip("Settings")
self.settings_btn.setCursor(Qt.CursorShape.PointingHandCursor)
self.settings_btn.clicked.connect(lambda: settings_dialog(self))
settings_button.clicked.connect(lambda: settings_dialog(self)) self.corner_layout.addWidget(self.settings_btn)
settings_button.setFixedSize(32, 32)
settings_button.setToolTip("Settings")
topLayout.addWidget(self.tracker_list) self.tab_wrapper = QWidget()
topLayout.addStretch() self.tab_layout = QVBoxLayout()
topLayout.addWidget(settings_button) self.tab_layout.setContentsMargins(0, 0, 0, 0)
self.tab_layout.setSpacing(0)
containerLayout.addLayout(topLayout)
containerLayout.addWidget(self.tabs) self.tab_bar_layout = QHBoxLayout()
self.tab_bar_layout.setContentsMargins(0, 0, 0, 0)
self.tab_bar_layout.setSpacing(0)
self.tab_bar_layout.addWidget(self.tabs.tabBar(), stretch=0)
self.tab_bar_layout.addStretch()
self.tab_bar_layout.addWidget(self.corner_widget, stretch=0)
self.tab_layout.addLayout(self.tab_bar_layout)
self.tab_layout.addWidget(self.tabs)
self.tab_wrapper.setLayout(self.tab_layout)
containerLayout.addWidget(self.tab_wrapper)
containerLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.consoleLog.setReadOnly(True) self.consoleLog.setReadOnly(True)
self.consoleLog.setFixedHeight(150) self.consoleLog.setFixedHeight(150)
+14 -2
View File
@@ -6,8 +6,19 @@ from core.utils.data.state import state
def create_config(): def create_config():
config = configparser.ConfigParser() config = configparser.ConfigParser()
config["General"] = {"debug": True, "api_url": f"{state.api_url}", "download_path": f"{state.download_path}", f"download_speed_limit": f"{state.down_speed_limit}", f"upload_speed_limit": f"{state.up_speed_limit}", config["General"] = {
"ignore_updates": f"{state.ignore_updates}", "image_path": f"{state.image_path}", "autoresume": f"{state.autoresume}", "max_connections": f"{state.max_connections}", "max_downloads": f"{state.max_downloads}"} "debug": True,
"api_url": f"{state.api_url}",
"download_path": f"{state.download_path}",
"download_speed_limit": f"{state.down_speed_limit}",
"upload_speed_limit": f"{state.up_speed_limit}",
"ignore_updates": f"{state.ignore_updates}",
"image_path": f"{state.image_path}",
"autoresume": f"{state.autoresume}",
"max_connections": f"{state.max_connections}",
"max_downloads": f"{state.max_downloads}",
"window_transparency": f"{state.window_transparency}",
}
if platform.system() == "Windows": if platform.system() == "Windows":
config_dir = os.environ.get("APPDATA", os.path.expanduser("~\\AppData\\Roaming")) config_dir = os.environ.get("APPDATA", os.path.expanduser("~\\AppData\\Roaming"))
@@ -48,5 +59,6 @@ def read_config():
state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume) state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume)
state.max_connections = config.getint("General", "max_connections", fallback=state.max_connections) state.max_connections = config.getint("General", "max_connections", fallback=state.max_connections)
state.max_downloads = config.getint("General", "max_downloads", fallback=state.max_downloads) state.max_downloads = config.getint("General", "max_downloads", fallback=state.max_downloads)
state.window_transparency = config.getboolean("General", "window_transparency", fallback=state.window_transparency)
create_config() create_config()
+1
View File
@@ -26,6 +26,7 @@ class AppState(QObject):
self.settings_path: str = None self.settings_path: str = None
self.dl_session: Any = None self.dl_session: Any = None
self.active_downloads: List[str] = {} self.active_downloads: List[str] = {}
self.window_transparency: bool = False
@property @property
def image_path(self) -> str: def image_path(self) -> str:
+8 -7
View File
@@ -8,9 +8,10 @@ from core.utils.general.wrappers import run_thread
from core.utils.general.loghandler import split_data, check_completed from core.utils.general.loghandler import split_data, check_completed
from core.utils.config.config import read_config from core.utils.config.config import read_config
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt
import qdarktheme import qdarktheme
import darkdetect
import threading import threading
import platform
import signal import signal
import argparse import argparse
import sys import sys
@@ -21,16 +22,16 @@ parser.add_argument("--debug", action="store_true")
args = parser.parse_args() args = parser.parse_args()
def run_gui(): def run_gui():
app = QtWidgets.QApplication([]) app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
if darkdetect.isDark: qdarktheme.setup_theme("auto")
app.setStyleSheet(qdarktheme.load_stylesheet("dark"))
else:
app.setStyleSheet(qdarktheme.load_stylesheet("light"))
widget = MainWindow() widget = MainWindow()
if state.window_transparency and platform.system() != "Windows":
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
qdarktheme.setup_theme("auto", custom_colors={"background": "#00000000"})
from core.utils.general.logs import set_main_window from core.utils.general.logs import set_main_window
set_main_window(widget) set_main_window(widget)
widget.show() widget.show()
sys.exit(app.exec()) sys.exit(app.exec())
+2 -2
View File
@@ -1,9 +1,9 @@
requests==2.32.2 requests==2.32.2
PySide6==6.10.1 PySide6==6.10.1
beautifulsoup4==4.13.5 beautifulsoup4==4.13.5
darkdetect==0.8.0 darkdetect==0.7.1
pyinstaller==6.15.0 pyinstaller==6.15.0
pyqtdarktheme==0.1.7 PyQtDarkTheme-fork==2.3.4
aiohttp==3.13.0 aiohttp==3.13.0
plyer==2.1.0 plyer==2.1.0
psutil==7.1.0 psutil==7.1.0