Merge pull request #12 from leafspark/modern-ui

feat(ui): add modern UI
This commit is contained in:
leafspark 2024-08-16 19:24:35 -07:00 committed by GitHub
commit 7ac297a3bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 313 additions and 151 deletions

View File

@ -1,6 +1,7 @@
import json
import re
import shutil
import os
from functools import partial
from PySide6.QtCore import *
@ -11,6 +12,7 @@
from KVOverrideEntry import KVOverrideEntry
from Logger import Logger
from ModelInfoDialog import ModelInfoDialog
from error_handling import show_error, handle_error
from imports_and_globals import (
open_file_safe,
resource_path,
@ -18,20 +20,88 @@
ensure_directory,
)
from Localizations import *
import presets
import ui_update
import lora_conversion
import utils
class CustomTitleBar(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
layout = QHBoxLayout(self)
layout.setContentsMargins(10, 5, 10, 5)
# Add your logo or app name here
self.title = QLabel("AutoGGUF")
layout.addWidget(self.title)
layout.addStretch(1) # This pushes the buttons to the right
# Add minimize and close buttons
self.minimize_button = QPushButton("")
self.close_button = QPushButton("")
for button in (self.minimize_button, self.close_button):
button.setFixedSize(30, 30)
button.setStyleSheet(
"""
QPushButton {
border: none;
background-color: transparent;
}
QPushButton:hover {
background-color: rgba(255, 255, 255, 0.1);
}
"""
)
layout.addWidget(self.minimize_button)
layout.addWidget(self.close_button)
self.minimize_button.clicked.connect(self.parent.showMinimized)
self.close_button.clicked.connect(self.parent.close)
self.start = QPoint(0, 0)
self.pressing = False
def mousePressEvent(self, event):
self.start = self.mapToGlobal(event.pos())
self.pressing = True
def mouseMoveEvent(self, event):
if self.pressing:
end = self.mapToGlobal(event.pos())
movement = end - self.start
self.parent.setGeometry(
self.parent.x() + movement.x(),
self.parent.y() + movement.y(),
self.parent.width(),
self.parent.height(),
)
self.start = end
def mouseReleaseEvent(self, event):
self.pressing = False
class AutoGGUF(QMainWindow):
def __init__(self):
super().__init__()
self.logger = Logger("AutoGGUF", "logs")
width, height = self.parse_resolution()
self.logger.info(INITIALIZING_AUTOGGUF)
self.setWindowTitle(WINDOW_TITLE)
self.setWindowIcon(QIcon(resource_path("assets/favicon.ico")))
self.setGeometry(100, 100, 1600, 1200)
self.setGeometry(100, 100, width, height)
self.setWindowFlag(Qt.FramelessWindowHint)
self.resize_factor = 1.1 # 10% increase/decrease
self.default_width, self.default_height = self.parse_resolution()
self.resize(self.default_width, self.default_height)
ensure_directory(os.path.abspath("quantized_models"))
ensure_directory(os.path.abspath("models"))
@ -49,6 +119,8 @@ def __init__(self):
self.browse_lora_output = utils.browse_lora_output.__get__(self)
self.convert_lora = lora_conversion.convert_lora.__get__(self)
self.show_about = show_about.__get__(self)
self.save_preset = presets.save_preset.__get__(self)
self.load_preset = presets.load_preset.__get__(self)
self.update_threads_spinbox = partial(ui_update.update_threads_spinbox, self)
self.update_threads_slider = partial(ui_update.update_threads_slider, self)
self.update_gpu_offload_spinbox = partial(
@ -63,59 +135,97 @@ def __init__(self):
ui_update.update_download_progress, self
)
# Create a central widget and main layout
central_widget = QWidget()
main_layout = QHBoxLayout(central_widget)
# Set up main widget and layout
main_widget = QWidget()
main_layout = QVBoxLayout(main_widget)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.setSpacing(0)
# Create a scroll area and set it as the central widget
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setWidget(central_widget)
self.setCentralWidget(scroll)
# Custom title bar
self.title_bar = CustomTitleBar(self)
main_layout.addWidget(self.title_bar)
# Create left and right widgets
left_widget = QWidget()
right_widget = QWidget()
# Set minimum widths to maintain proportions
left_widget.setMinimumWidth(800)
right_widget.setMinimumWidth(400)
menubar = QMenuBar(self)
self.layout().setMenuBar(menubar)
# Menu bar
self.menubar = QMenuBar()
self.title_bar.layout().insertWidget(1, self.menubar)
# File menu
file_menu = menubar.addMenu("&File")
file_menu = self.menubar.addMenu("&File")
close_action = QAction("&Close", self)
close_action.setShortcut(QKeySequence.Quit)
close_action.setShortcut(QKeySequence("Alt+F4"))
close_action.triggered.connect(self.close)
save_preset_action = QAction("&Save Preset", self)
save_preset_action.setShortcut(QKeySequence("Ctrl+S"))
save_preset_action.triggered.connect(self.save_preset)
load_preset_action = QAction("&Load Preset", self)
load_preset_action.setShortcut(QKeySequence("Ctrl+S"))
load_preset_action.triggered.connect(self.load_preset)
file_menu.addAction(close_action)
file_menu.addAction(save_preset_action)
file_menu.addAction(load_preset_action)
# Help menu
help_menu = menubar.addMenu("&Help")
help_menu = self.menubar.addMenu("&Help")
about_action = QAction("&About", self)
about_action.setShortcut(QKeySequence("Ctrl+Q"))
about_action.triggered.connect(self.show_about)
help_menu.addAction(about_action)
# Content widget
content_widget = QWidget()
content_layout = QHBoxLayout(content_widget)
# Wrap content in a scroll area
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True) # Allow content to resize
scroll_area.setWidget(content_widget)
# Add scroll area to main layout
main_layout.addWidget(scroll_area)
self.setCentralWidget(main_widget)
# Styling
self.setStyleSheet(
"""
AutoGGUF {
background-color: #2b2b2b;
border-radius: 10px;
}
"""
)
# Initialize threads
self.quant_threads = []
# Timer for updating system info
self.timer = QTimer()
self.timer.timeout.connect(self.update_system_info)
self.timer.start(200)
# Add all widgets to content_layout
left_widget = QWidget()
right_widget = QWidget()
left_widget.setMinimumWidth(1100)
right_widget.setMinimumWidth(400)
left_layout = QVBoxLayout(left_widget)
right_layout = QVBoxLayout(right_widget)
# Add left and right widgets to the main layout
main_layout.addWidget(left_widget, 2)
main_layout.addWidget(right_widget, 1)
content_layout.addWidget(left_widget)
content_layout.addWidget(right_widget)
# System info
self.ram_bar = QProgressBar()
self.cpu_label = QLabel(CPU_USAGE)
self.cpu_bar = QProgressBar()
self.cpu_label = QLabel()
self.gpu_monitor = GPUMonitor()
left_layout.addWidget(QLabel(RAM_USAGE))
left_layout.addWidget(self.ram_bar)
left_layout.addWidget(self.cpu_label)
left_layout.addWidget(QLabel(CPU_USAGE))
left_layout.addWidget(self.cpu_bar)
left_layout.addWidget(QLabel(GPU_USAGE))
left_layout.addWidget(self.gpu_monitor)
# Modify the backend selection
# Backend selection
backend_layout = QHBoxLayout()
self.backend_combo = QComboBox()
self.refresh_backends_button = QPushButton(REFRESH_BACKENDS)
@ -125,10 +235,9 @@ def __init__(self):
backend_layout.addWidget(self.refresh_backends_button)
left_layout.addLayout(backend_layout)
# Modify the Download llama.cpp section
# Download llama.cpp section
download_group = QGroupBox(DOWNLOAD_LLAMACPP)
download_layout = QFormLayout()
self.release_combo = QComboBox()
self.refresh_releases_button = QPushButton(REFRESH_RELEASES)
self.refresh_releases_button.clicked.connect(self.refresh_releases)
@ -136,35 +245,25 @@ def __init__(self):
release_layout.addWidget(self.release_combo)
release_layout.addWidget(self.refresh_releases_button)
download_layout.addRow(SELECT_RELEASE, release_layout)
self.asset_combo = QComboBox()
self.asset_combo.currentIndexChanged.connect(self.update_cuda_option)
download_layout.addRow(SELECT_ASSET, self.asset_combo)
self.cuda_extract_checkbox = QCheckBox(EXTRACT_CUDA_FILES)
self.cuda_extract_checkbox.setVisible(False)
download_layout.addRow(self.cuda_extract_checkbox)
self.cuda_backend_label = QLabel(SELECT_CUDA_BACKEND)
self.cuda_backend_label.setVisible(False)
self.backend_combo_cuda = QComboBox()
self.backend_combo_cuda.setVisible(False)
download_layout.addRow(self.cuda_backend_label, self.backend_combo_cuda)
self.download_progress = QProgressBar()
self.download_button = QPushButton(DOWNLOAD)
self.download_button.clicked.connect(self.download_llama_cpp)
download_layout.addRow(self.download_progress)
download_layout.addRow(self.download_button)
download_group.setLayout(download_layout)
right_layout.addWidget(download_group)
# Initialize releases and backends
if os.environ.get("AUTOGGUF_CHECK_BACKEND", "").lower() == "enabled":
self.refresh_releases()
self.refresh_backends()
# Models path
models_layout = QHBoxLayout()
self.models_input = QLineEdit(os.path.abspath("models"))
@ -374,18 +473,17 @@ def __init__(self):
kv_override_main_layout,
)
quant_options_widget.setLayout(quant_options_layout)
quant_options_scroll.setWidget(quant_options_widget)
quant_options_scroll.setWidgetResizable(True)
left_layout.addWidget(quant_options_scroll)
# Add this after the KV override section
self.extra_arguments = QLineEdit()
quant_options_layout.addRow(
self.create_label(EXTRA_ARGUMENTS, "Additional command-line arguments"),
self.extra_arguments,
)
quant_options_widget.setLayout(quant_options_layout)
quant_options_scroll.setWidget(quant_options_widget)
quant_options_scroll.setWidgetResizable(True)
left_layout.addWidget(quant_options_scroll)
# Quantize button layout
quantize_layout = QHBoxLayout()
quantize_button = QPushButton(QUANTIZE_MODEL)
@ -443,23 +541,21 @@ def __init__(self):
)
self.imatrix_frequency = QSpinBox()
self.imatrix_frequency.setRange(1, 100) # Set the range from 1 to 100
self.imatrix_frequency.setValue(1) # Set a default value
self.imatrix_frequency.setRange(1, 100)
self.imatrix_frequency.setValue(1)
imatrix_layout.addRow(
self.create_label(OUTPUT_FREQUENCY, HOW_OFTEN_TO_SAVE_IMATRIX),
self.imatrix_frequency,
)
# Context size input (now a spinbox)
self.imatrix_ctx_size = QSpinBox()
self.imatrix_ctx_size.setRange(1, 1048576) # Up to one million tokens
self.imatrix_ctx_size.setValue(512) # Set a default value
self.imatrix_ctx_size.setRange(1, 1048576)
self.imatrix_ctx_size.setValue(512)
imatrix_layout.addRow(
self.create_label(CONTEXT_SIZE, CONTEXT_SIZE_FOR_IMATRIX),
self.imatrix_ctx_size,
)
# Threads input with slider and spinbox
threads_layout = QHBoxLayout()
self.threads_slider = QSlider(Qt.Orientation.Horizontal)
self.threads_slider.setRange(1, 64)
@ -476,7 +572,6 @@ def __init__(self):
self.create_label(THREADS, NUMBER_OF_THREADS_FOR_IMATRIX), threads_layout
)
# GPU Offload for IMatrix (corrected version)
gpu_offload_layout = QHBoxLayout()
self.gpu_offload_slider = QSlider(Qt.Orientation.Horizontal)
self.gpu_offload_slider.setRange(0, 200)
@ -530,7 +625,6 @@ def __init__(self):
lora_output_layout,
)
# Output Type Dropdown
self.lora_output_type_combo = QComboBox()
self.lora_output_type_combo.addItems(["GGML", "GGUF"])
self.lora_output_type_combo.currentIndexChanged.connect(
@ -541,30 +635,24 @@ def __init__(self):
self.lora_output_type_combo,
)
# Base Model Path (initially hidden)
self.base_model_label = self.create_label(BASE_MODEL, SELECT_BASE_MODEL_FILE)
self.base_model_path = QLineEdit()
base_model_button = QPushButton(BROWSE)
base_model_button.clicked.connect(self.browse_base_model)
base_model_layout = QHBoxLayout()
base_model_layout.addWidget(self.base_model_path, 1) # Give it a stretch factor
base_model_layout.addWidget(self.base_model_path, 1)
base_model_layout.addWidget(base_model_button)
self.base_model_widget = QWidget()
self.base_model_widget.setLayout(base_model_layout)
# Create a wrapper widget to hold both label and input
self.base_model_wrapper = QWidget()
wrapper_layout = QHBoxLayout(self.base_model_wrapper)
wrapper_layout.addWidget(self.base_model_label)
wrapper_layout.addWidget(self.base_model_widget, 1) # Give it a stretch factor
wrapper_layout.setContentsMargins(
0, 0, 0, 0
) # Remove margins for better alignment
wrapper_layout.addWidget(self.base_model_widget, 1)
wrapper_layout.setContentsMargins(0, 0, 0, 0)
# Add the wrapper to the layout
lora_layout.addRow(self.base_model_wrapper)
# Set initial visibility
self.update_base_model_visibility(self.lora_output_type_combo.currentIndex())
lora_convert_button = QPushButton(CONVERT_LORA)
@ -598,7 +686,6 @@ def __init__(self):
self.create_label(OUTPUT, SELECT_OUTPUT_FILE), export_lora_output_layout
)
# GGML LoRA Adapters
self.export_lora_adapters = QListWidget()
add_adapter_button = QPushButton(ADD_ADAPTER)
add_adapter_button.clicked.connect(self.add_lora_adapter)
@ -612,10 +699,9 @@ def __init__(self):
adapters_layout,
)
# Threads
self.export_lora_threads = QSpinBox()
self.export_lora_threads.setRange(1, 64)
self.export_lora_threads.setValue(8) # Default value
self.export_lora_threads.setValue(8)
export_lora_layout.addRow(
self.create_label(THREADS, NUMBER_OF_THREADS_FOR_LORA_EXPORT),
self.export_lora_threads,
@ -626,9 +712,7 @@ def __init__(self):
export_lora_layout.addRow(export_lora_button)
export_lora_group.setLayout(export_lora_layout)
right_layout.addWidget(
export_lora_group
) # Add the Export LoRA group to the right layout
right_layout.addWidget(export_lora_group)
# HuggingFace to GGUF Conversion
hf_to_gguf_group = QGroupBox(HF_TO_GGUF_CONVERSION)
@ -686,21 +770,98 @@ def __init__(self):
self.task_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.task_list.customContextMenuRequested.connect(self.show_task_context_menu)
# Set inital state
# Set initial state
self.update_base_model_visibility(self.lora_output_type_combo.currentIndex())
# Timer for updating system info
self.timer = QTimer()
self.timer.timeout.connect(self.update_system_info)
self.timer.start(200)
# Initialize releases and backends
if os.environ.get("AUTOGGUF_CHECK_BACKEND", "").lower() == "enabled":
self.refresh_releases()
self.refresh_backends()
# Initialize threads
self.quant_threads = []
# Load theme based on environment variable
theme_path = os.environ.get("AUTOGGUF_THEME")
if theme_path:
try:
with open(theme_path, "r") as f:
theme = f.read()
self.setStyleSheet(theme)
except (FileNotFoundError, OSError):
# If the specified theme file is not found or inaccessible,
# fall back to the default theme
with open(resource_path("assets/default.css"), "r") as f:
default_theme = f.read()
self.setStyleSheet(default_theme)
else:
# If the environment variable is not set, use the default theme
with open(resource_path("assets/default.css"), "r") as f:
default_theme = f.read()
self.setStyleSheet(default_theme)
# Load models
self.load_models()
self.logger.info(AUTOGGUF_INITIALIZATION_COMPLETE)
def keyPressEvent(self, event):
if event.modifiers() == Qt.ControlModifier:
if (
event.key() == Qt.Key_Equal
): # Qt.Key_Plus doesn't work on some keyboards
self.resize_window(larger=True)
elif event.key() == Qt.Key_Minus:
self.resize_window(larger=False)
elif event.key() == Qt.Key_0:
self.reset_size()
super().keyPressEvent(event)
def resize_window(self, larger):
factor = 1.1 if larger else 1 / 1.1
current_width = self.width()
current_height = self.height()
new_width = int(current_width * factor)
new_height = int(current_height * factor)
self.resize(new_width, new_height)
def reset_size(self):
self.resize(self.default_width, self.default_height)
def parse_resolution(self):
res = os.environ.get("AUTOGGUF_RESOLUTION", "1650x1100")
try:
width, height = map(int, res.split("x"))
if width <= 0 or height <= 0:
raise ValueError
return width, height
except (ValueError, AttributeError):
return 1650, 1100
def resizeEvent(self, event):
super().resizeEvent(event)
path = QPainterPath()
path.addRoundedRect(self.rect(), 10, 10)
mask = QRegion(path.toFillPolygon().toPolygon())
self.setMask(mask)
def closeEvent(self, event: QCloseEvent):
self.logger.info(APPLICATION_CLOSING)
if self.quant_threads:
reply = QMessageBox.question(
self,
WARNING,
TASK_RUNNING_WARNING,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if reply == QMessageBox.StandardButton.Yes:
for thread in self.quant_threads:
thread.terminate()
event.accept()
else:
event.ignore()
else:
event.accept()
self.logger.info(APPLICATION_CLOSED)
def refresh_backends(self):
self.logger.info(REFRESHING_BACKENDS)
llama_bin = os.path.abspath("llama_bin")
@ -725,80 +886,6 @@ def refresh_backends(self):
self.backend_combo.setEnabled(False)
self.logger.info(FOUND_VALID_BACKENDS.format(self.backend_combo.count()))
def save_preset(self):
self.logger.info(SAVING_PRESET)
preset = {
"quant_types": [item.text() for item in self.quant_type.selectedItems()],
"allow_requantize": self.allow_requantize.isChecked(),
"leave_output_tensor": self.leave_output_tensor.isChecked(),
"pure": self.pure.isChecked(),
"imatrix": self.imatrix.text(),
"include_weights": self.include_weights.text(),
"exclude_weights": self.exclude_weights.text(),
"use_output_tensor_type": self.use_output_tensor_type.isChecked(),
"output_tensor_type": self.output_tensor_type.currentText(),
"use_token_embedding_type": self.use_token_embedding_type.isChecked(),
"token_embedding_type": self.token_embedding_type.currentText(),
"keep_split": self.keep_split.isChecked(),
"kv_overrides": [
entry.get_raw_override_string() for entry in self.kv_override_entries
],
"extra_arguments": self.extra_arguments.text(),
}
file_name, _ = QFileDialog.getSaveFileName(self, SAVE_PRESET, "", JSON_FILES)
if file_name:
with open(file_name, "w") as f:
json.dump(preset, f, indent=4)
QMessageBox.information(
self, PRESET_SAVED, PRESET_SAVED_TO.format(file_name)
)
self.logger.info(PRESET_SAVED_TO.format(file_name))
def load_preset(self):
self.logger.info(LOADING_PRESET)
file_name, _ = QFileDialog.getOpenFileName(self, LOAD_PRESET, "", JSON_FILES)
if file_name:
with open(file_name, "r") as f:
preset = json.load(f)
self.quant_type.clearSelection()
for quant_type in preset.get("quant_types", []):
items = self.quant_type.findItems(quant_type, Qt.MatchExactly)
if items:
items[0].setSelected(True)
self.allow_requantize.setChecked(preset.get("allow_requantize", False))
self.leave_output_tensor.setChecked(
preset.get("leave_output_tensor", False)
)
self.pure.setChecked(preset.get("pure", False))
self.imatrix.setText(preset.get("imatrix", ""))
self.include_weights.setText(preset.get("include_weights", ""))
self.exclude_weights.setText(preset.get("exclude_weights", ""))
self.use_output_tensor_type.setChecked(
preset.get("use_output_tensor_type", False)
)
self.output_tensor_type.setCurrentText(preset.get("output_tensor_type", ""))
self.use_token_embedding_type.setChecked(
preset.get("use_token_embedding_type", False)
)
self.token_embedding_type.setCurrentText(
preset.get("token_embedding_type", "")
)
self.keep_split.setChecked(preset.get("keep_split", False))
self.extra_arguments.setText(preset.get("extra_arguments", ""))
# Clear existing KV overrides and add new ones
for entry in self.kv_override_entries:
self.remove_kv_override(entry)
for override in preset.get("kv_overrides", []):
self.add_kv_override(override)
QMessageBox.information(
self, PRESET_LOADED, PRESET_LOADED_FROM.format(file_name)
)
self.logger.info(PRESET_LOADED_FROM.format(file_name))
def save_task_preset(self, task_item):
self.logger.info(SAVING_TASK_PRESET.format(task_item.task_name))
for thread in self.quant_threads:

74
src/presets.py Normal file
View File

@ -0,0 +1,74 @@
import json
from PySide6.QtWidgets import QFileDialog, QMessageBox
from Localizations import *
def save_preset(self):
self.logger.info(SAVING_PRESET)
preset = {
"quant_types": [item.text() for item in self.quant_type.selectedItems()],
"allow_requantize": self.allow_requantize.isChecked(),
"leave_output_tensor": self.leave_output_tensor.isChecked(),
"pure": self.pure.isChecked(),
"imatrix": self.imatrix.text(),
"include_weights": self.include_weights.text(),
"exclude_weights": self.exclude_weights.text(),
"use_output_tensor_type": self.use_output_tensor_type.isChecked(),
"output_tensor_type": self.output_tensor_type.currentText(),
"use_token_embedding_type": self.use_token_embedding_type.isChecked(),
"token_embedding_type": self.token_embedding_type.currentText(),
"keep_split": self.keep_split.isChecked(),
"kv_overrides": [
entry.get_raw_override_string() for entry in self.kv_override_entries
],
"extra_arguments": self.extra_arguments.text(),
}
file_name, _ = QFileDialog.getSaveFileName(self, SAVE_PRESET, "", JSON_FILES)
if file_name:
with open(file_name, "w") as f:
json.dump(preset, f, indent=4)
QMessageBox.information(self, PRESET_SAVED, PRESET_SAVED_TO.format(file_name))
self.logger.info(PRESET_SAVED_TO.format(file_name))
def load_preset(self):
self.logger.info(LOADING_PRESET)
file_name, _ = QFileDialog.getOpenFileName(self, LOAD_PRESET, "", JSON_FILES)
if file_name:
with open(file_name, "r") as f:
preset = json.load(f)
self.quant_type.clearSelection()
for quant_type in preset.get("quant_types", []):
items = self.quant_type.findItems(quant_type, Qt.MatchExactly)
if items:
items[0].setSelected(True)
self.allow_requantize.setChecked(preset.get("allow_requantize", False))
self.leave_output_tensor.setChecked(preset.get("leave_output_tensor", False))
self.pure.setChecked(preset.get("pure", False))
self.imatrix.setText(preset.get("imatrix", ""))
self.include_weights.setText(preset.get("include_weights", ""))
self.exclude_weights.setText(preset.get("exclude_weights", ""))
self.use_output_tensor_type.setChecked(
preset.get("use_output_tensor_type", False)
)
self.output_tensor_type.setCurrentText(preset.get("output_tensor_type", ""))
self.use_token_embedding_type.setChecked(
preset.get("use_token_embedding_type", False)
)
self.token_embedding_type.setCurrentText(preset.get("token_embedding_type", ""))
self.keep_split.setChecked(preset.get("keep_split", False))
self.extra_arguments.setText(preset.get("extra_arguments", ""))
# Clear existing KV overrides and add new ones
for entry in self.kv_override_entries:
self.remove_kv_override(entry)
for override in preset.get("kv_overrides", []):
self.add_kv_override(override)
QMessageBox.information(
self, PRESET_LOADED, PRESET_LOADED_FROM.format(file_name)
)
self.logger.info(PRESET_LOADED_FROM.format(file_name))

View File

@ -17,6 +17,7 @@ def update_system_info(self):
)
)
self.cpu_label.setText(CPU_USAGE_FORMAT.format(cpu))
self.cpu_bar.setValue(int(cpu))
def update_download_progress(self, progress):