minimal
This commit is contained in:
parent
b0eea9d62e
commit
b44bcd4810
12 changed files with 44 additions and 606 deletions
|
|
@ -1 +0,0 @@
|
|||
from .app import run
|
||||
13
gfd/app.py
13
gfd/app.py
|
|
@ -1,13 +0,0 @@
|
|||
from PySide6 import QtWidgets
|
||||
from gfd.ui.main_window import GFDWidget
|
||||
from gfd.i18n import Translator
|
||||
|
||||
|
||||
def run(lang="es"):
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
tr = Translator(lang)
|
||||
window = GFDWidget(tr)
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
import time
|
||||
import re
|
||||
import unicodedata
|
||||
from gfd.core import sfd
|
||||
|
||||
SUPPORTED_INSTALLERS = [
|
||||
{
|
||||
"os_type": "ubuntu24",
|
||||
"name": "Usuarios Linux - Ubuntu 24.04 LTS (DEB 64bits) - 78 MB",
|
||||
"md5": "bdc871e15f2096f930b285f0ed799aa0",
|
||||
},
|
||||
{
|
||||
"os_type": "debian",
|
||||
"name": "Usuarios Linux - Ubuntu 24.04 LTS (DEB 64bits) - 78 MB",
|
||||
"md5": "bdc871e15f2096f930b285f0ed799aa0",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _normalize(t):
|
||||
return re.sub(r"\s+", " ", unicodedata.normalize("NFKD", t).lower()).strip()
|
||||
|
||||
|
||||
def _fetch(max_attempts=3, delay=2):
|
||||
for i in range(max_attempts):
|
||||
data = sfd.fetchInstallerOptions()
|
||||
|
||||
if data:
|
||||
return data
|
||||
|
||||
if i < max_attempts - 1:
|
||||
time.sleep(delay)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def get_available_installers(os_type, installers=None):
|
||||
"""
|
||||
Return a list of confirmed available installers for the given OS type.
|
||||
|
||||
If an installer is listed, it means that for a given OS type:
|
||||
- There is a supported installation routine.
|
||||
- There is a downloadable archive from Soporte Firma Digital.
|
||||
"""
|
||||
src = installers or SUPPORTED_INSTALLERS
|
||||
locals_ = [i for i in src if i["os_type"] == os_type]
|
||||
|
||||
if not locals_:
|
||||
return []
|
||||
|
||||
remote = _fetch()
|
||||
|
||||
if not remote:
|
||||
return []
|
||||
|
||||
rmap = {_normalize(r["name"]): r.get("md5") for r in remote}
|
||||
confirmed = []
|
||||
|
||||
for local in locals_:
|
||||
lname, lmd5 = (
|
||||
_normalize(local["name"]),
|
||||
(local.get("md5") or "").lower() or None,
|
||||
)
|
||||
rmd5 = rmap.get(lname)
|
||||
|
||||
if (lmd5 == rmd5) or (lmd5 is None and rmd5 is None):
|
||||
confirmed.append((local["name"], lmd5))
|
||||
|
||||
return confirmed
|
||||
|
||||
|
||||
# TODO: Routine to check for installed version
|
||||
def get_installed_version():
|
||||
"""
|
||||
Return the current installed version data, or empty if not installed.
|
||||
"""
|
||||
return [] # Return always empty for now
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
os_type = "debian"
|
||||
print(f"=== TEST: {os_type.upper()} Installer Validation ===")
|
||||
confirmed = get_available_installers(os_type)
|
||||
print(
|
||||
f"{len(confirmed)} confirmed installer(s):"
|
||||
if confirmed
|
||||
else "No confirmed installers found."
|
||||
)
|
||||
|
||||
for n, m in confirmed:
|
||||
print(f" - {n} (MD5={m or 'N/A'})")
|
||||
|
||||
installed = get_installed_version()
|
||||
|
||||
if not installed:
|
||||
print("Status: NOT INSTALLED")
|
||||
else:
|
||||
print(f"Installed: {installed[0]} MD5={installed[1]}")
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
import platform
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _read_os_release():
|
||||
"""Read and parse /etc/os-release or /usr/lib/os-release if available."""
|
||||
paths = [Path("/etc/os-release"), Path("/usr/lib/os-release")]
|
||||
data = {}
|
||||
|
||||
for path in paths:
|
||||
if path.exists():
|
||||
try:
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
if "=" in line:
|
||||
key, val = line.split("=", 1)
|
||||
data[key.strip()] = val.strip().strip('"')
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
return data
|
||||
|
||||
|
||||
def get_os_type():
|
||||
"""
|
||||
Detect and return a simple OS type string.
|
||||
|
||||
Returns one of:
|
||||
'macos', 'windows', 'ubuntu24', 'ubuntu22', 'ubuntu20',
|
||||
'debian', 'arch', 'rpm', or None if unsupported.
|
||||
"""
|
||||
system = platform.system().lower()
|
||||
os_type = None
|
||||
|
||||
if system == "darwin":
|
||||
os_type = "macos"
|
||||
|
||||
elif system == "windows":
|
||||
os_type = "windows"
|
||||
|
||||
elif system == "linux":
|
||||
info = _read_os_release()
|
||||
id_name = info.get("ID", "").lower()
|
||||
version_id = info.get("VERSION_ID", "")
|
||||
|
||||
# Ubuntu
|
||||
if id_name == "ubuntu":
|
||||
if version_id.startswith("24"):
|
||||
os_type = "ubuntu24"
|
||||
elif version_id.startswith("22"):
|
||||
os_type = "ubuntu22"
|
||||
elif version_id.startswith("20"):
|
||||
os_type = "ubuntu20"
|
||||
|
||||
# Debian (11 or newer supported)
|
||||
elif id_name == "debian":
|
||||
try:
|
||||
version_num = int(re.findall(r"\d+", version_id or "0")[0])
|
||||
except IndexError:
|
||||
version_num = 0
|
||||
if version_num >= 11:
|
||||
os_type = "debian"
|
||||
|
||||
# Arch / RPM-based
|
||||
elif "arch" in id_name:
|
||||
os_type = "arch"
|
||||
elif id_name in ["fedora", "rhel", "centos", "rocky", "alma", "opensuse"]:
|
||||
os_type = "rpm"
|
||||
|
||||
return os_type
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Detected OS type:", get_os_type() or "NOT SUPPORTED")
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
"""
|
||||
routines.py - OS-specific installer routines
|
||||
|
||||
Defines a registry of install routines by OS type.
|
||||
If the current OS is supported and has an associated installer,
|
||||
the matching routine can be executed to perform installation steps.
|
||||
"""
|
||||
|
||||
from gfd.core.osinfo import get_os_type
|
||||
|
||||
|
||||
def install_ubuntu24():
|
||||
"""Simulated install routine for Ubuntu 24.04."""
|
||||
print("Running Ubuntu 24.04 (DEB) installation routine...")
|
||||
|
||||
|
||||
def install_debian():
|
||||
"""Simulated install routine for Debian."""
|
||||
print("Running Debian installation routine (using Ubuntu 24 package)...")
|
||||
|
||||
|
||||
SUPPORTED_ROUTINES = {
|
||||
"ubuntu24": install_ubuntu24,
|
||||
"debian": install_debian,
|
||||
}
|
||||
|
||||
|
||||
def run_install_routine(os_type=None):
|
||||
"""
|
||||
Run the appropriate install routine for the given OS type.
|
||||
|
||||
Args:
|
||||
os_type (str | None): OS key (e.g. 'ubuntu24', 'macos').
|
||||
If None, automatically detects current OS.
|
||||
|
||||
Returns:
|
||||
bool: True if the OS is supported and a routine was executed, else False.
|
||||
"""
|
||||
os_type = os_type or get_os_type()
|
||||
if not os_type:
|
||||
return False
|
||||
|
||||
routine = SUPPORTED_ROUTINES.get(os_type)
|
||||
if not routine:
|
||||
return False
|
||||
|
||||
routine()
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_install_routine()
|
||||
116
gfd/core/sfd.py
116
gfd/core/sfd.py
|
|
@ -1,116 +0,0 @@
|
|||
"""
|
||||
sfd.py - Installer List Parser for Soporte Firma Digital
|
||||
|
||||
This module is responsible for fetching and parsing the list of available
|
||||
digital signature installers from the official Soporte Firma Digital website.
|
||||
|
||||
It retrieves both the visible installer names (from the page’s <select> element)
|
||||
and the associated MD5 hashes (embedded within inline JavaScript).
|
||||
|
||||
The parsed results are used by the UI layer to display which installers
|
||||
are available and to match system OS types to the appropriate installer.
|
||||
"""
|
||||
|
||||
import re
|
||||
import unicodedata
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
SFD_URL = "https://soportefirmadigital.com/sfdj/dl.aspx"
|
||||
|
||||
|
||||
def _normalize_text(text: str) -> str:
|
||||
"""
|
||||
Normalize text for consistent matching.
|
||||
|
||||
Steps:
|
||||
1. Remove accents and diacritics using Unicode normalization.
|
||||
2. Replace consecutive spaces with a single space.
|
||||
3. Convert to lowercase and strip leading/trailing whitespace.
|
||||
|
||||
Args:
|
||||
text (str): Raw string to normalize.
|
||||
|
||||
Returns:
|
||||
str: Cleaned, normalized string suitable for fuzzy matching.
|
||||
"""
|
||||
text = unicodedata.normalize("NFKD", text)
|
||||
text = "".join(c for c in text if not unicodedata.combining(c))
|
||||
return re.sub(r"\s+", " ", text).strip().lower()
|
||||
|
||||
|
||||
def fetchInstallerOptions(url: str = SFD_URL):
|
||||
"""
|
||||
Fetch and parse the installer list from the Soporte Firma Digital website.
|
||||
|
||||
Example output:
|
||||
[
|
||||
{"name": "Usuarios Linux - Ubuntu 24.04 LTS (DEB 64bits) - 78 MB",
|
||||
"md5": "bdc871e15f2096f930b285f0ed799aa0"},
|
||||
{"name": "Usuarios Windows JCOP3 - 189 MB",
|
||||
"md5": "596c347e409d00388c3c1ee0a72b96c0"},
|
||||
...
|
||||
]
|
||||
|
||||
Args:
|
||||
url (str, optional): Target URL to fetch. Defaults to SFD_URL.
|
||||
|
||||
Returns:
|
||||
list[dict]: Each dict contains:
|
||||
- "name" (str): Installer display name.
|
||||
- "md5" (str | None): MD5 checksum (if found), else None.
|
||||
"""
|
||||
try:
|
||||
r = requests.get(url, timeout=8)
|
||||
r.raise_for_status()
|
||||
except requests.RequestException:
|
||||
# Network error, timeout, or unreachable server
|
||||
return []
|
||||
|
||||
soup = BeautifulSoup(r.text, "html.parser")
|
||||
|
||||
# Extract installer names from the dropdown list
|
||||
installers = [
|
||||
opt.text.strip()
|
||||
for opt in soup.select("#ctl00_certContents_ddlInstaladores option")
|
||||
]
|
||||
if not installers:
|
||||
return []
|
||||
|
||||
# Parse embedded JavaScript to extract name, MD5 mapping
|
||||
md5s = {}
|
||||
for name, md5 in re.findall(
|
||||
r"text\s*==\s*'([^']+)'.*?MD5\s*[:=]\s*([A-Za-z0-9]+)",
|
||||
r.text,
|
||||
re.DOTALL,
|
||||
):
|
||||
md5 = md5.lower()
|
||||
if re.fullmatch(r"[0-9a-f]{32}", md5): # Only valid 32-character MD5
|
||||
md5s[_normalize_text(name)] = md5
|
||||
|
||||
# Merge name list with MD5 map
|
||||
results = []
|
||||
for inst in installers:
|
||||
n = _normalize_text(inst)
|
||||
md5 = md5s.get(n)
|
||||
|
||||
# Try fuzzy match if exact normalization key not found
|
||||
if not md5:
|
||||
for key, val in md5s.items():
|
||||
if key in n or n in key:
|
||||
md5 = val
|
||||
break
|
||||
|
||||
results.append({"name": inst, "md5": md5})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Fetch and print all installers to stdout.
|
||||
"""
|
||||
installers = fetchInstallerOptions()
|
||||
print(f"Fetched {len(installers)} installers from {SFD_URL}\n")
|
||||
for item in installers:
|
||||
print(f"{item['name']:<70} MD5={item['md5'] or 'N/A'}")
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
translations = {
|
||||
"window_title": "Digital Signature Manager",
|
||||
"no_install_message": "Digital signature not installed",
|
||||
"recommended_version": "Recommended version:",
|
||||
"install": "Install",
|
||||
"installed_version": "Installed version:",
|
||||
"latest_version_installed": "Latest version installed",
|
||||
"update_digital_signature": "Update Digital Signature",
|
||||
"no_installers_found": "Could not fetch the installer list",
|
||||
"loading_installers": "Loading installers",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
translations = {
|
||||
"window_title": "Gestor de Firma Digital",
|
||||
"no_install_message": "No tiene la firma digital instalada",
|
||||
"recommended_version": "Versión recomendada:",
|
||||
"install": "Instalar",
|
||||
"installed_version": "Versión instalada:",
|
||||
"latest_version_installed": "Última versión instalada",
|
||||
"update_digital_signature": "Actualizar Firma Digital",
|
||||
"no_installers_found": "No se pudo obtener la lista de instaladores", # ES
|
||||
"loading_installers": "Cargando instaladores",
|
||||
}
|
||||
|
|
|
|||
44
gfd/sfd.py
Normal file
44
gfd/sfd.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import requests as req
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from typing import cast
|
||||
|
||||
SFD_INSTALLERS_URL = "https://soportefirmadigital.com/sfdj/dl.aspx"
|
||||
SFD_INSTALLERS_SELECT_ID = "#ctl00_certContents_ddlInstaladores"
|
||||
|
||||
|
||||
def get_installers(
|
||||
url: str = SFD_INSTALLERS_URL,
|
||||
select: str = SFD_INSTALLERS_SELECT_ID,
|
||||
timeout: int = 10,
|
||||
) -> dict[str, str]:
|
||||
try:
|
||||
r = req.get(url, timeout=timeout)
|
||||
r.raise_for_status()
|
||||
except req.exceptions.RequestException as e:
|
||||
raise RuntimeError(f"Error fetching installer list: {e}")
|
||||
|
||||
html = bs(r.text, "html.parser")
|
||||
|
||||
opts = html.select(f"{select} option")
|
||||
|
||||
if not opts:
|
||||
raise RuntimeError("Empty options")
|
||||
|
||||
return {
|
||||
cast(str, key): value
|
||||
for opt in opts
|
||||
if (key := opt.get("value")) and (value := opt.text.strip())
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
try:
|
||||
data = get_installers()
|
||||
|
||||
if data:
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False), flush=True)
|
||||
|
||||
except RuntimeError as e:
|
||||
raise SystemExit(e)
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
from PySide6 import QtWidgets, QtCore
|
||||
from gfd.core.osinfo import get_os_type
|
||||
from gfd.core.installers import get_available_installers, get_installed_version
|
||||
|
||||
|
||||
def check_installation_status():
|
||||
"""
|
||||
Determine system support and installer availability.
|
||||
|
||||
Returns:
|
||||
(installed: [version, md5] or [],
|
||||
available: list[[version, md5]],
|
||||
recommended: [version, md5] or None)
|
||||
"""
|
||||
os_type = get_os_type()
|
||||
|
||||
if not os_type:
|
||||
return [], [], None
|
||||
|
||||
available = get_available_installers(os_type)
|
||||
installed = get_installed_version()
|
||||
|
||||
if not available:
|
||||
return installed, [], None
|
||||
|
||||
# Assume the first installer in the list is the recommended one
|
||||
recommended = available[0]
|
||||
|
||||
return installed, available, recommended
|
||||
|
||||
|
||||
class GFDWidget(QtWidgets.QWidget):
|
||||
def __init__(self, tr):
|
||||
super().__init__()
|
||||
self.tr = tr
|
||||
self.setWindowTitle(self.tr("window_title"))
|
||||
self.resize(600, 400)
|
||||
|
||||
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
# Card container
|
||||
self.card = QtWidgets.QFrame()
|
||||
self.card_layout = QtWidgets.QVBoxLayout(self.card)
|
||||
self.card_layout.setSpacing(12)
|
||||
|
||||
# Title
|
||||
self.title = QtWidgets.QLabel(self.tr("window_title"))
|
||||
self.title.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.title.setStyleSheet("font-weight: bold; font-size: 16px;")
|
||||
self.card_layout.addWidget(self.title)
|
||||
|
||||
# Content area
|
||||
self.content_area = QtWidgets.QWidget()
|
||||
self.content_layout = QtWidgets.QVBoxLayout(self.content_area)
|
||||
self.content_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.card_layout.addWidget(self.content_area)
|
||||
|
||||
# Footer
|
||||
self.footer = QtWidgets.QHBoxLayout()
|
||||
self.footer.addStretch()
|
||||
self.refresh_btn = QtWidgets.QPushButton("⟳")
|
||||
self.refresh_btn.setFixedWidth(36)
|
||||
self.refresh_btn.clicked.connect(self.refresh_state)
|
||||
self.footer.addWidget(self.refresh_btn)
|
||||
self.card_layout.addLayout(self.footer)
|
||||
|
||||
self.main_layout.addWidget(self.card)
|
||||
self.setStyleSheet("""
|
||||
QPushButton {
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton[primary="true"] {
|
||||
background-color: #2f80ed;
|
||||
color: white;
|
||||
}
|
||||
QPushButton[primary="true"]:disabled {
|
||||
background-color: #a0c4ff;
|
||||
}
|
||||
QLabel, QComboBox {
|
||||
font-size: 16px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.states = ["not_installed", "installed", "update_available"]
|
||||
self.current_state = 0
|
||||
|
||||
# Run initial refresh
|
||||
self.refresh_state()
|
||||
|
||||
# --- LOGIC ---
|
||||
|
||||
def refresh_state(self):
|
||||
"""Fetch installer list and determine status."""
|
||||
self.refresh_btn.setEnabled(False)
|
||||
self.clear_content()
|
||||
loading_label = QtWidgets.QLabel(self.tr("loading_installers") + "…")
|
||||
loading_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.content_layout.addWidget(loading_label)
|
||||
|
||||
QtCore.QTimer.singleShot(150, self._refresh_check)
|
||||
|
||||
def _refresh_check(self):
|
||||
"""Evaluate OS type, installed version, and available installers."""
|
||||
self.installed, self.available, self.recommended = check_installation_status()
|
||||
|
||||
print("\n=== INSTALLER STATUS DEBUG ===")
|
||||
if not self.available:
|
||||
print("No available installers for this OS.")
|
||||
else:
|
||||
for i, (name, md5) in enumerate(self.available, start=1):
|
||||
print(f"{i:2d}. {name:<70} MD5={md5 or 'N/A'}")
|
||||
print("==============================\n")
|
||||
|
||||
# 🧩 Print simulated installed state
|
||||
if not self.installed:
|
||||
print("→ Status: NOT INSTALLED")
|
||||
else:
|
||||
print(f"→ Installed: {self.installed[0]} MD5={self.installed[1]}")
|
||||
|
||||
if not self.available:
|
||||
self.show_error()
|
||||
elif not self.recommended:
|
||||
print("→ Decision: OS NOT SUPPORTED")
|
||||
self.show_error()
|
||||
elif not self.installed:
|
||||
print(f"→ Decision: NOT INSTALLED (Recommended: {self.recommended[0]})")
|
||||
self.set_state("not_installed")
|
||||
else:
|
||||
installed_tuple = tuple(self.installed)
|
||||
available_tuples = [tuple(a) for a in self.available]
|
||||
|
||||
if installed_tuple in available_tuples:
|
||||
print("→ Decision: INSTALLED (latest version matches)")
|
||||
self.set_state("installed")
|
||||
else:
|
||||
print("→ Decision: UPDATE AVAILABLE (installed not in available list)")
|
||||
self.set_state("update_available")
|
||||
|
||||
self.refresh_btn.setEnabled(True)
|
||||
|
||||
def clear_content(self):
|
||||
"""Remove all widgets from content area."""
|
||||
for i in reversed(range(self.content_layout.count())):
|
||||
widget = self.content_layout.itemAt(i).widget()
|
||||
if widget:
|
||||
widget.setParent(None)
|
||||
|
||||
# --- UI STATES ---
|
||||
|
||||
def update_state(self):
|
||||
"""Render the current UI state."""
|
||||
self.clear_content()
|
||||
state = self.states[self.current_state]
|
||||
|
||||
if state == "not_installed":
|
||||
self.show_not_installed()
|
||||
elif state == "installed":
|
||||
self.show_installed()
|
||||
elif state == "update_available":
|
||||
self.show_update_available()
|
||||
|
||||
def show_error(self):
|
||||
"""Shown when no installer data is available."""
|
||||
self.clear_content()
|
||||
lbl = QtWidgets.QLabel(self.tr("no_installers_found"))
|
||||
lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.content_layout.addWidget(lbl)
|
||||
|
||||
def show_not_installed(self):
|
||||
recommended_name = self.recommended[0] if self.recommended else "?"
|
||||
lbl = QtWidgets.QLabel(
|
||||
f"{self.tr('no_install_message')}<br>"
|
||||
f"{self.tr('recommended_version')}<br><b>{recommended_name}</b>"
|
||||
)
|
||||
lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.content_layout.addWidget(lbl)
|
||||
|
||||
btn = QtWidgets.QPushButton(self.tr("install"))
|
||||
btn.setProperty("primary", True)
|
||||
self.content_layout.addWidget(btn)
|
||||
|
||||
combo = QtWidgets.QComboBox()
|
||||
for version, md5 in self.available:
|
||||
combo.addItem(f"{version} (MD5: {md5 or 'N/A'})")
|
||||
self.content_layout.addWidget(combo)
|
||||
|
||||
def show_installed(self):
|
||||
version, md5 = self.installed
|
||||
lbl = QtWidgets.QLabel(
|
||||
f"{self.tr('installed_version')}<br><b>{version}</b><br>MD5: {md5 or 'N/A'}"
|
||||
)
|
||||
lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.content_layout.addWidget(lbl)
|
||||
|
||||
btn = QtWidgets.QPushButton(self.tr("latest_version_installed"))
|
||||
btn.setEnabled(False)
|
||||
self.content_layout.addWidget(btn)
|
||||
|
||||
def show_update_available(self):
|
||||
version, md5 = self.installed
|
||||
lbl = QtWidgets.QLabel(
|
||||
f"{self.tr('installed_version')}<br><b>{version}</b><br>MD5: {md5 or 'N/A'}"
|
||||
)
|
||||
lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.content_layout.addWidget(lbl)
|
||||
|
||||
btn = QtWidgets.QPushButton(self.tr("update_digital_signature"))
|
||||
btn.setProperty("primary", True)
|
||||
self.content_layout.addWidget(btn)
|
||||
|
||||
combo = QtWidgets.QComboBox()
|
||||
for version, md5 in self.available:
|
||||
combo.addItem(f"{version} (MD5: {md5 or 'N/A'})")
|
||||
self.content_layout.addWidget(combo)
|
||||
|
||||
def set_state(self, state):
|
||||
if state in self.states:
|
||||
self.current_state = self.states.index(state)
|
||||
self.update_state()
|
||||
4
main.py
4
main.py
|
|
@ -1,4 +0,0 @@
|
|||
import gfd
|
||||
|
||||
if __name__ == "__main__":
|
||||
gfd.run("es")
|
||||
|
|
@ -1,12 +1,2 @@
|
|||
beautifulsoup4==4.14.2
|
||||
certifi==2025.10.5
|
||||
charset-normalizer==3.4.3
|
||||
idna==3.10
|
||||
PySide6==6.9.2
|
||||
PySide6_Addons==6.9.2
|
||||
PySide6_Essentials==6.9.2
|
||||
requests==2.32.5
|
||||
shiboken6==6.9.2
|
||||
soupsieve==2.8
|
||||
typing_extensions==4.15.0
|
||||
urllib3==2.5.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue