MainWindow: Implement worker queue in status bar
The active workers are placed in a static container on the left side. The queued workers are placed in a scrollarea on the right side of the status bar. The scrollbars are disabled but dragging works. Exiting with active workers will pop up a message for confirmation. Rare will clean the queued workers but the active ones will be waited on until they finish.
This commit is contained in:
parent
f2dedaaefc
commit
e40cfaafac
3 changed files with 102 additions and 20 deletions
|
@ -2,7 +2,7 @@ import os
|
|||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import Qt, QSettings, QTimer, QSize, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtGui import QCloseEvent, QCursor
|
||||
from PyQt5.QtGui import QCloseEvent, QCursor, QColor
|
||||
from PyQt5.QtWidgets import (
|
||||
QMainWindow,
|
||||
QApplication,
|
||||
|
@ -10,13 +10,18 @@ from PyQt5.QtWidgets import (
|
|||
QScrollArea,
|
||||
QScroller,
|
||||
QComboBox,
|
||||
QMessageBox, QLabel, QSizePolicy,
|
||||
QMessageBox,
|
||||
QLabel,
|
||||
QWidget,
|
||||
QHBoxLayout,
|
||||
)
|
||||
|
||||
from rare.components.tabs import MainTabWidget
|
||||
from rare.components.tray_icon import TrayIcon
|
||||
from rare.shared import RareCore
|
||||
from rare.shared.workers.worker import QueueWorkerState
|
||||
from rare.utils.paths import lock_file
|
||||
from rare.widgets.elide_label import ElideLabel
|
||||
|
||||
logger = getLogger("MainWindow")
|
||||
|
||||
|
@ -42,9 +47,44 @@ class MainWindow(QMainWindow):
|
|||
self.tab_widget.exit_app.connect(self.on_exit_app)
|
||||
self.setCentralWidget(self.tab_widget)
|
||||
|
||||
# Set up status bar stuff (jumping through a lot of hoops)
|
||||
self.status_bar = QStatusBar(self)
|
||||
self.setStatusBar(self.status_bar)
|
||||
|
||||
self.active_label = QLabel(self.tr("Active:"), self.status_bar)
|
||||
# lk: set top and botton margins to accommodate border for scroll area labels
|
||||
self.active_label.setContentsMargins(5, 1, 0, 1)
|
||||
self.status_bar.addWidget(self.active_label)
|
||||
|
||||
self.active_container = QWidget(self.status_bar)
|
||||
active_layout = QHBoxLayout(self.active_container)
|
||||
active_layout.setContentsMargins(0, 0, 0, 0)
|
||||
active_layout.setSizeConstraint(QHBoxLayout.SetFixedSize)
|
||||
self.status_bar.addWidget(self.active_container, stretch=0)
|
||||
|
||||
self.queued_label = QLabel(self.tr("Queued:"), self.status_bar)
|
||||
# lk: set top and botton margins to accommodate border for scroll area labels
|
||||
self.queued_label.setContentsMargins(5, 1, 0, 1)
|
||||
self.status_bar.addPermanentWidget(self.queued_label)
|
||||
|
||||
self.queued_scroll = QScrollArea(self.status_bar)
|
||||
self.queued_scroll.setFrameStyle(QScrollArea.NoFrame)
|
||||
self.queued_scroll.setWidgetResizable(True)
|
||||
self.queued_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.queued_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.queued_scroll.setFixedHeight(self.queued_label.sizeHint().height())
|
||||
self.status_bar.addPermanentWidget(self.queued_scroll, stretch=1)
|
||||
|
||||
self.queued_container = QWidget(self.queued_scroll)
|
||||
queued_layout = QHBoxLayout(self.queued_container)
|
||||
queued_layout.setContentsMargins(0, 0, 0, 0)
|
||||
queued_layout.setSizeConstraint(QHBoxLayout.SetFixedSize)
|
||||
|
||||
self.active_label.setVisible(False)
|
||||
self.active_container.setVisible(False)
|
||||
self.queued_label.setVisible(False)
|
||||
self.queued_scroll.setVisible(False)
|
||||
|
||||
self.signals.application.update_statusbar.connect(self.update_statusbar)
|
||||
self.signals.application.update_statusbar.connect(self.update_statusbar)
|
||||
|
||||
|
@ -124,21 +164,45 @@ class MainWindow(QMainWindow):
|
|||
|
||||
@pyqtSlot()
|
||||
def update_statusbar(self):
|
||||
for label in self.status_bar.findChildren(QLabel, options=Qt.FindDirectChildrenOnly):
|
||||
self.status_bar.removeWidget(label)
|
||||
self.active_label.setVisible(False)
|
||||
self.active_container.setVisible(False)
|
||||
self.queued_label.setVisible(False)
|
||||
self.queued_scroll.setVisible(False)
|
||||
for label in self.active_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly):
|
||||
self.active_container.layout().removeWidget(label)
|
||||
label.deleteLater()
|
||||
for info in self.rcore.queue_info():
|
||||
label = QLabel(f"{info.worker_type}: {info.app_title}")
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
label.setStyleSheet(
|
||||
for label in self.queued_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly):
|
||||
self.queued_container.layout().removeWidget(label)
|
||||
label.deleteLater()
|
||||
stylesheet = """
|
||||
QLabel#QueueWorkerLabel {{
|
||||
border-radius: 3px;
|
||||
border: 1px solid {br_color};
|
||||
background-color: {bg_color};
|
||||
}}
|
||||
"""
|
||||
QLabel {
|
||||
border-width: 1px;
|
||||
background-color: gray;
|
||||
color = {
|
||||
"Verify": QColor("#d6af57"),
|
||||
"Move": QColor("#41cad9"),
|
||||
}
|
||||
"""
|
||||
)
|
||||
self.status_bar.addWidget(label)
|
||||
for info in self.rcore.queue_info():
|
||||
label = ElideLabel(info.app_title)
|
||||
label.setObjectName("QueueWorkerLabel")
|
||||
label.setToolTip(f"<b>{info.worker_type}</b>: {info.app_title}")
|
||||
label.setFixedWidth(150)
|
||||
label.setContentsMargins(3, 0, 3, 0)
|
||||
label.setStyleSheet(stylesheet.format(
|
||||
br_color=color[info.worker_type].darker(200).name(),
|
||||
bg_color=color[info.worker_type].darker(400).name(),
|
||||
))
|
||||
if info.state == QueueWorkerState.ACTIVE:
|
||||
self.active_container.layout().addWidget(label)
|
||||
self.active_label.setVisible(True)
|
||||
self.active_container.setVisible(True)
|
||||
elif info.state == QueueWorkerState.QUEUED:
|
||||
self.queued_container.layout().addWidget(label)
|
||||
self.queued_label.setVisible(True)
|
||||
self.queued_scroll.setVisible(True)
|
||||
|
||||
def timer_finished(self):
|
||||
file_path = lock_file()
|
||||
|
@ -169,17 +233,28 @@ class MainWindow(QMainWindow):
|
|||
self.hide()
|
||||
e.ignore()
|
||||
return
|
||||
# FIXME: Fix this with the download tab redesign
|
||||
if not self.args.offline and self.tab_widget.downloads_tab.is_download_active:
|
||||
|
||||
print(self.rcore.queue_threadpool.activeThreadCount())
|
||||
# FIXME: Move this to RareCore once the download manager is implemented
|
||||
if not self.args.offline and (
|
||||
self.tab_widget.downloads_tab.is_download_active or self.rcore.queue_threadpool.activeThreadCount()
|
||||
):
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
self.tr("Quit {}?").format(QApplication.applicationName()),
|
||||
self.tr("There are active downloads. Are you sure you want to quit?"),
|
||||
self.tr("There are active operations. Are you sure you want to quit?"),
|
||||
buttons=(QMessageBox.Yes | QMessageBox.No),
|
||||
defaultButton=QMessageBox.No,
|
||||
)
|
||||
if reply == QMessageBox.Yes:
|
||||
if self.tab_widget.downloads_tab.is_download_active:
|
||||
self.tab_widget.downloads_tab.stop_download(omit_queue=True)
|
||||
if self.rcore.queue_threadpool.activeThreadCount():
|
||||
self.rcore.queue_threadpool.clear()
|
||||
for qw in self.rcore.queued_workers():
|
||||
self.rcore.queue_workers.remove(qw)
|
||||
self.update_statusbar()
|
||||
self.rcore.queue_threadpool.waitForDone()
|
||||
else:
|
||||
e.ignore()
|
||||
return
|
||||
|
|
|
@ -15,7 +15,7 @@ from rare.models.game import RareGameBase, RareGame, RareEosOverlay
|
|||
from rare.models.signals import GlobalSignals
|
||||
from .workers import QueueWorker, VerifyWorker, MoveWorker
|
||||
from .image_manager import ImageManager
|
||||
from .workers.worker import QueueWorkerInfo
|
||||
from .workers.worker import QueueWorkerInfo, QueueWorkerState
|
||||
|
||||
logger = getLogger("RareCore")
|
||||
|
||||
|
@ -62,6 +62,12 @@ class RareCore(QObject):
|
|||
self.queue_threadpool.start(worker, priority=0)
|
||||
self.__signals.application.update_statusbar.emit()
|
||||
|
||||
def active_workers(self) -> Iterator[QueueWorker]:
|
||||
return filter(lambda w: w.state == QueueWorkerState.ACTIVE, self.queue_workers)
|
||||
|
||||
def queued_workers(self) -> Iterator[QueueWorker]:
|
||||
return filter(lambda w: w.state == QueueWorkerState.QUEUED, self.queue_workers)
|
||||
|
||||
def queue_info(self) -> List[QueueWorkerInfo]:
|
||||
return [w.worker_info() for w in self.queue_workers]
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ class VerifyWorker(QueueWorker):
|
|||
|
||||
def run_real(self):
|
||||
self.rgame.signals.progress.start.emit()
|
||||
|
||||
cli = LegendaryCLI(self.core)
|
||||
status = LgndrIndirectStatus()
|
||||
args = LgndrVerifyGameArgs(
|
||||
|
|
Loading…
Reference in a new issue