mirror of https://github.com/leafspark/AutoGGUF
202 lines
7.2 KiB
Python
202 lines
7.2 KiB
Python
from typing import List
|
|
|
|
from PySide6.QtCore import *
|
|
from PySide6.QtGui import QAction
|
|
from PySide6.QtWidgets import *
|
|
|
|
from Localizations import (
|
|
DELETING_TASK,
|
|
CANCELLING_TASK,
|
|
CONFIRM_DELETION_TITLE,
|
|
CONFIRM_DELETION,
|
|
SHOWING_TASK_CONTEXT_MENU,
|
|
CANCELED,
|
|
CANCEL,
|
|
PROPERTIES,
|
|
COMPLETED,
|
|
SHOWING_PROPERTIES_FOR_TASK,
|
|
DELETE,
|
|
RESTART,
|
|
IN_PROGRESS,
|
|
ERROR,
|
|
RESTARTING_TASK,
|
|
)
|
|
from ModelInfoDialog import ModelInfoDialog
|
|
from QuantizationThread import QuantizationThread
|
|
from Logger import Logger
|
|
from error_handling import handle_error
|
|
|
|
|
|
class TaskListItem(QWidget):
|
|
def __init__(
|
|
self,
|
|
task_name,
|
|
log_file,
|
|
show_progress_bar=True,
|
|
parent=None,
|
|
show_properties=False,
|
|
logger=Logger,
|
|
quant_threads=List[QuantizationThread],
|
|
) -> None:
|
|
super().__init__(parent)
|
|
self.quant_threads = quant_threads
|
|
self.task_name = task_name
|
|
self.log_file = log_file
|
|
self.logger = logger
|
|
self.show_properties = show_properties
|
|
self.status = "Pending"
|
|
layout = QHBoxLayout(self)
|
|
|
|
self.task_label = QLabel(task_name)
|
|
self.progress_bar = QProgressBar()
|
|
self.progress_bar.setRange(0, 100)
|
|
self.status_label = QLabel(self.status)
|
|
layout.addWidget(self.task_label)
|
|
layout.addWidget(self.progress_bar)
|
|
layout.addWidget(self.status_label)
|
|
|
|
# Hide progress bar if show_progress_bar is False
|
|
self.progress_bar.setVisible(show_progress_bar)
|
|
|
|
# Use indeterminate progress bar if not showing percentage
|
|
if not show_progress_bar:
|
|
self.progress_bar.setRange(0, 0)
|
|
|
|
self.progress_timer = QTimer(self)
|
|
self.progress_timer.timeout.connect(self.update_progress)
|
|
self.progress_value = 0
|
|
|
|
def show_task_context_menu(self, position) -> None:
|
|
self.logger.debug(SHOWING_TASK_CONTEXT_MENU)
|
|
item = self.task_list.itemAt(position)
|
|
if item is not None:
|
|
context_menu = QMenu(self)
|
|
|
|
properties_action = QAction(PROPERTIES, self)
|
|
properties_action.triggered.connect(lambda: self.show_task_properties(item))
|
|
context_menu.addAction(properties_action)
|
|
|
|
task_item = self.task_list.itemWidget(item)
|
|
if task_item.status != COMPLETED:
|
|
cancel_action = QAction(CANCEL, self)
|
|
cancel_action.triggered.connect(lambda: self.cancel_task(item))
|
|
context_menu.addAction(cancel_action)
|
|
|
|
if task_item.status == CANCELED:
|
|
restart_action = QAction(RESTART, self)
|
|
restart_action.triggered.connect(lambda: self.restart_task(task_item))
|
|
context_menu.addAction(restart_action)
|
|
|
|
delete_action = QAction(DELETE, self)
|
|
delete_action.triggered.connect(lambda: self.delete_task(item))
|
|
context_menu.addAction(delete_action)
|
|
|
|
context_menu.exec(self.task_list.viewport().mapToGlobal(position))
|
|
|
|
def show_task_properties(self, item) -> None:
|
|
self.logger.debug(SHOWING_PROPERTIES_FOR_TASK.format(item.text()))
|
|
for thread in self.quant_threads:
|
|
model_info_dialog = ModelInfoDialog(thread.model_info, self)
|
|
model_info_dialog.exec()
|
|
break
|
|
|
|
def cancel_task(self, item) -> None:
|
|
# TODO: fix possibly buggy signal behavior
|
|
task_item = self.task_list.itemWidget(item)
|
|
if task_item:
|
|
task_name = task_item.task_name # Store the name before any changes
|
|
self.logger.info(CANCELLING_TASK.format(task_name))
|
|
|
|
# Find the thread and disconnect signals before terminating
|
|
for thread in self.quant_threads:
|
|
if thread.log_file == task_item.log_file:
|
|
# Disconnect all signals from this thread first
|
|
try:
|
|
thread.error_signal.disconnect() # Disconnect all error signal connections
|
|
thread.output_signal.disconnect() # Disconnect all output signal connections
|
|
except TypeError:
|
|
# No connections to disconnect
|
|
pass
|
|
|
|
# Now terminate the thread
|
|
thread.terminate()
|
|
self.quant_threads.remove(thread)
|
|
break
|
|
|
|
def delete_task(self, item) -> None:
|
|
task_item = self.task_list.itemWidget(item)
|
|
if not task_item:
|
|
return
|
|
|
|
task_name = task_item.task_name # Store task_name before deletion
|
|
self.logger.info(DELETING_TASK.format(task_name))
|
|
|
|
reply = QMessageBox.question(
|
|
self,
|
|
CONFIRM_DELETION_TITLE,
|
|
CONFIRM_DELETION,
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
QMessageBox.StandardButton.No,
|
|
)
|
|
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|
# Cancel the task first (which disconnects signals)
|
|
self.cancel_task(item)
|
|
|
|
# Now remove from list and delete
|
|
row = self.task_list.row(item)
|
|
self.task_list.takeItem(row)
|
|
|
|
# Delete the widget after removing from list
|
|
task_item.deleteLater()
|
|
|
|
def update_status(self, status) -> None:
|
|
self.status = status
|
|
self.status_label.setText(status)
|
|
if status == IN_PROGRESS:
|
|
# Only start timer if showing percentage progress
|
|
if self.progress_bar.isVisible():
|
|
self.progress_bar.setRange(0, 100)
|
|
self.progress_timer.start(100)
|
|
elif status == COMPLETED:
|
|
self.progress_timer.stop()
|
|
self.progress_bar.setValue(100)
|
|
elif status == CANCELED:
|
|
self.progress_timer.stop()
|
|
self.progress_bar.setValue(0)
|
|
|
|
def set_error(self) -> None:
|
|
self.status = ERROR
|
|
self.status_label.setText(ERROR)
|
|
self.status_label.setStyleSheet("color: red;")
|
|
self.progress_bar.setRange(0, 100)
|
|
self.progress_timer.stop()
|
|
|
|
def update_progress(self, value=None) -> None:
|
|
if value is not None:
|
|
# Update progress bar with specific value
|
|
self.progress_value = value
|
|
self.progress_bar.setValue(self.progress_value)
|
|
else:
|
|
return
|
|
|
|
def restart_task(self, task_item) -> None:
|
|
self.logger.info(RESTARTING_TASK.format(task_item.task_name))
|
|
for thread in self.quant_threads:
|
|
if thread.log_file == task_item.log_file:
|
|
new_thread = QuantizationThread(
|
|
thread.command, thread.cwd, thread.log_file
|
|
)
|
|
self.quant_threads.append(new_thread)
|
|
new_thread.status_signal.connect(task_item.update_status)
|
|
new_thread.finished_signal.connect(
|
|
lambda: self.task_finished(new_thread, task_item)
|
|
)
|
|
new_thread.error_signal.connect(
|
|
lambda err: handle_error(self.logger, err, task_item)
|
|
)
|
|
new_thread.model_info_signal.connect(self.update_model_info)
|
|
new_thread.start()
|
|
task_item.update_status(IN_PROGRESS)
|
|
break
|