summaryrefslogtreecommitdiff
path: root/raveos-center/raveos_center
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-center/raveos_center')
-rw-r--r--raveos-center/raveos_center/__init__.py1
-rw-r--r--raveos-center/raveos_center/__main__.py5
-rw-r--r--raveos-center/raveos_center/adat/logo.pngbin0 -> 128202 bytes
-rw-r--r--raveos-center/raveos_center/alkalmazas.py192
-rw-r--r--raveos-center/raveos_center/beallitasok.py30
-rw-r--r--raveos-center/raveos_center/diagnosztika.py85
-rw-r--r--raveos-center/raveos_center/driverek.py179
-rw-r--r--raveos-center/raveos_center/elemek.py387
-rw-r--r--raveos-center/raveos_center/folyamat.py68
-rw-r--r--raveos-center/raveos_center/gaming.py272
-rw-r--r--raveos-center/raveos_center/hatter.py101
-rw-r--r--raveos-center/raveos_center/ikonok.py30
-rw-r--r--raveos-center/raveos_center/jegyek_api.py135
-rw-r--r--raveos-center/raveos_center/karbantartas.py141
-rw-r--r--raveos-center/raveos_center/katalogus.py192
-rw-r--r--raveos-center/raveos_center/mentesek.py156
-rw-r--r--raveos-center/raveos_center/muveletek.py107
-rw-r--r--raveos-center/raveos_center/oldalak/__init__.py2
-rw-r--r--raveos-center/raveos_center/oldalak/alkalmazasok.py508
-rw-r--r--raveos-center/raveos_center/oldalak/attekintes.py262
-rw-r--r--raveos-center/raveos_center/oldalak/frissitesek.py331
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/__init__.py49
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/driverek.py159
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/gaming.py306
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/karbantartas.py177
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/mentes.py283
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/secureboot.py256
-rw-r--r--raveos-center/raveos_center/oldalak/rendszer/snapshotok.py247
-rw-r--r--raveos-center/raveos_center/oldalak/visszajelzes.py887
-rw-r--r--raveos-center/raveos_center/proton.py125
-rw-r--r--raveos-center/raveos_center/rendszerinfo.py240
-rw-r--r--raveos-center/raveos_center/snapshotok.py116
-rw-r--r--raveos-center/raveos_center/stilus.py436
33 files changed, 6465 insertions, 0 deletions
diff --git a/raveos-center/raveos_center/__init__.py b/raveos-center/raveos_center/__init__.py
new file mode 100644
index 0000000..c86676b
--- /dev/null
+++ b/raveos-center/raveos_center/__init__.py
@@ -0,0 +1 @@
+VERZIO = "0.1.0"
diff --git a/raveos-center/raveos_center/__main__.py b/raveos-center/raveos_center/__main__.py
new file mode 100644
index 0000000..a33359a
--- /dev/null
+++ b/raveos-center/raveos_center/__main__.py
@@ -0,0 +1,5 @@
+import sys
+
+from raveos_center.alkalmazas import futtat
+
+sys.exit(futtat(sys.argv))
diff --git a/raveos-center/raveos_center/adat/logo.png b/raveos-center/raveos_center/adat/logo.png
new file mode 100644
index 0000000..aa412b5
--- /dev/null
+++ b/raveos-center/raveos_center/adat/logo.png
Binary files differ
diff --git a/raveos-center/raveos_center/alkalmazas.py b/raveos-center/raveos_center/alkalmazas.py
new file mode 100644
index 0000000..2c68777
--- /dev/null
+++ b/raveos-center/raveos_center/alkalmazas.py
@@ -0,0 +1,192 @@
+import importlib
+from pathlib import Path
+
+from PyQt6.QtCore import QSize, Qt
+from PyQt6.QtGui import QColor, QIcon, QPalette, QPixmap
+from PyQt6.QtWidgets import (
+ QApplication,
+ QFrame,
+ QHBoxLayout,
+ QLabel,
+ QMainWindow,
+ QPushButton,
+ QStackedWidget,
+ QVBoxLayout,
+ QWidget,
+)
+
+from raveos_center import VERZIO, hatter, oldalak, rendszerinfo, stilus
+from raveos_center.elemek import cimke, frissit_stilus
+from raveos_center.ikonok import ikon
+
+ADAT = Path(__file__).parent / "adat"
+
+
+class Menupont(QPushButton):
+ def __init__(self, modul):
+ super().__init__(modul.MENU)
+ self.modul = modul
+ self.setObjectName("menupont")
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.setIconSize(QSize(18, 18))
+ sor = QHBoxLayout(self)
+ sor.setContentsMargins(0, 0, 12, 0)
+ sor.addStretch(1)
+ self.jelveny = cimke("", "jelveny")
+ self.jelveny.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+ self.jelveny.setFixedHeight(18)
+ self.jelveny.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.jelveny.hide()
+ sor.addWidget(self.jelveny, 0, Qt.AlignmentFlag.AlignVCenter)
+ self.beallit(False)
+
+ def beallit(self, aktiv):
+ self.setProperty("aktiv", aktiv)
+ self.setIcon(ikon(self.modul.IKON, stilus.ZOLD if aktiv else stilus.SZOVEG_MASOD))
+ frissit_stilus(self)
+
+ def szam(self, ertek):
+ self.jelveny.setText(str(ertek))
+ self.jelveny.setVisible(bool(ertek))
+
+
+class Kozpont:
+ def __init__(self, ablak):
+ self.ablak = ablak
+
+ def van(self, azon, *args):
+ oldal = self.ablak.oldalak.get(azon)
+ return bool(oldal) and oldal.elerheto(*args)
+
+ def mutat(self, azon, *args):
+ return self.ablak.mutat(azon, *args)
+
+ def jelveny(self, azon, szam):
+ pont = self.ablak.menupontok.get(azon)
+ if pont:
+ pont.szam(szam)
+
+
+class FoAblak(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("RaveOS Center")
+ self.setWindowIcon(QIcon(str(ADAT / "logo.png")))
+ self.resize(1280, 820)
+ self.setMinimumSize(760, 520)
+ self.kozpont = Kozpont(self)
+ self.oldalak = {}
+ self.menupontok = {}
+
+ alap = QWidget()
+ self.setCentralWidget(alap)
+ sor = QHBoxLayout(alap)
+ sor.setContentsMargins(0, 0, 0, 0)
+ sor.setSpacing(0)
+
+ self.oldalsav = QFrame()
+ self.oldalsav.setObjectName("oldalsav")
+ self.oldalsav.setFixedWidth(232)
+ sav = QVBoxLayout(self.oldalsav)
+ sav.setContentsMargins(16, 28, 16, 20)
+ sav.setSpacing(4)
+ sav.addLayout(self._marka())
+ sav.addSpacing(28)
+
+ self.verem = QStackedWidget()
+ sor.addWidget(self.oldalsav)
+ sor.addWidget(self.verem, 1)
+
+ for nev in oldalak.FENT:
+ self._betolt(nev, sav)
+ sav.addStretch(1)
+ vonal = QFrame()
+ vonal.setObjectName("elvalaszto")
+ sav.addWidget(vonal)
+ sav.addSpacing(12)
+ for nev in oldalak.LENT:
+ self._betolt(nev, sav)
+ sav.addWidget(cimke(f"{rendszerinfo.raveos_verzio()} · rp1.hu", "lablec"))
+
+ for oldal in self.oldalak.values():
+ oldal.indulaskor()
+ if oldalak.FENT:
+ self.mutat(oldalak.FENT[0])
+
+ def _marka(self):
+ sor = QHBoxLayout()
+ sor.setContentsMargins(8, 0, 8, 0)
+ sor.setSpacing(12)
+ logo = QLabel()
+ kep = QPixmap(str(ADAT / "logo.png"))
+ kep.setDevicePixelRatio(2)
+ logo.setPixmap(
+ kep.scaled(84, 78, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
+ )
+ sor.addWidget(logo)
+ nevek = QVBoxLayout()
+ nevek.setSpacing(0)
+ nevek.addWidget(cimke("RAVEOS", "markanev"))
+ nevek.addWidget(cimke("CENTER", "markaalcim"))
+ sor.addLayout(nevek)
+ sor.addStretch(1)
+ return sor
+
+ def _betolt(self, nev, sav):
+ modul = importlib.import_module(f"raveos_center.oldalak.{nev}")
+ oldal = modul.OLDAL(self.kozpont)
+ self.oldalak[modul.AZON] = oldal
+ self.verem.addWidget(oldal)
+ pont = Menupont(modul)
+ pont.clicked.connect(lambda _=False, a=modul.AZON: self.mutat(a))
+ self.menupontok[modul.AZON] = pont
+ sav.addWidget(pont)
+
+ def mutat(self, azon, *args):
+ oldal = self.oldalak.get(azon)
+ if not oldal:
+ return False
+ for a, pont in self.menupontok.items():
+ pont.beallit(a == azon)
+ self.verem.setCurrentWidget(oldal)
+ oldal.megjelenik(*args)
+ return True
+
+
+def paletta():
+ p = QPalette()
+ szinek = {
+ QPalette.ColorRole.Window: stilus.HATTER,
+ QPalette.ColorRole.WindowText: stilus.SZOVEG,
+ QPalette.ColorRole.Base: stilus.HATTER,
+ QPalette.ColorRole.AlternateBase: stilus.FELULET,
+ QPalette.ColorRole.Text: stilus.SZOVEG,
+ QPalette.ColorRole.Button: stilus.FELULET,
+ QPalette.ColorRole.ButtonText: stilus.SZOVEG,
+ QPalette.ColorRole.Highlight: stilus.ZOLD,
+ QPalette.ColorRole.HighlightedText: stilus.ZOLDON_SZOVEG,
+ QPalette.ColorRole.ToolTipBase: stilus.FELULET,
+ QPalette.ColorRole.ToolTipText: stilus.SZOVEG,
+ QPalette.ColorRole.PlaceholderText: stilus.SZOVEG_HALVANY,
+ QPalette.ColorRole.Link: stilus.ZOLD,
+ QPalette.ColorRole.Mid: stilus.KERET,
+ QPalette.ColorRole.Dark: stilus.KERET_EROS,
+ }
+ for szerep, szin in szinek.items():
+ p.setColor(szerep, QColor(szin))
+ for szerep in (QPalette.ColorRole.WindowText, QPalette.ColorRole.Text, QPalette.ColorRole.ButtonText):
+ p.setColor(QPalette.ColorGroup.Disabled, szerep, QColor(stilus.SZOVEG_HALVANY))
+ return p
+
+
+def futtat(argv):
+ app = QApplication(argv)
+ app.setApplicationName("RaveOS Center")
+ app.setDesktopFileName("raveos-center")
+ app.setStyle("Fusion")
+ app.setPalette(paletta())
+ app.setStyleSheet(stilus.QSS)
+ app.aboutToQuit.connect(hatter.leallitas)
+ ablak = FoAblak()
+ ablak.show()
+ return app.exec()
diff --git a/raveos-center/raveos_center/beallitasok.py b/raveos-center/raveos_center/beallitasok.py
new file mode 100644
index 0000000..e041706
--- /dev/null
+++ b/raveos-center/raveos_center/beallitasok.py
@@ -0,0 +1,30 @@
+import json
+import os
+import threading
+from pathlib import Path
+
+KONFIG = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "raveos-center"
+FAJL = KONFIG / "beallitasok.json"
+_zar = threading.Lock()
+
+
+def _olvas():
+ try:
+ return json.loads(FAJL.read_text())
+ except (OSError, ValueError):
+ return {}
+
+
+def ertek(kulcs, alap=None):
+ with _zar:
+ return _olvas().get(kulcs, alap)
+
+
+def beallit(kulcs, ertek_):
+ with _zar:
+ adat = _olvas()
+ adat[kulcs] = ertek_
+ KONFIG.mkdir(parents=True, exist_ok=True)
+ ideiglenes = FAJL.with_suffix(".uj")
+ ideiglenes.write_text(json.dumps(adat, ensure_ascii=False, indent=2))
+ os.replace(ideiglenes, FAJL)
diff --git a/raveos-center/raveos_center/diagnosztika.py b/raveos-center/raveos_center/diagnosztika.py
new file mode 100644
index 0000000..445c4d4
--- /dev/null
+++ b/raveos-center/raveos_center/diagnosztika.py
@@ -0,0 +1,85 @@
+import getpass
+import platform
+import re
+import socket
+import subprocess
+from datetime import datetime
+
+from raveos_center import rendszerinfo
+
+TARTALOM = [
+ "rendszer: RaveOS verzió, kernel, asztali környezet",
+ "hardver: processzor, memória, videokártya, hálózati és hangeszközök, lemezek",
+ "illesztőprogramok: betöltött GPU modulok",
+ "csomagok: RaveOS csomagok és a telepített csomagok száma",
+ "hibák: hibás szolgáltatások és a rendszernapló hibái az utolsó indítás óta",
+ "lemezhasználat és fájlrendszer",
+]
+
+KITAKARAS = "IP- és MAC-címek, lemez UUID-k, egyedi azonosítók, a gép neve és a felhasználóneved"
+
+
+def _ki(parancs, ido=30):
+ try:
+ k = subprocess.run(parancs, capture_output=True, text=True, timeout=ido)
+ except (OSError, subprocess.TimeoutExpired):
+ return "(nem elérhető)"
+ return (k.stdout or k.stderr or "").rstrip() or "(üres)"
+
+
+def _szakasz(cim, tartalom):
+ return f"== {cim} ==\n{tartalom}\n"
+
+
+def kitakar(szoveg):
+ szoveg = re.sub(r"\b(\d{1,3}\.){3}\d{1,3}\b", "[ip]", szoveg)
+ szoveg = re.sub(r"\b([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\b", "[mac]", szoveg)
+ szoveg = re.sub(r"\b([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b", "[ip6]", szoveg)
+ szoveg = re.sub(r"\b([0-9a-fA-F]{1,4}:){1,6}:([0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4})*)?", "[ip6]", szoveg)
+ szoveg = re.sub(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", "[uuid]", szoveg)
+ szoveg = re.sub(r"\b[0-9a-fA-F]{32}\b", "[id]", szoveg)
+ szoveg = re.sub(r"(?i)\b(pass(word)?|passwd|token|secret|api[_-]?key|auth)(\s*[=:]\s*)\S+", r"\1\3[titok]", szoveg)
+ gep = socket.gethostname()
+ if gep and len(gep) > 1:
+ szoveg = re.sub(rf"\b{re.escape(gep)}\b", "[gep]", szoveg)
+ felhasznalo = getpass.getuser()
+ if felhasznalo and felhasznalo != "root" and len(felhasznalo) > 1:
+ szoveg = re.sub(rf"\b{re.escape(felhasznalo)}\b", "[user]", szoveg)
+ return szoveg
+
+
+def jelentes():
+ reszek = [
+ f"RaveOS Center diagnosztika\n{datetime.now():%Y-%m-%d %H:%M}\n",
+ _szakasz(
+ "rendszer",
+ f"verzio: {rendszerinfo.raveos_verzio()}\nkernel: {platform.release()}\n"
+ f"asztal: {rendszerinfo.asztal()}\nuzemido: {rendszerinfo.uzemido()}",
+ ),
+ _szakasz(
+ "hardver",
+ f"processzor: {rendszerinfo.cpu()}\nmemoria: {rendszerinfo.memoria()}\n"
+ + _ki(["sh", "-c", "lspci -nn | grep -iE 'vga|3d|display|ethernet|network|wireless|audio'"])
+ + "\n"
+ + _ki(["lsblk", "-d", "-n", "-e", "7,11", "-o", "NAME,SIZE,MODEL,TRAN,ROTA"]),
+ ),
+ _szakasz("illesztoprogramok", ", ".join(rendszerinfo.gpu_driverek()) or "(nincs)"),
+ _szakasz(
+ "csomagok",
+ _ki(["sh", "-c", "pacman -Q | grep -E '^(raveos|calamares|linux|mesa|nvidia)'"])
+ + f"\nosszes: {_ki(['sh', '-c', 'pacman -Qq | wc -l'])}",
+ ),
+ _szakasz("hibas szolgaltatasok", _ki(["systemctl", "--failed", "--no-legend", "--no-pager"])),
+ _szakasz("rendszernaplo hibai", _ki(["journalctl", "-b", "-p", "err", "-n", "150", "--no-pager"])),
+ _szakasz("lemez", _ki(["df", "-h", "/", "/home"]) + f"\nfajlrendszer: {rendszerinfo.fajlrendszer()}"),
+ ]
+ return kitakar("\n".join(reszek))
+
+
+def rendszer_osszegzes():
+ return {
+ "RaveOS": rendszerinfo.raveos_verzio(),
+ "Kernel": platform.release(),
+ "Asztal": rendszerinfo.asztal(),
+ "GPU": ", ".join(rendszerinfo.gpuk()) or "ismeretlen",
+ }
diff --git a/raveos-center/raveos_center/driverek.py b/raveos-center/raveos_center/driverek.py
new file mode 100644
index 0000000..6ef7842
--- /dev/null
+++ b/raveos-center/raveos_center/driverek.py
@@ -0,0 +1,179 @@
+import re
+import subprocess
+from pathlib import Path
+
+SZKRIPT_HELYEK = [
+ Path("/usr/lib/raveos-center/raveos-gpu-drivers"),
+ Path(__file__).resolve().parent.parent / "driverek" / "raveos-gpu-drivers",
+]
+
+KATEGORIAK = [
+ ("gpu", "Videokártya"),
+ ("wifi", "Wi-Fi"),
+ ("halozat", "Vezetékes hálózat"),
+ ("hang", "Hang"),
+ ("bemenet", "Kontrollerek"),
+]
+
+OSZTALYOK = {
+ "0300": "gpu",
+ "0302": "gpu",
+ "0380": "gpu",
+ "0280": "wifi",
+ "0200": "halozat",
+ "0401": "hang",
+ "0403": "hang",
+}
+
+KONTROLLEREK = {
+ "045e": "Microsoft (Xbox)",
+ "054c": "Sony (PlayStation)",
+ "057e": "Nintendo",
+ "28de": "Valve (Steam)",
+ "2dc8": "8BitDo",
+ "0e6f": "PDP",
+ "24c6": "PowerA",
+ "046d": "Logitech",
+}
+
+XBOX_ADAPTEREK = {"02e6", "02fe", "091e"}
+
+
+def _ki(parancs, ido=30):
+ try:
+ return subprocess.run(parancs, capture_output=True, text=True, timeout=ido).stdout
+ except (OSError, subprocess.TimeoutExpired):
+ return ""
+
+
+def szkript():
+ for hely in SZKRIPT_HELYEK:
+ if hely.is_file():
+ return hely
+ return None
+
+
+def _telepitett():
+ return set(_ki(["pacman", "-Qq"]).split())
+
+
+def _gpu_terv():
+ hely = szkript()
+ if not hely:
+ return {}
+ terv = {}
+ for sor in _ki(["bash", str(hely), "--terv"]).splitlines():
+ if "=" in sor:
+ k, _, v = sor.partition("=")
+ terv[k] = v
+ return terv
+
+
+def _azonosito(szoveg):
+ talalat = re.search(r"\[([0-9a-f]{4})\]$", szoveg.strip())
+ return talalat.group(1) if talalat else ""
+
+
+def _nev(szoveg):
+ return re.sub(r"\s*\[[0-9a-f]{4}\]$", "", szoveg.strip())
+
+
+def _pci_eszkozok():
+ blokkok = _ki(["lspci", "-vmmnnk"]).strip().split("\n\n")
+ eszkozok = []
+ for blokk in blokkok:
+ mezok = {}
+ for sor in blokk.splitlines():
+ if ":\t" in sor:
+ k, _, v = sor.partition(":\t")
+ mezok.setdefault(k, v)
+ osztaly = _azonosito(mezok.get("Class", ""))
+ kategoria = OSZTALYOK.get(osztaly)
+ if not kategoria:
+ continue
+ gyarto = _nev(mezok.get("Vendor", ""))
+ eszkoz = _nev(mezok.get("Device", ""))
+ rovid = re.search(r"\[([^\]]+)\]", eszkoz)
+ eszkozok.append(
+ {
+ "kategoria": kategoria,
+ "nev": f"{gyarto.replace(' Corporation', '').replace(', Inc.', '')} {rovid.group(1) if rovid else eszkoz}".strip(),
+ "gyarto_id": _azonosito(mezok.get("Vendor", "")),
+ "jelenlegi": mezok.get("Driver", ""),
+ }
+ )
+ return eszkozok
+
+
+def _usb_kontrollerek():
+ lista = []
+ for sor in _ki(["lsusb"]).splitlines():
+ talalat = re.search(r"ID ([0-9a-f]{4}):([0-9a-f]{4})\s*(.*)", sor)
+ if not talalat:
+ continue
+ gyarto, termek, nev = talalat.groups()
+ if gyarto not in KONTROLLEREK:
+ continue
+ if not re.search(r"(?i)controller|gamepad|joystick|wireless adapter|xbox|dualsense|dualshock|pro controller|8bitdo", nev):
+ continue
+ lista.append({"gyarto": gyarto, "termek": termek, "nev": nev or KONTROLLEREK[gyarto]})
+ return lista
+
+
+def allapot():
+ telepitett = _telepitett()
+ terv = _gpu_terv()
+ kozos = terv.get("kozos", "").split()
+ gyartoi = {
+ "8086": terv.get("intel_csomagok", "").split(),
+ "1002": terv.get("amd_csomagok", "").split(),
+ "10de": terv.get("nvidia_csomagok", "").split(),
+ }
+ lista = []
+ for e in _pci_eszkozok():
+ elem = dict(e, ajanlott=[], megjegyzes="", gpu=False)
+ if e["kategoria"] == "gpu":
+ elem["gpu"] = True
+ elem["ajanlott"] = kozos + gyartoi.get(e["gyarto_id"], [])
+ if terv.get("virtualis") == "1" and e["gyarto_id"] in ("1af4", "1234", "15ad", "1b36", "80ee"):
+ elem["megjegyzes"] = "Virtuális gépben fut, a 3D gyorsítást a gazdagép adja."
+ elif e["gyarto_id"] == "10de":
+ csalad = terv.get("nvidia_csalad", "")
+ elem["megjegyzes"] = {
+ "open": "NVIDIA, Turing vagy újabb: nyílt forrású NVIDIA kernelmodul.",
+ "580xx": "NVIDIA, Maxwell, Pascal vagy Volta: 580-as hosszú támogatású driver.",
+ "470xx": "NVIDIA, Kepler: 470-es örökölt driver, már nem kap frissítést.",
+ "nem_tamogatott": "Ehhez a régi NVIDIA kártyához nincs zárt driver, a nyílt nouveau marad.",
+ }.get(csalad, "")
+ elif e["kategoria"] == "wifi" and e["gyarto_id"] == "14e4" and not e["jelenlegi"]:
+ elem["ajanlott"] = ["broadcom-wl-dkms"] + (terv.get("fejlecek", "").split() or ["linux-cachyos-headers"])
+ elem["megjegyzes"] = "Broadcom Wi-Fi: a zárt wl driver szükséges hozzá."
+ elif e["kategoria"] == "hang" and "sof" in e["jelenlegi"]:
+ elem["ajanlott"] = ["sof-firmware"]
+ elem["megjegyzes"] = "Intel Sound Open Firmware hangkártya."
+ elif not e["jelenlegi"]:
+ elem["ajanlott"] = ["linux-firmware"]
+ elem["megjegyzes"] = "Nincs betöltött driver, a firmware-csomag hiányozhat."
+ elem["hianyzik"] = [p for p in elem["ajanlott"] if p not in telepitett]
+ lista.append(elem)
+ for k in _usb_kontrollerek():
+ elem = {
+ "kategoria": "bemenet",
+ "nev": k["nev"],
+ "jelenlegi": "kernel (beépített)",
+ "ajanlott": [],
+ "megjegyzes": "",
+ "gpu": False,
+ }
+ if k["gyarto"] == "045e" and k["termek"] in XBOX_ADAPTEREK:
+ elem["ajanlott"] = ["xone-dkms"] + (terv.get("fejlecek", "").split() or ["linux-cachyos-headers"])
+ elem["jelenlegi"] = ""
+ elem["megjegyzes"] = "Xbox vezeték nélküli adapter: az xone driver kell hozzá."
+ elem["hianyzik"] = [p for p in elem["ajanlott"] if p not in telepitett]
+ lista.append(elem)
+ return lista
+
+
+def ujrainditas_kell(elem):
+ return elem["gpu"] and any(p.startswith(("nvidia", "lib32-nvidia")) for p in elem["hianyzik"])
+
diff --git a/raveos-center/raveos_center/elemek.py b/raveos-center/raveos_center/elemek.py
new file mode 100644
index 0000000..72f68ea
--- /dev/null
+++ b/raveos-center/raveos_center/elemek.py
@@ -0,0 +1,387 @@
+import math
+
+from PyQt6.QtCore import QEvent, QPointF, QRectF, QSize, Qt, QTimer, pyqtSignal
+from PyQt6.QtGui import QColor, QPainter, QPolygonF
+from PyQt6.QtWidgets import (
+ QAbstractButton,
+ QFrame,
+ QGridLayout,
+ QHBoxLayout,
+ QLabel,
+ QPushButton,
+ QScrollArea,
+ QSizePolicy,
+ QVBoxLayout,
+ QWidget,
+)
+
+
+def cimke(szoveg, nev=None, tordel=False):
+ c = QLabel(szoveg)
+ if nev:
+ c.setObjectName(nev)
+ c.setWordWrap(tordel)
+ return c
+
+
+def gomb(szoveg, fo=False, kicsi=False):
+ g = QPushButton(szoveg)
+ g.setCursor(Qt.CursorShape.PointingHandCursor)
+ if fo:
+ g.setProperty("fo", True)
+ if kicsi:
+ g.setProperty("kicsi", True)
+ return g
+
+
+def frissit_stilus(widget):
+ widget.style().unpolish(widget)
+ widget.style().polish(widget)
+
+
+class Kartya(QFrame):
+ def __init__(self, cim=None, terkoz=10, margo=20):
+ super().__init__()
+ self.setObjectName("kartya")
+ self.elrendezes = QVBoxLayout(self)
+ self.elrendezes.setContentsMargins(margo, margo, margo, margo)
+ self.elrendezes.setSpacing(terkoz)
+ if cim:
+ self.elrendezes.addWidget(cimke(cim.upper(), "kartyacim"))
+
+
+class Racs(QWidget):
+ def __init__(self, min_szelesseg, terkoz=16):
+ super().__init__()
+ self.min_szelesseg = min_szelesseg
+ self.elemek = []
+ self.rejtett = set()
+ self.oszlopok = 0
+ self.racs = QGridLayout(self)
+ self.racs.setContentsMargins(0, 0, 0, 0)
+ self.racs.setSpacing(terkoz)
+
+ def hozzaad(self, widget):
+ self.elemek.append(widget)
+ self._rendez(force=True)
+
+ def resizeEvent(self, esemeny):
+ super().resizeEvent(esemeny)
+ self._rendez()
+
+ def szur(self, lathatok):
+ self.rejtett = {w for w in self.elemek if w not in lathatok}
+ for w in self.elemek:
+ w.setVisible(w not in self.rejtett)
+ self._rendez(force=True)
+
+ def _rendez(self, force=False):
+ terkoz = self.racs.spacing()
+ lathatok = [w for w in self.elemek if w not in self.rejtett]
+ oszlopok = max(1, (self.width() + terkoz) // (self.min_szelesseg + terkoz))
+ if self.min_szelesseg < 260:
+ oszlopok = min(oszlopok, max(1, len(lathatok)))
+ if oszlopok == self.oszlopok and not force:
+ return
+ self.oszlopok = oszlopok
+ for w in self.elemek:
+ self.racs.removeWidget(w)
+ for i, w in enumerate(lathatok):
+ self.racs.addWidget(w, i // oszlopok, i % oszlopok)
+ self.updateGeometry()
+ oldal = self.parent()
+ while oldal is not None and not isinstance(oldal, Oldal):
+ oldal = oldal.parent()
+ if oldal is not None and oldal.kitolt:
+ QTimer.singleShot(0, oldal._igazit)
+ for o in range(self.racs.columnCount()):
+ self.racs.setColumnStretch(o, 1 if o < oszlopok else 0)
+
+
+class _KitoltoTartalom(QWidget):
+ def sizeHint(self):
+ return self.minimumSizeHint()
+
+ def heightForWidth(self, szelesseg):
+ elrendezes = self.layout()
+ if elrendezes and elrendezes.hasHeightForWidth():
+ return elrendezes.minimumHeightForWidth(szelesseg)
+ return self.minimumSizeHint().height()
+
+
+class Oldal(QScrollArea):
+ def __init__(self, kozpont, eyebrow, cim, kitolt=False):
+ super().__init__()
+ self.kozpont = kozpont
+ self.setWidgetResizable(True)
+ self.setFrameShape(QFrame.Shape.NoFrame)
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+ self.kitolt = kitolt
+ tartalom = _KitoltoTartalom() if kitolt else QWidget()
+ self.setWidget(tartalom)
+ if kitolt:
+ tartalom.installEventFilter(self)
+ self.torzs = QVBoxLayout(tartalom)
+ self.torzs.setContentsMargins(48, 40, 48, 40)
+ self.torzs.setSpacing(32)
+
+ fejlec = QHBoxLayout()
+ fejlec.setSpacing(16)
+ cimsor = QVBoxLayout()
+ cimsor.setSpacing(6)
+ self.eyebrow = cimke(eyebrow, "eyebrow")
+ self.cim = cimke(cim.upper(), "h1")
+ cimsor.addWidget(self.eyebrow)
+ cimsor.addWidget(self.cim)
+ fejlec.addLayout(cimsor)
+ fejlec.addStretch(1)
+ self.gombsor = QHBoxLayout()
+ self.gombsor.setSpacing(8)
+ fejlec.addLayout(self.gombsor)
+ fejlec.setAlignment(self.gombsor, Qt.AlignmentFlag.AlignBottom)
+ self.torzs.addLayout(fejlec)
+
+ def eventFilter(self, objektum, esemeny):
+ if self.kitolt and objektum is self.widget() and esemeny.type() == QEvent.Type.LayoutRequest:
+ QTimer.singleShot(0, self._igazit)
+ return super().eventFilter(objektum, esemeny)
+
+ def resizeEvent(self, esemeny):
+ super().resizeEvent(esemeny)
+ if self.kitolt:
+ self._igazit()
+
+ def _igazit(self):
+ tartalom = self.widget()
+ nezet = self.viewport()
+ magassag = max(nezet.height(), tartalom.heightForWidth(nezet.width()))
+ if tartalom.height() != magassag or tartalom.width() != nezet.width():
+ tartalom.resize(nezet.width(), magassag)
+
+ def fejlec_gomb(self, gomb_widget):
+ self.gombsor.addWidget(gomb_widget)
+ return gomb_widget
+
+ def megjelenik(self, *_):
+ pass
+
+ def indulaskor(self):
+ pass
+
+ def elerheto(self, *_):
+ return True
+
+
+class Fulsor(QWidget):
+ def __init__(self, fulek, valtas):
+ super().__init__()
+ self.setObjectName("fulsor")
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ sor = QHBoxLayout(self)
+ sor.setContentsMargins(0, 0, 0, 0)
+ sor.setSpacing(0)
+ self.gombok = {}
+ for azon, szoveg in fulek:
+ g = QPushButton(szoveg)
+ g.setObjectName("ful")
+ g.setCursor(Qt.CursorShape.PointingHandCursor)
+ g.clicked.connect(lambda _=False, a=azon: valtas(a))
+ self.gombok[azon] = g
+ sor.addWidget(g)
+ sor.addStretch(1)
+
+ def aktiv(self, azon):
+ for a, g in self.gombok.items():
+ g.setProperty("aktiv", a == azon)
+ frissit_stilus(g)
+
+
+class Verem(QWidget):
+ currentChanged = pyqtSignal(int)
+
+ def __init__(self):
+ super().__init__()
+ self._lapok = []
+ self._aktualis = -1
+ self._elrendezes = QVBoxLayout(self)
+ self._elrendezes.setContentsMargins(0, 0, 0, 0)
+ self._elrendezes.setSpacing(0)
+
+ def addWidget(self, widget):
+ self._lapok.append(widget)
+ self._elrendezes.addWidget(widget)
+ if self._aktualis < 0:
+ self.setCurrentIndex(0)
+ else:
+ widget.hide()
+ return len(self._lapok) - 1
+
+ def count(self):
+ return len(self._lapok)
+
+ def widget(self, index):
+ return self._lapok[index] if 0 <= index < len(self._lapok) else None
+
+ def indexOf(self, widget):
+ return self._lapok.index(widget) if widget in self._lapok else -1
+
+ def currentIndex(self):
+ return self._aktualis
+
+ def currentWidget(self):
+ return self.widget(self._aktualis)
+
+ def setCurrentIndex(self, index):
+ if not 0 <= index < len(self._lapok):
+ return
+ for i, lap in enumerate(self._lapok):
+ lap.setVisible(i == index)
+ valtozott = index != self._aktualis
+ self._aktualis = index
+ self.updateGeometry()
+ if valtozott:
+ self.currentChanged.emit(index)
+
+ def setCurrentWidget(self, widget):
+ self.setCurrentIndex(self.indexOf(widget))
+
+
+class Kapcsolo(QAbstractButton):
+ def __init__(self):
+ super().__init__()
+ self.setCheckable(True)
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.setFixedSize(QSize(44, 24))
+ self.setAccessibleName("kapcsoló")
+
+ def sizeHint(self):
+ return QSize(44, 24)
+
+ def paintEvent(self, _):
+ from raveos_center import stilus
+
+ festo = QPainter(self)
+ festo.setRenderHint(QPainter.RenderHint.Antialiasing, False)
+ be = self.isChecked()
+ aktiv = self.isEnabled()
+ palya = QColor(stilus.ZOLD if be else stilus.AKTIV)
+ keret = QColor(stilus.ZOLD if be else stilus.KERET_EROS)
+ if not aktiv:
+ palya = QColor(stilus.KERET if not be else "#3d5a36")
+ keret = palya
+ festo.setPen(keret)
+ festo.setBrush(palya)
+ festo.drawRect(QRectF(0.5, 0.5, self.width() - 1, self.height() - 1))
+ gomb_meret = self.height() - 8
+ x = self.width() - gomb_meret - 4 if be else 4
+ festo.setPen(Qt.PenStyle.NoPen)
+ festo.setBrush(QColor(stilus.ZOLDON_SZOVEG if be else stilus.SZOVEG_HALVANY))
+ festo.drawRect(QRectF(x, 4, gomb_meret, gomb_meret))
+ festo.end()
+
+
+class Csillagok(QWidget):
+ valasztva = pyqtSignal(int)
+ TERKOZ = 6
+ LEPESEK = {Qt.Key.Key_Right: 1, Qt.Key.Key_Up: 1, Qt.Key.Key_Left: -1, Qt.Key.Key_Down: -1}
+ SZAMOK = {Qt.Key.Key_1: 1, Qt.Key.Key_2: 2, Qt.Key.Key_3: 3, Qt.Key.Key_4: 4, Qt.Key.Key_5: 5}
+
+ def __init__(self, meret=22, valaszthato=False):
+ super().__init__()
+ self.meret = meret
+ self.ertek = 0.0
+ self.mutatott = 0
+ self.valaszthato = valaszthato
+ self.setFixedSize(self.sizeHint())
+ self.setAccessibleName("csillagos értékelés")
+ if valaszthato:
+ self.setMouseTracking(True)
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+
+ def sizeHint(self):
+ return QSize(self.meret * 5 + self.TERKOZ * 4, self.meret)
+
+ def beallit(self, ertek):
+ self.ertek = max(0.0, min(5.0, float(ertek)))
+ self.setToolTip(f"{self.ertek:.1f} / 5" if not self.valaszthato else "")
+ self.update()
+
+ def _index(self, pont):
+ return max(1, min(5, int(pont.x() // (self.meret + self.TERKOZ)) + 1))
+
+ def _valaszt(self, ertek):
+ self.beallit(ertek)
+ self.valasztva.emit(int(ertek))
+
+ def mouseMoveEvent(self, esemeny):
+ if self.valaszthato and self.isEnabled():
+ self.mutatott = self._index(esemeny.position())
+ self.update()
+
+ def leaveEvent(self, esemeny):
+ self.mutatott = 0
+ self.update()
+ super().leaveEvent(esemeny)
+
+ def mouseReleaseEvent(self, esemeny):
+ if self.valaszthato and esemeny.button() == Qt.MouseButton.LeftButton and self.rect().contains(
+ esemeny.position().toPoint()
+ ):
+ self._valaszt(self._index(esemeny.position()))
+ super().mouseReleaseEvent(esemeny)
+
+ def keyPressEvent(self, esemeny):
+ if not self.valaszthato:
+ return super().keyPressEvent(esemeny)
+ try:
+ kulcs = Qt.Key(esemeny.key())
+ except ValueError:
+ kulcs = None
+ if kulcs in self.LEPESEK:
+ self._valaszt(max(1, min(5, int(self.ertek) + self.LEPESEK[kulcs])))
+ elif kulcs in self.SZAMOK:
+ self._valaszt(self.SZAMOK[kulcs])
+ else:
+ super().keyPressEvent(esemeny)
+
+ def _sokszog(self, bal):
+ r = self.meret / 2
+ kozep = QPointF(bal + r, r * 1.05)
+ pontok = []
+ for i in range(10):
+ sugar = r if i % 2 == 0 else r * 0.42
+ szog = math.radians(-90 + i * 36)
+ pontok.append(QPointF(kozep.x() + sugar * math.cos(szog), kozep.y() + sugar * math.sin(szog)))
+ return QPolygonF(pontok)
+
+ def paintEvent(self, _):
+ from raveos_center import stilus
+
+ festo = QPainter(self)
+ festo.setRenderHint(QPainter.RenderHint.Antialiasing, True)
+ festo.setPen(Qt.PenStyle.NoPen)
+ ertek = self.mutatott or self.ertek
+ tele = QColor(stilus.SARGA if self.isEnabled() else stilus.SZOVEG_HALVANY)
+ for i in range(5):
+ bal = i * (self.meret + self.TERKOZ)
+ csillag = self._sokszog(bal)
+ festo.setBrush(QColor(stilus.KERET_EROS))
+ festo.drawPolygon(csillag)
+ resz = max(0.0, min(1.0, ertek - i))
+ if resz > 0:
+ festo.save()
+ festo.setClipRect(QRectF(bal, 0, self.meret * resz, self.meret))
+ festo.setBrush(tele)
+ festo.drawPolygon(csillag)
+ festo.restore()
+ if self.hasFocus():
+ festo.setPen(QColor(stilus.ZOLD))
+ festo.setBrush(Qt.BrushStyle.NoBrush)
+ festo.drawRect(QRectF(0.5, 0.5, self.width() - 1, self.height() - 1))
+ festo.end()
+
+
+def nyujthato(widget):
+ widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
+ return widget
diff --git a/raveos-center/raveos_center/folyamat.py b/raveos-center/raveos_center/folyamat.py
new file mode 100644
index 0000000..586491f
--- /dev/null
+++ b/raveos-center/raveos_center/folyamat.py
@@ -0,0 +1,68 @@
+from PyQt6.QtCore import Qt
+from PyQt6.QtWidgets import QDialog, QHBoxLayout, QPlainTextEdit, QVBoxLayout
+
+from raveos_center import hatter, muveletek
+from raveos_center.elemek import cimke, gomb
+
+
+class FolyamatAblak(QDialog):
+ def __init__(self, szulo, cim, lepesek, siker_szoveg="Kész.", hiba_szoveg="A művelet nem sikerült."):
+ super().__init__(szulo)
+ self.setWindowTitle(cim)
+ self.resize(760, 520)
+ self.siker_szoveg = siker_szoveg
+ self.hiba_szoveg = hiba_szoveg
+ self.rendben = False
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(28, 24, 28, 20)
+ doboz.setSpacing(12)
+ self.felirat = cimke("FOLYAMATBAN", "eyebrow")
+ doboz.addWidget(self.felirat)
+ self.cim = cimke(cim.upper(), "h2", True)
+ doboz.addWidget(self.cim)
+ self.allapot = cimke("Indul...", "masodlagos", True)
+ doboz.addWidget(self.allapot)
+ self.naplo = QPlainTextEdit()
+ self.naplo.setObjectName("naplo")
+ self.naplo.setReadOnly(True)
+ self.naplo.setMaximumBlockCount(5000)
+ doboz.addWidget(self.naplo, 1)
+ sor = QHBoxLayout()
+ sor.addStretch(1)
+ self.bezar = gomb("Bezárás", fo=True)
+ self.bezar.setEnabled(False)
+ self.bezar.clicked.connect(self.accept)
+ sor.addWidget(self.bezar)
+ doboz.addLayout(sor)
+ self.futtato = muveletek.Futtato(self)
+ self.futtato.sor.connect(self.naplo.appendPlainText)
+ self.futtato.lepes_indul.connect(self.allapot.setText)
+ self.futtato.kesz.connect(self._kesz)
+ self.lepesek = lepesek
+
+ def exec(self):
+ self.futtato.indit(self.lepesek)
+ return super().exec()
+
+ def _kesz(self, rendben, hibasak):
+ hatter.ervenytelenit()
+ self.rendben = rendben
+ self.felirat.setText("KÉSZ" if rendben else "HIBA")
+ if rendben:
+ self.allapot.setText(self.siker_szoveg)
+ else:
+ self.allapot.setText(f"{self.hiba_szoveg} Sikertelen lépés: {', '.join(hibasak)}")
+ self.allapot.setObjectName("figyelem")
+ self.allapot.style().unpolish(self.allapot)
+ self.allapot.style().polish(self.allapot)
+ self.bezar.setEnabled(True)
+
+ def reject(self):
+ if self.futtato.fut():
+ return
+ super().reject()
+
+ def keyPressEvent(self, esemeny):
+ if esemeny.key() == Qt.Key.Key_Escape and self.futtato.fut():
+ return
+ super().keyPressEvent(esemeny)
diff --git a/raveos-center/raveos_center/gaming.py b/raveos-center/raveos_center/gaming.py
new file mode 100644
index 0000000..9652958
--- /dev/null
+++ b/raveos-center/raveos_center/gaming.py
@@ -0,0 +1,272 @@
+import os
+import re
+import subprocess
+from pathlib import Path
+
+from raveos_center.muveletek import Lepes, root
+
+KONFIG = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
+KORNYEZET = KONFIG / "environment.d" / "raveos-gaming.conf"
+GAMEMODE_INI = KONFIG / "gamemode.ini"
+GAMESCOPE = ["gamescope", "gamescope-session-git", "gamescope-session-steam-git"]
+MANGOHUD_CONF = KONFIG / "MangoHud" / "MangoHud.conf"
+MANGOHUD_MODOK = [
+ ("", "Alapértelmezett"),
+ ("1", "Csak FPS"),
+ ("2", "Vízszintes sáv"),
+ ("3", "Bővített"),
+ ("4", "Részletes"),
+]
+SCHED_EXT = Path("/sys/kernel/sched_ext")
+PROTON_CACHYOS = "proton-cachyos-slr"
+GAMING_MOD = ["gamemode", "governor", "ntsync", "utemezo"]
+GOV_ALAPBOL = "A rendszer alapból is performance módban fut."
+
+
+def _telepitett():
+ try:
+ kimenet = subprocess.run(["pacman", "-Qq"], capture_output=True, text=True, timeout=30).stdout
+ except (OSError, subprocess.TimeoutExpired):
+ return set()
+ return set(kimenet.split())
+
+
+def _cpufreq():
+ return Path("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor").exists()
+
+
+def _ntsync_beepitett():
+ try:
+ kimenet = subprocess.run(["modinfo", "-F", "filename", "ntsync"], capture_output=True, text=True, timeout=10)
+ except (OSError, subprocess.TimeoutExpired):
+ return False
+ return kimenet.stdout.strip() == "(builtin)"
+
+
+def _rendszer_gov():
+ try:
+ return Path("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor").read_text().strip()
+ except OSError:
+ return ""
+
+
+def _desiredgov():
+ try:
+ sorok = GAMEMODE_INI.read_text(encoding="utf-8").splitlines()
+ except OSError:
+ return None
+ szakasz = ""
+ for sor in sorok:
+ t = sor.strip()
+ if t.startswith("[") and t.endswith("]"):
+ szakasz = t[1:-1].strip()
+ elif szakasz == "general":
+ talalat = re.match(r"desiredgov\s*=\s*(\S+)", t)
+ if talalat:
+ return talalat.group(1)
+ return None
+
+
+def _desiredgov_ir(ertek):
+ try:
+ sorok = GAMEMODE_INI.read_text(encoding="utf-8").splitlines()
+ except FileNotFoundError:
+ sorok = []
+ uj = []
+ szakasz = ""
+ general_volt = False
+ beirva = False
+ for sor in sorok:
+ t = sor.strip()
+ if t.startswith("[") and t.endswith("]"):
+ if szakasz == "general" and not beirva:
+ uj.append(f"desiredgov={ertek}")
+ beirva = True
+ szakasz = t[1:-1].strip()
+ general_volt = general_volt or szakasz == "general"
+ elif szakasz == "general" and re.match(r"desiredgov\s*=", t):
+ if not beirva:
+ uj.append(f"desiredgov={ertek}")
+ beirva = True
+ continue
+ uj.append(sor)
+ if not beirva:
+ if not general_volt:
+ uj.append("[general]")
+ uj.append(f"desiredgov={ertek}")
+ GAMEMODE_INI.parent.mkdir(parents=True, exist_ok=True)
+ ideiglenes = GAMEMODE_INI.with_suffix(".ini.uj")
+ ideiglenes.write_text("\n".join(uj) + "\n", encoding="utf-8")
+ os.replace(ideiglenes, GAMEMODE_INI)
+
+
+def _mangohud_sor():
+ try:
+ return any(s.strip() == "MANGOHUD=1" for s in KORNYEZET.read_text().splitlines())
+ except OSError:
+ return False
+
+
+def _utemezo_allapot():
+ if not SCHED_EXT.is_dir():
+ return {
+ "aktiv": False,
+ "elerheto": False,
+ "megjegyzes": "A futó kernel nem támogatja (sched_ext nélkül fordították).",
+ }
+ try:
+ fut = (SCHED_EXT / "state").read_text().strip() == "enabled"
+ nev = (SCHED_EXT / "root" / "ops").read_text().strip() if fut else ""
+ except OSError:
+ fut, nev = False, ""
+ return {"aktiv": fut, "elerheto": True, "megjegyzes": f"Most fut: {nev}" if nev else ""}
+
+
+def mangohud_mod():
+ try:
+ sorok = MANGOHUD_CONF.read_text(encoding="utf-8").splitlines()
+ except OSError:
+ return ""
+ for sor in sorok:
+ talalat = re.match(r"\s*preset\s*=\s*(-?\d)", sor)
+ if talalat:
+ return talalat.group(1)
+ return ""
+
+
+def mangohud_mod_beallit(mod):
+ try:
+ sorok = MANGOHUD_CONF.read_text(encoding="utf-8").splitlines()
+ except FileNotFoundError:
+ sorok = []
+ sorok = [s for s in sorok if not re.match(r"\s*preset\s*=", s)]
+ if mod:
+ tobbi = [m for m, _ in MANGOHUD_MODOK if m and m != mod]
+ sorok.insert(0, "preset=" + ",".join([mod] + tobbi + ["0"]))
+ if not sorok:
+ MANGOHUD_CONF.unlink(missing_ok=True)
+ return
+ MANGOHUD_CONF.parent.mkdir(parents=True, exist_ok=True)
+ ideiglenes = MANGOHUD_CONF.with_suffix(".conf.uj")
+ ideiglenes.write_text("\n".join(sorok) + "\n", encoding="utf-8")
+ os.replace(ideiglenes, MANGOHUD_CONF)
+
+
+def _proton_cachyos_elerheto():
+ try:
+ return subprocess.run(["pacman", "-Si", PROTON_CACHYOS], capture_output=True, timeout=30).returncode == 0
+ except (OSError, subprocess.TimeoutExpired):
+ return False
+
+
+def _governor_allapot(gamemode):
+ rendszer = _rendszer_gov()
+ kivant = _desiredgov() or "performance"
+ if not gamemode:
+ return {"aktiv": False, "elerheto": False, "megjegyzes": "Ehhez előbb a GameMode-ot kapcsold be."}
+ if not rendszer:
+ return {"aktiv": False, "elerheto": False, "megjegyzes": "Ezen a gépen nincs órajel-szabályozás (pl. virtuális gép)."}
+ if rendszer == "performance":
+ return {"aktiv": True, "elerheto": False, "megjegyzes": GOV_ALAPBOL}
+ return {"aktiv": kivant == "performance", "elerheto": True, "megjegyzes": ""}
+
+
+def allapot():
+ telepitett = _telepitett()
+ gamemode = {"gamemode", "lib32-gamemode"} <= telepitett
+ mangohud = "mangohud" in telepitett
+ ntsync_beepitett = _ntsync_beepitett()
+ return {
+ "gamemode": {"aktiv": gamemode, "elerheto": True, "megjegyzes": "", "root": True},
+ "mangohud": {
+ "aktiv": _mangohud_sor() and mangohud,
+ "elerheto": True,
+ "megjegyzes": "Kijelentkezés és újra bejelentkezés után lép életbe.",
+ "root": not mangohud,
+ },
+ "gamescope": {
+ "aktiv": set(GAMESCOPE) <= telepitett,
+ "elerheto": True,
+ "megjegyzes": "" if "steam" in telepitett else "A Steam munkamenethez a Steam is szükséges.",
+ "root": True,
+ },
+ "governor": dict(_governor_allapot(gamemode), root=False, cpufreq=bool(_rendszer_gov())),
+ "ntsync": {
+ "aktiv": ntsync_beepitett
+ or (Path("/etc/modules-load.d/ntsync.conf").exists() and Path("/dev/ntsync").exists()),
+ "elerheto": not ntsync_beepitett,
+ "megjegyzes": "A RaveOS kernelébe be van építve, mindig aktív." if ntsync_beepitett else "",
+ "root": True,
+ },
+ "utemezo": dict(_utemezo_allapot(), root=True),
+ "proton_cachyos": {
+ "aktiv": PROTON_CACHYOS in telepitett,
+ "elerheto": PROTON_CACHYOS in telepitett or _proton_cachyos_elerheto(),
+ "megjegyzes": "",
+ "root": True,
+ },
+ }
+
+
+def lepesek(azon, be):
+ if azon == "gamemode":
+ csomagok = ["gamemode", "lib32-gamemode"]
+ return [Lepes("GameMode", root("telepit" if be else "eltavolit", *csomagok))], None
+ if azon == "mangohud":
+ lista = []
+ if be and "mangohud" not in _telepitett():
+ lista.append(Lepes("MangoHud telepítése", root("telepit", "mangohud", "lib32-mangohud")))
+ return lista, lambda: _mangohud(be)
+ if azon == "gamescope":
+ return [Lepes("Gamescope session", root("telepit" if be else "eltavolit", *(GAMESCOPE if be else GAMESCOPE[1:])))], None
+ if azon == "governor":
+ return [], lambda: _governor(be)
+ if azon == "ntsync":
+ return [Lepes("NTSync", root("ntsync", "be" if be else "ki"))], None
+ if azon == "utemezo":
+ return [Lepes("Játékra hangolt ütemező", root("utemezo", "be" if be else "ki"))], None
+ if azon == "proton_cachyos":
+ return [Lepes("Proton-CachyOS", root("telepit" if be else "eltavolit", PROTON_CACHYOS))], None
+ return [], None
+
+
+def _mangohud(be):
+ sorok = []
+ try:
+ sorok = [s for s in KORNYEZET.read_text().splitlines() if s.strip() != "MANGOHUD=1"]
+ except OSError:
+ pass
+ if be:
+ sorok.append("MANGOHUD=1")
+ if sorok:
+ KORNYEZET.parent.mkdir(parents=True, exist_ok=True)
+ KORNYEZET.write_text("\n".join(sorok) + "\n")
+ else:
+ KORNYEZET.unlink(missing_ok=True)
+
+
+def _governor(be):
+ rendszer = _rendszer_gov()
+ if be:
+ _desiredgov_ir("performance")
+ elif rendszer and rendszer != "performance":
+ _desiredgov_ir(rendszer)
+
+
+def gaming_mod_valtozasok(adat, be):
+ valtozasok = []
+ gamemode_lesz = adat["gamemode"]["aktiv"] or be
+ for a in GAMING_MOD:
+ if a == "gamemode" and not be:
+ continue
+ if a == "governor":
+ elerheto = gamemode_lesz and adat[a]["cpufreq"] and adat[a]["megjegyzes"] != GOV_ALAPBOL
+ else:
+ elerheto = adat[a]["elerheto"]
+ if elerheto and adat[a]["aktiv"] != be:
+ valtozasok.append((a, be))
+ return valtozasok
+
+
+def gaming_mod_aktiv(adat):
+ return all(adat[a]["aktiv"] for a in GAMING_MOD if adat[a]["elerheto"] or adat[a]["aktiv"])
diff --git a/raveos-center/raveos_center/hatter.py b/raveos-center/raveos_center/hatter.py
new file mode 100644
index 0000000..6c7349c
--- /dev/null
+++ b/raveos-center/raveos_center/hatter.py
@@ -0,0 +1,101 @@
+import time
+
+from PyQt6.QtCore import QObject, QThreadPool, pyqtSignal
+
+
+class _Jelzo(QObject):
+ kesz = pyqtSignal(object)
+ hiba = pyqtSignal(str)
+
+
+_elo = set()
+
+
+def hatterben(fuggveny, *args, kesz=None, hiba=None):
+ jelzo = _Jelzo()
+ _elo.add(jelzo)
+
+ def vege(*_):
+ _elo.discard(jelzo)
+
+ if kesz:
+ jelzo.kesz.connect(kesz)
+ if hiba:
+ jelzo.hiba.connect(hiba)
+ jelzo.kesz.connect(vege)
+ jelzo.hiba.connect(vege)
+
+ def futas():
+ try:
+ eredmeny = fuggveny(*args)
+ except BaseException as e:
+ jelzo.hiba.emit(str(e) or type(e).__name__)
+ else:
+ jelzo.kesz.emit(eredmeny)
+
+ QThreadPool.globalInstance().start(futas)
+
+
+_tar = {}
+_futok = {}
+_generacio = {}
+
+
+def _indit(kulcs, fuggveny, args, varok):
+ generacio = _generacio.get(kulcs, 0)
+ _futok[kulcs] = {"fuggveny": fuggveny, "args": args, "varok": varok}
+
+ def vege(ertek):
+ if _generacio.get(kulcs, 0) != generacio:
+ return
+ futo = _futok.pop(kulcs, None)
+ regi = _tar.get(kulcs)
+ _tar[kulcs] = (time.monotonic(), ertek)
+ for k, _, kiszolgalva in futo["varok"] if futo else []:
+ if k and (not kiszolgalva or regi is None or regi[1] != ertek):
+ k(ertek)
+
+ def hibas(uzenet):
+ if _generacio.get(kulcs, 0) != generacio:
+ return
+ futo = _futok.pop(kulcs, None)
+ for _, h, _ in futo["varok"] if futo else []:
+ if h:
+ h(uzenet)
+
+ hatterben(fuggveny, *args, kesz=vege, hiba=hibas)
+
+
+def gyorsitott(kulcs, fuggveny, *args, kesz=None, hiba=None, kor=60):
+ meglevo = _tar.get(kulcs)
+ if meglevo and kesz:
+ kesz(meglevo[1])
+ if meglevo and time.monotonic() - meglevo[0] < kor:
+ return
+ var = (kesz, hiba, meglevo is not None)
+ if kulcs in _futok:
+ _futok[kulcs]["varok"].append(var)
+ return
+ _indit(kulcs, fuggveny, args, [var])
+
+
+def tarolt(kulcs):
+ meglevo = _tar.get(kulcs)
+ return meglevo[1] if meglevo else None
+
+
+def ervenytelenit(*kulcsok):
+ for kulcs in kulcsok or list(set(_tar) | set(_futok)):
+ _tar.pop(kulcs, None)
+ _generacio[kulcs] = _generacio.get(kulcs, 0) + 1
+ futo = _futok.pop(kulcs, None)
+ if futo:
+ _indit(kulcs, futo["fuggveny"], futo["args"], [(k, h, False) for k, h, _ in futo["varok"]])
+
+
+def leallitas(ido_ms=3000):
+ _tar.clear()
+ _futok.clear()
+ pool = QThreadPool.globalInstance()
+ pool.clear()
+ pool.waitForDone(ido_ms)
diff --git a/raveos-center/raveos_center/ikonok.py b/raveos-center/raveos_center/ikonok.py
new file mode 100644
index 0000000..16d402c
--- /dev/null
+++ b/raveos-center/raveos_center/ikonok.py
@@ -0,0 +1,30 @@
+from PyQt6.QtCore import QByteArray, Qt
+from PyQt6.QtGui import QIcon, QPainter, QPixmap
+from PyQt6.QtSvg import QSvgRenderer
+
+UTAK = {
+ "attekintes": '<path d="M3 11l9-7 9 7v9a1 1 0 0 1-1 1h-5v-6H9v6H4a1 1 0 0 1-1-1z"/>',
+ "alkalmazasok": '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>'
+ '<rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>',
+ "frissitesek": '<path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/>',
+ "rendszer": '<rect x="5" y="5" width="14" height="14"/><rect x="9" y="9" width="6" height="6"/>'
+ '<path d="M9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3"/>',
+ "visszajelzes": '<path d="M4 4h16v12H8l-4 4z"/><path d="M8 9h8M8 12h5"/>',
+ "beallitasok": '<circle cx="12" cy="12" r="3"/>'
+ '<path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M4.9 19.1L7 17M17 7l2.1-2.1"/>',
+}
+
+
+def ikon(nev, szin, meret=18):
+ svg = (
+ f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="{szin}" '
+ f'stroke-width="2" stroke-linecap="square">{UTAK[nev]}</svg>'
+ )
+ renderelo = QSvgRenderer(QByteArray(svg.encode()))
+ kep = QPixmap(meret * 2, meret * 2)
+ kep.fill(Qt.GlobalColor.transparent)
+ festo = QPainter(kep)
+ renderelo.render(festo)
+ festo.end()
+ kep.setDevicePixelRatio(2)
+ return QIcon(kep)
diff --git a/raveos-center/raveos_center/jegyek_api.py b/raveos-center/raveos_center/jegyek_api.py
new file mode 100644
index 0000000..c8c7268
--- /dev/null
+++ b/raveos-center/raveos_center/jegyek_api.py
@@ -0,0 +1,135 @@
+import json
+import os
+import threading
+import urllib.error
+import urllib.request
+from pathlib import Path
+
+ALAP = os.environ.get("RAVEOS_JEGYEK_URL", "https://jegyek.rp1.hu/api/v1")
+KONFIG = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "raveos-center"
+KULCSFAJL = KONFIG / "kliens.json"
+
+
+class ApiHiba(Exception):
+ def __init__(self, kod, uzenet):
+ super().__init__(uzenet)
+ self.kod = kod
+
+
+def _keres(modszer, ut, adat=None, nyers=None, tipus="application/json", kulcs=None, ido=30):
+ fejlec = {"User-Agent": "raveos-center"}
+ torzs = None
+ if adat is not None:
+ torzs = json.dumps(adat).encode()
+ fejlec["Content-Type"] = "application/json"
+ elif nyers is not None:
+ torzs = nyers
+ fejlec["Content-Type"] = tipus
+ if kulcs:
+ fejlec["Authorization"] = f"Bearer {kulcs}"
+ keres = urllib.request.Request(ALAP + ut, data=torzs, headers=fejlec, method=modszer)
+ try:
+ with urllib.request.urlopen(keres, timeout=ido) as valasz:
+ return json.loads(valasz.read() or b"{}")
+ except urllib.error.HTTPError as e:
+ try:
+ uzenet = json.loads(e.read()).get("hiba", str(e))
+ except ValueError:
+ uzenet = str(e)
+ raise ApiHiba(e.code, uzenet) from None
+ except (urllib.error.URLError, OSError) as e:
+ raise ApiHiba(0, "nincs_kapcsolat") from e
+
+
+_kulcs_zar = threading.Lock()
+
+
+def kulcs():
+ with _kulcs_zar:
+ try:
+ return json.loads(KULCSFAJL.read_text())["kulcs"]
+ except (OSError, ValueError, KeyError):
+ pass
+ uj = _keres("POST", "/kliens")["kulcs"]
+ KONFIG.mkdir(parents=True, exist_ok=True)
+ os.chmod(KONFIG, 0o700)
+ ideiglenes = KULCSFAJL.with_suffix(".uj")
+ fd = os.open(ideiglenes, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
+ with os.fdopen(fd, "w") as f:
+ json.dump({"kulcs": uj}, f)
+ os.replace(ideiglenes, KULCSFAJL)
+ return uj
+
+
+def _hitelesitett(modszer, ut, **kw):
+ try:
+ return _keres(modszer, ut, kulcs=kulcs(), **kw)
+ except ApiHiba as e:
+ if e.kod != 401:
+ raise
+ with _kulcs_zar:
+ KULCSFAJL.unlink(missing_ok=True)
+ return _keres(modszer, ut, kulcs=kulcs(), **kw)
+
+
+def jegyeim():
+ return _hitelesitett("GET", "/jegyek")["jegyek"]
+
+
+def jegy(jegy_id):
+ return _hitelesitett("GET", f"/jegyek/{jegy_id}")
+
+
+def uj_jegy(tipus, cim, szoveg, rendszer=None, naplo=None):
+ adat = {"tipus": tipus, "cim": cim, "szoveg": szoveg}
+ if rendszer:
+ adat["rendszer"] = rendszer
+ if naplo:
+ adat["naplo"] = naplo
+ return _hitelesitett("POST", "/jegyek", adat=adat)
+
+
+def valasz(jegy_id, szoveg):
+ return _hitelesitett("POST", f"/jegyek/{jegy_id}/uzenetek", adat={"szoveg": szoveg})
+
+
+def otletek():
+ return _hitelesitett("GET", "/otletek")["otletek"]
+
+
+def szavaz(jegy_id, tamogat):
+ return _hitelesitett("PUT" if tamogat else "DELETE", f"/otletek/{jegy_id}/szavazat")
+
+
+def ertekeles():
+ return _hitelesitett("GET", "/ertekeles")
+
+
+def ertekel(csillag, szoveg):
+ return _hitelesitett("PUT", "/ertekeles", adat={"csillag": csillag, "szoveg": szoveg})
+
+
+def allapot():
+ return _hitelesitett("GET", "/allapot")
+
+
+def naplo_feltolt(szoveg):
+ return _hitelesitett(
+ "POST", "/naplok?forras=center", nyers=szoveg.encode(), tipus="text/plain; charset=utf-8", ido=60
+ )["azonosito"]
+
+
+HIBAUZENETEK = {
+ "nincs_kapcsolat": "Nem sikerült elérni a szervert. Ellenőrizd az internetkapcsolatot.",
+ "tul_sok_keres": "Túl sok kérés rövid idő alatt. Próbáld újra kicsit később.",
+ "lezart_jegy": "Ez a jegy le van zárva, már nem lehet rá válaszolni.",
+ "nincs_jegy": "Ez a jegy már nem létezik.",
+ "nincs_ilyen_naplo": "A megadott naplóazonosító nem létezik.",
+ "kitiltva": "Ez a gép ki van tiltva a Visszajelzés használatából.",
+}
+
+
+def emberi(hiba):
+ if isinstance(hiba, ApiHiba):
+ return HIBAUZENETEK.get(str(hiba), f"A szerver hibát jelzett ({hiba}).")
+ return str(hiba)
diff --git a/raveos-center/raveos_center/karbantartas.py b/raveos-center/raveos_center/karbantartas.py
new file mode 100644
index 0000000..2f929c0
--- /dev/null
+++ b/raveos-center/raveos_center/karbantartas.py
@@ -0,0 +1,141 @@
+import os
+import re
+from pathlib import Path
+import shutil
+import subprocess
+
+from raveos_center.muveletek import Lepes, root
+
+CACHE = Path("/var/cache/pacman/pkg")
+
+
+def _ki(parancs, bemenet=None, ido=60):
+ try:
+ kimenet = subprocess.run(
+ parancs, input=bemenet, capture_output=True, text=True, timeout=ido, env=dict(os.environ, LC_ALL="C")
+ )
+ except (OSError, subprocess.TimeoutExpired):
+ return ""
+ return kimenet.stdout + kimenet.stderr
+
+
+def _paccache(*kapcsolok):
+ kimenet = _ki(["paccache", "-dv", *kapcsolok])
+ return {s.strip() for s in kimenet.splitlines() if re.search(r"\.pkg\.tar(\.\w+)?$", s.strip())}
+
+
+def _meret(bajt):
+ for egyseg in ("B", "KiB", "MiB", "GiB"):
+ if bajt < 1024 or egyseg == "GiB":
+ return f"{bajt:.0f} {egyseg}" if egyseg == "B" else f"{bajt:.1f} {egyseg}"
+ bajt /= 1024
+ return ""
+
+
+def cache_elonezet():
+ if not shutil.which("paccache"):
+ return {"elerheto": False, "osszegzes": "A paccache nincs telepítve (pacman-contrib).", "elemek": []}
+ elemek = sorted(_paccache("-k2") | _paccache("-uk0"))
+ osszes = 0
+ for nev in elemek:
+ for fajl in (CACHE / nev, CACHE / f"{nev}.sig"):
+ try:
+ osszes += fajl.stat().st_size
+ except OSError:
+ pass
+ return {
+ "elerheto": True,
+ "osszegzes": f"{len(elemek)} csomagfájl, {_meret(osszes)}" if elemek else "Nincs törölhető csomagfájl.",
+ "elemek": elemek,
+ }
+
+
+def arva_elonezet():
+ kimenet = _ki(["pacman", "-Qdtq"])
+ elemek = [s.strip() for s in kimenet.splitlines() if re.fullmatch(r"[a-z0-9@_+][a-z0-9@._+-]*", s.strip())]
+ return {
+ "elerheto": True,
+ "osszegzes": f"{len(elemek)} árva csomag" if elemek else "Nincs árva csomag.",
+ "elemek": elemek,
+ }
+
+
+def naplo_elonezet():
+ meret = re.search(r"take up ([0-9.]+)\s*([KMGT]?)", _ki(["journalctl", "--disk-usage"]))
+ return {
+ "elerheto": True,
+ "osszegzes": f"Jelenleg {meret.group(1)} {meret.group(2)}B" if meret else "A napló mérete nem olvasható.",
+ "elemek": [],
+ }
+
+
+def _flatpak_nem_hasznalt(telepites):
+ kimenet = _ki(["flatpak", "uninstall", f"--{telepites}", "--unused"], bemenet="n\n")
+ return [re.sub(r"\s+", " ", s.strip()) for s in kimenet.splitlines() if re.match(r"\s*[0-9]+\.\s", s)]
+
+
+def flatpak_elonezet():
+ if not shutil.which("flatpak"):
+ return {"elerheto": False, "osszegzes": "A Flatpak nincs telepítve.", "elemek": [], "telepitesek": []}
+ telepitesek = []
+ elemek = []
+ for telepites in ("user", "system"):
+ lista = _flatpak_nem_hasznalt(telepites)
+ if lista:
+ telepitesek.append(telepites)
+ elemek += [f"{telepites}: {e}" for e in lista]
+ return {
+ "elerheto": True,
+ "osszegzes": f"{len(elemek)} nem használt runtime" if elemek else "Nincs nem használt runtime.",
+ "elemek": elemek,
+ "telepitesek": telepitesek,
+ }
+
+
+def tukor_allapot():
+ try:
+ with open("/etc/pacman.d/mirrorlist", encoding="utf-8") as f:
+ szerverek = sum(1 for s in f if s.startswith("Server"))
+ except OSError:
+ szerverek = 0
+ return {"elerheto": True, "osszegzes": f"{szerverek} aktív tükör", "elemek": []}
+
+
+def kulcs_allapot():
+ gpg = [str(p) for p in Path("/usr/share/pacman/keyrings").glob("*.gpg")]
+ kulcsok = sorted(set(_ki(["pacman", "-Qqo", *gpg]).split())) if gpg else []
+ return {"elerheto": True, "osszegzes": ", ".join(kulcsok) or "Nincs kulcscsomag.", "elemek": []}
+
+
+ELONEZETEK = {
+ "cache": cache_elonezet,
+ "arva": arva_elonezet,
+ "naplo": naplo_elonezet,
+ "tukrok": tukor_allapot,
+ "kulcsok": kulcs_allapot,
+ "flatpak": flatpak_elonezet,
+}
+
+
+def lepesek(azon, adat, kivalasztott=None):
+ if azon == "cache":
+ return [Lepes("Csomag cache ürítése", root("karbantartas", "cache"))]
+ if azon == "arva":
+ csomagok = kivalasztott if kivalasztott is not None else adat["elemek"]
+ return [Lepes("Árva csomagok eltávolítása", root("arva", *csomagok))] if csomagok else []
+ if azon == "naplo":
+ return [Lepes("Naplók takarítása", root("karbantartas", "naplo"))]
+ if azon == "tukrok":
+ return [Lepes("Tükrök frissítése", root("karbantartas", "tukrok"))]
+ if azon == "kulcsok":
+ return [Lepes("Kulcsok frissítése", root("karbantartas", "kulcsok"))]
+ if azon == "flatpak":
+ lista = []
+ if "user" in adat.get("telepitesek", []):
+ lista.append(
+ Lepes("Flatpak (felhasználói)", ["flatpak", "uninstall", "--user", "--unused", "-y", "--noninteractive"], False)
+ )
+ if "system" in adat.get("telepitesek", []):
+ lista.append(Lepes("Flatpak (rendszer)", root("karbantartas", "flatpak"), False))
+ return lista
+ return []
diff --git a/raveos-center/raveos_center/katalogus.py b/raveos-center/raveos_center/katalogus.py
new file mode 100644
index 0000000..118d948
--- /dev/null
+++ b/raveos-center/raveos_center/katalogus.py
@@ -0,0 +1,192 @@
+import json
+import locale
+import os
+import shlex
+import subprocess
+from pathlib import Path
+
+from raveos_center.muveletek import Lepes, root
+
+ADAT_HELYEK = [Path("/usr/lib/raveos-center/adat"), Path(__file__).resolve().parent.parent / "adat"]
+
+KATEGORIAK = [
+ ("all", "Mind"),
+ ("Games", "Gaming"),
+ ("Media", "Média"),
+ ("Internet", "Internet"),
+ ("Office", "Iroda"),
+ ("Dev", "Fejlesztés"),
+ ("System", "Rendszer"),
+ ("Security", "Biztonság"),
+]
+
+GAMING_ALAPOK = ["steam", "lutris", "heroic", "gamemode", "mangohud"]
+
+FLATHUB = "https://dl.flathub.org/repo/flathub.flatpakrepo"
+
+
+def adat_mappa():
+ for hely in ADAT_HELYEK:
+ if (hely / "katalogus" / "app-list.json").is_file():
+ return hely
+ raise RuntimeError("Nem található az alkalmazáskatalógus (app-list.json).")
+
+
+def nyelv():
+ kod = (locale.getlocale()[0] or os.environ.get("LANG", "hu"))[:2]
+ return kod if kod in ("hu", "en", "de") else "hu"
+
+
+def loc(ertek):
+ if isinstance(ertek, dict):
+ return ertek.get(nyelv()) or ertek.get("hu") or ertek.get("en") or next(iter(ertek.values()), "")
+ return ertek or ""
+
+
+def betolt():
+ adat = json.loads((adat_mappa() / "katalogus" / "app-list.json").read_text(encoding="utf-8"))
+ lista = []
+ for a in adat.get("apps", []):
+ if not isinstance(a, dict) or "id" not in a or "name" not in a:
+ continue
+ a.setdefault("type", "pacman")
+ a.setdefault("category", "System")
+ lista.append(a)
+ lista.sort(key=lambda a: a["name"].lower())
+ return lista
+
+
+def _halmaz(parancs):
+ try:
+ kimenet = subprocess.run(parancs, capture_output=True, text=True, timeout=30)
+ except (OSError, subprocess.TimeoutExpired):
+ return set()
+ return {s.strip() for s in kimenet.stdout.splitlines() if s.strip()}
+
+
+def telepitett_index():
+ return {
+ "pacman": _halmaz(["pacman", "-Qq"]),
+ "flatpak": _halmaz(["flatpak", "list", "--app", "--columns=application"]),
+ }
+
+
+def csomagok(app):
+ return app.get("packages") or [app["id"]]
+
+
+def opcio_telepitve(opcio, index):
+ return all(p in index["pacman"] for p in (opcio.get("packages") or [opcio["id"]]))
+
+
+def telepitve(app, index):
+ tipus = app["type"]
+ if tipus in ("pacman", "aur"):
+ return all(p in index["pacman"] for p in csomagok(app))
+ if tipus == "flatpak-user":
+ return bool(app.get("flatpak_id")) and app["flatpak_id"] in index["flatpak"]
+ if tipus == "script":
+ ellenorzes = app.get("check_packages")
+ if ellenorzes:
+ return all(p in index["pacman"] for p in ellenorzes)
+ return False
+ if tipus == "choice":
+ return any(opcio_telepitve(o, index) for o in app.get("options", []))
+ return False
+
+
+def forras(app):
+ return {"flatpak-user": "flatpak", "aur": "aur", "message": "info"}.get(app["type"], "pacman")
+
+
+def _konfig_lepesek(app):
+ lepesek = []
+ otthon = str(Path.home())
+ konfigok = adat_mappa() / "configs"
+ parok = []
+ if app.get("config_dir") and app.get("config_dest"):
+ parok.append((app["config_dir"], app["config_dest"]))
+ for m in app.get("config_mappings", []):
+ if m.get("source") and m.get("dest"):
+ parok.append((m["source"], m["dest"]))
+ for forras_mappa, cel in parok:
+ honnan = konfigok / forras_mappa
+ if not honnan.is_dir():
+ continue
+ cel = cel.replace("{HOME}", otthon)
+ kapcsolo = "-rf" if app.get("config_overwrite") else "-rn"
+ lepesek.append(
+ Lepes(
+ f"Beállítások: {app['name']}",
+ [
+ "bash",
+ "-c",
+ f"mkdir -p {shlex.quote(cel)} && cp {kapcsolo} {shlex.quote(str(honnan))}/. {shlex.quote(cel)}/ "
+ f"&& rm -f {shlex.quote(cel)}/Singleton*",
+ ],
+ kotelezo=False,
+ )
+ )
+ return lepesek
+
+
+def lepesek(app, telepites, opciok=None):
+ tipus = app["type"]
+ nev = app["name"]
+ lista = []
+ if tipus in ("pacman", "aur") or tipus == "choice":
+ if tipus == "choice":
+ valasztott = [o for o in app.get("options", []) if o["id"] in (opciok or [])]
+ pkgs = [p for o in valasztott for p in (o.get("packages") or [o["id"]])]
+ else:
+ pkgs = csomagok(app)
+ if not pkgs:
+ return []
+ if tipus == "aur":
+ parancs = ["yay", "-S" if telepites else "-Rns", "--noconfirm", "--color", "never", "--sudo", "pkexec"]
+ if telepites:
+ parancs += ["--needed", "--answerclean", "None", "--answerdiff", "None", "--answeredit", "None"]
+ lista.append(Lepes(f"{'Telepítés' if telepites else 'Eltávolítás'}: {nev}", parancs + pkgs))
+ else:
+ lista.append(
+ Lepes(f"{'Telepítés' if telepites else 'Eltávolítás'}: {nev}", root("telepit" if telepites else "eltavolit", *pkgs))
+ )
+ if telepites:
+ for parancs in app.get("post_install", []):
+ reszek = shlex.split(parancs)
+ if reszek[:2] == ["systemctl", "enable"] and len(reszek) == 3:
+ lista.append(Lepes(f"Szolgáltatás bekapcsolása: {reszek[2]}", root("szolgaltatas", reszek[2]), kotelezo=False))
+ else:
+ lista.append(Lepes(f"Utólagos lépés: {nev}", reszek, kotelezo=False))
+ elif tipus == "flatpak-user":
+ fid = app.get("flatpak_id")
+ if not fid:
+ return []
+ if telepites:
+ lista.append(
+ Lepes("Flathub tároló", ["flatpak", "remote-add", "--if-not-exists", "--user", "flathub", FLATHUB])
+ )
+ lista.append(
+ Lepes(
+ f"Telepítés: {nev}",
+ ["flatpak", "install", "--user", "--noninteractive", "--or-update", "flathub", fid],
+ )
+ )
+ for parancs in app.get("post_install", []):
+ lista.append(Lepes(f"Utólagos lépés: {nev}", shlex.split(parancs), kotelezo=False))
+ else:
+ lista.append(Lepes(f"Eltávolítás: {nev}", ["flatpak", "uninstall", "--user", "-y", fid]))
+ elif tipus == "script":
+ szkript = app.get("script", "")
+ if not szkript:
+ return []
+ lista.append(
+ Lepes(f"{'Telepítés' if telepites else 'Eltávolítás'}: {nev}", root("akcio", szkript, "install" if telepites else "remove"))
+ )
+ if telepites and tipus != "script":
+ lista += _konfig_lepesek(app)
+ return lista
+
+
+def eltavolithato(app):
+ return app["type"] != "message" and app.get("removable", True)
diff --git a/raveos-center/raveos_center/mentesek.py b/raveos-center/raveos_center/mentesek.py
new file mode 100644
index 0000000..7afae01
--- /dev/null
+++ b/raveos-center/raveos_center/mentesek.py
@@ -0,0 +1,156 @@
+import io
+import json
+import subprocess
+import tarfile
+import time
+from pathlib import Path
+
+from raveos_center import katalogus
+from raveos_center.muveletek import HELPER
+
+JEGYZEK = "raveos-mentes.json"
+JEGYZEK_VERZIO = 1
+
+
+def csoportok():
+ utvonal = katalogus.adat_mappa() / "mentes" / "backup-paths.json"
+ return json.loads(utvonal.read_text(encoding="utf-8")).get("groups", [])
+
+
+def elemek():
+ lista = []
+ for csoport in csoportok():
+ for elem in csoport.get("items", []):
+ lista.append(dict(elem, csoport=csoport["id"]))
+ return lista
+
+
+def abszolut(elem):
+ if elem.get("scope") == "system":
+ return Path(elem["path"])
+ return Path.home() / elem["path"]
+
+
+class Megszakitva(Exception):
+ pass
+
+
+def _pkexec_hiba(kimenet):
+ if kimenet.returncode in (126, 127):
+ raise Megszakitva("a hitelesítés elmaradt, a további rendszerelemek kimaradnak")
+
+
+def _root_tar(elem_id):
+ kimenet = subprocess.run(["pkexec", HELPER, "mentes-root", elem_id], capture_output=True, timeout=600)
+ _pkexec_hiba(kimenet)
+ if kimenet.returncode != 0:
+ raise RuntimeError(kimenet.stderr.decode(errors="replace").strip()[:200] or "a rendszerelem mentése nem sikerült")
+ return kimenet.stdout
+
+
+def mentes(cel, kivalasztott, naplo, haladas):
+ jegyzek = {"verzio": JEGYZEK_VERZIO, "letrehozva": int(time.time()), "elemek": []}
+ megszakitva = False
+ with tarfile.open(cel, "w:gz") as tar:
+ for i, elem in enumerate(kivalasztott):
+ ut = abszolut(elem)
+ naplo(f"+ {katalogus.loc(elem.get('label'))} ({ut})")
+ try:
+ if elem.get("scope") == "system":
+ if megszakitva:
+ naplo(" kihagyva, mert a hitelesítés elmaradt")
+ continue
+ adat = _root_tar(elem["id"])
+ info = tarfile.TarInfo(f"rendszer/{elem['id']}.tar")
+ info.size = len(adat)
+ info.mtime = int(time.time())
+ tar.addfile(info, io.BytesIO(adat))
+ elif ut.exists():
+ tar.add(ut.resolve() if ut.is_symlink() else ut, arcname=f"home/{elem['path']}")
+ else:
+ naplo(" nincs ilyen mappa ezen a gépen, kihagyva")
+ continue
+ jegyzek["elemek"].append(
+ {"id": elem["id"], "label": elem.get("label"), "scope": elem.get("scope", "home"), "path": elem["path"]}
+ )
+ except Megszakitva as e:
+ naplo(f" MEGSZAKÍTVA: {e}")
+ megszakitva = True
+ except Exception as e:
+ naplo(f" HIBA, kihagyva: {e}")
+ haladas(int((i + 1) * 100 / max(1, len(kivalasztott))))
+ adat = json.dumps(jegyzek, ensure_ascii=False, indent=2).encode()
+ info = tarfile.TarInfo(JEGYZEK)
+ info.size = len(adat)
+ info.mtime = int(time.time())
+ tar.addfile(info, io.BytesIO(adat))
+ return len(jegyzek["elemek"])
+
+
+def tartalom(forras):
+ with tarfile.open(forras, "r:gz") as tar:
+ try:
+ tag = tar.getmember(JEGYZEK)
+ except KeyError:
+ return _regi_tartalom(tar)
+ return json.load(tar.extractfile(tag))["elemek"]
+
+
+def _regi_tartalom(tar):
+ nevek = {t.name.split("/")[0] for t in tar.getmembers()}
+ talalt = []
+ for elem in elemek():
+ if Path(elem["path"]).name in nevek:
+ talalt.append(
+ {"id": elem["id"], "label": elem.get("label"), "scope": elem.get("scope", "home"), "path": elem["path"], "regi": True}
+ )
+ return talalt
+
+
+def _home_tagok(tar, elem):
+ if elem.get("regi"):
+ elotag = Path(elem["path"]).name
+ cel_szulo = (Path.home() / elem["path"]).parent
+ else:
+ elotag = f"home/{elem['path']}"
+ cel_szulo = Path.home()
+ for tag in tar.getmembers():
+ if tag.name == elotag or tag.name.startswith(elotag + "/"):
+ if not elem.get("regi"):
+ tag.name = tag.name[len("home/"):]
+ if tag.islnk() and tag.linkname.startswith("home/"):
+ tag.linkname = tag.linkname[len("home/"):]
+ yield tag, cel_szulo
+
+
+def visszaallitas(forras, kivalasztott, naplo, haladas):
+ kesz = 0
+ megszakitva = False
+ with tarfile.open(forras, "r:gz") as tar:
+ for i, elem in enumerate(kivalasztott):
+ naplo(f"< {katalogus.loc(elem.get('label'))}")
+ try:
+ if elem.get("scope") == "system":
+ if elem.get("regi"):
+ raise RuntimeError("régi formátumú mentésből a rendszerelemek nem állíthatók vissza")
+ adat = tar.extractfile(tar.getmember(f"rendszer/{elem['id']}.tar")).read()
+ if megszakitva:
+ naplo(" kihagyva, mert a hitelesítés elmaradt")
+ continue
+ kimenet = subprocess.run(
+ ["pkexec", HELPER, "visszaallit-root", elem["id"]], input=adat, capture_output=True, timeout=600
+ )
+ _pkexec_hiba(kimenet)
+ if kimenet.returncode != 0:
+ raise RuntimeError(kimenet.stderr.decode(errors="replace").strip()[:200] or "sikertelen")
+ else:
+ for tag, cel_szulo in _home_tagok(tar, elem):
+ tar.extract(tag, path=cel_szulo, filter="data")
+ kesz += 1
+ except Megszakitva as e:
+ naplo(f" MEGSZAKÍTVA: {e}")
+ megszakitva = True
+ except Exception as e:
+ naplo(f" HIBA, kihagyva: {e}")
+ haladas(int((i + 1) * 100 / max(1, len(kivalasztott))))
+ return kesz
diff --git a/raveos-center/raveos_center/muveletek.py b/raveos-center/raveos_center/muveletek.py
new file mode 100644
index 0000000..3243814
--- /dev/null
+++ b/raveos-center/raveos_center/muveletek.py
@@ -0,0 +1,107 @@
+import os
+import shutil
+
+from PyQt6.QtCore import QObject, QProcess, QProcessEnvironment, pyqtSignal
+
+HELPER = os.environ.get("RAVEOS_CENTER_HELPER", "/usr/lib/raveos-center/raveos-center-helper")
+
+
+def helper_elerheto():
+ return os.access(HELPER, os.X_OK) and shutil.which("pkexec") is not None
+
+
+def root(*args):
+ return ["pkexec", HELPER, *args]
+
+
+class Lepes:
+ def __init__(self, cimke, parancs, kotelezo=True):
+ self.cimke = cimke
+ self.parancs = parancs
+ self.kotelezo = kotelezo
+
+
+class Futtato(QObject):
+ sor = pyqtSignal(str)
+ lepes_indul = pyqtSignal(str)
+ kesz = pyqtSignal(bool, list)
+
+ def __init__(self, szulo=None):
+ super().__init__(szulo)
+ self.lepesek = []
+ self.hibasak = []
+ self.folyamat = None
+ self.utolso_kod = None
+ self._maradek = b""
+
+ def fut(self):
+ return self.folyamat is not None
+
+ def indit(self, lepesek):
+ if self.fut():
+ return
+ self.lepesek = list(lepesek)
+ self.hibasak = []
+ self._kovetkezo()
+
+ def _kovetkezo(self):
+ if not self.lepesek:
+ self.folyamat = None
+ self.kesz.emit(not self.hibasak, self.hibasak)
+ return
+ lepes = self.lepesek.pop(0)
+ self.aktualis = lepes
+ self.lepes_indul.emit(lepes.cimke)
+ self.sor.emit(f":: {lepes.cimke}")
+ folyamat = QProcess(self)
+ folyamat.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
+ kornyezet = QProcessEnvironment.systemEnvironment()
+ kornyezet.insert("LC_ALL", "C.UTF-8")
+ folyamat.setProcessEnvironment(kornyezet)
+ folyamat.readyReadStandardOutput.connect(self._olvas)
+ folyamat.finished.connect(self._vege)
+ folyamat.errorOccurred.connect(self._inditasi_hiba)
+ self._maradek = b""
+ self.folyamat = folyamat
+ folyamat.start(lepes.parancs[0], lepes.parancs[1:])
+
+ def _olvas(self):
+ if self.folyamat is None:
+ return
+ adat = self._maradek + bytes(self.folyamat.readAllStandardOutput())
+ *sorok, self._maradek = adat.replace(b"\r", b"\n").split(b"\n")
+ for s in sorok:
+ szoveg = s.decode("utf-8", errors="replace").rstrip()
+ if szoveg:
+ self.sor.emit(szoveg)
+
+ def _inditasi_hiba(self, hiba):
+ if hiba == QProcess.ProcessError.FailedToStart:
+ self.sor.emit(f"[HIBA] nem indítható: {self.aktualis.parancs[0]}")
+ self._lezar(False)
+
+ def _vege(self, kod, allapot):
+ self._olvas()
+ if self._maradek:
+ self.sor.emit(self._maradek.decode("utf-8", errors="replace").rstrip())
+ self._maradek = b""
+ rendben = allapot == QProcess.ExitStatus.NormalExit and kod == 0
+ if not rendben and kod in (126, 127) and self.aktualis.parancs[0] == "pkexec":
+ self.sor.emit("[MEGSZAKÍTVA] a hitelesítés elmaradt")
+ self._lezar(rendben, kod)
+
+ def _lezar(self, rendben, kod=None):
+ if self.folyamat is None:
+ return
+ self.utolso_kod = kod
+ self.folyamat.deleteLater()
+ self.folyamat = None
+ if rendben:
+ self.sor.emit(f"[OK] {self.aktualis.cimke}")
+ else:
+ self.sor.emit(f"[HIBA{f' {kod}' if kod is not None else ''}] {self.aktualis.cimke}")
+ self.hibasak.append(self.aktualis.cimke)
+ if self.aktualis.kotelezo:
+ self.lepesek = []
+ self.folyamat = None
+ self._kovetkezo()
diff --git a/raveos-center/raveos_center/oldalak/__init__.py b/raveos-center/raveos_center/oldalak/__init__.py
new file mode 100644
index 0000000..36a9048
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/__init__.py
@@ -0,0 +1,2 @@
+FENT = ["attekintes", "alkalmazasok", "frissitesek", "rendszer", "visszajelzes"]
+LENT = []
diff --git a/raveos-center/raveos_center/oldalak/alkalmazasok.py b/raveos-center/raveos_center/oldalak/alkalmazasok.py
new file mode 100644
index 0000000..4ab95e6
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/alkalmazasok.py
@@ -0,0 +1,508 @@
+from PyQt6.QtCore import Qt
+from PyQt6.QtWidgets import (
+ QCheckBox,
+ QFrame,
+ QHBoxLayout,
+ QLineEdit,
+ QMessageBox,
+ QPushButton,
+ QVBoxLayout,
+ QWidget,
+)
+
+from raveos_center import katalogus
+from raveos_center.elemek import Kartya, Oldal, cimke, frissit_stilus, gomb
+from raveos_center.folyamat import FolyamatAblak
+from raveos_center.hatter import gyorsitott
+
+AZON = "alkalmazasok"
+MENU = "Alkalmazások"
+IKON = "alkalmazasok"
+
+CSAK_TELEPITETT = "telepitett"
+
+
+class KattinthatoSor(QWidget):
+ def __init__(self, jelolo):
+ super().__init__()
+ self.jelolo = jelolo
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
+
+ def mouseReleaseEvent(self, esemeny):
+ if esemeny.button() == Qt.MouseButton.LeftButton and self.rect().contains(esemeny.position().toPoint()):
+ self.jelolo.toggle()
+ super().mouseReleaseEvent(esemeny)
+
+
+class AppSor(QWidget):
+ def __init__(self, app, oldal):
+ super().__init__()
+ self.app = app
+ self.oldal = oldal
+ self.telepitve = False
+ self.setObjectName("appsor")
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(16, 12, 16, 12)
+ doboz.setSpacing(8)
+
+ felso = QHBoxLayout()
+ felso.setSpacing(14)
+ self.jelolo = QCheckBox()
+ self.jelolo.setVisible(app["type"] != "message")
+ self.jelolo.toggled.connect(self._sor_jelolve)
+ felso.addWidget(self.jelolo)
+ betu = cimke(app["name"][:1].upper(), "kezdobetu")
+ betu.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ betu.setFixedSize(36, 36)
+ felso.addWidget(betu)
+ szovegek = QVBoxLayout()
+ szovegek.setSpacing(2)
+ nevsor = QHBoxLayout()
+ nevsor.setSpacing(10)
+ nev = cimke(app["name"])
+ nev.setStyleSheet("font-size: 14px; font-weight: 600;")
+ nevsor.addWidget(nev)
+ nevsor.addWidget(cimke(katalogus.forras(app), "monohalvany"))
+ self.allapot = cimke("TELEPÍTVE", "allapotcimke")
+ self.allapot.setProperty("szin", "zold")
+ self.allapot.hide()
+ nevsor.addWidget(self.allapot)
+ nevsor.addStretch(1)
+ szovegek.addLayout(nevsor)
+ leiras = katalogus.loc(app.get("desc"))
+ self.leiras = cimke(leiras, "halvany")
+ self.leiras.setToolTip(leiras)
+ self.leiras.setMinimumWidth(80)
+ szovegek.addWidget(self.leiras)
+ felso.addLayout(szovegek, 1)
+
+ self.eltavolit_gomb = None
+ if app["type"] == "choice":
+ self.eltavolit_gomb = gomb("Eltávolítás", kicsi=True)
+ self.eltavolit_gomb.clicked.connect(lambda: self._opcio_muvelet(False))
+ felso.addWidget(self.eltavolit_gomb)
+ self.gomb = gomb("", kicsi=True)
+ self.gomb.setMinimumWidth(110)
+ self.gomb.clicked.connect(self._kattint)
+ felso.addWidget(self.gomb)
+ doboz.addLayout(felso)
+
+ self.opciok = []
+ if app["type"] == "choice":
+ self._opcio_lista(doboz)
+ elif app["type"] != "message":
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
+ self.frissit(oldal.index)
+
+ def mouseReleaseEvent(self, esemeny):
+ if (
+ esemeny.button() == Qt.MouseButton.LeftButton
+ and self.app["type"] not in ("choice", "message")
+ and self.rect().contains(esemeny.position().toPoint())
+ ):
+ self.jelolo.toggle()
+ super().mouseReleaseEvent(esemeny)
+
+ def resizeEvent(self, esemeny):
+ super().resizeEvent(esemeny)
+ teljes = self.leiras.toolTip()
+ self.leiras.setText(
+ self.leiras.fontMetrics().elidedText(teljes, Qt.TextElideMode.ElideRight, max(80, self.leiras.width()))
+ )
+
+ def _opcio_lista(self, doboz):
+ lista = QVBoxLayout()
+ lista.setContentsMargins(82, 4, 0, 0)
+ lista.setSpacing(6)
+ fej = QHBoxLayout()
+ fej.addWidget(cimke("VÁLASSZ, AKÁR TÖBBET IS", "monohalvany"))
+ fej.addStretch(1)
+ mind = gomb("Mind kijelölése", kicsi=True)
+ mind.clicked.connect(self._mind)
+ fej.addWidget(mind)
+ lista.addLayout(fej)
+ for o in self.app.get("options", []):
+ jelolo = QCheckBox(o["name"])
+ jelolo.setMinimumWidth(130)
+ jelolo.toggled.connect(lambda _: self._gombok_opcio())
+ kontener = KattinthatoSor(jelolo)
+ sor = QHBoxLayout(kontener)
+ sor.setContentsMargins(0, 2, 0, 2)
+ sor.setSpacing(10)
+ sor.addWidget(jelolo)
+ leiras = cimke(katalogus.loc(o.get("desc")), "halvany")
+ sor.addWidget(leiras, 1)
+ allapot = cimke("TELEPÍTVE", "allapotcimke")
+ allapot.setProperty("szin", "zold")
+ sor.addWidget(allapot)
+ lista.addWidget(kontener)
+ self.opciok.append((jelolo, allapot, o))
+ doboz.addLayout(lista)
+
+ def _mind(self):
+ mind = all(j.isChecked() for j, _, _ in self.opciok)
+ for j, _, _ in self.opciok:
+ j.setChecked(not mind)
+
+ def _sor_jelolve(self, jelolve):
+ if self.opciok:
+ for j, _, _ in self.opciok:
+ j.blockSignals(True)
+ j.setChecked(jelolve)
+ j.blockSignals(False)
+ self._gombok_opcio()
+ self.oldal.kijeles_valtozott()
+
+ def torol_kijeles(self):
+ self.jelolo.setChecked(False)
+
+ def telepitendo(self):
+ if self.app["type"] == "choice":
+ opciok = self._kijelolt(False)
+ return (self.app, opciok) if opciok else None
+ if self.jelolo.isChecked() and not self.telepitve and self.app["type"] != "message":
+ return (self.app, None)
+ return None
+
+ def eltavolitando(self):
+ if self.app["type"] == "choice":
+ opciok = self._kijelolt(True)
+ return (self.app, opciok) if opciok else None
+ if self.jelolo.isChecked() and self.telepitve and katalogus.eltavolithato(self.app):
+ return (self.app, None)
+ return None
+
+ def _kijelolt(self, telepitett):
+ return [
+ o["id"]
+ for j, _, o in self.opciok
+ if j.isChecked() and katalogus.opcio_telepitve(o, self.oldal.index) == telepitett
+ ]
+
+ def _gombok_opcio(self):
+ uj = self._kijelolt(False)
+ regi = self._kijelolt(True)
+ self.jelolo.blockSignals(True)
+ self.jelolo.setChecked(any(j.isChecked() for j, _, _ in self.opciok))
+ self.jelolo.blockSignals(False)
+ self.oldal.kijeles_valtozott()
+ self.gomb.setText(f"Telepítés ({len(uj)})" if uj else "Telepítés")
+ self.gomb.setEnabled(bool(uj))
+ self.gomb.setProperty("fo", True)
+ frissit_stilus(self.gomb)
+ self.eltavolit_gomb.setText(f"Eltávolítás ({len(regi)})" if regi else "Eltávolítás")
+ self.eltavolit_gomb.setEnabled(bool(regi))
+
+ def _opcio_muvelet(self, telepites):
+ kijelolt = self._kijelolt(not telepites)
+ if kijelolt:
+ self.oldal.muvelet(self.app, telepites, kijelolt)
+
+ def frissit(self, index):
+ if self.app["type"] == "choice":
+ db = 0
+ for _, allapot, o in self.opciok:
+ fent = katalogus.opcio_telepitve(o, index)
+ allapot.setVisible(fent)
+ db += fent
+ self.telepitve = db > 0
+ self.allapot.setText(f"{db} / {len(self.opciok)} TELEPÍTVE")
+ self.allapot.setVisible(db > 0)
+ self._gombok_opcio()
+ return
+ if self.app["type"] == "message":
+ self.gomb.setText("Tudnivalók")
+ self.gomb.setProperty("fo", False)
+ else:
+ self.telepitve = katalogus.telepitve(self.app, index)
+ self.allapot.setVisible(self.telepitve)
+ if self.telepitve:
+ self.gomb.setText("Eltávolítás")
+ self.gomb.setEnabled(katalogus.eltavolithato(self.app))
+ self.gomb.setProperty("fo", False)
+ else:
+ self.gomb.setText("Telepítés")
+ self.gomb.setEnabled(True)
+ self.gomb.setProperty("fo", True)
+ frissit_stilus(self.gomb)
+
+ def _kattint(self):
+ if self.app["type"] == "message":
+ QMessageBox.information(self, self.app["name"], katalogus.loc(self.app.get("message")))
+ return
+ if self.app["type"] == "choice":
+ self._opcio_muvelet(True)
+ return
+ self.oldal.muvelet(self.app, not self.telepitve)
+
+
+class Csoport(QWidget):
+ def __init__(self, nev):
+ super().__init__()
+ self.nev = nev
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 0, 0, 0)
+ doboz.setSpacing(10)
+ self.fejlec = cimke("", "h2")
+ doboz.addWidget(self.fejlec)
+ self.lista = Kartya(terkoz=0, margo=0)
+ doboz.addWidget(self.lista)
+ self.sorok = []
+
+ def hozzaad(self, sor):
+ self.sorok.append(sor)
+ self.lista.elrendezes.addWidget(sor)
+
+ def szur(self, lathatok):
+ db = 0
+ utolso = None
+ for sor in self.sorok:
+ lathato = sor in lathatok
+ sor.setVisible(lathato)
+ sor.setProperty("utolso", False)
+ if lathato:
+ db += 1
+ utolso = sor
+ if utolso:
+ utolso.setProperty("utolso", True)
+ for sor in self.sorok:
+ frissit_stilus(sor)
+ self.fejlec.setText(f"{self.nev.upper()} · {db}")
+ self.setVisible(db > 0)
+ return db
+
+
+class Alkalmazasok(Oldal):
+ def __init__(self, kozpont):
+ super().__init__(kozpont, "PACMAN · AUR · FLATPAK", "Alkalmazások")
+ self.katalogus = []
+ self.index = {"pacman": set(), "flatpak": set()}
+ self.sorok = []
+ self.csoportok = {}
+ self.kategoria = "all"
+
+ self.kereso = QLineEdit()
+ self.kereso.setPlaceholderText("Keresés, pl. Steam, OBS, Discord")
+ self.kereso.setClearButtonEnabled(True)
+ self.kereso.setMinimumWidth(220)
+ self.kereso.setMaximumWidth(340)
+ self.kereso.textChanged.connect(self._szur)
+ self.fejlec_gomb(self.kereso)
+
+ pillek = QHBoxLayout()
+ pillek.setSpacing(8)
+ self.pillek = {}
+ for azon, nev in katalogus.KATEGORIAK + [(CSAK_TELEPITETT, "Csak telepítettek")]:
+ p = QPushButton(nev)
+ p.setObjectName("pill")
+ p.setCursor(Qt.CursorShape.PointingHandCursor)
+ p.clicked.connect(lambda _=False, a=azon: self._kategoria(a))
+ self.pillek[azon] = p
+ if azon == CSAK_TELEPITETT:
+ pillek.addStretch(1)
+ pillek.addWidget(p)
+ self.torzs.addLayout(pillek)
+
+ self.kiemelt = Kartya(terkoz=8, margo=24)
+ self.kiemelt.setObjectName("kiemelt")
+ ks = QHBoxLayout()
+ ks.setSpacing(24)
+ kszoveg = QVBoxLayout()
+ kszoveg.setSpacing(6)
+ kszoveg.addWidget(cimke("CSOMAG · GAMING ALAPOK", "eyebrow"))
+ kszoveg.addWidget(cimke("EGY KATTINTÁS, MINDEN AMI JÁTÉKHOZ KELL", "h2", True))
+ kszoveg.addWidget(
+ cimke("Steam (Proton-GE-vel), Lutris, Heroic, GameMode és MangoHud egyben, előre beállítva.", "masodlagos", True)
+ )
+ ks.addLayout(kszoveg, 1)
+ self.kiemelt_gomb = gomb("Csomag telepítése", fo=True)
+ self.kiemelt_gomb.clicked.connect(self._gaming_alapok)
+ ks.addWidget(self.kiemelt_gomb, 0, Qt.AlignmentFlag.AlignVCenter)
+ self.kiemelt.elrendezes.addLayout(ks)
+ self.torzs.addWidget(self.kiemelt)
+
+ self.hiba = cimke("", "figyelem", True)
+ self.hiba.hide()
+ self.torzs.addWidget(self.hiba)
+ self._kijeles_sav()
+ self.lista = QVBoxLayout()
+ self.lista.setSpacing(28)
+ self.torzs.addLayout(self.lista)
+ self.ures = cimke("Nincs találat.", "halvany")
+ self.ures.hide()
+ self.torzs.addWidget(self.ures)
+ self.torzs.addStretch(1)
+ self._betolt()
+ self._kategoria("all")
+
+ def _kijeles_sav(self):
+ self.sav = QFrame(self)
+ self.sav.setObjectName("kijelessav")
+ s = QHBoxLayout(self.sav)
+ s.setContentsMargins(48, 12, 48, 12)
+ s.setSpacing(8)
+ self.sav_szoveg = cimke("", "mono")
+ s.addWidget(self.sav_szoveg)
+ s.addStretch(1)
+ torles = gomb("Kijelölés törlése", kicsi=True)
+ torles.clicked.connect(self._kijeles_torles)
+ s.addWidget(torles)
+ self.sav_eltavolit = gomb("Kijelöltek eltávolítása")
+ self.sav_eltavolit.clicked.connect(lambda: self._tomeges(False))
+ s.addWidget(self.sav_eltavolit)
+ self.sav_telepit = gomb("Kijelöltek telepítése", fo=True)
+ self.sav_telepit.clicked.connect(lambda: self._tomeges(True))
+ s.addWidget(self.sav_telepit)
+ self.sav.hide()
+
+ def resizeEvent(self, esemeny):
+ super().resizeEvent(esemeny)
+ magassag = self.sav.sizeHint().height()
+ self.sav.setGeometry(0, self.height() - magassag, self.width(), magassag)
+
+ def kijeles_valtozott(self):
+ if not hasattr(self, "sav"):
+ return
+ uj = [x for x in (s.telepitendo() for s in self.sorok) if x]
+ regi = [x for x in (s.eltavolitando() for s in self.sorok) if x]
+ kijelolt = sum(1 for s in self.sorok if s.jelolo.isChecked())
+ latszik = kijelolt > 0
+ self.sav.setVisible(latszik)
+ self.setViewportMargins(0, 0, 0, self.sav.sizeHint().height() if latszik else 0)
+ self.sav_szoveg.setText(f"{kijelolt} KIJELÖLVE")
+ self.sav_telepit.setText(f"Kijelöltek telepítése ({len(uj)})")
+ self.sav_telepit.setEnabled(bool(uj))
+ self.sav_eltavolit.setText(f"Kijelöltek eltávolítása ({len(regi)})")
+ self.sav_eltavolit.setEnabled(bool(regi))
+ if latszik:
+ self.sav.raise_()
+ magassag = self.sav.sizeHint().height()
+ self.sav.setGeometry(0, self.height() - magassag, self.width(), magassag)
+
+ def _kijeles_torles(self):
+ for s in self.sorok:
+ s.torol_kijeles()
+
+ def _nev(self, app, opciok):
+ if opciok:
+ return ", ".join(f"{app['name']}: {o['name']}" for o in app.get("options", []) if o["id"] in opciok)
+ return app["name"]
+
+ def _tomeges(self, telepites):
+ tetelek = [x for x in ((s.telepitendo() if telepites else s.eltavolitando()) for s in self.sorok) if x]
+ if not tetelek:
+ return
+ nevek = [self._nev(app, opciok) for app, opciok in tetelek]
+ if not telepites:
+ valasz = QMessageBox.question(
+ self,
+ "Eltávolítás",
+ "Biztosan eltávolítod ezeket?\n\n" + "\n".join(nevek),
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ QMessageBox.StandardButton.No,
+ )
+ if valasz != QMessageBox.StandardButton.Yes:
+ return
+ lepesek = []
+ for app, opciok in tetelek:
+ for lepes in katalogus.lepesek(app, telepites, opciok):
+ lepes.kotelezo = False
+ lepesek.append(lepes)
+ if not lepesek:
+ return
+ cim = f"{'Telepítés' if telepites else 'Eltávolítás'}: {len(tetelek)} alkalmazás"
+ FolyamatAblak(self, cim, lepesek, "Minden lépés sikeresen lefutott.", "Nem minden lépés sikerült.").exec()
+ self._kijeles_torles()
+ self.megjelenik()
+
+ def _betolt(self):
+ try:
+ self.katalogus = katalogus.betolt()
+ except (OSError, ValueError, RuntimeError) as e:
+ self.hiba.setText(f"Az alkalmazáskatalógus nem tölthető be: {e}")
+ self.hiba.show()
+ return
+ for azon, nev in katalogus.KATEGORIAK[1:]:
+ csoport = Csoport(nev)
+ self.csoportok[azon] = csoport
+ self.lista.addWidget(csoport)
+ for app in self.katalogus:
+ csoport = self.csoportok.get(app["category"]) or self.csoportok["System"]
+ sor = AppSor(app, self)
+ self.sorok.append(sor)
+ csoport.hozzaad(sor)
+
+ def megjelenik(self, *_):
+ gyorsitott("telepitett", katalogus.telepitett_index, kesz=self._index_kesz, kor=30)
+
+ def _index_kesz(self, index):
+ if index == self.index:
+ return
+ self.index = index
+ for s in self.sorok:
+ s.frissit(index)
+ alap = [a for a in self.katalogus if a["id"] in katalogus.GAMING_ALAPOK]
+ hianyzik = [a for a in alap if not katalogus.telepitve(a, index)]
+ self.kiemelt_gomb.setEnabled(bool(hianyzik))
+ self.kiemelt_gomb.setText("Csomag telepítése" if hianyzik else "Minden telepítve")
+ self._szur()
+
+ def _kategoria(self, azon):
+ self.kategoria = azon
+ for a, p in self.pillek.items():
+ p.setProperty("aktiv", a == azon)
+ frissit_stilus(p)
+ self._szur()
+
+ def _szur(self):
+ szoveg = self.kereso.text().strip().lower()
+ lathatok = set()
+ for s in self.sorok:
+ app = s.app
+ if self.kategoria == CSAK_TELEPITETT:
+ jo = s.telepitve
+ else:
+ jo = self.kategoria == "all" or app["category"] == self.kategoria
+ if jo and (not szoveg or szoveg in app["name"].lower() or szoveg in katalogus.loc(app.get("desc")).lower()):
+ lathatok.add(s)
+ osszes = sum(c.szur(lathatok) for c in self.csoportok.values())
+ self.ures.setText(
+ "Még nincs telepítve egyetlen alkalmazás sem a listából."
+ if self.kategoria == CSAK_TELEPITETT and not szoveg
+ else "Nincs találat."
+ )
+ self.ures.setVisible(osszes == 0)
+ self.kiemelt.setVisible(self.kategoria in ("all", "Games") and not szoveg)
+
+ def muvelet(self, app, telepites, opciok=None):
+ if not telepites:
+ nevek = ", ".join(o["name"] for o in app.get("options", []) if o["id"] in (opciok or [])) or app["name"]
+ valasz = QMessageBox.question(
+ self,
+ "Eltávolítás",
+ f"Biztosan eltávolítod: {nevek}?",
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ QMessageBox.StandardButton.No,
+ )
+ if valasz != QMessageBox.StandardButton.Yes:
+ return
+ lepesek = katalogus.lepesek(app, telepites, opciok)
+ if not lepesek:
+ return
+ cim = f"{'Telepítés' if telepites else 'Eltávolítás'}: {app['name']}"
+ FolyamatAblak(self, cim, lepesek, f"{app['name']} {'telepítve' if telepites else 'eltávolítva'}.").exec()
+ self.megjelenik()
+
+ def _gaming_alapok(self):
+ lepesek = []
+ for app in self.katalogus:
+ if app["id"] in katalogus.GAMING_ALAPOK and not katalogus.telepitve(app, self.index):
+ lepesek += katalogus.lepesek(app, True)
+ if not lepesek:
+ return
+ FolyamatAblak(self, "Gaming alapok telepítése", lepesek, "A Gaming alapok telepítve.").exec()
+ self.megjelenik()
+
+
+OLDAL = Alkalmazasok
diff --git a/raveos-center/raveos_center/oldalak/attekintes.py b/raveos-center/raveos_center/oldalak/attekintes.py
new file mode 100644
index 0000000..9ebc848
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/attekintes.py
@@ -0,0 +1,262 @@
+from datetime import datetime
+
+from PyQt6.QtCore import Qt, QUrl
+from PyQt6.QtGui import QDesktopServices
+from PyQt6.QtWidgets import QHBoxLayout, QMessageBox, QProgressBar, QPushButton, QVBoxLayout, QWidget
+
+from raveos_center import gaming, muveletek, rendszerinfo
+from raveos_center.elemek import Kartya, Oldal, Racs, cimke, frissit_stilus, gomb
+from raveos_center.hatter import ervenytelenit, gyorsitott, tarolt
+
+AZON = "attekintes"
+MENU = "Áttekintés"
+IKON = "attekintes"
+
+GYORS = [
+ ("01", "Alkalmazás telepítése", "Böngészés kategóriák szerint", "alkalmazasok", ()),
+ ("02", "Rendszerfrissítés", "pacman + AUR + Flatpak", "frissitesek", ()),
+ ("03", "Driverek", "GPU, Wi-Fi, kontrollerek", "rendszer", ("driverek",)),
+ ("04", "Takarítás", "Cache, árva csomagok", "rendszer", ("karbantartas",)),
+ ("05", "Gaming tweakek", "GameMode, Proton, MangoHud", "rendszer", ("gaming",)),
+ ("06", "Visszaállítás", "Snapshot betöltése", "rendszer", ("snapshotok",)),
+]
+
+KOZOSSEG = [
+ ("rp1.hu", "https://rp1.hu"),
+ ("Discord", "https://discord.gg/gSdVMXRFQc"),
+ ("Stream", "https://kick.com/rpslair"),
+]
+
+
+class Csempe(QPushButton):
+ def __init__(self, kod, cim, alcim):
+ super().__init__()
+ self.setObjectName("csempe")
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(18, 18, 18, 18)
+ doboz.setSpacing(4)
+ self.kod = cimke(kod, "monozold")
+ doboz.addWidget(self.kod)
+ doboz.addStretch(1)
+ self.cim = cimke(cim)
+ self.cim.setStyleSheet("font-size: 15px; font-weight: 600;")
+ doboz.addWidget(self.cim)
+ self.alcim = cimke(alcim, "halvany", True)
+ doboz.addWidget(self.alcim)
+ for c in (self.kod, self.cim, self.alcim):
+ c.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+
+
+def allapotkartya(cim):
+ kartya = Kartya(cim)
+ ertek = cimke("...", "nagyszam")
+ kartya.elrendezes.addWidget(ertek)
+ return kartya, ertek
+
+
+class Attekintes(Oldal):
+ def __init__(self, kozpont):
+ super().__init__(kozpont, "ÜDV ÚJRA", "Áttekintés")
+ self.frissites_gomb = self.fejlec_gomb(gomb("Frissítés most", fo=True))
+ self.frissites_gomb.clicked.connect(lambda: self.kozpont.mutat("frissitesek"))
+ self.snapshot_gomb = self.fejlec_gomb(gomb("Snapshot készítése"))
+ self.snapshot_gomb.clicked.connect(lambda: self.kozpont.mutat("rendszer", "snapshotok", "uj"))
+
+ allapot = Racs(220)
+ k, self.frissites_szam = allapotkartya("Frissítések")
+ self.frissites_szam.setProperty("zold", True)
+ self.frissites_reszlet = cimke("ellenőrzés...", "masodlagos")
+ k.elrendezes.addWidget(self.frissites_reszlet)
+ allapot.hozzaad(k)
+
+ k, self.snapshot_ertek = allapotkartya("Utolsó snapshot")
+ self.snapshot_reszlet = cimke("", "masodlagos")
+ k.elrendezes.addWidget(self.snapshot_reszlet)
+ allapot.hozzaad(k)
+
+ k, self.lemez_ertek = allapotkartya("Lemez")
+ self.lemez_sav = QProgressBar()
+ self.lemez_sav.setObjectName("sav")
+ self.lemez_sav.setTextVisible(False)
+ self.lemez_sav.setFixedHeight(6)
+ k.elrendezes.addWidget(self.lemez_sav)
+ allapot.hozzaad(k)
+
+ k, self.gaming_ertek = allapotkartya("Gaming mód")
+ self.gaming_adat = None
+ self.gaming_gomb = gomb("...", kicsi=True)
+ self.gaming_gomb.clicked.connect(self._gaming_valt)
+ k.elrendezes.addWidget(self.gaming_gomb, 0, Qt.AlignmentFlag.AlignLeft)
+ allapot.hozzaad(k)
+ self.torzs.addWidget(allapot)
+
+ also = QHBoxLayout()
+ also.setSpacing(24)
+ bal = QVBoxLayout()
+ bal.setSpacing(16)
+ bal.addWidget(cimke("GYORS MŰVELETEK", "h2"))
+ self.gyors = Racs(180, 12)
+ self.csempek = []
+ for kod, cim, alcim, cel, al in GYORS:
+ csempe = Csempe(kod, cim, alcim)
+ csempe.clicked.connect(lambda _=False, c=cel, a=al: self.kozpont.mutat(c, *a))
+ self.csempek.append((csempe, cel, al))
+ self.gyors.hozzaad(csempe)
+ bal.addWidget(self.gyors)
+ bal.addStretch(1)
+ also.addLayout(bal, 2)
+
+ jobb = QVBoxLayout()
+ jobb.setSpacing(16)
+ jobb.addWidget(cimke("RENDSZER", "h2"))
+ self.info = Kartya(terkoz=0, margo=20)
+ self.info.elrendezes.setContentsMargins(20, 8, 20, 8)
+ self.info_sorok = {}
+ for kulcs in ("Kernel", "Asztal", "CPU", "GPU", "Memória", "Fájlrendszer", "Uptime"):
+ sor = QWidget()
+ sl = QHBoxLayout(sor)
+ sl.setContentsMargins(0, 12, 0, 12)
+ sl.addWidget(cimke(kulcs, "monohalvany"))
+ sl.addStretch(1)
+ ertek = cimke("...", "mono")
+ ertek.setAlignment(Qt.AlignmentFlag.AlignRight)
+ ertek.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
+ sl.addWidget(ertek)
+ self.info_sorok[kulcs] = ertek
+ self.info.elrendezes.addWidget(sor)
+ jobb.addWidget(self.info)
+
+ kozosseg = Kartya("Közösség", terkoz=12)
+ nev = cimke("RaveCore")
+ nev.setStyleSheet("font-size: 15px; font-weight: 600;")
+ kozosseg.elrendezes.addWidget(nev)
+ kozosseg.elrendezes.addWidget(
+ cimke("Kérdésed van, vagy hibát találtál? Gyere a közösségbe, vagy írj nekünk hibajegyet.", "masodlagos", True)
+ )
+ linkek = QHBoxLayout()
+ linkek.setSpacing(16)
+ for szoveg, cim in KOZOSSEG:
+ link = cimke(f'<a href="{cim}" style="color:#62a052; text-decoration:none">{szoveg}</a>', "link")
+ link.setOpenExternalLinks(True)
+ linkek.addWidget(link)
+ self.hibajegy_link = cimke('<a href="#" style="color:#62a052; text-decoration:none">Hibajegy</a>', "link")
+ self.hibajegy_link.linkActivated.connect(lambda _: self.kozpont.mutat("visszajelzes"))
+ linkek.addWidget(self.hibajegy_link)
+ linkek.addStretch(1)
+ kozosseg.elrendezes.addLayout(linkek)
+ jobb.addWidget(kozosseg)
+ jobb.addStretch(1)
+ also.addLayout(jobb, 1)
+ self.torzs.addLayout(also)
+ self.torzs.addStretch(1)
+
+ def megjelenik(self, *_):
+ self.frissites_gomb.setEnabled(self.kozpont.van("frissitesek"))
+ self.snapshot_gomb.setEnabled(self.kozpont.van("rendszer", "snapshotok"))
+ if tarolt("gaming") is None:
+ self.gaming_gomb.setEnabled(False)
+ gyorsitott("gaming", gaming.allapot, kesz=self._gaming_kesz, kor=30)
+ for csempe, cel, al in self.csempek:
+ elerheto = self.kozpont.van(cel, *al)
+ csempe.setEnabled(elerheto)
+ csempe.setToolTip("" if elerheto else "Hamarosan")
+ self._lemez()
+ gyorsitott("info", self._info_gyujt, kesz=self._info_kesz, kor=300)
+ gyorsitott("frissitesek", rendszerinfo.frissitesek, kesz=self._frissites_kesz, hiba=self._frissites_hiba, kor=60)
+ gyorsitott("snapshot", rendszerinfo.utolso_snapshot, kesz=self._snapshot_kesz, kor=60)
+
+ def _gaming_kesz(self, adat):
+ self.gaming_adat = adat
+ aktiv = gaming.gaming_mod_aktiv(adat)
+ self.gaming_ertek.setText("BEKAPCSOLVA" if aktiv else "KIKAPCSOLVA")
+ self.gaming_ertek.setProperty("zold", aktiv)
+ frissit_stilus(self.gaming_ertek)
+ self.gaming_gomb.setText("Kikapcsolás" if aktiv else "Bekapcsolás")
+ self.gaming_gomb.setProperty("fo", not aktiv)
+ frissit_stilus(self.gaming_gomb)
+ valtozas = gaming.gaming_mod_valtozasok(adat, not aktiv)
+ self.gaming_gomb.setEnabled(bool(valtozas) and muveletek.helper_elerheto())
+ self.gaming_gomb.setToolTip(
+ "" if valtozas else "Ezen a gépen a Gaming mód elemei nem kapcsolhatók ki, a GameMode a Rendszer > Gaming fülön távolítható el."
+ )
+
+ def _gaming_valt(self):
+ if not self.gaming_adat:
+ return
+ from raveos_center.oldalak.rendszer.gaming import alkalmaz
+
+ self.gaming_gomb.setEnabled(False)
+ aktiv = gaming.gaming_mod_aktiv(self.gaming_adat)
+ hiba = alkalmaz(self, gaming.gaming_mod_valtozasok(self.gaming_adat, not aktiv))
+ if hiba:
+ QMessageBox.warning(self, "Gaming mód", hiba)
+ ervenytelenit("gaming")
+ gyorsitott("gaming", gaming.allapot, kesz=self._gaming_kesz)
+
+ def _lemez(self):
+ hasznalt, ossz = rendszerinfo.lemez()
+ self.lemez_ertek.setText(f"{hasznalt / 1e9:.0f} / {ossz / 1e9:.0f} GB")
+ self.lemez_sav.setValue(int(hasznalt * 100 / ossz) if ossz else 0)
+
+ @staticmethod
+ def _info_gyujt():
+ gpu = ", ".join(rendszerinfo.gpuk()) or "ismeretlen"
+ driver = ", ".join(rendszerinfo.gpu_driverek())
+ return {
+ "Kernel": rendszerinfo.kernel(),
+ "Asztal": rendszerinfo.asztal(),
+ "CPU": rendszerinfo.cpu(),
+ "GPU": f"{gpu} · {driver}" if driver else gpu,
+ "Memória": rendszerinfo.memoria(),
+ "Fájlrendszer": rendszerinfo.fajlrendszer(),
+ "Uptime": rendszerinfo.uzemido(),
+ }
+
+ def _info_kesz(self, adat):
+ for kulcs, ertek in adat.items():
+ self.info_sorok[kulcs].setText(ertek)
+
+ def _frissites_kesz(self, csomagok):
+ szam = len(csomagok)
+ self.kozpont.jelveny("frissitesek", szam)
+ if not szam:
+ self.frissites_szam.setText("NAPRAKÉSZ")
+ self.frissites_reszlet.setText("nincs elérhető frissítés")
+ return
+ self.frissites_szam.setText(f"{szam} ELÉRHETŐ")
+ reszek = []
+ ujrainditos = sum(1 for c in csomagok if c["ujrainditas"])
+ aur = sum(1 for c in csomagok if c["forras"] == "aur")
+ flatpak = sum(1 for c in csomagok if c["forras"] == "flatpak")
+ if ujrainditos:
+ reszek.append(f"{ujrainditos} újraindítást igényel")
+ if aur:
+ reszek.append(f"{aur} AUR")
+ if flatpak:
+ reszek.append(f"{flatpak} Flatpak")
+ self.frissites_reszlet.setText(" · ".join(reszek) or "csak hivatalos csomagok")
+
+ def _frissites_hiba(self, _):
+ self.frissites_szam.setText("-")
+ self.frissites_reszlet.setText("az ellenőrzés nem sikerült")
+
+ def _snapshot_kesz(self, snapshot):
+ if snapshot is None:
+ self.snapshot_ertek.setText("-")
+ self.snapshot_reszlet.setText("a snapper nem érhető el")
+ return
+ if not snapshot:
+ self.snapshot_ertek.setText("NINCS")
+ self.snapshot_reszlet.setText("még nem készült snapshot")
+ return
+ try:
+ ido = datetime.fromisoformat(snapshot.get("date", ""))
+ nap = (datetime.now() - ido).days
+ self.snapshot_ertek.setText("MA" if nap == 0 else f"{nap} NAPJA")
+ except (TypeError, ValueError):
+ self.snapshot_ertek.setText("?")
+ self.snapshot_reszlet.setText(f"btrfs · {snapshot.get('description') or snapshot.get('type', '')}")
+
+
+OLDAL = Attekintes
diff --git a/raveos-center/raveos_center/oldalak/frissitesek.py b/raveos-center/raveos_center/oldalak/frissitesek.py
new file mode 100644
index 0000000..baa1ccc
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/frissitesek.py
@@ -0,0 +1,331 @@
+from datetime import datetime
+
+from PyQt6.QtCore import Qt
+from PyQt6.QtWidgets import (
+ QAbstractItemView,
+ QCheckBox,
+ QHBoxLayout,
+ QHeaderView,
+ QPlainTextEdit,
+ QTableWidget,
+ QTableWidgetItem,
+ QVBoxLayout,
+ QWidget,
+)
+
+from raveos_center import muveletek, rendszerinfo, snapshotok
+from raveos_center.elemek import Kartya, Oldal, Racs, cimke, gomb
+from raveos_center.hatter import ervenytelenit, hatterben
+
+AZON = "frissitesek"
+MENU = "Frissítések"
+IKON = "frissitesek"
+
+FORRAS = {"pacman": "pacman", "aur": "AUR", "flatpak": "Flatpak"}
+
+
+def mb(meret):
+ if meret is None:
+ return "-"
+ return f"{meret / 1e6:.1f} MB"
+
+
+class Frissitesek(Oldal):
+ def __init__(self, kozpont):
+ super().__init__(kozpont, "UTOLSÓ ELLENŐRZÉS: -", "Frissítések", kitolt=True)
+ self.csomagok = []
+ self.ujrainditas_kell = False
+ self.futtato = muveletek.Futtato(self)
+ self.futtato.sor.connect(self._naplo)
+ self.futtato.kesz.connect(self._futas_kesz)
+
+ self.torzs.setSpacing(20)
+ self.ellenorzes = self.fejlec_gomb(gomb("Ellenőrzés"))
+ self.ellenorzes.clicked.connect(lambda: self._ellenoriz(True))
+ self.inditas = self.fejlec_gomb(gomb("Nincs kijelölve", fo=True))
+ self.inditas.clicked.connect(self._indit)
+
+ osszegzes = Racs(220)
+ k = Kartya("Kijelölve")
+ self.kijelolt = cimke("0 / 0 CSOMAG", "nagyszam")
+ k.elrendezes.addWidget(self.kijelolt)
+ osszegzes.hozzaad(k)
+ k = Kartya("Letöltés")
+ self.letoltes = cimke("0 MB", "nagyszam")
+ k.elrendezes.addWidget(self.letoltes)
+ osszegzes.hozzaad(k)
+ k = Kartya("Biztonság")
+ self.snapshot = QCheckBox("Snapshot készítése frissítés előtt")
+ self.snapshot.setChecked(True)
+ k.elrendezes.addWidget(self.snapshot)
+ self.snapshot_megj = cimke("", "halvany", True)
+ k.elrendezes.addWidget(self.snapshot_megj)
+ osszegzes.hozzaad(k)
+ self.torzs.addWidget(osszegzes)
+
+ self.figyelem = cimke("", "figyelem", True)
+ self.figyelem.hide()
+ self.torzs.addWidget(self.figyelem)
+ self.ujraindito_sav = QWidget()
+ us = QHBoxLayout(self.ujraindito_sav)
+ us.setContentsMargins(0, 0, 0, 0)
+ us.setSpacing(12)
+ us.addWidget(
+ cimke("A frissítés után újraindítás szükséges, hogy az új kernel vagy driver betöltődjön.", "figyelem", True),
+ 1,
+ )
+ ujraindit = gomb("Újraindítás most", fo=True)
+ ujraindit.clicked.connect(self._ujraindit)
+ us.addWidget(ujraindit)
+ self.ujraindito_sav.hide()
+ self.torzs.addWidget(self.ujraindito_sav)
+
+ csomag_resz = QVBoxLayout()
+ csomag_resz.setSpacing(12)
+ fej = QHBoxLayout()
+ fej.addWidget(cimke("CSOMAGOK", "h2"))
+ fej.addStretch(1)
+ self.mind = gomb("Mind kijelölése", kicsi=True)
+ self.mind.clicked.connect(self._mind)
+ fej.addWidget(self.mind)
+ csomag_resz.addLayout(fej)
+ csomag_resz.addWidget(
+ cimke(
+ "A hivatalos csomagok (pacman) csak együtt frissíthetők: Arch-alapú rendszeren a részleges frissítés "
+ "elronthatja a rendszert. Az AUR és a Flatpak csomagok egyenként is kijelölhetők.",
+ "halvany",
+ True,
+ )
+ )
+ self.tabla = QTableWidget(0, 6)
+ self.tabla.setHorizontalHeaderLabels(["", "Csomag", "Forrás", "Jelenlegi", "Új", "Méret"])
+ self.tabla.verticalHeader().hide()
+ self.tabla.setShowGrid(False)
+ self.tabla.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
+ self.tabla.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.tabla.setFocusPolicy(Qt.FocusPolicy.NoFocus)
+ fejlec = self.tabla.horizontalHeader()
+ fejlec.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
+ fejlec.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
+ fejlec.setDefaultAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
+ self.tabla.setMinimumHeight(120)
+ self.tabla.itemChanged.connect(self._valtozott)
+ csomag_resz.addWidget(self.tabla, 1)
+ self.ures = cimke("", "masodlagos", True)
+ self.ures.hide()
+ csomag_resz.addWidget(self.ures)
+ self.torzs.addLayout(csomag_resz, 3)
+
+ naplo_resz = QVBoxLayout()
+ naplo_resz.setSpacing(12)
+ naplo_resz.addWidget(cimke("NAPLÓ", "h2"))
+ self.naplo = QPlainTextEdit()
+ self.naplo.setObjectName("naplo")
+ self.naplo.setReadOnly(True)
+ self.naplo.setMaximumBlockCount(5000)
+ self.naplo.setMinimumHeight(90)
+ naplo_resz.addWidget(self.naplo, 1)
+ self.torzs.addLayout(naplo_resz, 2)
+
+ def indulaskor(self):
+ self._ellenoriz(False)
+
+ def megjelenik(self, *_):
+ snapper = rendszerinfo.snapper_elerheto()
+ automatikus = snapper and snapshotok.snap_pac()
+ self.snapshot.setVisible(not automatikus)
+ self.snapshot.setEnabled(snapper and not automatikus and not self.futtato.fut())
+ if automatikus:
+ self.snapshot.setChecked(False)
+ self.snapshot_megj.setText(
+ "Minden csomagművelet előtt automatikusan snapshot készül, ha gond van, a Rendszer > Snapshotok "
+ "fülön vissza tudsz állni."
+ )
+ elif not snapper:
+ self.snapshot.setChecked(False)
+ self.snapshot_megj.setText("Snapshot csak btrfs fájlrendszeren, beállított snapperrel készíthető.")
+ else:
+ self.snapshot_megj.setText("Ha a frissítés után gond van, erre vissza tudsz állni.")
+ if not muveletek.helper_elerheto():
+ self.figyelem.setText(
+ "A RaveOS Center rendszerhelpere nincs telepítve, ezért a rendszercsomagok frissítése nem indítható."
+ )
+ self.figyelem.show()
+ if not self.futtato.fut() and not self.csomagok:
+ self._ellenoriz(False)
+
+ def _naplo(self, szoveg):
+ self.naplo.appendPlainText(szoveg)
+
+ def _ellenoriz(self, friss):
+ if self.futtato.fut():
+ return
+ self.ellenorzes.setEnabled(False)
+ self.ellenorzes.setText("Ellenőrzés...")
+ self._naplo(":: Frissítések ellenőrzése (pacman, AUR, Flatpak)...")
+ hatterben(rendszerinfo.frissitesek, friss, kesz=self._ellenorzes_kesz, hiba=self._ellenorzes_hiba)
+
+ def _ellenorzes_vege(self):
+ self.ellenorzes.setEnabled(True)
+ self.ellenorzes.setText("Ellenőrzés")
+ mikor = rendszerinfo.frissites_ellenorizve()
+ if mikor:
+ self.eyebrow.setText(f"UTOLSÓ ELLENŐRZÉS: {datetime.fromtimestamp(mikor):%H:%M}")
+
+ def _ellenorzes_kesz(self, csomagok):
+ ervenytelenit("frissitesek")
+ self._ellenorzes_vege()
+ self.csomagok = csomagok
+ szamok = {f: sum(1 for c in csomagok if c["forras"] == f) for f in FORRAS}
+ self._naplo(
+ f":: {len(csomagok)} frissítés: {szamok['pacman']} pacman, {szamok['aur']} AUR, {szamok['flatpak']} Flatpak"
+ )
+ self.kozpont.jelveny(AZON, len(csomagok))
+ self._tablat_tolt()
+
+ def _ellenorzes_hiba(self, uzenet):
+ self._ellenorzes_vege()
+ self._naplo(f"[HIBA] {uzenet}")
+ self.figyelem.setText(f"Az ellenőrzés nem sikerült: {uzenet}")
+ self.figyelem.show()
+
+ def _tablat_tolt(self):
+ self.tabla.blockSignals(True)
+ self.tabla.setRowCount(0)
+ for c in self.csomagok:
+ sor = self.tabla.rowCount()
+ self.tabla.insertRow(sor)
+ jelolo = QTableWidgetItem()
+ jelolo.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
+ jelolo.setCheckState(Qt.CheckState.Checked)
+ self.tabla.setItem(sor, 0, jelolo)
+ nev = cimke(
+ c["nev"]
+ + (
+ ' <span style="color:#d9a441; font-family:\'Noto Sans Mono\'; font-size:11px">ÚJRAINDÍTÁS</span>'
+ if c["ujrainditas"]
+ else ""
+ )
+ )
+ nev.setContentsMargins(8, 0, 8, 0)
+ self.tabla.setCellWidget(sor, 1, nev)
+ for oszlop, ertek in ((2, FORRAS[c["forras"]]), (3, c["regi"] or "-"), (4, c["uj"]), (5, mb(c["meret"]))):
+ elem = QTableWidgetItem(ertek)
+ elem.setFlags(Qt.ItemFlag.ItemIsEnabled)
+ self.tabla.setItem(sor, oszlop, elem)
+ self.tabla.blockSignals(False)
+ ures = not self.csomagok
+ self.tabla.setVisible(not ures)
+ self.ures.setVisible(ures)
+ self.ures.setText("A rendszer naprakész, nincs elérhető frissítés.")
+ self._osszegez()
+
+ def _kijelolt(self):
+ return [
+ c
+ for i, c in enumerate(self.csomagok)
+ if self.tabla.item(i, 0) and self.tabla.item(i, 0).checkState() == Qt.CheckState.Checked
+ ]
+
+ def _valtozott(self, elem):
+ if elem.column() != 0:
+ return
+ if self.csomagok[elem.row()]["forras"] == "pacman":
+ self.tabla.blockSignals(True)
+ for i, c in enumerate(self.csomagok):
+ if c["forras"] == "pacman":
+ self.tabla.item(i, 0).setCheckState(elem.checkState())
+ self.tabla.blockSignals(False)
+ self._osszegez()
+
+ def _osszegez(self):
+ kijelolt = self._kijelolt()
+ self.kijelolt.setText(f"{len(kijelolt)} / {len(self.csomagok)} CSOMAG")
+ meret = sum(c["meret"] or 0 for c in kijelolt)
+ self.letoltes.setText(f"{meret / 1e6:.0f} MB")
+ mind = bool(self.csomagok) and len(kijelolt) == len(self.csomagok)
+ self.mind.setText("Kijelölés törlése" if mind else "Mind kijelölése")
+ self.mind.setEnabled(bool(self.csomagok) and not self.futtato.fut())
+ tiltott = any(c["forras"] == "pacman" for c in kijelolt) and not muveletek.helper_elerheto()
+ self.inditas.setEnabled(bool(kijelolt) and not self.futtato.fut() and not tiltott)
+ self.inditas.setText(f"Frissítés indítása ({len(kijelolt)})" if kijelolt else "Nincs kijelölve")
+
+ def _mind(self):
+ mind = len(self._kijelolt()) == len(self.csomagok)
+ allapot = Qt.CheckState.Unchecked if mind else Qt.CheckState.Checked
+ self.tabla.blockSignals(True)
+ for i in range(self.tabla.rowCount()):
+ self.tabla.item(i, 0).setCheckState(allapot)
+ self.tabla.blockSignals(False)
+ self._osszegez()
+
+ def _indit(self):
+ kijelolt = self._kijelolt()
+ if not kijelolt:
+ return
+ lepesek = []
+ pacman = [c for c in kijelolt if c["forras"] == "pacman"]
+ aur = [c["nev"] for c in kijelolt if c["forras"] == "aur"]
+ flatpak = [c["nev"] for c in kijelolt if c["forras"] == "flatpak"]
+ if self.snapshot.isChecked() and self.snapshot.isEnabled():
+ lepesek.append(
+ muveletek.Lepes("Snapshot készítése", muveletek.root("snapshot", "Frissítés előtt (RaveOS Center)"))
+ )
+ if pacman:
+ lepesek.append(muveletek.Lepes("Rendszercsomagok frissítése (pacman)", muveletek.root("frissites")))
+ if aur:
+ lepesek.append(
+ muveletek.Lepes(
+ "AUR csomagok frissítése",
+ [
+ "yay", "-S", "--noconfirm", "--color", "never", "--sudo", "pkexec",
+ "--answerclean", "None", "--answerdiff", "None", "--answeredit", "None",
+ *aur,
+ ],
+ kotelezo=False,
+ )
+ )
+ if flatpak:
+ lepesek.append(
+ muveletek.Lepes(
+ "Flatpak alkalmazások frissítése",
+ ["flatpak", "update", "-y", "--noninteractive", *flatpak],
+ kotelezo=False,
+ )
+ )
+ self.ujrainditas_kell = any(c["ujrainditas"] for c in pacman)
+ self.figyelem.hide()
+ self.ujraindito_sav.hide()
+ self._zarol(True)
+ self._naplo("")
+ self._naplo(f":: Frissítés indul, {len(kijelolt)} csomag")
+ self.futtato.indit(lepesek)
+
+ def _zarol(self, fut):
+ for w in (self.ellenorzes, self.inditas, self.mind, self.tabla):
+ w.setEnabled(not fut)
+ self.snapshot.setEnabled(not fut and rendszerinfo.snapper_elerheto() and not snapshotok.snap_pac())
+ if fut:
+ self.inditas.setText("Frissítés folyamatban...")
+
+ def _futas_kesz(self, rendben, hibasak):
+ ervenytelenit()
+ self._zarol(False)
+ if rendben:
+ self._naplo(":: A frissítés sikeresen befejeződött.")
+ if self.ujrainditas_kell:
+ self.ujraindito_sav.show()
+ else:
+ self._naplo(f":: A frissítés hibával ért véget: {', '.join(hibasak)}")
+ self.figyelem.setText(
+ "A frissítés nem sikerült teljesen. A részleteket a lenti napló mutatja. "
+ "Ha nem érted, mi történt, küldj hibajegyet a Visszajelzés oldalon."
+ )
+ self.figyelem.show()
+ self._ellenoriz(True)
+
+ def _ujraindit(self):
+ muveletek.Futtato(self).indit([muveletek.Lepes("Újraindítás", ["systemctl", "reboot"])])
+
+
+OLDAL = Frissitesek
diff --git a/raveos-center/raveos_center/oldalak/rendszer/__init__.py b/raveos-center/raveos_center/oldalak/rendszer/__init__.py
new file mode 100644
index 0000000..60b5768
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/__init__.py
@@ -0,0 +1,49 @@
+import importlib
+
+from raveos_center.elemek import Fulsor, Oldal, Verem
+
+AZON = "rendszer"
+MENU = "Rendszer"
+IKON = "rendszer"
+
+FULEK = ["driverek", "gaming", "karbantartas", "snapshotok", "secureboot", "mentes"]
+
+
+class Rendszer(Oldal):
+ def __init__(self, kozpont):
+ super().__init__(kozpont, "DRIVEREK · GAMING · KARBANTARTÁS · SNAPSHOTOK", "Rendszer", kitolt=True)
+ self.fulek = {}
+ modulok = [importlib.import_module(f"{__name__}.{nev}") for nev in FULEK]
+ self.fulsor = Fulsor([(m.AZON, m.CIM) for m in modulok], self.valt)
+ self.torzs.addWidget(self.fulsor)
+ self.verem = Verem()
+ for m in modulok:
+ ful = m.FUL(kozpont)
+ self.fulek[m.AZON] = ful
+ self.verem.addWidget(ful)
+ self.torzs.addWidget(self.verem, 1)
+ self.aktiv = modulok[0].AZON if modulok else None
+ if self.aktiv:
+ self.fulsor.aktiv(self.aktiv)
+
+ def valt(self, azon, *args):
+ if azon not in self.fulek:
+ return
+ self.aktiv = azon
+ self.fulsor.aktiv(azon)
+ self.verem.setCurrentWidget(self.fulek[azon])
+ self.fulek[azon].megjelenik(*args)
+
+ def elerheto(self, ful=None, *_):
+ return ful is None or ful in self.fulek
+
+ def megjelenik(self, ful=None, *args):
+ self.valt(ful if ful in self.fulek else self.aktiv, *args)
+
+ def indulaskor(self):
+ for ful in self.fulek.values():
+ if hasattr(ful, "indulaskor"):
+ ful.indulaskor()
+
+
+OLDAL = Rendszer
diff --git a/raveos-center/raveos_center/oldalak/rendszer/driverek.py b/raveos-center/raveos_center/oldalak/rendszer/driverek.py
new file mode 100644
index 0000000..499e87a
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/driverek.py
@@ -0,0 +1,159 @@
+from PyQt6.QtCore import Qt
+from PyQt6.QtWidgets import QHBoxLayout, QMessageBox, QVBoxLayout, QWidget
+
+from raveos_center import driverek, muveletek
+from raveos_center.elemek import Kartya, cimke, gomb
+from raveos_center.folyamat import FolyamatAblak
+from raveos_center.hatter import ervenytelenit, gyorsitott
+
+AZON = "driverek"
+CIM = "Driverek"
+
+
+class EszkozSor(QWidget):
+ def __init__(self, elem, telepit):
+ super().__init__()
+ self.setObjectName("appsor")
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ sor = QHBoxLayout(self)
+ sor.setContentsMargins(16, 12, 16, 12)
+ sor.setSpacing(16)
+ szovegek = QVBoxLayout()
+ szovegek.setSpacing(3)
+ nev = cimke(elem["nev"], tordel=True)
+ nev.setStyleSheet("font-size: 14px; font-weight: 600;")
+ szovegek.addWidget(nev)
+ szovegek.addWidget(cimke(f"Jelenlegi driver: {elem['jelenlegi'] or 'nincs betöltve'}", "monohalvany"))
+ if elem["hianyzik"]:
+ hiany = cimke(f"Hiányzó csomagok: {' '.join(elem['hianyzik'])}", "monohalvany", True)
+ hiany.setStyleSheet("color: #d9a441;")
+ szovegek.addWidget(hiany)
+ if elem["megjegyzes"]:
+ szovegek.addWidget(cimke(elem["megjegyzes"], "halvany", True))
+ sor.addLayout(szovegek, 1)
+ if elem["hianyzik"]:
+ g = gomb("Ajánlott telepítése", fo=True, kicsi=True)
+ g.setEnabled(muveletek.helper_elerheto())
+ g.clicked.connect(lambda: telepit([elem]))
+ sor.addWidget(g, 0, Qt.AlignmentFlag.AlignVCenter)
+ else:
+ rendben = cimke("RENDBEN", "allapotcimke")
+ rendben.setProperty("szin", "zold")
+ sor.addWidget(rendben, 0, Qt.AlignmentFlag.AlignVCenter)
+
+
+class Driverek(QWidget):
+ def __init__(self, kozpont):
+ super().__init__()
+ self.kozpont = kozpont
+ self.elemek = []
+ self.kapott = None
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 20, 0, 0)
+ doboz.setSpacing(16)
+ fej = QHBoxLayout()
+ fej.addWidget(
+ cimke(
+ "A RaveOS felismeri a géped eszközeit, és megmutatja, melyikhez van ajánlott driver. "
+ "A videokártya drivere a telepítéskor is ugyanígy kerül fel.",
+ "masodlagos",
+ True,
+ ),
+ 1,
+ )
+ self.ujra = gomb("Újraellenőrzés", kicsi=True)
+ self.ujra.clicked.connect(self.frissit)
+ fej.addWidget(self.ujra, 0, Qt.AlignmentFlag.AlignTop)
+ self.mind = gomb("Minden ajánlott telepítése", fo=True, kicsi=True)
+ self.mind.clicked.connect(lambda: self._telepit([e for e in self.elemek if e["hianyzik"]]))
+ self.mind.hide()
+ fej.addWidget(self.mind, 0, Qt.AlignmentFlag.AlignTop)
+ doboz.addLayout(fej)
+ self.figyelem = cimke("", "figyelem", True)
+ self.figyelem.hide()
+ doboz.addWidget(self.figyelem)
+ self.lista = QVBoxLayout()
+ self.lista.setSpacing(20)
+ doboz.addLayout(self.lista)
+ doboz.addStretch(1)
+
+ def megjelenik(self, *_):
+ gyorsitott("driverek", driverek.allapot, kesz=self._kesz, hiba=self._hiba, kor=120)
+
+ def frissit(self):
+ self.ujra.setEnabled(False)
+ ervenytelenit("driverek")
+ gyorsitott("driverek", driverek.allapot, kesz=self._kesz, hiba=self._hiba)
+
+ def _hiba(self, uzenet):
+ self.ujra.setEnabled(True)
+ self.figyelem.setText(f"A hardver felismerése nem sikerült: {uzenet}")
+ self.figyelem.show()
+
+ def _kesz(self, elemek):
+ self.ujra.setEnabled(True)
+ if elemek == self.kapott:
+ return
+ self.kapott = elemek
+ self.elemek = elemek
+ while self.lista.count():
+ w = self.lista.takeAt(0).widget()
+ if w:
+ w.deleteLater()
+ if not muveletek.helper_elerheto():
+ self.figyelem.setText("A RaveOS Center rendszerhelpere nincs telepítve, ezért driver nem telepíthető.")
+ self.figyelem.show()
+ else:
+ self.figyelem.hide()
+ if driverek.szkript() is None:
+ self.figyelem.setText("A driverfelismerő szkript nincs telepítve.")
+ self.figyelem.show()
+ for azon, nev in driverek.KATEGORIAK:
+ sajat = [e for e in elemek if e["kategoria"] == azon]
+ if not sajat:
+ continue
+ csoport = QWidget()
+ cs = QVBoxLayout(csoport)
+ cs.setContentsMargins(0, 0, 0, 0)
+ cs.setSpacing(10)
+ cs.addWidget(cimke(nev.upper(), "h2"))
+ kartya = Kartya(terkoz=0, margo=0)
+ for i, e in enumerate(sajat):
+ sor = EszkozSor(e, self._telepit)
+ sor.setProperty("utolso", i == len(sajat) - 1)
+ kartya.elrendezes.addWidget(sor)
+ cs.addWidget(kartya)
+ self.lista.addWidget(csoport)
+ if not elemek:
+ self.lista.addWidget(cimke("Nem található kezelhető eszköz.", "halvany"))
+ hianyzo = [e for e in elemek if e["hianyzik"]]
+ self.mind.setVisible(len(hianyzo) > 1)
+ self.mind.setEnabled(muveletek.helper_elerheto())
+
+ def _telepit(self, elemek):
+ if not elemek:
+ return
+ lepesek = []
+ egyeb = []
+ if any(e["gpu"] for e in elemek):
+ lepesek.append(muveletek.Lepes("Videokártya driverek", muveletek.root("driverek")))
+ for e in elemek:
+ if not e["gpu"]:
+ egyeb += [p for p in e["hianyzik"] if p not in egyeb]
+ if egyeb:
+ lepesek.append(muveletek.Lepes("További driverek", muveletek.root("telepit", *egyeb)))
+ ablak = FolyamatAblak(self, "Driverek telepítése", lepesek, "A driverek telepítve.")
+ ablak.exec()
+ if ablak.rendben and any(driverek.ujrainditas_kell(e) for e in elemek):
+ doboz = QMessageBox(self)
+ doboz.setWindowTitle("Újraindítás szükséges")
+ doboz.setText("Az új videokártya-driver az újraindítás után töltődik be.")
+ most = doboz.addButton("Újraindítás most", QMessageBox.ButtonRole.AcceptRole)
+ doboz.addButton("Később", QMessageBox.ButtonRole.RejectRole)
+ doboz.exec()
+ if doboz.clickedButton() is most:
+ muveletek.Futtato(self).indit([muveletek.Lepes("Újraindítás", ["systemctl", "reboot"])])
+ self.frissit()
+
+
+FUL = Driverek
diff --git a/raveos-center/raveos_center/oldalak/rendszer/gaming.py b/raveos-center/raveos_center/oldalak/rendszer/gaming.py
new file mode 100644
index 0000000..5d410e9
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/gaming.py
@@ -0,0 +1,306 @@
+from PyQt6.QtCore import QObject, Qt, pyqtSignal
+from PyQt6.QtWidgets import QComboBox, QHBoxLayout, QMessageBox, QProgressBar, QVBoxLayout, QWidget
+
+from raveos_center import gaming, muveletek, proton
+from raveos_center.elemek import Kapcsolo, Kartya, cimke, gomb
+from raveos_center.folyamat import FolyamatAblak
+from raveos_center.hatter import ervenytelenit, gyorsitott, hatterben, tarolt
+
+AZON = "gaming"
+CIM = "Gaming"
+
+BEALLITASOK = [
+ ("gamemode", "GameMode", "CPU és I/O prioritás játék közben. A Lutris és a Heroic magától használja, Steamben a "
+ "játék indítási opciójába írd: gamemoderun %command%"),
+ ("mangohud", "MangoHud alapból", "FPS, hőmérséklet és terhelés kijelzése minden Vulkan és OpenGL játékban. "
+ "Játék közben a jobb Shift + F12 elrejti, a jobb Shift + F10 vált a kijelző módok között."),
+ ("utemezo", "Játékra hangolt ütemező", "Az scx_lavd ütemező gaming módban: egyenletesebb képkockaidő, amikor a "
+ "háttérben más is fut. Kikapcsolva a kernel saját BORE ütemezője dolgozik."),
+ ("gamescope", "Gamescope session", "Külön, konzolszerű Steam játékmenet, a bejelentkező képernyőn választható."),
+ ("governor", "Performance CPU governor", "Játék közben teljes órajel, a játék bezárása után visszaáll."),
+ ("ntsync", "NTSync", "Gyorsabb Windows-szinkronizáció Wine és Proton alatt."),
+]
+
+
+class _Jelzo(QObject):
+ haladas = pyqtSignal(int)
+
+
+def alkalmaz(szulo, valtozasok):
+ lepesek = []
+ helyi = []
+ for azon, be in valtozasok:
+ l, h = gaming.lepesek(azon, be)
+ lepesek += l
+ if h:
+ helyi.append(h)
+ if lepesek:
+ ablak = FolyamatAblak(szulo, "Gaming beállítások", lepesek, "A beállítás elkészült.")
+ ablak.exec()
+ if not ablak.rendben:
+ return "A beállítás nem sikerült teljesen, a részleteket a napló mutatja."
+ for h in helyi:
+ try:
+ h()
+ except OSError as e:
+ return f"A beállítás nem sikerült: {e}"
+ return None
+
+
+class Sor(QWidget):
+ def __init__(self, nev, leiras, utolso=False):
+ super().__init__()
+ self.setObjectName("appsor")
+ self.setProperty("utolso", utolso)
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ self.sor = QHBoxLayout(self)
+ self.sor.setContentsMargins(16, 14, 16, 14)
+ self.sor.setSpacing(16)
+ self.szovegek = QVBoxLayout()
+ self.szovegek.setSpacing(3)
+ cim = cimke(nev)
+ cim.setStyleSheet("font-size: 14px; font-weight: 600;")
+ self.szovegek.addWidget(cim)
+ self.szovegek.addWidget(cimke(leiras, "halvany", True))
+ self.megjegyzes = cimke("", "monohalvany", True)
+ self.megjegyzes.hide()
+ self.szovegek.addWidget(self.megjegyzes)
+ self.sor.addLayout(self.szovegek, 1)
+
+ def jegyzet(self, szoveg):
+ self.megjegyzes.setText(szoveg)
+ self.megjegyzes.setVisible(bool(szoveg))
+
+
+class BeallitasSor(Sor):
+ def __init__(self, azon, nev, leiras, valt, utolso=False):
+ super().__init__(nev, leiras, utolso)
+ self.azon = azon
+ self.kapcsolo = Kapcsolo()
+ self.kapcsolo.setAccessibleName(nev)
+ self.kapcsolo.clicked.connect(lambda be: valt(self.azon, be))
+ self.sor.addWidget(self.kapcsolo, 0, Qt.AlignmentFlag.AlignVCenter)
+
+ def beallit(self, adat, foglalt):
+ self.kapcsolo.blockSignals(True)
+ self.kapcsolo.setChecked(adat["aktiv"])
+ self.kapcsolo.blockSignals(False)
+ helper = muveletek.helper_elerheto() or not adat["root"]
+ self.kapcsolo.setEnabled(adat["elerheto"] and helper and not foglalt)
+ self.jegyzet(adat["megjegyzes"])
+ self.kapcsolo.update()
+
+
+class ProtonGeSor(Sor):
+ def __init__(self, szulo):
+ super().__init__(
+ "Proton-GE",
+ "A GloriousEggroll Proton-változata, több játékhoz tartalmaz javítást és videokodeket. A Steam "
+ "újraindítása után a játék Tulajdonságok > Kompatibilitás menüjében választható.",
+ )
+ self.szulo = szulo
+ self.kiadas = None
+ self.fut = False
+ self.jelzo = _Jelzo()
+ self.sav = QProgressBar()
+ self.sav.setObjectName("sav")
+ self.sav.setTextVisible(False)
+ self.sav.setFixedHeight(6)
+ self.sav.hide()
+ self.jelzo.haladas.connect(self.sav.setValue)
+ self.szovegek.addWidget(self.sav)
+ gombok = QVBoxLayout()
+ gombok.setSpacing(6)
+ self.telepit_gomb = gomb("Legújabb telepítése", fo=True, kicsi=True)
+ self.telepit_gomb.clicked.connect(self._telepit)
+ self.regiek_gomb = gomb("Régiek törlése", kicsi=True)
+ self.regiek_gomb.clicked.connect(self._regiek)
+ self.torol_gomb = gomb("Mind törlése", kicsi=True)
+ self.torol_gomb.clicked.connect(self._mind)
+ for g in (self.telepit_gomb, self.regiek_gomb, self.torol_gomb):
+ gombok.addWidget(g)
+ gombok.addStretch(1)
+ self.sor.addLayout(gombok)
+
+ def frissit(self):
+ if self.fut:
+ return
+ self._mutat()
+ gyorsitott("proton_ge", proton.legujabb, kesz=self._kiadas, hiba=lambda _: self._kiadas(None), kor=3600)
+
+ def _kiadas(self, kiadas):
+ self.kiadas = kiadas or self.kiadas
+ if not self.fut:
+ self._mutat()
+
+ def _mutat(self, szoveg=None):
+ lista = proton.telepitett()
+ van_steam = bool(proton.celmappak())
+ naprakesz = bool(self.kiadas and lista and lista[0] == self.kiadas["nev"])
+ if szoveg is None:
+ szoveg = f"Telepítve: {', '.join(lista)}" if lista else "Nincs telepítve."
+ if self.kiadas and not naprakesz:
+ szoveg += f" Legújabb: {self.kiadas['nev']}"
+ if not van_steam:
+ szoveg = "Nem található Steam. Telepítsd, és indítsd el egyszer."
+ self.jegyzet(szoveg)
+ self.telepit_gomb.setText("Naprakész" if naprakesz else "Legújabb telepítése")
+ self.telepit_gomb.setEnabled(not self.fut and not naprakesz and van_steam)
+ self.regiek_gomb.setVisible(bool(proton.regiek()))
+ self.regiek_gomb.setEnabled(not self.fut)
+ self.torol_gomb.setVisible(bool(lista))
+ self.torol_gomb.setEnabled(not self.fut)
+
+ def _telepit(self):
+ self.fut = True
+ self.sav.setValue(0)
+ self.sav.show()
+ self._mutat("Letöltés és ellenőrzés, ez néhány percig tarthat.")
+ hatterben(self._letolt, kesz=self._kesz, hiba=self._hiba)
+
+ def _letolt(self):
+ kiadas = self.kiadas or proton.legujabb()
+ proton.telepit(kiadas, self.jelzo.haladas.emit)
+ return kiadas
+
+ def _kesz(self, kiadas):
+ self.fut = False
+ self.kiadas = kiadas
+ self.sav.hide()
+ self._mutat()
+
+ def _hiba(self, uzenet):
+ self.fut = False
+ self.sav.hide()
+ self._mutat()
+ QMessageBox.warning(self.szulo, "Proton-GE", f"A telepítés nem sikerült: {uzenet}")
+
+ def _torles(self, parok, kerdes):
+ if not parok or QMessageBox.question(self.szulo, "Proton-GE", kerdes) != QMessageBox.StandardButton.Yes:
+ return
+ try:
+ for mappa, nev in parok:
+ proton.torol(nev, [mappa] if mappa else None)
+ except OSError as e:
+ QMessageBox.warning(self.szulo, "Proton-GE", f"A törlés nem sikerült: {e}")
+ self._mutat()
+
+ def _regiek(self):
+ parok = proton.regiek()
+ nevek = ", ".join(sorted({n for _, n in parok}))
+ self._torles(parok, f"Törlöd a régebbi verziókat ({nevek})? Minden Steam-telepítésben a legújabb megmarad.")
+
+ def _mind(self):
+ self._torles(
+ [(None, n) for n in proton.telepitett()],
+ "Törlöd az összes Proton-GE verziót? Az ezeket használó játékok a Steam alap Protonjára váltanak.",
+ )
+
+
+class Gaming(QWidget):
+ def __init__(self, kozpont):
+ super().__init__()
+ self.kozpont = kozpont
+ self.adat = None
+ self.foglalt = False
+ self.hibauzenet = ""
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 20, 0, 0)
+ doboz.setSpacing(16)
+ doboz.addWidget(
+ cimke(
+ "Játékhoz hasznos beállítások. Mindegyik bármikor visszakapcsolható. Az Áttekintés Gaming mód "
+ "kapcsolója a GameMode-ot, a performance governort, az NTSync-et és a játékra hangolt ütemezőt "
+ "kapcsolja egyszerre.",
+ "masodlagos",
+ True,
+ )
+ )
+ self.figyelem = cimke("", "figyelem", True)
+ self.figyelem.hide()
+ doboz.addWidget(self.figyelem)
+
+ kartya = Kartya(terkoz=0, margo=0)
+ self.sorok = {}
+ for i, (azon, nev, leiras) in enumerate(BEALLITASOK):
+ sor = BeallitasSor(azon, nev, leiras, self.valt, i == len(BEALLITASOK) - 1)
+ self.sorok[azon] = sor
+ kartya.elrendezes.addWidget(sor)
+ self.hud_mod = QComboBox()
+ for mod, nev in gaming.MANGOHUD_MODOK:
+ self.hud_mod.addItem(nev, mod)
+ self.hud_mod.activated.connect(self._hud_mod)
+ hud_sor = QHBoxLayout()
+ hud_sor.setSpacing(8)
+ hud_sor.addWidget(cimke("Kijelző:", "halvany"))
+ hud_sor.addWidget(self.hud_mod)
+ hud_sor.addStretch(1)
+ self.sorok["mangohud"].szovegek.addSpacing(6)
+ self.sorok["mangohud"].szovegek.addLayout(hud_sor)
+ doboz.addWidget(kartya)
+
+ doboz.addSpacing(8)
+ doboz.addWidget(cimke("PROTON VERZIÓK", "h2"))
+ proton_kartya = Kartya(terkoz=0, margo=0)
+ self.proton_ge = ProtonGeSor(self)
+ proton_kartya.elrendezes.addWidget(self.proton_ge)
+ self.sorok["proton_cachyos"] = BeallitasSor(
+ "proton_cachyos",
+ "Proton-CachyOS",
+ "A CachyOS Proton-változata a rendszer csomagkezelőjéből, a rendszerfrissítéssel együtt frissül. "
+ "Kb. 1,6 GB. A Steam újraindítása után választható.",
+ self.valt,
+ True,
+ )
+ proton_kartya.elrendezes.addWidget(self.sorok["proton_cachyos"])
+ doboz.addWidget(proton_kartya)
+ doboz.addStretch(1)
+
+ def megjelenik(self, *_):
+ self.frissit()
+ self.proton_ge.frissit()
+
+ def frissit(self):
+ if tarolt("gaming") is None:
+ self.foglalt = True
+ for sor in self.sorok.values():
+ sor.kapcsolo.setEnabled(False)
+ gyorsitott("gaming", gaming.allapot, kesz=self._kesz, hiba=lambda _: self._kesz(self.adat), kor=30)
+
+ def _kesz(self, adat):
+ self.foglalt = False
+ index = self.hud_mod.findData(gaming.mangohud_mod())
+ self.hud_mod.setCurrentIndex(max(0, index))
+ if adat is None:
+ self._figyelmeztet("Az állapot nem olvasható be, próbáld újra a fül újbóli megnyitásával.")
+ return
+ self.adat = adat
+ for azon, sor in self.sorok.items():
+ sor.beallit(adat[azon], self.foglalt)
+ if self.hibauzenet:
+ self._figyelmeztet(self.hibauzenet)
+ elif not muveletek.helper_elerheto():
+ self._figyelmeztet("A RaveOS Center rendszerhelpere nincs telepítve, a legtöbb beállítás nem módosítható.")
+ else:
+ self.figyelem.hide()
+
+ def _figyelmeztet(self, szoveg):
+ self.figyelem.setText(szoveg)
+ self.figyelem.show()
+
+ def _hud_mod(self):
+ try:
+ gaming.mangohud_mod_beallit(self.hud_mod.currentData())
+ except OSError as e:
+ QMessageBox.warning(self, "MangoHud", f"A beállítás nem sikerült: {e}")
+
+ def valt(self, azon, be):
+ self.alkalmaz([(azon, be)])
+
+ def alkalmaz(self, valtozasok):
+ self.hibauzenet = alkalmaz(self, valtozasok) or ""
+ ervenytelenit("gaming")
+ self.frissit()
+
+
+FUL = Gaming
diff --git a/raveos-center/raveos_center/oldalak/rendszer/karbantartas.py b/raveos-center/raveos_center/oldalak/rendszer/karbantartas.py
new file mode 100644
index 0000000..91b7113
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/karbantartas.py
@@ -0,0 +1,177 @@
+from PyQt6.QtCore import Qt
+from PyQt6.QtWidgets import QDialog, QHBoxLayout, QListWidget, QListWidgetItem, QVBoxLayout, QWidget
+
+from raveos_center import karbantartas, muveletek
+from raveos_center.elemek import Kartya, Racs, cimke, gomb
+from raveos_center.folyamat import FolyamatAblak
+from raveos_center.hatter import gyorsitott, tarolt
+
+AZON = "karbantartas"
+CIM = "Karbantartás"
+
+MUVELETEK = [
+ ("cache", "paccache -rk2", "Csomag cache ürítése",
+ "Minden csomagból csak az utolsó két verzió marad, az eltávolított csomagok fájljai törlődnek.", True),
+ ("arva", "pacman -Qdtq", "Árva csomagok",
+ "Már semmi által nem használt függőségek eltávolítása. Az előnézetben egyenként kiveheted, amit megtartanál.", True),
+ ("naplo", "journalctl --vacuum", "Naplók takarítása",
+ "A két hétnél régebbi rendszernaplók törlése, legfeljebb 100 MB marad.", True),
+ ("tukrok", "reflector", "Tükrök frissítése",
+ "A leggyorsabb közeli letöltési szerverek kiválasztása. A régi lista biztonsági másolatként megmarad.", False),
+ ("kulcsok", "pacman-key --populate", "Kulcsok frissítése",
+ "A csomagaláíró kulcsok újratöltése. Segít, ha a frissítés aláírási hibával áll le.", False),
+ ("flatpak", "flatpak uninstall --unused", "Flatpak takarítás",
+ "A már egyik alkalmazás által sem használt runtime-ok eltávolítása.", True),
+]
+
+
+class ElonezetAblak(QDialog):
+ def __init__(self, szulo, cim, szoveg, elemek, valaszthato, gomb_szoveg):
+ super().__init__(szulo)
+ self.setWindowTitle(cim)
+ self.resize(640, 480)
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(28, 24, 28, 20)
+ doboz.setSpacing(12)
+ doboz.addWidget(cimke("ELŐNÉZET", "eyebrow"))
+ doboz.addWidget(cimke(cim.upper(), "h2", True))
+ doboz.addWidget(cimke(szoveg, "masodlagos", True))
+ self.lista = QListWidget()
+ for elem in elemek:
+ sor = QListWidgetItem(elem)
+ if valaszthato:
+ sor.setFlags(sor.flags() | Qt.ItemFlag.ItemIsUserCheckable)
+ sor.setCheckState(Qt.CheckState.Checked)
+ self.lista.addItem(sor)
+ self.lista.setVisible(bool(elemek))
+ doboz.addWidget(self.lista, 1)
+ if not elemek:
+ doboz.addStretch(1)
+ gombok = QHBoxLayout()
+ gombok.addStretch(1)
+ megse = gomb("Mégse")
+ megse.clicked.connect(self.reject)
+ gombok.addWidget(megse)
+ self.mehet = gomb(gomb_szoveg, fo=True)
+ self.mehet.clicked.connect(self.accept)
+ gombok.addWidget(self.mehet)
+ doboz.addLayout(gombok)
+ if valaszthato:
+ self.lista.itemChanged.connect(lambda _: self.mehet.setEnabled(bool(self.kivalasztott())))
+
+ def kivalasztott(self):
+ return [
+ self.lista.item(i).text()
+ for i in range(self.lista.count())
+ if self.lista.item(i).checkState() == Qt.CheckState.Checked
+ ]
+
+
+class MuveletKartya(Kartya):
+ def __init__(self, parancs, nev, leiras, futtat):
+ super().__init__(terkoz=8, margo=20)
+ self.elrendezes.addWidget(cimke(parancs, "monohalvany"))
+ cim = cimke(nev)
+ cim.setStyleSheet("font-size: 15px; font-weight: 600;")
+ self.elrendezes.addWidget(cim)
+ self.elrendezes.addWidget(cimke(leiras, "halvany", True))
+ self.elrendezes.addStretch(1)
+ self.osszegzes = cimke("Ellenőrzés...", "monozold", True)
+ self.elrendezes.addWidget(self.osszegzes)
+ sor = QHBoxLayout()
+ self.gomb = gomb("Futtatás", kicsi=True)
+ self.gomb.clicked.connect(futtat)
+ sor.addWidget(self.gomb)
+ sor.addStretch(1)
+ self.elrendezes.addLayout(sor)
+
+
+class Karbantartas(QWidget):
+ def __init__(self, kozpont):
+ super().__init__()
+ self.kozpont = kozpont
+ self.adat = {}
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 20, 0, 0)
+ doboz.setSpacing(16)
+ doboz.addWidget(
+ cimke(
+ "Rendszeres takarítás és javítás. A törléssel járó műveletek előtt megmutatjuk, mi kerülne törlésre, "
+ "és csak a megerősítésed után futnak.",
+ "masodlagos",
+ True,
+ )
+ )
+ self.figyelem = cimke("", "figyelem", True)
+ self.figyelem.hide()
+ doboz.addWidget(self.figyelem)
+ self.racs = Racs(260, 12)
+ self.kartyak = {}
+ for azon, parancs, nev, leiras, _ in MUVELETEK:
+ kartya = MuveletKartya(parancs, nev, leiras, lambda _=False, a=azon: self._futtat(a))
+ self.kartyak[azon] = kartya
+ self.racs.hozzaad(kartya)
+ doboz.addWidget(self.racs)
+ doboz.addStretch(1)
+
+ def megjelenik(self, *_):
+ helper = muveletek.helper_elerheto()
+ self.figyelem.setText("A RaveOS Center rendszerhelpere nincs telepítve, a műveletek nem futtathatók.")
+ self.figyelem.setVisible(not helper)
+ for azon in self.kartyak:
+ self._ellenoriz(azon)
+
+ def _ellenoriz(self, azon):
+ kartya = self.kartyak[azon]
+ if tarolt(f"karbantartas_{azon}") is None:
+ kartya.gomb.setEnabled(False)
+ kartya.osszegzes.setText("Ellenőrzés...")
+ gyorsitott(
+ f"karbantartas_{azon}",
+ karbantartas.ELONEZETEK[azon],
+ kesz=lambda adat, a=azon: self._kesz(a, adat),
+ hiba=lambda uzenet, a=azon: self._kesz(a, {"elerheto": False, "osszegzes": uzenet, "elemek": []}),
+ kor=60,
+ )
+
+ def _kesz(self, azon, adat):
+ self.adat[azon] = adat
+ kartya = self.kartyak[azon]
+ kartya.osszegzes.setText(adat["osszegzes"])
+ torlo = next(m[4] for m in MUVELETEK if m[0] == azon)
+ teendo = not torlo or azon == "naplo" or bool(adat["elemek"])
+ kartya.gomb.setEnabled(adat["elerheto"] and teendo and muveletek.helper_elerheto())
+
+ def _futtat(self, azon):
+ adat = self.adat.get(azon)
+ if not adat:
+ return
+ nev = next(m[2] for m in MUVELETEK if m[0] == azon)
+ kivalasztott = None
+ if azon == "cache":
+ szoveg = f"Ezek a csomagfájlok törlődnek a cache-ből ({adat['osszegzes']}). A telepített programokat nem érinti."
+ elif azon == "arva":
+ szoveg = (
+ "Pontosan ezek a csomagok törlődnek, más nem. Vedd ki a pipát annál, amit megtartanál. "
+ "Utána újabb árva függőségek jelenhetnek meg, ezeket a kártya újra megmutatja."
+ )
+ elif azon == "naplo":
+ szoveg = f"{adat['osszegzes']}. A két hétnél régebbi naplók törlődnek, és legfeljebb 100 MB marad."
+ elif azon == "flatpak":
+ szoveg = "Ezek a runtime-ok törlődnek. Az alkalmazásaid továbbra is működnek."
+ else:
+ szoveg = ""
+ if szoveg:
+ ablak = ElonezetAblak(self, nev, szoveg, adat["elemek"], azon == "arva", "Törlés")
+ if ablak.exec() != QDialog.DialogCode.Accepted:
+ return
+ if azon == "arva":
+ kivalasztott = ablak.kivalasztott()
+ lepesek = karbantartas.lepesek(azon, adat, kivalasztott)
+ if not lepesek:
+ return
+ FolyamatAblak(self, nev, lepesek, "A művelet elkészült.").exec()
+ self._ellenoriz(azon)
+
+
+FUL = Karbantartas
diff --git a/raveos-center/raveos_center/oldalak/rendszer/mentes.py b/raveos-center/raveos_center/oldalak/rendszer/mentes.py
new file mode 100644
index 0000000..6ad5967
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/mentes.py
@@ -0,0 +1,283 @@
+from datetime import datetime
+from pathlib import Path
+
+from PyQt6.QtCore import QObject, Qt, pyqtSignal
+from PyQt6.QtWidgets import (
+ QCheckBox,
+ QFrame,
+ QScrollArea,
+ QFileDialog,
+ QHBoxLayout,
+ QLabel,
+ QLineEdit,
+ QMessageBox,
+ QPlainTextEdit,
+ QProgressBar,
+ QSizePolicy,
+ QVBoxLayout,
+ QWidget,
+)
+
+from raveos_center import katalogus, mentesek, muveletek
+from raveos_center.elemek import Kartya, cimke, gomb
+from raveos_center.hatter import hatterben
+
+AZON = "mentes"
+CIM = "Mentés"
+
+MERET = {"small": "kicsi", "medium": "közepes", "large": "nagy", "xlarge": "nagyon nagy"}
+
+
+class RovidUt(QLabel):
+ def __init__(self, ut):
+ super().__init__(ut)
+ self.teljes = ut
+ self.setObjectName("monohalvany")
+ self.setToolTip(ut)
+ self.setMinimumWidth(60)
+ self.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
+
+ def resizeEvent(self, esemeny):
+ super().resizeEvent(esemeny)
+ self.setText(self.fontMetrics().elidedText(self.teljes, Qt.TextElideMode.ElideMiddle, self.width()))
+
+
+class _Jelzo(QObject):
+ naplo = pyqtSignal(str)
+ haladas = pyqtSignal(int)
+
+
+def elem_sor(elem, bejelolve):
+ sor = QWidget()
+ s = QHBoxLayout(sor)
+ s.setContentsMargins(0, 4, 0, 4)
+ s.setSpacing(12)
+ jelolo = QCheckBox()
+ jelolo.setChecked(bejelolve)
+ s.addWidget(jelolo)
+ szovegek = QVBoxLayout()
+ szovegek.setSpacing(0)
+ nev = cimke(katalogus.loc(elem.get("label")) or elem["id"])
+ nev.setStyleSheet("font-weight: 600;")
+ szovegek.addWidget(nev)
+ szovegek.addWidget(RovidUt(str(mentesek.abszolut(elem))))
+ if elem.get("note"):
+ szovegek.addWidget(cimke(katalogus.loc(elem["note"]), "halvany", True))
+ s.addLayout(szovegek, 1)
+ if elem.get("scope") == "system":
+ s.addWidget(cimke("ROOT", "allapotcimke"))
+ if elem.get("size_hint"):
+ s.addWidget(cimke(MERET.get(elem["size_hint"], elem["size_hint"]), "halvany"))
+ jelolo.nev = nev
+ return sor, jelolo
+
+
+class Mentes(QWidget):
+ def __init__(self, kozpont):
+ super().__init__()
+ self.kozpont = kozpont
+ self.fut = False
+ self.mentes_jelolok = []
+ self.visszaallitas_jelolok = []
+ self.jelzo = _Jelzo()
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 24, 0, 0)
+ doboz.setSpacing(20)
+ doboz.addWidget(
+ cimke(
+ "Mentsd el az alkalmazásaid beállításait és a fontos mappáidat egyetlen fájlba, és állítsd vissza "
+ "őket egy másik gépen vagy újratelepítés után. A ROOT jelölésű elemekhez a rendszer jelszót kér.",
+ "masodlagos",
+ True,
+ )
+ )
+
+ oszlopok = QHBoxLayout()
+ oszlopok.setSpacing(24)
+ doboz.addLayout(oszlopok, 1)
+ bal = QVBoxLayout()
+ bal.setSpacing(16)
+ jobb = QVBoxLayout()
+ jobb.setSpacing(16)
+ oszlopok.addLayout(bal, 3)
+ oszlopok.addLayout(jobb, 2)
+
+ mentes = Kartya("Mentés", terkoz=10, margo=20)
+ gorgeto = QScrollArea()
+ gorgeto.setWidgetResizable(True)
+ gorgeto.setFrameShape(QFrame.Shape.NoFrame)
+ gorgeto.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+ gorgeto.setMinimumHeight(160)
+ lista_doboz = QWidget()
+ gorgeto.setWidget(lista_doboz)
+ self.mentes_lista = QVBoxLayout(lista_doboz)
+ self.mentes_lista.setContentsMargins(0, 0, 12, 0)
+ self.mentes_lista.setSpacing(2)
+ mentes.elrendezes.addWidget(gorgeto, 1)
+ cel_sor = QHBoxLayout()
+ cel_sor.setSpacing(8)
+ self.cel = QLineEdit(str(Path.home() / f"raveos-mentes-{datetime.now():%Y%m%d}.tar.gz"))
+ cel_sor.addWidget(self.cel, 1)
+ tallozas = gomb("Tallózás", kicsi=True)
+ tallozas.clicked.connect(self._cel_valaszt)
+ cel_sor.addWidget(tallozas)
+ mentes.elrendezes.addSpacing(8)
+ mentes.elrendezes.addWidget(cimke("Mentés helye", "kartyacim"))
+ mentes.elrendezes.addLayout(cel_sor)
+ gs = QHBoxLayout()
+ gs.addStretch(1)
+ self.mentes_gomb = gomb("Mentés indítása", fo=True)
+ self.mentes_gomb.clicked.connect(self._mentes)
+ gs.addWidget(self.mentes_gomb)
+ mentes.elrendezes.addLayout(gs)
+ bal.addWidget(mentes, 1)
+
+ vissza = Kartya("Visszaállítás", terkoz=10, margo=20)
+ fs = QHBoxLayout()
+ fs.setSpacing(8)
+ self.forras = QLineEdit()
+ self.forras.setPlaceholderText("Válaszd ki a mentésfájlt (.tar.gz)")
+ self.forras.setReadOnly(True)
+ fs.addWidget(self.forras, 1)
+ valaszt = gomb("Mentés kiválasztása", kicsi=True)
+ valaszt.clicked.connect(self._forras_valaszt)
+ fs.addWidget(valaszt)
+ vissza.elrendezes.addLayout(fs)
+ self.vissza_lista = QVBoxLayout()
+ self.vissza_lista.setSpacing(2)
+ vissza.elrendezes.addLayout(self.vissza_lista)
+ vissza.elrendezes.addWidget(
+ cimke("A visszaállítás felülírja a meglévő fájlokat a kiválasztott helyeken.", "halvany", True)
+ )
+ vs = QHBoxLayout()
+ vs.addStretch(1)
+ self.vissza_gomb = gomb("Visszaállítás", fo=True)
+ self.vissza_gomb.setEnabled(False)
+ self.vissza_gomb.clicked.connect(self._visszaallitas)
+ vs.addWidget(self.vissza_gomb)
+ vissza.elrendezes.addLayout(vs)
+ jobb.addWidget(vissza)
+
+ self.sav = QProgressBar()
+ self.sav.setObjectName("sav")
+ self.sav.setTextVisible(False)
+ self.sav.setFixedHeight(6)
+ jobb.addWidget(self.sav)
+ jobb.addWidget(cimke("NAPLÓ", "h2"))
+ self.naplo = QPlainTextEdit()
+ self.naplo.setObjectName("naplo")
+ self.naplo.setReadOnly(True)
+ self.naplo.setMinimumHeight(100)
+ jobb.addWidget(self.naplo, 1)
+ self.jelzo.naplo.connect(self.naplo.appendPlainText)
+ self.jelzo.haladas.connect(self.sav.setValue)
+ self._mentes_lista_epit()
+
+ def _mentes_lista_epit(self):
+ try:
+ csoportok = mentesek.csoportok()
+ except (OSError, ValueError, RuntimeError) as e:
+ self.mentes_lista.addWidget(cimke(f"A mentési lista nem tölthető be: {e}", "figyelem", True))
+ return
+ self.mentes_lista.addStretch(0)
+ for csoport in csoportok:
+ fej = cimke(katalogus.loc(csoport.get("label")).upper(), "monozold")
+ self.mentes_lista.addSpacing(8)
+ self.mentes_lista.addWidget(fej)
+ if csoport.get("description"):
+ self.mentes_lista.addWidget(cimke(katalogus.loc(csoport["description"]), "halvany", True))
+ for elem in csoport.get("items", []):
+ sor, jelolo = elem_sor(elem, elem.get("default_selected", False))
+ self.mentes_lista.addWidget(sor)
+ self.mentes_jelolok.append((jelolo, elem))
+
+ def megjelenik(self, *_):
+ self._gombok()
+
+ def _gombok(self):
+ self.mentes_gomb.setEnabled(not self.fut)
+ self.vissza_gomb.setEnabled(not self.fut and bool(self.visszaallitas_jelolok))
+
+ def _cel_valaszt(self):
+ ut, _ = QFileDialog.getSaveFileName(self, "Mentés helye", self.cel.text(), "Mentés (*.tar.gz)")
+ if ut:
+ self.cel.setText(ut if ut.endswith(".tar.gz") else ut + ".tar.gz")
+
+ def _forras_valaszt(self):
+ ut, _ = QFileDialog.getOpenFileName(self, "Mentés kiválasztása", str(Path.home()), "Mentés (*.tar.gz)")
+ if not ut:
+ return
+ self.forras.setText(ut)
+ self._tartalom([])
+ hatterben(mentesek.tartalom, ut, kesz=self._tartalom, hiba=lambda u: self._hiba(f"A fájl nem olvasható: {u}"))
+
+ def _tartalom(self, lista):
+ while self.vissza_lista.count():
+ w = self.vissza_lista.takeAt(0).widget()
+ if w:
+ w.deleteLater()
+ self.visszaallitas_jelolok = []
+ if not lista:
+ self.vissza_lista.addWidget(cimke("Ebben a fájlban nincs visszaállítható elem.", "halvany"))
+ for elem in lista:
+ sor, jelolo = elem_sor(elem, True)
+ self.vissza_lista.addWidget(sor)
+ self.visszaallitas_jelolok.append((jelolo, elem))
+ if any(e.get("regi") for e in lista):
+ self.vissza_lista.addWidget(
+ cimke("Régi (App Installer) formátumú mentés: a rendszerelemek ebből nem állíthatók vissza.", "halvany", True)
+ )
+ self._gombok()
+
+ def _hiba(self, szoveg):
+ self.naplo.appendPlainText(f"[HIBA] {szoveg}")
+
+ def _rootot_kell(self, elemek):
+ if any(e.get("scope") == "system" for e in elemek) and not muveletek.helper_elerheto():
+ QMessageBox.warning(self, "Mentés", "A rendszerelemekhez a RaveOS Center rendszerhelpere szükséges, de nincs telepítve.")
+ return False
+ return True
+
+ def _mentes(self):
+ elemek = [e for j, e in self.mentes_jelolok if j.isChecked()]
+ cel = self.cel.text().strip()
+ if not elemek or not cel or not self._rootot_kell(elemek):
+ return
+ if Path(cel).exists():
+ if QMessageBox.question(self, "Mentés", f"A fájl már létezik, felülírod?\n{cel}") != QMessageBox.StandardButton.Yes:
+ return
+ self._indul(f"Mentés indul: {cel}")
+ hatterben(
+ mentesek.mentes, cel, elemek, self.jelzo.naplo.emit, self.jelzo.haladas.emit,
+ kesz=lambda n: self._vege(f"Mentés kész: {n} elem, {cel}"),
+ hiba=lambda u: self._vege(f"A mentés nem sikerült: {u}", False),
+ )
+
+ def _visszaallitas(self):
+ elemek = [e for j, e in self.visszaallitas_jelolok if j.isChecked()]
+ if not elemek or not self._rootot_kell(elemek):
+ return
+ if QMessageBox.question(
+ self, "Visszaállítás", f"{len(elemek)} elem visszaállítása felülírja a meglévő fájlokat. Folytatod?"
+ ) != QMessageBox.StandardButton.Yes:
+ return
+ self._indul(f"Visszaállítás indul: {self.forras.text()}")
+ hatterben(
+ mentesek.visszaallitas, self.forras.text(), elemek, self.jelzo.naplo.emit, self.jelzo.haladas.emit,
+ kesz=lambda n: self._vege(f"Visszaállítás kész: {n} elem."),
+ hiba=lambda u: self._vege(f"A visszaállítás nem sikerült: {u}", False),
+ )
+
+ def _indul(self, szoveg):
+ self.fut = True
+ self._gombok()
+ self.sav.setValue(0)
+ self.naplo.appendPlainText(szoveg)
+
+ def _vege(self, szoveg, rendben=True):
+ self.fut = False
+ self._gombok()
+ self.naplo.appendPlainText(szoveg if rendben else f"[HIBA] {szoveg}")
+
+
+FUL = Mentes
diff --git a/raveos-center/raveos_center/oldalak/rendszer/secureboot.py b/raveos-center/raveos_center/oldalak/rendszer/secureboot.py
new file mode 100644
index 0000000..00ca444
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/secureboot.py
@@ -0,0 +1,256 @@
+import shutil
+import subprocess
+
+from PyQt6.QtWidgets import QHBoxLayout, QMessageBox, QPlainTextEdit, QVBoxLayout, QWidget
+
+from raveos_center import muveletek
+from raveos_center.elemek import Kartya, Racs, cimke, gomb
+from raveos_center.hatter import ervenytelenit, gyorsitott, tarolt
+
+AZON = "secureboot"
+CIM = "Secure Boot"
+
+SZKRIPT = "raveos-secureboot-setup"
+UJRAINDITAS_KELL = 10
+
+LEPESEK_SZOVEG = [
+ "telepíti a szükséges eszközöket (sbsigntools, efitools),",
+ "létrehoz egy saját, csak ehhez a géphez tartozó aláíró kulcsot,",
+ "aláírja a rendszerbetöltőt, a kernelt és a külső kernelmodulokat (pl. NVIDIA),",
+ "beírja a firmware-be a RaveOS, a Microsoft és a saját géped kulcsát, így a Windows is tovább indul,",
+ "beállít egy automatikus aláírást, ami minden kernelfrissítés után újra aláír.",
+]
+
+
+def allapot_lekerdez():
+ if not shutil.which(SZKRIPT):
+ return None
+ kimenet = subprocess.run([SZKRIPT, "--status"], capture_output=True, text=True, timeout=30)
+ adat = {}
+ for sor in kimenet.stdout.splitlines():
+ if "=" in sor and not sor.startswith("["):
+ k, _, v = sor.partition("=")
+ adat[k.strip()] = v.strip()
+ if not adat and "Nem UEFI" in kimenet.stdout:
+ adat["uefi"] = "0"
+ return adat
+
+
+class SecureBoot(QWidget):
+ def __init__(self, kozpont):
+ super().__init__()
+ self.kozpont = kozpont
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 20, 0, 0)
+ doboz.setSpacing(16)
+ doboz.addWidget(
+ cimke(
+ "A Secure Boot csak aláírt rendszerbetöltőt és kernelt enged elindulni. Akkor hasznos, ha a gépen "
+ "Windows is van, és ott olyan játék vagy program fut, ami bekapcsolt Secure Bootot kér (például egyes "
+ "csalásvédelmek vagy a Windows 11). A beállítás után a RaveOS és a Windows is elindul.",
+ "masodlagos",
+ True,
+ )
+ )
+ oszlopok = QHBoxLayout()
+ oszlopok.setSpacing(24)
+ bal = QVBoxLayout()
+ bal.setSpacing(12)
+ oszlopok.addLayout(bal, 3)
+ doboz.addLayout(oszlopok, 1)
+ self.racs = Racs(110, 10)
+ self.ertekek = {}
+ for kulcs, nev in (
+ ("secureboot", "Secure Boot"),
+ ("setupmode", "Setup Mode"),
+ ("keys", "Gépkulcs"),
+ ("signed", "Aláírások"),
+ ):
+ k = Kartya(nev, terkoz=6, margo=14)
+ ertek = cimke("...", "nagyszam")
+ k.elrendezes.addWidget(ertek)
+ self.ertekek[kulcs] = ertek
+ self.racs.hozzaad(k)
+ bal.addWidget(self.racs)
+ self.figyelem = cimke("", "figyelem", True)
+ self.figyelem.hide()
+ bal.addWidget(self.figyelem)
+ bal.addWidget(
+ cimke(
+ "FIGYELEM, BITLOCKER: ha a gépen Windows is van BitLockerrel, a beállítás előtt a Windowsban függeszd "
+ "fel a BitLockert (manage-bde -protectors -disable C: -rebootcount 2), vagy legyen kéznél a 48 jegyű "
+ "helyreállítási kulcs. Enélkül a Windows a következő indításkor ezt a kulcsot fogja kérni.",
+ "figyelem",
+ True,
+ )
+ )
+
+ leiras = Kartya("A beállítás lépései", terkoz=6)
+ leiras.elrendezes.addWidget(cimke("A Beállítás gomb egymás után:", "masodlagos"))
+ for i, szoveg in enumerate(LEPESEK_SZOVEG, 1):
+ sor = QHBoxLayout()
+ sor.setSpacing(10)
+ jel = cimke(f"{i}.", "monozold")
+ jel.setFixedWidth(22)
+ sor.addWidget(jel)
+ sor.addWidget(cimke(szoveg, "masodlagos", True), 1)
+ leiras.elrendezes.addLayout(sor)
+ leiras.elrendezes.addSpacing(6)
+ leiras.elrendezes.addWidget(
+ cimke(
+ "Ehhez a firmware-nek Setup Mode-ban kell lennie: a BIOS/UEFI beállításokban töröld a Secure Boot "
+ "kulcsokat (Clear Secure Boot keys / Reset to Setup Mode). Ha nincs Setup Mode-ban, a Center szól, "
+ "és újraindítás után a beállítás magától folytatódik.",
+ "halvany",
+ True,
+ )
+ )
+
+ leiras.setMinimumWidth(300)
+ leiras.setMaximumWidth(420)
+ jobb = QVBoxLayout()
+ jobb.addWidget(leiras)
+ jobb.addStretch(1)
+ oszlopok.addLayout(jobb, 2)
+
+ gombok = QHBoxLayout()
+ gombok.setSpacing(8)
+ self.frissit_gomb = gomb("Állapot frissítése")
+ self.frissit_gomb.clicked.connect(self.frissit)
+ self.ujra_gomb = gomb("Újra aláírás")
+ self.ujra_gomb.setToolTip("Ha a gépkulcs már megvan, csak újra aláírja a rendszerbetöltőt, a kernelt és a modulokat.")
+ self.ujra_gomb.clicked.connect(lambda: self._futtat("resign", "Újra aláírás"))
+ self.beallit_gomb = gomb("Beállítás", fo=True)
+ self.beallit_gomb.clicked.connect(self._beallit)
+ gombok.addWidget(self.frissit_gomb)
+ gombok.addStretch(1)
+ gombok.addWidget(self.ujra_gomb)
+ gombok.addWidget(self.beallit_gomb)
+ bal.addLayout(gombok)
+
+ bal.addWidget(cimke("NAPLÓ", "h2"))
+ self.naplo = QPlainTextEdit()
+ self.naplo.setObjectName("naplo")
+ self.naplo.setReadOnly(True)
+ self.naplo.setMinimumHeight(80)
+ bal.addWidget(self.naplo, 1)
+
+ self.futtato = muveletek.Futtato(self)
+ self.futtato.sor.connect(self.naplo.appendPlainText)
+ self.futtato.kesz.connect(self._kesz)
+ self.adat = {}
+
+ def megjelenik(self, *_):
+ self._betolt()
+
+ def frissit(self):
+ ervenytelenit("secureboot")
+ self._betolt()
+
+ def _betolt(self):
+ if tarolt("secureboot") is None:
+ for e in self.ertekek.values():
+ e.setText("...")
+ gyorsitott(
+ "secureboot", allapot_lekerdez, kesz=self._allapot,
+ hiba=lambda u: self._hiba(f"Az állapot nem olvasható: {u}"), kor=60,
+ )
+
+ def _hiba(self, szoveg):
+ self.figyelem.setText(szoveg)
+ self.figyelem.show()
+
+ def _allapot(self, adat):
+ self.adat = adat or {}
+ self.figyelem.hide()
+ if adat is None:
+ for e in self.ertekek.values():
+ e.setText("-")
+ self._hiba("A Secure Boot beállító (raveos-secureboot-setup) nincs telepítve.")
+ self._gombok(False)
+ return
+ if adat.get("uefi") == "0":
+ for e in self.ertekek.values():
+ e.setText("-")
+ self._hiba("Ez a gép nem UEFI módban indult, ezért a Secure Boot nem érhető el.")
+ self._gombok(False)
+ return
+ def ki(ertek, igen, nem):
+ return igen if ertek == "1" else nem if ertek == "0" else "?"
+ self._beir("secureboot", ki(adat.get("secureboot"), "BE", "KI"), adat.get("secureboot") == "1")
+ self._beir("setupmode", ki(adat.get("setupmode"), "IGEN", "NEM"), adat.get("setupmode") == "1")
+ self._beir("keys", ki(adat.get("keys"), "MEGVAN", "NINCS"), adat.get("keys") == "1")
+ self._beir("signed", ki(adat.get("signed"), "RENDBEN", "HIÁNYZIK"), adat.get("signed") == "1")
+ if adat.get("pending") == "1":
+ self._hiba("A beállítás félbemaradt, és a következő újraindításkor magától folytatódik.")
+ self._gombok(True)
+
+ def _beir(self, kulcs, szoveg, zold):
+ e = self.ertekek[kulcs]
+ e.setText(szoveg)
+ e.setProperty("zold", zold)
+ e.style().unpolish(e)
+ e.style().polish(e)
+
+ def _gombok(self, elerheto):
+ fut = self.futtato.fut()
+ helper = muveletek.helper_elerheto()
+ self.beallit_gomb.setEnabled(elerheto and helper and not fut)
+ self.ujra_gomb.setEnabled(elerheto and helper and not fut and self.adat.get("keys") == "1")
+ self.frissit_gomb.setEnabled(not fut)
+
+ def _beallit(self):
+ valasz = QMessageBox.question(
+ self,
+ "Secure Boot beállítása",
+ "A beállítás kulcsokat ír a firmware-be, és aláírja a rendszerbetöltőt és a kernelt. "
+ "Ha Windows is van a gépen BitLockerrel, előtte függeszd fel a BitLockert.\n\nFolytatod?",
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ QMessageBox.StandardButton.No,
+ )
+ if valasz == QMessageBox.StandardButton.Yes:
+ self._futtat("setup", "Secure Boot beállítása")
+
+ def _futtat(self, mod, cimke_szoveg):
+ self.naplo.clear()
+ self._gombok(False)
+ self.futtato.indit([muveletek.Lepes(cimke_szoveg, muveletek.root("secureboot", mod))])
+
+ def _bios_ujrainditas(self, cim, szoveg):
+ doboz = QMessageBox(self)
+ doboz.setWindowTitle(cim)
+ doboz.setText(szoveg)
+ most = doboz.addButton("Újraindítás a BIOS-ba", QMessageBox.ButtonRole.AcceptRole)
+ doboz.addButton("Később", QMessageBox.ButtonRole.RejectRole)
+ doboz.exec()
+ if doboz.clickedButton() is most:
+ muveletek.Futtato(self).indit(
+ [
+ muveletek.Lepes(
+ "Újraindítás a firmware beállításaiba",
+ ["bash", "-c", "systemctl reboot --firmware-setup || systemctl reboot"],
+ )
+ ]
+ )
+
+ def _kesz(self, rendben, _hibasak):
+ kod = self.futtato.utolso_kod
+ if rendben:
+ self._bios_ujrainditas(
+ "Secure Boot",
+ "Az aláírások és a kulcsok készen vannak.\n\nA BIOS-ban állítsd a Secure Bootot Enabled-re, "
+ "majd mentsd el a beállítást és indítsd újra a gépet.",
+ )
+ elif kod == UJRAINDITAS_KELL:
+ self._bios_ujrainditas(
+ "Újraindítás szükséges",
+ "A firmware nincs Setup Mode-ban.\n\nA BIOS-ban a Secure Boot menüben töröld a kulcsokat "
+ "(Key Management, Clear Secure Boot keys), így a gép Setup Mode-ba kerül. Mentés után indítsd újra "
+ "a gépet: a beállítás indítás után magától befejeződik.",
+ )
+ else:
+ QMessageBox.warning(self, "Secure Boot", "A művelet nem sikerült. A részleteket a napló mutatja.")
+ self.frissit()
+
+
+FUL = SecureBoot
diff --git a/raveos-center/raveos_center/oldalak/rendszer/snapshotok.py b/raveos-center/raveos_center/oldalak/rendszer/snapshotok.py
new file mode 100644
index 0000000..8d9b08c
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/rendszer/snapshotok.py
@@ -0,0 +1,247 @@
+from PyQt6.QtCore import QProcess, Qt
+from PyQt6.QtWidgets import QHBoxLayout, QInputDialog, QLineEdit, QMessageBox, QVBoxLayout, QWidget
+
+from raveos_center import muveletek, snapshotok
+from raveos_center.elemek import Kartya, cimke, gomb
+from raveos_center.folyamat import FolyamatAblak
+from raveos_center.hatter import ervenytelenit, gyorsitott, tarolt
+
+AZON = "snapshotok"
+CIM = "Snapshotok"
+
+
+class SnapshotSor(QWidget):
+ def __init__(self, elem, utolso, visszaallit, torol):
+ super().__init__()
+ self.setObjectName("appsor")
+ self.setProperty("utolso", utolso)
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ sor = QHBoxLayout(self)
+ sor.setContentsMargins(16, 12, 16, 12)
+ sor.setSpacing(16)
+ datum = cimke(elem["datum"], "mono")
+ datum.setFixedWidth(150)
+ sor.addWidget(datum)
+ szovegek = QVBoxLayout()
+ szovegek.setSpacing(2)
+ szovegek.addWidget(cimke(elem["leiras"] or "-", None, True))
+ szovegek.addWidget(cimke(f"#{elem['szam']} {elem['tipus_nev']}", "monohalvany"))
+ sor.addLayout(szovegek, 1)
+ helper = muveletek.helper_elerheto()
+ vissza = gomb("Visszaállítás", kicsi=True)
+ vissza.setEnabled(helper)
+ vissza.clicked.connect(lambda: visszaallit(elem))
+ sor.addWidget(vissza, 0, Qt.AlignmentFlag.AlignVCenter)
+ torles = gomb("Törlés", kicsi=True)
+ torles.setEnabled(helper)
+ torles.clicked.connect(lambda: torol(elem))
+ sor.addWidget(torles, 0, Qt.AlignmentFlag.AlignVCenter)
+
+
+class Snapshotok(QWidget):
+ def __init__(self, kozpont):
+ super().__init__()
+ self.kozpont = kozpont
+ self.adat = None
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 20, 0, 0)
+ doboz.setSpacing(16)
+ fej = QHBoxLayout()
+ fej.setSpacing(16)
+ self.leiras = cimke(
+ "A btrfs snapshot a rendszer egy pillanatnyi állapota. Ha egy frissítés után gond van, egy korábbi "
+ "állapotra vissza tudsz állni. A saját fájljaidat (/home) a visszaállítás nem érinti.",
+ "masodlagos",
+ True,
+ )
+ fej.addWidget(self.leiras, 1)
+ self.uj_gomb = gomb("Új snapshot", fo=True)
+ self.uj_gomb.clicked.connect(self.uj)
+ fej.addWidget(self.uj_gomb, 0, Qt.AlignmentFlag.AlignTop)
+ doboz.addLayout(fej)
+ self.info = cimke("", "monozold", True)
+ self.info.hide()
+ doboz.addWidget(self.info)
+ self.figyelem = cimke("", "figyelem", True)
+ self.figyelem.hide()
+ doboz.addWidget(self.figyelem)
+ self.beallitas_gomb = gomb("Snapper beállítása", fo=True)
+ self.beallitas_gomb.clicked.connect(self._beallitas)
+ self.beallitas_gomb.hide()
+ gs = QHBoxLayout()
+ gs.addWidget(self.beallitas_gomb)
+ gs.addStretch(1)
+ doboz.addLayout(gs)
+ self.kartya = Kartya(terkoz=0, margo=0)
+ doboz.addWidget(self.kartya)
+ doboz.addStretch(1)
+
+ def megjelenik(self, *args):
+ if tarolt("snapshotok") is None:
+ self.uj_gomb.setEnabled(False)
+ self._info("Betöltés...")
+ gyorsitott("snapshotok", snapshotok.allapot, kesz=self._kesz, hiba=self._hiba, kor=30)
+ if args and args[0] == "uj":
+ self.uj()
+
+ def frissit(self):
+ ervenytelenit("snapshotok", "snapshot")
+ gyorsitott("snapshotok", snapshotok.allapot, kesz=self._kesz, hiba=self._hiba)
+
+ def _hiba(self, uzenet):
+ self._figyelmeztet(f"A snapshotok nem olvashatók: {uzenet}")
+
+ def _info(self, szoveg):
+ self.info.setText(szoveg)
+ self.info.setVisible(bool(szoveg))
+
+ def _figyelmeztet(self, szoveg):
+ self.figyelem.setText(szoveg)
+ self.figyelem.setVisible(bool(szoveg))
+
+ def _kesz(self, adat):
+ self.adat = adat
+ helper = muveletek.helper_elerheto()
+ kesz = adat["fajlrendszer"] == "btrfs" and adat["snapper"]
+ self.uj_gomb.setVisible(kesz)
+ self.uj_gomb.setEnabled(kesz and helper)
+ self.beallitas_gomb.setVisible(snapshotok.snapper_telepitheto(adat))
+ self.beallitas_gomb.setEnabled(helper)
+ self.kartya.setVisible(kesz)
+ if adat["fajlrendszer"] != "btrfs":
+ self._info("")
+ self._figyelmeztet(
+ f"Snapshothoz btrfs fájlrendszer kell. Ez a gép {adat['fajlrendszer'] or 'ismeretlen'} fájlrendszert "
+ "használ, ezért itt nem érhető el. A RaveOS telepítésekor a lemez partícionálásánál ext4 és btrfs "
+ "közül lehet választani: ha snapshotot szeretnél, a btrfs-t válaszd."
+ )
+ return
+ if not adat["snapper"]:
+ self._info("")
+ if snapshotok.snapper_telepitheto(adat):
+ self._figyelmeztet(
+ "A gép btrfs-t használ, de a snapper még nincs beállítva. Egy kattintással beállítható: "
+ "utána minden csomagművelet előtt automatikusan snapshot készül."
+ )
+ else:
+ self._figyelmeztet(
+ "A gép btrfs-t használ, de az alvolumen-elrendezése eltér a RaveOS-étól (@ és @snapshots), "
+ "ezért a snapper itt nem állítható be automatikusan."
+ )
+ return
+ if adat["alvolumen"] != "/@":
+ self._figyelmeztet("A rendszer most nem a fő alvolumenről (@) fut, ezért visszaállítás nem indítható.")
+ elif adat["hiba"]:
+ self._figyelmeztet(adat["hiba"])
+ elif not helper:
+ self._figyelmeztet("A RaveOS Center rendszerhelpere nincs telepítve, ezért snapshot nem kezelhető.")
+ else:
+ self._figyelmeztet("")
+ self._info(
+ ("Automatikus snapshot minden csomagművelet előtt és után (snap-pac). " if adat["snap_pac"] else "")
+ + f"{len(adat['lista'])} snapshot, a régiek automatikusan törlődnek."
+ )
+ self._lista(adat)
+
+ def _lista(self, adat):
+ elrendezes = self.kartya.elrendezes
+ while elrendezes.count():
+ w = elrendezes.takeAt(0).widget()
+ if w:
+ w.deleteLater()
+ if not adat["lista"]:
+ ures = cimke("Még nincs snapshot.", "halvany")
+ ures.setContentsMargins(16, 16, 16, 16)
+ elrendezes.addWidget(ures)
+ return
+ for i, elem in enumerate(adat["lista"]):
+ elrendezes.addWidget(SnapshotSor(elem, i == len(adat["lista"]) - 1, self.visszaallit, self.torol))
+
+ def _futtat(self, cim, lepes, siker):
+ ablak = FolyamatAblak(self, cim, [lepes], siker)
+ ablak.exec()
+ self.frissit()
+ return ablak.rendben
+
+ def uj(self):
+ if not self.adat or not self.uj_gomb.isEnabled():
+ return
+ leiras, ok = QInputDialog.getText(
+ self, "Új snapshot", "Leírás (például: Driver csere előtt):", QLineEdit.EchoMode.Normal, "Kézi snapshot"
+ )
+ leiras = leiras.strip()
+ if not ok:
+ return
+ if not snapshotok.ervenyes_leiras(leiras):
+ QMessageBox.warning(self, "Új snapshot", "A leírás 1-80 karakter lehet: betűk, számok, szóköz és . _ : , ( ) -")
+ return
+ self._futtat("Új snapshot", muveletek.Lepes("Snapshot készítése", muveletek.root("snapshot", leiras)), "A snapshot elkészült.")
+
+ def torol(self, elem):
+ if QMessageBox.question(
+ self, "Snapshot törlése", f"Törlöd a(z) #{elem['szam']} snapshotot?\n{elem['datum']} {elem['leiras']}"
+ ) != QMessageBox.StandardButton.Yes:
+ return
+ self._futtat(
+ "Snapshot törlése",
+ muveletek.Lepes("Snapshot törlése", muveletek.root("snapshot-torles", str(elem["szam"]))),
+ "A snapshot törölve.",
+ )
+
+ def visszaallit(self, elem):
+ kernelek = snapshotok.kernel_valtozas(elem["szam"])
+ ellenorizheto = kernelek is not None
+ kernelek = kernelek or []
+ hianyzo = [k for k in kernelek if not k["cache"]]
+ if hianyzo:
+ QMessageBox.warning(
+ self,
+ "Visszaállítás",
+ "Ennek a snapshotnak a kernelcsomagja már nincs meg a csomag-cache-ben "
+ f"({', '.join(k['nev'] + ' ' + k['regi'] for k in hianyzo)}), ezért a visszaállított rendszer nem "
+ "indulna el. Válassz újabb snapshotot.",
+ )
+ return
+ kernel_szoveg = "".join(f"\n- {k['nev']}: {k['most'] or 'nincs'} -> {k['regi']}" for k in kernelek)
+ szoveg = (
+ f"A rendszer visszaáll erre az állapotra:\n#{elem['szam']} {elem['datum']} {elem['leiras']}\n\n"
+ "A saját fájljaid (/home) nem változnak. A mostani rendszer a következő visszaállításig megmarad "
+ "tartalékként. A változás újraindítás után lép életbe."
+ + (f"\n\nA kernel is visszaáll:{kernel_szoveg}" if kernelek else "")
+ + ("" if ellenorizheto else "\n\nA snapshot kernelverziója innen nem olvasható, a visszaállítás előtt a rendszer "
+ "ellenőrzi, és ha a kernelcsomag hiányzik, nem végzi el.")
+ + "\n\nFolytatod?"
+ )
+ if QMessageBox.question(self, "Visszaállítás", szoveg) != QMessageBox.StandardButton.Yes:
+ return
+ rendben = self._futtat(
+ "Visszaállítás",
+ muveletek.Lepes("Snapshot visszaállítása", muveletek.root("snapshot-visszaallit", str(elem["szam"]))),
+ "A visszaállítás elkészült. Indítsd újra a gépet.",
+ )
+ if not rendben:
+ return
+ doboz = QMessageBox(self)
+ doboz.setWindowTitle("Visszaállítás")
+ doboz.setIcon(QMessageBox.Icon.Warning)
+ doboz.setText(
+ "A visszaállítás újraindítás után lép életbe. Amit addig a gépen változtatsz (telepítés, beállítás, "
+ "rendszerfájlok), az a tartalék rendszerbe kerül, és elvész. A /home-ban lévő fájljaid megmaradnak.\n\n"
+ "Javasolt most újraindítani."
+ )
+ most = doboz.addButton("Újraindítás most", QMessageBox.ButtonRole.AcceptRole)
+ doboz.addButton("Később", QMessageBox.ButtonRole.RejectRole)
+ doboz.setDefaultButton(most)
+ doboz.exec()
+ if doboz.clickedButton() is most:
+ QProcess.startDetached("systemctl", ["reboot"])
+
+ def _beallitas(self):
+ self._futtat(
+ "Snapper beállítása",
+ muveletek.Lepes("Snapper beállítása", muveletek.root("snapper-beallitas")),
+ "A snapper beállítva, az első snapshot elkészült.",
+ )
+
+
+FUL = Snapshotok
diff --git a/raveos-center/raveos_center/oldalak/visszajelzes.py b/raveos-center/raveos_center/oldalak/visszajelzes.py
new file mode 100644
index 0000000..56664ab
--- /dev/null
+++ b/raveos-center/raveos_center/oldalak/visszajelzes.py
@@ -0,0 +1,887 @@
+from datetime import datetime
+
+from PyQt6.QtCore import Qt, QTimer
+from PyQt6.QtGui import QTextCursor
+from PyQt6.QtWidgets import (
+ QCheckBox,
+ QDialog,
+ QHBoxLayout,
+ QLineEdit,
+ QListWidget,
+ QListWidgetItem,
+ QPlainTextEdit,
+ QPushButton,
+ QScrollArea,
+ QVBoxLayout,
+ QWidget,
+)
+
+from raveos_center import beallitasok, diagnosztika, jegyek_api
+from raveos_center.elemek import Csillagok, Fulsor, Kartya, Oldal, Verem, cimke, gomb
+from raveos_center.hatter import gyorsitott, hatterben
+
+AZON = "visszajelzes"
+MENU = "Visszajelzés"
+IKON = "visszajelzes"
+
+ALLAPOT = {
+ "uj": ("Beküldve", ""),
+ "folyamatban": ("Folyamatban", "sarga"),
+ "tervezett": ("Tervezett", "sarga"),
+ "megoldva": ("Megoldva", "zold"),
+ "kesz": ("Elkészült", "zold"),
+ "lezarva": ("Lezárva", ""),
+ "elutasitva": ("Elutasítva", ""),
+}
+
+
+def ido(t):
+ return datetime.fromtimestamp(t).strftime("%Y.%m.%d %H:%M")
+
+
+def allapotcimke(allapot):
+ szoveg, szin = ALLAPOT.get(allapot, (allapot, ""))
+ c = cimke(szoveg.upper(), "allapotcimke")
+ if szin:
+ c.setProperty("szin", szin)
+ return c
+
+
+def uzenetsav(uzenet):
+ sav = cimke(uzenet, "figyelem", True)
+ sav.hide()
+ return sav
+
+
+class JegySor(QWidget):
+ def __init__(self, jegy):
+ super().__init__()
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(14, 12, 14, 12)
+ doboz.setSpacing(6)
+ felso = QHBoxLayout()
+ felso.setSpacing(8)
+ tipus = "HIBA" if jegy["tipus"] == "hiba" else "ÖTLET"
+ felso.addWidget(cimke(tipus, "monozold"))
+ felso.addStretch(1)
+ if jegy["uj_valasz"]:
+ felso.addWidget(cimke("ÚJ VÁLASZ", "jelveny"))
+ felso.addWidget(allapotcimke(jegy["allapot"]))
+ doboz.addLayout(felso)
+ cim = cimke(jegy["cim"])
+ cim.setStyleSheet("font-weight: 600;")
+ cim.setText(cim.fontMetrics().elidedText(jegy["cim"], Qt.TextElideMode.ElideRight, 300))
+ cim.setToolTip(jegy["cim"])
+ doboz.addWidget(cim)
+ doboz.addWidget(cimke(ido(jegy["frissitve"]), "halvany"))
+ for w in self.findChildren(QWidget):
+ w.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+
+
+class Uzenet(QWidget):
+ def __init__(self, uzenet):
+ super().__init__()
+ self.setObjectName("uzenet")
+ self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
+ csapat = uzenet["szerzo"] == "admin"
+ self.setProperty("csapat", csapat)
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(14, 12, 14, 12)
+ doboz.setSpacing(6)
+ ki = "RaveOS csapat" if csapat else "Te"
+ doboz.addWidget(cimke(f"{ki} · {ido(uzenet['ido'])}", "halvany"))
+ szoveg = cimke(uzenet["szoveg"], tordel=True)
+ szoveg.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
+ doboz.addWidget(szoveg)
+
+
+class JelentesAblak(QDialog):
+ def __init__(self, szulo, szoveg):
+ super().__init__(szulo)
+ self.setWindowTitle("A csatolt diagnosztika tartalma")
+ self.resize(820, 600)
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(20, 20, 20, 20)
+ doboz.setSpacing(12)
+ doboz.addWidget(cimke("Pontosan ez kerül feltöltésre, ha bejelölöd a csatolást:", "masodlagos"))
+ mezo = QPlainTextEdit(szoveg)
+ mezo.setReadOnly(True)
+ mezo.setObjectName("mono")
+ doboz.addWidget(mezo, 1)
+ bezar = gomb("Bezárás", fo=True)
+ bezar.clicked.connect(self.accept)
+ doboz.addWidget(bezar, 0, Qt.AlignmentFlag.AlignRight)
+
+
+class Urlap(QWidget):
+ def __init__(self, oldal):
+ super().__init__()
+ self.oldal = oldal
+ self.tipus = "hiba"
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 0, 0, 0)
+ doboz.setSpacing(12)
+ self.cim = cimke("", "h2")
+ doboz.addWidget(self.cim)
+ self.leiras = cimke("", "masodlagos", True)
+ doboz.addWidget(self.leiras)
+ doboz.addWidget(cimke("Cím", "kartyacim"))
+ self.cim_mezo = QLineEdit()
+ self.cim_mezo.setMaxLength(120)
+ doboz.addWidget(self.cim_mezo)
+ doboz.addWidget(cimke("Leírás", "kartyacim"))
+ self.szoveg_mezo = QPlainTextEdit()
+ self.szoveg_mezo.setMinimumHeight(160)
+ doboz.addWidget(self.szoveg_mezo)
+
+ self.diag = Kartya(terkoz=8, margo=16)
+ self.diag_jelolo = QCheckBox("Diagnosztika csatolása (ajánlott, segít megtalálni a hibát)")
+ self.diag.elrendezes.addWidget(self.diag_jelolo)
+ lista = "\n".join(f" - {t}" for t in diagnosztika.TARTALOM)
+ self.diag.elrendezes.addWidget(
+ cimke(
+ f"Ha bejelölöd, egy jelentést is csatolunk a gépedről:\n{lista}\n\n"
+ f"Feltöltés előtt kitakarjuk: {diagnosztika.KITAKARAS}. Jelszó nem kerül bele. "
+ "Titkosított kapcsolaton megy el, csak a RaveOS fejlesztői látják, és 30 nap után törlődik.",
+ "halvany",
+ True,
+ )
+ )
+ self.megnez = gomb("Megnézem, mi kerül bele", kicsi=True)
+ self.megnez.clicked.connect(self._megnez)
+ self.diag.elrendezes.addWidget(self.megnez, 0, Qt.AlignmentFlag.AlignLeft)
+ doboz.addWidget(self.diag)
+
+ doboz.addWidget(
+ cimke(
+ "A jegyhez mindig csatoljuk a RaveOS-verziót, a kernelt, az asztali környezetet és a videokártya nevét.",
+ "halvany",
+ True,
+ )
+ )
+ self.hiba = uzenetsav("")
+ doboz.addWidget(self.hiba)
+ gombok = QHBoxLayout()
+ gombok.addStretch(1)
+ self.megse = gomb("Mégse")
+ self.megse.clicked.connect(self.oldal.urlap_bezar)
+ self.kuld = gomb("Beküldés", fo=True)
+ self.kuld.clicked.connect(self._kuld)
+ gombok.addWidget(self.megse)
+ gombok.addWidget(self.kuld)
+ doboz.addLayout(gombok)
+ doboz.addStretch(1)
+
+ def nyit(self, tipus):
+ self.tipus = tipus
+ self.cim_mezo.clear()
+ self.szoveg_mezo.clear()
+ self.diag_jelolo.setChecked(False)
+ self.hiba.hide()
+ self.kuld.setEnabled(True)
+ if tipus == "hiba":
+ self.cim.setText("ÚJ HIBAJEGY")
+ self.leiras.setText("Írd le, mi történt, és mit csináltál előtte. Ha válaszolunk, itt fogod látni.")
+ self.cim_mezo.setPlaceholderText("Például: Nem indul a Steam a frissítés után")
+ self.szoveg_mezo.setPlaceholderText("Mi a hiba, mikor jelentkezik, mit próbáltál már?")
+ self.diag.show()
+ else:
+ self.cim.setText("ÚJ ÖTLET")
+ self.leiras.setText(
+ "Mi hiányzik a RaveOS-ből? Az ötleted a jóváhagyásunk után jelenik meg a közös listában, "
+ "ahol mások is támogathatják."
+ )
+ self.cim_mezo.setPlaceholderText("Például: Sötét és világos téma váltó")
+ self.szoveg_mezo.setPlaceholderText("Mit szeretnél, és miért lenne hasznos?")
+ self.diag.hide()
+ self.cim_mezo.setFocus()
+
+ def _megnez(self):
+ self.megnez.setEnabled(False)
+ self.megnez.setText("Összeállítás...")
+
+ def kesz(szoveg):
+ self.megnez.setEnabled(True)
+ self.megnez.setText("Megnézem, mi kerül bele")
+ JelentesAblak(self, szoveg).exec()
+
+ hatterben(diagnosztika.jelentes, kesz=kesz)
+
+ def _kuld(self):
+ cim = self.cim_mezo.text().strip()
+ szoveg = self.szoveg_mezo.toPlainText().strip()
+ if len(cim) < 3 or len(szoveg) < 10:
+ self.hiba.setText("Adj meg egy legalább 3 karakteres címet és egy legalább 10 karakteres leírást.")
+ self.hiba.show()
+ return
+ self.hiba.hide()
+ self.kuld.setEnabled(False)
+ self.kuld.setText("Küldés...")
+ csatol = self.tipus == "hiba" and self.diag_jelolo.isChecked()
+ tipus = self.tipus
+
+ def munka():
+ naplo = jegyek_api.naplo_feltolt(diagnosztika.jelentes()) if csatol else None
+ return jegyek_api.uj_jegy(tipus, cim, szoveg, diagnosztika.rendszer_osszegzes(), naplo)
+
+ hatterben(munka, kesz=self._kuldve, hiba=self._kuldes_hiba)
+
+ def _kuldve(self, jegy):
+ self.kuld.setText("Beküldés")
+ self.oldal.bekuldve(jegy)
+
+ def _kuldes_hiba(self, uzenet):
+ self.kuld.setEnabled(True)
+ self.kuld.setText("Beküldés")
+ self.hiba.setText(jegyek_api.HIBAUZENETEK.get(uzenet, f"A beküldés nem sikerült: {uzenet}"))
+ self.hiba.show()
+
+
+class Reszletek(QWidget):
+ def __init__(self, oldal):
+ super().__init__()
+ self.oldal = oldal
+ self.jegy_id = None
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 0, 0, 0)
+ doboz.setSpacing(12)
+ self.cim = cimke("", "h2", True)
+ doboz.addWidget(self.cim)
+ self.meta = QHBoxLayout()
+ self.meta.setSpacing(10)
+ doboz.addLayout(self.meta)
+ self.lezart = uzenetsav("")
+ doboz.addWidget(self.lezart)
+ gorgeto = QScrollArea()
+ gorgeto.setWidgetResizable(True)
+ self.szal_doboz = QWidget()
+ self.szal = QVBoxLayout(self.szal_doboz)
+ self.szal.setContentsMargins(0, 0, 8, 0)
+ self.szal.setSpacing(10)
+ gorgeto.setWidget(self.szal_doboz)
+ self.gorgeto = gorgeto
+ doboz.addWidget(gorgeto, 1)
+ self.valasz_doboz = QWidget()
+ vd = QVBoxLayout(self.valasz_doboz)
+ vd.setContentsMargins(0, 0, 0, 0)
+ vd.setSpacing(8)
+ self.valasz_mezo = QPlainTextEdit()
+ self.valasz_mezo.setPlaceholderText("Válasz a RaveOS csapatnak")
+ self.valasz_mezo.setFixedHeight(96)
+ vd.addWidget(self.valasz_mezo)
+ self.hiba = uzenetsav("")
+ vd.addWidget(self.hiba)
+ sor = QHBoxLayout()
+ sor.addStretch(1)
+ self.kuld = gomb("Válasz küldése", fo=True)
+ self.kuld.clicked.connect(self._kuld)
+ sor.addWidget(self.kuld)
+ vd.addLayout(sor)
+ doboz.addWidget(self.valasz_doboz)
+
+ def betolt(self, jegy):
+ self.jegy_id = jegy["id"]
+ self.cim.setText(jegy["cim"])
+ while self.meta.count():
+ w = self.meta.takeAt(0).widget()
+ if w:
+ w.deleteLater()
+ self.meta.addWidget(cimke("HIBAJEGY" if jegy["tipus"] == "hiba" else "ÖTLET", "monozold"))
+ self.meta.addWidget(allapotcimke(jegy["allapot"]))
+ self.meta.addWidget(cimke(f"#{jegy['id']} · {ido(jegy['letrehozva'])}", "halvany"))
+ if jegy["tipus"] == "otlet":
+ self.meta.addWidget(cimke("nyilvános" if jegy["nyilvanos"] else "jóváhagyásra vár", "halvany"))
+ self.meta.addStretch(1)
+ if jegy.get("torles"):
+ self.lezart.setText(
+ f"Ez a jegy le van zárva, és {ido(jegy['torles'])} után automatikusan törlődik. "
+ "Ha a hiba újra jelentkezik, nyiss új jegyet."
+ )
+ self.lezart.show()
+ self.valasz_doboz.hide()
+ else:
+ self.lezart.hide()
+ self.valasz_doboz.show()
+ while self.szal.count():
+ w = self.szal.takeAt(0).widget()
+ if w:
+ w.deleteLater()
+ for u in jegy.get("uzenetek", []):
+ self.szal.addWidget(Uzenet(u))
+ self.szal.addStretch(1)
+ self.hiba.hide()
+ self.valasz_mezo.clear()
+
+ def _kuld(self):
+ szoveg = self.valasz_mezo.toPlainText().strip()
+ if not szoveg:
+ return
+ self.kuld.setEnabled(False)
+ jegy_id = self.jegy_id
+
+ def munka():
+ jegyek_api.valasz(jegy_id, szoveg)
+ return jegyek_api.jegy(jegy_id)
+
+ def kesz(jegy):
+ self.kuld.setEnabled(True)
+ self.betolt(jegy)
+ self.oldal.lista_frissit()
+
+ def hiba(uzenet):
+ self.kuld.setEnabled(True)
+ if uzenet == "nincs_jegy":
+ self.oldal.jegy_eltunt()
+ return
+ self.hiba.setText(jegyek_api.HIBAUZENETEK.get(uzenet, f"A küldés nem sikerült: {uzenet}"))
+ self.hiba.show()
+
+ hatterben(munka, kesz=kesz, hiba=hiba)
+
+
+class ErtekelesLap(QWidget):
+ SZINTEK = {1: "Nagyon rossz", 2: "Rossz", 3: "Elmegy", 4: "Jó", 5: "Kiváló"}
+ MAX = 500
+
+ def __init__(self, oldal):
+ super().__init__()
+ self.oldal = oldal
+ self.mentett = None
+ self.szerkesztve = False
+ self.kuldes = False
+ doboz = QVBoxLayout(self)
+ doboz.setContentsMargins(0, 0, 12, 0)
+ doboz.setSpacing(16)
+
+ osszesito = Kartya("A RaveOS értékelése", terkoz=8, margo=20)
+ sor = QHBoxLayout()
+ sor.setSpacing(16)
+ self.atlag = cimke("-", "nagyszam")
+ sor.addWidget(self.atlag)
+ jobb = QVBoxLayout()
+ jobb.setSpacing(4)
+ self.atlag_csillag = Csillagok(24)
+ jobb.addWidget(self.atlag_csillag)
+ self.darab = cimke("", "halvany")
+ jobb.addWidget(self.darab)
+ sor.addLayout(jobb)
+ sor.addStretch(1)
+ osszesito.elrendezes.addLayout(sor)
+ doboz.addWidget(osszesito)
+
+ sajat = Kartya("A te értékelésed", terkoz=10, margo=20)
+ valaszto = QHBoxLayout()
+ valaszto.setSpacing(14)
+ self.csillagok = Csillagok(34, valaszthato=True)
+ self.csillagok.valasztva.connect(self._valasztva)
+ valaszto.addWidget(self.csillagok)
+ self.szint = cimke("Kattints a csillagokra", "masodlagos")
+ valaszto.addWidget(self.szint)
+ valaszto.addStretch(1)
+ sajat.elrendezes.addLayout(valaszto)
+ self.szoveg = QPlainTextEdit()
+ self.szoveg.setPlaceholderText("Röviden: mi tetszik, mi nem? (nem kötelező)")
+ self.szoveg.setFixedHeight(96)
+ self.szoveg.textChanged.connect(self._szoveg_valtozott)
+ sajat.elrendezes.addWidget(self.szoveg)
+ self.szamlalo = cimke(f"0 / {self.MAX}", "monohalvany")
+ sajat.elrendezes.addWidget(self.szamlalo, 0, Qt.AlignmentFlag.AlignRight)
+ sajat.elrendezes.addWidget(
+ cimke(
+ "A szöveges véleményt csak a RaveOS fejlesztői látják, nyilvánosan csak a csillagok átlaga jelenik meg. "
+ "Gépenként egy értékelés számít, bármikor módosíthatod.",
+ "halvany",
+ True,
+ )
+ )
+ self.hiba = uzenetsav("")
+ sajat.elrendezes.addWidget(self.hiba)
+ gombok = QHBoxLayout()
+ self.allapot = cimke("", "monozold")
+ gombok.addWidget(self.allapot)
+ gombok.addStretch(1)
+ self.kuld = gomb("Értékelés küldése", fo=True)
+ self.kuld.setEnabled(False)
+ self.kuld.clicked.connect(self._kuld)
+ gombok.addWidget(self.kuld)
+ sajat.elrendezes.addLayout(gombok)
+ doboz.addWidget(sajat)
+ doboz.addStretch(1)
+
+ def betolt(self, adat):
+ self.atlag.setText(f"{adat['atlag']:.1f}".replace(".", ",") if adat["darab"] else "-")
+ self.atlag_csillag.beallit(adat["atlag"])
+ self.darab.setText(
+ f"{adat['darab']} értékelés alapján" if adat["darab"] else "Még nincs értékelés. Legyél te az első!"
+ )
+ sajat = adat.get("sajat")
+ if sajat == self.mentett:
+ return
+ self.mentett = sajat
+ self.allapot.setText(f"ELKÜLDVE: {ido(sajat['ido'])}" if sajat else "")
+ if not self.szerkesztve:
+ self.csillagok.beallit(sajat["csillag"] if sajat else 0)
+ self.szint.setText(self.SZINTEK[sajat["csillag"]] if sajat else "Kattints a csillagokra")
+ self.szoveg.blockSignals(True)
+ self.szoveg.setPlainText(sajat["szoveg"] if sajat else "")
+ self.szoveg.blockSignals(False)
+ self.szamlalo.setText(f"{len(self.szoveg.toPlainText())} / {self.MAX}")
+ self._gomb_frissit()
+
+ def _valasztva(self, csillag):
+ self.szerkesztve = True
+ self.szint.setText(self.SZINTEK[csillag])
+ self._gomb_frissit()
+
+ def _szoveg_valtozott(self):
+ self.szerkesztve = True
+ szoveg = self.szoveg.toPlainText()
+ if len(szoveg) > self.MAX:
+ self.szoveg.blockSignals(True)
+ self.szoveg.setPlainText(szoveg[: self.MAX])
+ self.szoveg.moveCursor(QTextCursor.MoveOperation.End)
+ self.szoveg.blockSignals(False)
+ szoveg = szoveg[: self.MAX]
+ self.szamlalo.setText(f"{len(szoveg)} / {self.MAX}")
+ self._gomb_frissit()
+
+ def _valtozott(self):
+ if not self.mentett:
+ return True
+ return int(self.csillagok.ertek) != self.mentett["csillag"] or (
+ self.szoveg.toPlainText().strip() != self.mentett["szoveg"]
+ )
+
+ def _gomb_frissit(self):
+ self.kuld.setText("Értékelés módosítása" if self.mentett else "Értékelés küldése")
+ szabad = self.oldal.szabad() and not self.kuldes
+ self.csillagok.setEnabled(szabad)
+ self.szoveg.setEnabled(szabad)
+ self.kuld.setEnabled(szabad and self.csillagok.ertek >= 1 and self._valtozott())
+
+ def _kuld(self):
+ csillag = int(self.csillagok.ertek)
+ szoveg = self.szoveg.toPlainText().strip()
+ self.hiba.hide()
+ self.kuldes = True
+ self._gomb_frissit()
+ self.kuld.setText("Küldés...")
+
+ def kesz(adat):
+ self.kuldes = False
+ self.szerkesztve = False
+ self.mentett = None
+ self.betolt(adat)
+ self.allapot.setText("KÖSZÖNJÜK, ELMENTETTÜK")
+
+ def hiba(uzenet):
+ self.kuldes = False
+ self.hiba.setText(jegyek_api.HIBAUZENETEK.get(uzenet, f"A küldés nem sikerült: {uzenet}"))
+ self.hiba.show()
+ self._gomb_frissit()
+
+ hatterben(jegyek_api.ertekel, csillag, szoveg, kesz=kesz, hiba=hiba)
+
+
+ELFOGADAS_VERZIO = 3
+
+ISMERTETO = [
+ ("Mire jó?", "", [
+ "Itt közvetlenül a RaveOS fejlesztőinek jelezhetsz hibát, és küldhetsz ötletet. "
+ "Minden jegyet elolvasunk, és itt, a Centerben válaszolunk.",
+ ]),
+ ("Hogyan működik?", "szam", [
+ "Hibajegy: írd le, mi történt. Ha bejelölöd, a géped diagnosztikáját is csatoljuk, ami sokat segít; "
+ "előtte megnézheted, mi kerül bele.",
+ "Válasz: ha válaszolunk, a Visszajelzés menüpont mellett szám jelenik meg, és a jegyben vissza is írhatsz.",
+ "Ötlet: a jóváhagyott ötletek felkerülnek a közös listára, ahol mások is támogathatják. "
+ "A legtöbbet támogatottakkal foglalkozunk először.",
+ "Értékelés: 1-5 csillaggal értékelheted a RaveOS-t, és röviden leírhatod a véleményed. A szöveget csak "
+ "a fejlesztők látják, a többiek csak a csillagok átlagát. Gépenként egy értékelés számít, bármikor "
+ "módosíthatod.",
+ ]),
+ ("Miért így?", "szam", [
+ "Nem kell fiók: a géped egy névtelen azonosítót kap, ezzel látod a saját jegyeidet. "
+ "Újratelepítés után a régi jegyeid nem látszanak.",
+ "Csak azt küldjük el, amit beírsz, vagy amihez hozzájárulsz. A diagnosztikából kitakarjuk a személyes "
+ "adatokat, és 30 nap után törlődik.",
+ "A lezárt jegyek 7 nap után maguktól törlődnek.",
+ ]),
+ ("Szabályok", "szam", [
+ "A Visszajelzés csak hibák jelzésére és ötletek beküldésére való.",
+ "Egy hibáról egy jegyet nyiss. Ha már van róla jegyed, abba írj, ne nyiss újat.",
+ "Ötlet beküldése előtt nézd meg az Ötletek listát. Ha van hasonló, támogasd azt, ne küldj újat.",
+ "Ne írj a jegybe vagy az értékelésbe személyes vagy titkos adatot: jelszót, e-mail-címet, telefonszámot vagy más emberek "
+ "adatait. A diagnosztikát kitakarjuk, a beírt szöveget nem.",
+ "Kulturált hangnemet kérünk. Sértő, fenyegető vagy trágár üzenet tiltással jár.",
+ "Reklámot és nem ide illő linkeket ne küldj.",
+ "A jegyekre nem azonnal válaszolunk, ezért kérünk, légy türelmes, és ne küldd el többször ugyanazt.",
+ "Visszaélés, spam, sértő vagy nem ide illő tartalom esetén a géped kitiltható a Visszajelzésből, "
+ "ideiglenesen vagy véglegesen. A Center minden más része ettől ugyanúgy működik.",
+ "Két óránként legfeljebb 4 jegyet küldhetsz. Ennél több beküldés automatikusan 24 órás tiltással jár.",
+ ]),
+]
+
+HOZZAJARULAS = (
+ "Az Elfogadom gombbal elfogadod a fenti szabályokat, és hozzájárulsz, hogy a beírt szöveget, a géped "
+ "alapadatait (RaveOS-verzió, kernel, asztali környezet, videokártya) és, ha bejelölöd, a diagnosztikát "
+ "elküldjük a RaveOS fejlesztőinek."
+)
+
+
+class IsmertetoAblak(QDialog):
+ def __init__(self, szulo, elfogadva):
+ super().__init__(szulo)
+ self.setWindowTitle("Visszajelzés")
+ kepernyo = szulo.screen().availableGeometry() if szulo and szulo.screen() else None
+ magassag = min(860, kepernyo.height() - 80) if kepernyo else 760
+ self.resize(700, magassag)
+ kulso = QVBoxLayout(self)
+ kulso.setContentsMargins(0, 0, 0, 20)
+ kulso.setSpacing(12)
+ gorgeto = QScrollArea()
+ gorgeto.setWidgetResizable(True)
+ tartalom = QWidget()
+ gorgeto.setWidget(tartalom)
+ kulso.addWidget(gorgeto, 1)
+ doboz = QVBoxLayout(tartalom)
+ doboz.setContentsMargins(32, 28, 32, 8)
+ doboz.setSpacing(12)
+ doboz.addWidget(cimke("ISMERTETŐ ÉS SZABÁLYOK", "eyebrow"))
+ doboz.addWidget(cimke("HOGYAN MŰKÖDIK A VISSZAJELZÉS?", "h2", True))
+ sorszam = 0
+ for cim, jeloles, pontok in ISMERTETO:
+ doboz.addSpacing(4)
+ fejlec = cimke(cim)
+ fejlec.setStyleSheet("font-size: 15px; font-weight: 600;")
+ doboz.addWidget(fejlec)
+ for pont in pontok:
+ if not jeloles:
+ doboz.addWidget(cimke(pont, "masodlagos", True))
+ continue
+ sorszam += 1
+ sor = QHBoxLayout()
+ sor.setSpacing(10)
+ jel = cimke(f"{sorszam}.", "monozold")
+ jel.setFixedWidth(26)
+ sor.addWidget(jel, 0, Qt.AlignmentFlag.AlignTop)
+ sor.addWidget(cimke(pont, "masodlagos", True), 1)
+ doboz.addLayout(sor)
+ doboz.addStretch(1)
+ also = QVBoxLayout()
+ also.setContentsMargins(32, 0, 32, 0)
+ also.setSpacing(12)
+ kulso.addLayout(also)
+ gombok = QHBoxLayout()
+ gombok.addStretch(1)
+ if elfogadva:
+ also.addWidget(cimke("A szabályokat már elfogadtad.", "halvany"))
+ ok = gomb("Bezárás", fo=True)
+ ok.clicked.connect(self.accept)
+ gombok.addWidget(ok)
+ else:
+ also.addWidget(cimke(HOZZAJARULAS, "figyelem", True))
+ nem = gomb("Most nem")
+ nem.clicked.connect(self.reject)
+ igen = gomb("Elfogadom", fo=True)
+ igen.clicked.connect(self.accept)
+ gombok.addWidget(nem)
+ gombok.addWidget(igen)
+ also.addLayout(gombok)
+
+
+class OtletKartya(Kartya):
+ def __init__(self, otlet, szavaz):
+ super().__init__(terkoz=8, margo=18)
+ felso = QHBoxLayout()
+ cim = cimke(otlet["cim"], tordel=True)
+ cim.setStyleSheet("font-size: 15px; font-weight: 600;")
+ felso.addWidget(cim, 1)
+ felso.addWidget(allapotcimke(otlet["allapot"]), 0, Qt.AlignmentFlag.AlignTop)
+ self.elrendezes.addLayout(felso)
+ self.elrendezes.addWidget(cimke(otlet.get("leiras") or "", "masodlagos", True))
+ also = QHBoxLayout()
+ also.addWidget(cimke(f"{otlet['szavazat']} támogató", "monohalvany"))
+ if otlet.get("sajat"):
+ also.addWidget(cimke("· a te ötleted", "monohalvany"))
+ also.addStretch(1)
+ tamogatva = otlet["sajat_szavazat"]
+ g = gomb("Támogatva" if tamogatva else "Támogatom", fo=not tamogatva, kicsi=True)
+ g.clicked.connect(lambda: szavaz(otlet["id"], not tamogatva, g))
+ also.addWidget(g)
+ self.elrendezes.addLayout(also)
+
+
+class Visszajelzes(Oldal):
+ def __init__(self, kozpont):
+ super().__init__(kozpont, "HIBÁK ÉS ÖTLETEK", "Visszajelzés", kitolt=True)
+ self.uj_hiba = self.fejlec_gomb(gomb("Új hibajegy", fo=True))
+ self.uj_hiba.clicked.connect(lambda: self.urlap_nyit("hiba"))
+ self.uj_otlet = self.fejlec_gomb(gomb("Új ötlet"))
+ self.uj_otlet.clicked.connect(lambda: self.urlap_nyit("otlet"))
+ sugo = self.fejlec_gomb(gomb("Hogyan működik?"))
+ sugo.clicked.connect(self.ismerteto)
+
+ self.elfogadas_sav = QWidget()
+ es = QHBoxLayout(self.elfogadas_sav)
+ es.setContentsMargins(0, 0, 0, 0)
+ es.setSpacing(12)
+ es.addWidget(cimke("A Visszajelzés használatához el kell fogadnod a szabályokat.", "figyelem", True), 1)
+ elolvas = gomb("Szabályok elolvasása", fo=True)
+ elolvas.clicked.connect(self.ismerteto)
+ es.addWidget(elolvas)
+ self.elfogadas_sav.hide()
+ self.torzs.addWidget(self.elfogadas_sav)
+ self.tiltas_sav = cimke("", "figyelem", True)
+ self.tiltas_sav.hide()
+ self.torzs.addWidget(self.tiltas_sav)
+ self.kitiltva = False
+ self.fulsor = Fulsor([("jegyeim", "Jegyeim"), ("otletek", "Ötletek"), ("ertekeles", "Értékelés")], self.ful)
+ self.torzs.addWidget(self.fulsor)
+ self.hiba = uzenetsav("")
+ self.torzs.addWidget(self.hiba)
+ self.fulek = Verem()
+ self.torzs.addWidget(self.fulek, 1)
+ self.tolto = QWidget()
+ self.tolto.hide()
+ self.torzs.addWidget(self.tolto, 1)
+
+ jegyeim = QWidget()
+ sor = QHBoxLayout(jegyeim)
+ sor.setContentsMargins(0, 0, 0, 0)
+ sor.setSpacing(24)
+ bal = QVBoxLayout()
+ bal.setSpacing(8)
+ self.lista = QListWidget()
+ self.lista.setFixedWidth(340)
+ self.lista.setMinimumHeight(160)
+ self.lista.setVerticalScrollMode(QListWidget.ScrollMode.ScrollPerPixel)
+ self.lista.currentItemChanged.connect(self._kivalaszt)
+ bal.addWidget(self.lista, 1)
+ sor.addLayout(bal)
+ self.jobb = Verem()
+ self.ures = QWidget()
+ ud = QVBoxLayout(self.ures)
+ ud.setContentsMargins(0, 0, 0, 0)
+ self.ures_szoveg = cimke("", "masodlagos", True)
+ ud.addWidget(self.ures_szoveg)
+ ud.addStretch(1)
+ self.reszletek = Reszletek(self)
+ self.urlap = Urlap(self)
+ self.urlap_gorgeto = QScrollArea()
+ self.urlap_gorgeto.setWidgetResizable(True)
+ self.urlap_gorgeto.setWidget(self.urlap)
+ self.urlap.setContentsMargins(0, 0, 12, 0)
+ for w in (self.ures, self.reszletek, self.urlap_gorgeto):
+ self.jobb.addWidget(w)
+ sor.addWidget(self.jobb, 1)
+ self.fulek.addWidget(jegyeim)
+
+ otletek_gorgeto = QScrollArea()
+ otletek_gorgeto.setWidgetResizable(True)
+ otletek = QWidget()
+ otletek_gorgeto.setWidget(otletek)
+ od = QVBoxLayout(otletek)
+ od.setContentsMargins(0, 0, 12, 0)
+ od.setSpacing(12)
+ od.addWidget(
+ cimke(
+ "Mások ötletei, amelyeket jóváhagytunk. Támogasd, amit te is szeretnél, így látjuk, mi a legfontosabb.",
+ "masodlagos",
+ True,
+ )
+ )
+ self.otlet_doboz = QVBoxLayout()
+ self.otlet_doboz.setSpacing(12)
+ od.addLayout(self.otlet_doboz)
+ od.addStretch(1)
+ self.fulek.addWidget(otletek_gorgeto)
+ ertekeles_gorgeto = QScrollArea()
+ ertekeles_gorgeto.setWidgetResizable(True)
+ self.ertekeles_lap = ErtekelesLap(self)
+ ertekeles_gorgeto.setWidget(self.ertekeles_lap)
+ self.fulek.addWidget(ertekeles_gorgeto)
+ self.jegyek = []
+ self.lista_betoltve = False
+ self.ful("jegyeim")
+
+ def indulaskor(self):
+ hatterben(jegyek_api.jegyeim, kesz=self._jelveny_frissit)
+
+ def elfogadva(self):
+ return (beallitasok.ertek("visszajelzes_elfogadva", 0) or 0) >= ELFOGADAS_VERZIO
+
+ def szabad(self):
+ return self.elfogadva() and not self.kitiltva
+
+ def ismerteto(self):
+ if IsmertetoAblak(self, self.elfogadva()).exec() and not self.elfogadva():
+ beallitasok.beallit("visszajelzes_elfogadva", ELFOGADAS_VERZIO)
+ self._allapot_frissit()
+
+ def _allapot_frissit(self):
+ self.elfogadas_sav.setVisible(not self.elfogadva() and not self.kitiltva)
+ self.tiltas_sav.setVisible(self.kitiltva)
+ self.fulsor.setVisible(not self.kitiltva)
+ self.fulek.setVisible(not self.kitiltva)
+ self.tolto.setVisible(self.kitiltva)
+ for g in (self.uj_hiba, self.uj_otlet):
+ g.setEnabled(self.szabad())
+ self.reszletek.valasz_doboz.setEnabled(self.szabad())
+ for g in self.fulek.widget(1).findChildren(QPushButton):
+ g.setEnabled(self.szabad())
+ self.ertekeles_lap._gomb_frissit()
+
+ def _tiltas_kesz(self, adat):
+ self.kitiltva = bool(adat.get("kitiltva"))
+ if self.kitiltva:
+ meddig = f"eddig: {ido(adat['lejar'])}" if adat.get("lejar") else "véglegesen"
+ self.tiltas_sav.setText(
+ f"Ez a gép {meddig} ki van tiltva a Visszajelzés használatából a szabályok megsértése miatt. "
+ f"Ok: {adat.get('ok') or '-'}\n\nA RaveOS Center minden más része ugyanúgy működik."
+ )
+ self._allapot_frissit()
+ if not self.kitiltva:
+ self.lista_frissit()
+ if self.fulek.currentIndex() == 1:
+ self._otletek_frissit()
+ elif self.fulek.currentIndex() == 2:
+ self._ertekeles_frissit()
+
+ def megjelenik(self, *_):
+ self._allapot_frissit()
+ if not self.elfogadva():
+ QTimer.singleShot(200, self.ismerteto)
+ gyorsitott("jegy_allapot", jegyek_api.allapot, kesz=self._tiltas_kesz, hiba=self._hibat_mutat, kor=60)
+
+ def ful(self, azon):
+ self.fulsor.aktiv(azon)
+ self.fulek.setCurrentIndex({"jegyeim": 0, "otletek": 1, "ertekeles": 2}[azon])
+ if azon == "otletek":
+ self._otletek_frissit()
+ elif azon == "ertekeles":
+ self._ertekeles_frissit()
+
+ def _ertekeles_frissit(self):
+ def kesz(adat):
+ self.hiba.hide()
+ self.ertekeles_lap.betolt(adat)
+
+ hatterben(jegyek_api.ertekeles, kesz=kesz, hiba=self._hibat_mutat)
+
+ def jegy_eltunt(self):
+ self.reszletek.jegy_id = None
+ self.lista.setCurrentRow(-1)
+ self.jobb.setCurrentWidget(self.ures)
+ self.lista_frissit()
+
+ def _jegy_hiba(self, uzenet):
+ if uzenet == "nincs_jegy":
+ self.jegy_eltunt()
+ else:
+ self._hibat_mutat(uzenet)
+
+ def _hibat_mutat(self, uzenet):
+ self.hiba.setText(jegyek_api.HIBAUZENETEK.get(uzenet, f"Hiba történt: {uzenet}"))
+ self.hiba.show()
+
+ def _jelveny_frissit(self, jegyek):
+ self.kozpont.jelveny(AZON, sum(1 for j in jegyek if j["uj_valasz"]))
+
+ def lista_frissit(self, kivalaszt=None):
+ def kesz(jegyek):
+ self.hiba.hide()
+ self._jelveny_frissit(jegyek)
+ aktualis = kivalaszt or self.reszletek.jegy_id
+ if jegyek == self.jegyek and self.lista_betoltve and not kivalaszt:
+ return
+ self.jegyek = jegyek
+ self.lista_betoltve = True
+ self.lista.blockSignals(True)
+ self.lista.clear()
+ for j in jegyek:
+ elem = QListWidgetItem()
+ elem.setData(Qt.ItemDataRole.UserRole, j["id"])
+ sor = JegySor(j)
+ elem.setSizeHint(sor.sizeHint())
+ self.lista.addItem(elem)
+ self.lista.setItemWidget(elem, sor)
+ if j["id"] == aktualis:
+ self.lista.setCurrentItem(elem)
+ self.lista.blockSignals(False)
+ if self.jobb.currentWidget() is not self.urlap_gorgeto and not self.lista.currentItem():
+ self.ures_szoveg.setText(
+ "Még nincs jegyed. Ha hibát találtál, vagy ötleted van, írj nekünk a fenti gombokkal."
+ if not jegyek
+ else "Válassz egy jegyet a listából."
+ )
+ self.jobb.setCurrentWidget(self.ures)
+
+ hatterben(jegyek_api.jegyeim, kesz=kesz, hiba=self._hibat_mutat)
+
+ def _kivalaszt(self, elem, _elozo=None):
+ if not elem:
+ return
+ jegy_id = elem.data(Qt.ItemDataRole.UserRole)
+
+ def kesz(jegy):
+ aktualis = self.lista.currentItem()
+ if not aktualis or aktualis.data(Qt.ItemDataRole.UserRole) != jegy_id:
+ return
+ self.reszletek.betolt(jegy)
+ self.jobb.setCurrentWidget(self.reszletek)
+ self.lista_frissit_csendben(jegy_id)
+
+ hatterben(jegyek_api.jegy, jegy_id, kesz=kesz, hiba=self._jegy_hiba)
+
+ def lista_frissit_csendben(self, jegy_id):
+ for j in self.jegyek:
+ if j["id"] == jegy_id and j["uj_valasz"]:
+ self.lista_frissit(jegy_id)
+ return
+
+ def urlap_nyit(self, tipus):
+ self.ful("jegyeim")
+ self.lista.setCurrentRow(-1)
+ self.urlap.nyit(tipus)
+ self.jobb.setCurrentWidget(self.urlap_gorgeto)
+
+ def urlap_bezar(self):
+ self.jobb.setCurrentWidget(self.ures)
+ self.lista_frissit()
+
+ def bekuldve(self, jegy):
+ self.lista_frissit(jegy["id"])
+ self._kivalaszt_id(jegy["id"])
+
+ def _kivalaszt_id(self, jegy_id):
+ hatterben(
+ jegyek_api.jegy,
+ jegy_id,
+ kesz=lambda j: (self.reszletek.betolt(j), self.jobb.setCurrentWidget(self.reszletek)),
+ hiba=self._jegy_hiba,
+ )
+
+ def _otletek_frissit(self):
+ def kesz(otletek):
+ self.hiba.hide()
+ while self.otlet_doboz.count():
+ w = self.otlet_doboz.takeAt(0).widget()
+ if w:
+ w.deleteLater()
+ if not otletek:
+ self.otlet_doboz.addWidget(cimke("Még nincs közzétett ötlet.", "halvany"))
+ for o in otletek:
+ kartya = OtletKartya(o, self._szavaz)
+ for g in kartya.findChildren(QPushButton):
+ g.setEnabled(self.szabad())
+ self.otlet_doboz.addWidget(kartya)
+
+ hatterben(jegyek_api.otletek, kesz=kesz, hiba=self._hibat_mutat)
+
+ def _szavaz(self, jegy_id, tamogat, g):
+ g.setEnabled(False)
+ hatterben(jegyek_api.szavaz, jegy_id, tamogat, kesz=lambda _: self._otletek_frissit(), hiba=self._hibat_mutat)
+
+
+OLDAL = Visszajelzes
diff --git a/raveos-center/raveos_center/proton.py b/raveos-center/raveos_center/proton.py
new file mode 100644
index 0000000..bff9649
--- /dev/null
+++ b/raveos-center/raveos_center/proton.py
@@ -0,0 +1,125 @@
+import hashlib
+import json
+import re
+import shutil
+import tarfile
+import tempfile
+import urllib.request
+from pathlib import Path
+
+from raveos_center import VERZIO
+
+KIADAS = "https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest"
+STEAM_HELYEK = [
+ Path.home() / ".steam" / "root",
+ Path.home() / ".local" / "share" / "Steam",
+ Path.home() / ".var" / "app" / "com.valvesoftware.Steam" / "data" / "Steam",
+]
+NEV_MINTA = re.compile(r"^GE-Proton([0-9]+)-([0-9]+)(-x86_64)?$")
+FEJLEC = {"User-Agent": f"RaveOS-Center/{VERZIO}"}
+
+
+def celmappak():
+ lista = []
+ latott = set()
+ for hely in STEAM_HELYEK:
+ if not hely.is_dir():
+ continue
+ valodi = hely.resolve()
+ if valodi in latott:
+ continue
+ latott.add(valodi)
+ lista.append(valodi / "compatibilitytools.d")
+ return lista
+
+
+def _sorrend(nevek):
+ return sorted(nevek, key=lambda n: [int(x) for x in NEV_MINTA.match(n).groups()[:2]], reverse=True)
+
+
+def _mappaban(mappa):
+ if not mappa.is_dir():
+ return []
+ return _sorrend(p.name for p in mappa.iterdir() if p.is_dir() and not p.is_symlink() and NEV_MINTA.match(p.name))
+
+
+def telepitett():
+ nevek = set()
+ for mappa in celmappak():
+ nevek.update(_mappaban(mappa))
+ return _sorrend(nevek)
+
+
+def regiek():
+ return [(mappa, nev) for mappa in celmappak() for nev in _mappaban(mappa)[1:]]
+
+
+def _lekeres(url, ido=30):
+ return urllib.request.urlopen(urllib.request.Request(url, headers=FEJLEC), timeout=ido)
+
+
+def legujabb():
+ with _lekeres(KIADAS) as valasz:
+ adat = json.load(valasz)
+ nev = adat.get("tag_name", "")
+ if not NEV_MINTA.match(nev):
+ raise RuntimeError("ismeretlen kiadásnév")
+ eszkozok = {e["name"]: e for e in adat.get("assets", [])}
+ for mappa in (f"{nev}-x86_64", nev):
+ tar = eszkozok.get(f"{mappa}.tar.gz")
+ osszeg = eszkozok.get(f"{mappa}.sha512sum")
+ if tar and osszeg:
+ return {
+ "nev": mappa,
+ "url": tar["browser_download_url"],
+ "meret": tar.get("size", 0),
+ "osszeg_url": osszeg["browser_download_url"],
+ }
+ raise RuntimeError("a kiadásból hiányzik a csomag vagy az ellenőrzőösszeg")
+
+
+def telepit(kiadas, haladas):
+ mappak = celmappak()
+ if not mappak:
+ raise RuntimeError("nem található Steam, előbb indítsd el egyszer a Steamet")
+ with _lekeres(kiadas["osszeg_url"]) as valasz:
+ vart = valasz.read().decode().split()[0].lower()
+ if not re.fullmatch(r"[0-9a-f]{128}", vart):
+ raise RuntimeError("érvénytelen ellenőrzőösszeg")
+ mappak[0].mkdir(parents=True, exist_ok=True)
+ with tempfile.NamedTemporaryFile(dir=mappak[0], suffix=".tar.gz") as ideiglenes:
+ hasito = hashlib.sha512()
+ letoltve = 0
+ with _lekeres(kiadas["url"], 60) as valasz:
+ while darab := valasz.read(1 << 20):
+ ideiglenes.write(darab)
+ hasito.update(darab)
+ letoltve += len(darab)
+ if kiadas["meret"]:
+ haladas(min(90, int(letoltve * 90 / kiadas["meret"])))
+ ideiglenes.flush()
+ if hasito.hexdigest() != vart:
+ raise RuntimeError("a letöltött fájl ellenőrzőösszege nem egyezik")
+ for mappa in mappak:
+ mappa.mkdir(parents=True, exist_ok=True)
+ cel = mappa / kiadas["nev"]
+ if cel.is_symlink() or cel.is_file():
+ cel.unlink()
+ elif cel.exists():
+ shutil.rmtree(cel)
+ with tarfile.open(ideiglenes.name, "r:gz") as tar:
+ tagok = tar.getmembers()
+ if any(t.name != kiadas["nev"] and not t.name.startswith(kiadas["nev"] + "/") for t in tagok):
+ raise RuntimeError("váratlan tartalom a csomagban")
+ tar.extractall(mappa, members=tagok, filter="data")
+ haladas(100)
+ return kiadas["nev"]
+
+
+def torol(nev, mappak=None):
+ if not NEV_MINTA.match(nev):
+ raise ValueError("érvénytelen név")
+ for mappa in mappak or celmappak():
+ cel = mappa / nev
+ if cel.is_dir() and not cel.is_symlink():
+ shutil.rmtree(cel)
diff --git a/raveos-center/raveos_center/rendszerinfo.py b/raveos-center/raveos_center/rendszerinfo.py
new file mode 100644
index 0000000..b526670
--- /dev/null
+++ b/raveos-center/raveos_center/rendszerinfo.py
@@ -0,0 +1,240 @@
+import json
+import os
+import platform
+import re
+import shutil
+import subprocess
+import threading
+import time
+from pathlib import Path
+
+
+def _futtat(parancs, ido=60):
+ try:
+ kimenet = subprocess.run(parancs, capture_output=True, text=True, timeout=ido)
+ except (OSError, subprocess.TimeoutExpired):
+ return None
+ return kimenet
+
+
+def os_release():
+ adat = {}
+ try:
+ for sor in Path("/etc/os-release").read_text().splitlines():
+ if "=" in sor:
+ k, _, v = sor.partition("=")
+ adat[k] = v.strip().strip('"')
+ except OSError:
+ pass
+ return adat
+
+
+def raveos_verzio():
+ adat = os_release()
+ nev = adat.get("NAME", "RaveOS")
+ verzio = adat.get("VERSION_ID") or adat.get("BUILD_ID") or ""
+ return f"{nev} {verzio}".strip()
+
+
+def kernel():
+ return platform.release()
+
+
+def asztal():
+ nev = os.environ.get("XDG_CURRENT_DESKTOP") or os.environ.get("DESKTOP_SESSION") or "ismeretlen"
+ nev = nev.split(":")[-1]
+ tipus = os.environ.get("XDG_SESSION_TYPE", "")
+ return f"{nev} ({tipus.capitalize()})" if tipus else nev
+
+
+def cpu():
+ try:
+ for sor in Path("/proc/cpuinfo").read_text().splitlines():
+ if sor.startswith("model name"):
+ nev = sor.split(":", 1)[1].strip()
+ return re.sub(r"\s+", " ", re.sub(r"\((R|TM)\)|CPU|Processor", "", nev)).strip()
+ except OSError:
+ pass
+ return platform.processor() or "ismeretlen"
+
+
+def gpuk():
+ lista = []
+ kimenet = _futtat(["lspci", "-mm"])
+ if not kimenet or kimenet.returncode != 0:
+ return lista
+ for sor in kimenet.stdout.splitlines():
+ mezok = re.findall(r'"([^"]*)"', sor)
+ if len(mezok) >= 3 and re.search(r"VGA|3D|Display", mezok[0]):
+ gyarto = mezok[1].replace("Advanced Micro Devices, Inc. ", "").replace(" Corporation", "")
+ gyarto = gyarto.replace("[AMD/ATI]", "AMD").strip()
+ nev = mezok[2]
+ talalat = re.search(r"\[([^\]]+)\]", nev)
+ lista.append(f"{gyarto} {talalat.group(1) if talalat else nev}".strip())
+ return lista
+
+
+def gpu_driverek():
+ driverek = []
+ for kartya in sorted(Path("/sys/class/drm").glob("card[0-9]")):
+ try:
+ nev = (kartya / "device" / "driver").resolve().name
+ except OSError:
+ continue
+ if nev and nev not in driverek:
+ driverek.append(nev)
+ return driverek
+
+
+def memoria():
+ try:
+ for sor in Path("/proc/meminfo").read_text().splitlines():
+ if sor.startswith("MemTotal:"):
+ kb = int(sor.split()[1])
+ return f"{kb / 1024 / 1024:.1f} GB"
+ except (OSError, ValueError):
+ pass
+ return "ismeretlen"
+
+
+def fajlrendszer(hely="/"):
+ kimenet = _futtat(["findmnt", "-no", "FSTYPE", hely])
+ if kimenet and kimenet.returncode == 0 and kimenet.stdout.strip():
+ return kimenet.stdout.strip().splitlines()[0]
+ try:
+ for sor in reversed(Path("/proc/mounts").read_text().splitlines()):
+ mezok = sor.split()
+ if len(mezok) > 2 and mezok[1] == hely:
+ return mezok[2]
+ except OSError:
+ pass
+ return "ismeretlen"
+
+
+def uzemido():
+ try:
+ mp = int(float(Path("/proc/uptime").read_text().split()[0]))
+ except (OSError, ValueError):
+ return "ismeretlen"
+ nap, mp = divmod(mp, 86400)
+ ora, mp = divmod(mp, 3600)
+ perc = mp // 60
+ if nap:
+ return f"{nap} nap {ora} óra"
+ if ora:
+ return f"{ora} óra {perc} perc"
+ return f"{perc} perc"
+
+
+def lemez(hely="/"):
+ hasznalat = shutil.disk_usage(hely)
+ return hasznalat.used, hasznalat.total
+
+
+UJRAINDITOS = re.compile(
+ r"^(linux|linux-[a-z0-9-]+|nvidia.*|mesa|lib32-mesa|amd-ucode|intel-ucode|systemd|glibc|dbus|dbus-broker)$"
+)
+
+_frissites_zar = threading.Lock()
+_frissites_tar = {"ido": 0.0, "lista": None}
+FRISSITES_TAR_IDO = 600
+
+
+def _pacman_meretek(nevek):
+ db = os.environ.get("CHECKUPDATES_DB") or f"{os.environ.get('TMPDIR', '/tmp')}/checkup-db-{os.getuid()}"
+ kimenet = _futtat(
+ ["pacman", "-Sup", "--dbpath", db, "--logfile", "/dev/null", "--print-format", "%n %s"], ido=60
+ )
+ meretek = {}
+ if kimenet and kimenet.returncode == 0:
+ for sor in kimenet.stdout.splitlines():
+ r = sor.split()
+ if len(r) == 2 and r[1].isdigit():
+ meretek[r[0]] = int(r[1])
+ return {n: meretek.get(n) for n in nevek}
+
+
+def _meret_szoveg(szoveg):
+ talalat = re.match(r"([\d.,]+)\s*([kMG]?B)", szoveg.replace("\xa0", " "))
+ if not talalat:
+ return None
+ szam = float(talalat.group(1).replace(",", "."))
+ return int(szam * {"B": 1, "kB": 1e3, "MB": 1e6, "GB": 1e9}[talalat.group(2)])
+
+
+def _frissitesek_lekerdez():
+ csomagok = []
+ if not shutil.which("checkupdates"):
+ raise RuntimeError("a checkupdates nem elérhető (pacman-contrib)")
+ pac = _futtat(["checkupdates", "--nocolor"], ido=180)
+ if not pac or pac.returncode not in (0, 2):
+ raise RuntimeError("a pacman frissítések ellenőrzése nem sikerült")
+ if pac.returncode == 0:
+ for sor in pac.stdout.splitlines():
+ r = sor.split()
+ if len(r) >= 4:
+ csomagok.append({"nev": r[0], "forras": "pacman", "regi": r[1], "uj": r[3], "meret": None})
+ meretek = _pacman_meretek([c["nev"] for c in csomagok])
+ for c in csomagok:
+ c["meret"] = meretek.get(c["nev"])
+ if shutil.which("yay"):
+ aur = _futtat(["yay", "-Qua", "--color", "never"], ido=180)
+ if aur and aur.returncode in (0, 1):
+ for sor in aur.stdout.splitlines():
+ r = sor.split()
+ if len(r) >= 4 and r[2] == "->":
+ csomagok.append({"nev": r[0], "forras": "aur", "regi": r[1], "uj": r[3], "meret": None})
+ if shutil.which("flatpak"):
+ fp = _futtat(
+ ["flatpak", "remote-ls", "--updates", "--columns=application,version,download-size"], ido=180
+ )
+ if fp and fp.returncode == 0:
+ for sor in fp.stdout.splitlines():
+ r = sor.split("\t")
+ if r and r[0] and r[0] != "Application ID":
+ csomagok.append({
+ "nev": r[0],
+ "forras": "flatpak",
+ "regi": "",
+ "uj": r[1] if len(r) > 1 else "",
+ "meret": _meret_szoveg(r[2]) if len(r) > 2 else None,
+ })
+ for c in csomagok:
+ c["ujrainditas"] = c["forras"] == "pacman" and bool(UJRAINDITOS.match(c["nev"]))
+ return csomagok
+
+
+def frissitesek(friss=False):
+ with _frissites_zar:
+ tar = _frissites_tar
+ if not friss and tar["lista"] is not None and time.monotonic() - tar["ido"] < FRISSITES_TAR_IDO:
+ return [dict(c) for c in tar["lista"]]
+ lista = _frissitesek_lekerdez()
+ tar["lista"] = lista
+ tar["ido"] = time.monotonic()
+ tar["mikor"] = time.time()
+ return [dict(c) for c in lista]
+
+
+def frissites_ellenorizve():
+ return _frissites_tar.get("mikor")
+
+
+def snapper_elerheto():
+ return bool(shutil.which("snapper")) and Path("/etc/snapper/configs/root").exists()
+
+
+def utolso_snapshot():
+ if not shutil.which("snapper"):
+ return None
+ kimenet = _futtat(["snapper", "--jsonout", "-c", "root", "list"], ido=30)
+ if not kimenet or kimenet.returncode != 0:
+ return None
+ try:
+ lista = json.loads(kimenet.stdout).get("root", [])
+ except ValueError:
+ return None
+ lista = [s for s in lista if s.get("number", 0) != 0]
+ if not lista:
+ return {}
+ return lista[-1]
diff --git a/raveos-center/raveos_center/snapshotok.py b/raveos-center/raveos_center/snapshotok.py
new file mode 100644
index 0000000..f4bd097
--- /dev/null
+++ b/raveos-center/raveos_center/snapshotok.py
@@ -0,0 +1,116 @@
+import json
+import re
+import subprocess
+from pathlib import Path
+
+from raveos_center import rendszerinfo
+
+SNAP_PAC = Path("/usr/share/libalpm/hooks/05-snap-pac-pre.hook")
+TIPUSOK = {"pre": "csomagművelet előtt", "post": "csomagművelet után", "single": "kézi"}
+
+
+def _futtat(parancs, ido=30):
+ try:
+ return subprocess.run(parancs, capture_output=True, text=True, timeout=ido)
+ except (OSError, subprocess.TimeoutExpired):
+ return None
+
+
+def fajlrendszer():
+ kimenet = _futtat(["findmnt", "-no", "FSTYPE", "/"])
+ return kimenet.stdout.strip() if kimenet and kimenet.returncode == 0 else ""
+
+
+def gyoker_alvolumen():
+ kimenet = _futtat(["findmnt", "-no", "OPTIONS", "/"])
+ if not kimenet:
+ return ""
+ for opcio in kimenet.stdout.strip().split(","):
+ if opcio.startswith("subvol="):
+ return opcio[len("subvol="):]
+ return ""
+
+
+def snapshots_csatolva():
+ kimenet = _futtat(["findmnt", "-no", "SOURCE", "/.snapshots"])
+ return bool(kimenet and kimenet.returncode == 0 and kimenet.stdout.strip())
+
+
+def snap_pac():
+ return SNAP_PAC.exists()
+
+
+def _elem(nyers):
+ tipus = nyers.get("type", "single")
+ adat = nyers.get("userdata") or {}
+ return {
+ "szam": nyers.get("number", 0),
+ "datum": (nyers.get("date") or "")[:16],
+ "tipus": tipus,
+ "tipus_nev": "telepítés" if adat.get("important") == "yes" and tipus == "single" else TIPUSOK.get(tipus, tipus),
+ "leiras": nyers.get("description") or "",
+ "pre_szam": nyers.get("pre-number"),
+ "aktiv": bool(nyers.get("active")),
+ }
+
+
+def allapot():
+ fs = fajlrendszer()
+ adat = {
+ "fajlrendszer": fs,
+ "snapper": rendszerinfo.snapper_elerheto(),
+ "snap_pac": snap_pac(),
+ "alvolumen": gyoker_alvolumen(),
+ "csatolva": snapshots_csatolva(),
+ "lista": [],
+ "hiba": "",
+ }
+ if fs != "btrfs" or not adat["snapper"]:
+ return adat
+ kimenet = _futtat(["snapper", "--jsonout", "-c", "root", "list"])
+ if not kimenet or kimenet.returncode != 0:
+ adat["hiba"] = (kimenet.stderr.strip() if kimenet else "") or "A snapshotok listája nem olvasható."
+ return adat
+ try:
+ nyers = json.loads(kimenet.stdout).get("root", [])
+ except ValueError:
+ adat["hiba"] = "A snapper válasza nem értelmezhető."
+ return adat
+ adat["lista"] = [_elem(n) for n in reversed(nyers) if n.get("number", 0) != 0]
+ return adat
+
+
+def ervenyes_leiras(szoveg):
+ return 1 <= len(szoveg) <= 80 and all(c.isalnum() or c.isspace() or c in "._:,()-" for c in szoveg)
+
+
+def kernel_valtozas(szam):
+ gyoker = Path(f"/.snapshots/{int(szam)}/snapshot")
+ valtozasok = []
+ try:
+ if not (gyoker / "usr/lib/modules").is_dir():
+ return None
+ pkgbase_fajlok = list(gyoker.glob("usr/lib/modules/*/pkgbase"))
+ except OSError:
+ return None
+ for pb in pkgbase_fajlok:
+ try:
+ nev = pb.read_text().strip()
+ except OSError:
+ continue
+ if not re.fullmatch(r"[a-z0-9][a-z0-9._+-]*", nev):
+ continue
+ regi = _futtat(["pacman", "--dbpath", str(gyoker / "var/lib/pacman"), "-Q", nev])
+ most = _futtat(["pacman", "-Q", nev])
+ regi_v = regi.stdout.split()[1] if regi and regi.returncode == 0 and len(regi.stdout.split()) > 1 else ""
+ most_v = most.stdout.split()[1] if most and most.returncode == 0 and len(most.stdout.split()) > 1 else ""
+ if regi_v and regi_v != most_v:
+ cache = any(
+ not p.name.endswith(".sig") for p in Path("/var/cache/pacman/pkg").glob(f"{nev}-{regi_v}-*.pkg.tar.*")
+ )
+ valtozasok.append({"nev": nev, "regi": regi_v, "most": most_v, "cache": cache})
+ return valtozasok
+
+
+def snapper_telepitheto(adat):
+ return adat["fajlrendszer"] == "btrfs" and not adat["snapper"] and adat["alvolumen"] == "/@" and adat["csatolva"]
diff --git a/raveos-center/raveos_center/stilus.py b/raveos-center/raveos_center/stilus.py
new file mode 100644
index 0000000..383e736
--- /dev/null
+++ b/raveos-center/raveos_center/stilus.py
@@ -0,0 +1,436 @@
+HATTER = "#1a1b1d"
+FELULET = "#232427"
+AKTIV = "#2f3034"
+KERET = "#34363a"
+KERET_EROS = "#44464b"
+SZOVEG = "#e8e8e6"
+SZOVEG_MASOD = "#b7b9bd"
+SZOVEG_HALVANY = "#9a9ca1"
+ZOLD = "#62a052"
+ZOLD_HOVER = "#7dbb6c"
+ZOLDON_SZOVEG = "#1a1b1d"
+SARGA = "#d9a441"
+SARGA_PANEL = "#2e2a20"
+
+BETU = "Noto Sans"
+CIM_BETU = "Noto Sans"
+MONO = "Noto Sans Mono"
+
+QSS = f"""
+QWidget {{
+ background: {HATTER};
+ color: {SZOVEG};
+ font-family: "{BETU}";
+ font-size: 14px;
+}}
+QLabel {{
+ background: transparent;
+}}
+QScrollArea, QScrollArea > QWidget > QWidget {{
+ background: transparent;
+ border: none;
+}}
+QScrollBar:vertical {{
+ background: transparent;
+ width: 10px;
+ margin: 0;
+}}
+QScrollBar::handle:vertical {{
+ background: {KERET_EROS};
+ min-height: 32px;
+}}
+QScrollBar::add-line, QScrollBar::sub-line {{
+ height: 0;
+}}
+QScrollBar::add-page, QScrollBar::sub-page {{
+ background: transparent;
+}}
+#oldalsav {{
+ background: {FELULET};
+ border-right: 1px solid {KERET};
+}}
+#oldalsav QWidget {{
+ background: transparent;
+}}
+#menupont {{
+ background: transparent;
+ border: none;
+ color: {SZOVEG_MASOD};
+ text-align: left;
+ padding: 0 12px;
+ min-height: 44px;
+ font-weight: 500;
+}}
+#menupont:hover {{
+ background: {AKTIV};
+}}
+#menupont[aktiv="true"] {{
+ background: {AKTIV};
+ color: {SZOVEG};
+ font-weight: 600;
+}}
+#jelveny, #oldalsav #jelveny {{
+ background: {ZOLD};
+ color: {ZOLDON_SZOVEG};
+ font-family: "{MONO}";
+ font-size: 11px;
+ padding: 1px 6px;
+}}
+#markanev {{
+ font-family: "{CIM_BETU}";
+ font-size: 24px;
+ font-weight: 700;
+ letter-spacing: 1px;
+}}
+#markaalcim, #lablec {{
+ font-family: "{MONO}";
+ font-size: 11px;
+ color: {SZOVEG_HALVANY};
+ letter-spacing: 2px;
+}}
+#eyebrow {{
+ font-family: "{MONO}";
+ font-size: 12px;
+ color: {ZOLD};
+ letter-spacing: 2px;
+}}
+#h1 {{
+ font-family: "{CIM_BETU}";
+ font-size: 44px;
+ font-weight: 700;
+ letter-spacing: 1px;
+}}
+#h2 {{
+ font-family: "{CIM_BETU}";
+ font-size: 22px;
+ font-weight: 700;
+ letter-spacing: 1px;
+}}
+#kartya {{
+ background: {FELULET};
+ border: 1px solid {KERET};
+}}
+#kartya QWidget {{
+ background: transparent;
+}}
+#kartya QPushButton {{
+ background: transparent;
+}}
+#kartya QPushButton:hover {{
+ background: {AKTIV};
+}}
+#kartya QPushButton[fo="true"] {{
+ background: {ZOLD};
+ color: {ZOLDON_SZOVEG};
+}}
+#kartya QPushButton[fo="true"]:hover {{
+ background: {ZOLD_HOVER};
+}}
+#kartya QPushButton:disabled, #kartya QPushButton[fo="true"]:disabled {{
+ background: {KERET};
+ color: {SZOVEG_HALVANY};
+ border-color: {KERET};
+}}
+#kartyacim {{
+ font-size: 12px;
+ color: {SZOVEG_HALVANY};
+ letter-spacing: 1px;
+}}
+#nagyszam {{
+ font-family: "{CIM_BETU}";
+ font-size: 30px;
+ font-weight: 700;
+}}
+#nagyszam[zold="true"] {{
+ color: {ZOLD};
+}}
+#masodlagos {{
+ font-size: 13px;
+ color: {SZOVEG_MASOD};
+}}
+#halvany {{
+ font-size: 13px;
+ color: {SZOVEG_HALVANY};
+}}
+#mono {{
+ font-family: "{MONO}";
+ font-size: 13px;
+}}
+#monohalvany {{
+ font-family: "{MONO}";
+ font-size: 13px;
+ color: {SZOVEG_HALVANY};
+}}
+#monozold {{
+ font-family: "{MONO}";
+ font-size: 12px;
+ color: {ZOLD};
+}}
+QPushButton {{
+ min-height: 44px;
+ padding: 0 20px;
+ background: transparent;
+ color: {SZOVEG};
+ border: 1px solid {KERET_EROS};
+ font-weight: 500;
+}}
+QPushButton:hover {{
+ background: {AKTIV};
+}}
+QPushButton:disabled {{
+ color: {SZOVEG_HALVANY};
+ border-color: {KERET};
+}}
+QPushButton[fo="true"] {{
+ background: {ZOLD};
+ color: {ZOLDON_SZOVEG};
+ border: none;
+ font-weight: 600;
+}}
+QPushButton[fo="true"]:hover {{
+ background: {ZOLD_HOVER};
+}}
+QPushButton[fo="true"]:disabled {{
+ background: {KERET};
+ color: {SZOVEG_HALVANY};
+}}
+QPushButton[kicsi="true"] {{
+ min-height: 32px;
+ padding: 0 12px;
+ font-size: 12px;
+}}
+#csempe {{
+ background: {FELULET};
+ border: 1px solid {KERET};
+ text-align: left;
+ min-height: 120px;
+ padding: 0;
+}}
+#csempe:hover {{
+ border-color: {ZOLD};
+}}
+#csempe:disabled {{
+ border-color: {KERET};
+}}
+#csempe:disabled QLabel {{
+ color: {SZOVEG_HALVANY};
+}}
+#sav {{
+ background: {KERET};
+ border: none;
+}}
+#sav::chunk {{
+ background: {ZOLD};
+}}
+#elvalaszto {{
+ background: {AKTIV};
+ max-height: 1px;
+ min-height: 1px;
+}}
+#link {{
+ color: {ZOLD};
+ font-size: 13px;
+ font-weight: 600;
+}}
+QLineEdit, QTextEdit, QPlainTextEdit {{
+ background: {HATTER};
+ border: 1px solid {KERET_EROS};
+ padding: 8px 10px;
+ selection-background-color: {ZOLD};
+ selection-color: {ZOLDON_SZOVEG};
+}}
+QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {{
+ border-color: {ZOLD};
+}}
+QListWidget {{
+ background: {FELULET};
+ border: 1px solid {KERET};
+ outline: none;
+}}
+QListWidget::item {{
+ border-bottom: 1px solid {KERET};
+}}
+QListWidget::item:selected {{
+ background: {AKTIV};
+}}
+QCheckBox {{
+ background: transparent;
+ spacing: 10px;
+}}
+QCheckBox::indicator {{
+ width: 18px;
+ height: 18px;
+ border: 1px solid {KERET_EROS};
+ background: {HATTER};
+}}
+QCheckBox::indicator:checked {{
+ background: {ZOLD};
+ border-color: {ZOLD};
+}}
+#ful {{
+ background: transparent;
+ border: none;
+ border-bottom: 2px solid transparent;
+ color: {SZOVEG_MASOD};
+ min-height: 40px;
+ padding: 0 4px;
+ margin-right: 20px;
+ font-weight: 500;
+}}
+#ful:hover {{
+ color: {SZOVEG};
+ background: transparent;
+}}
+#ful[aktiv="true"] {{
+ color: {SZOVEG};
+ border-bottom-color: {ZOLD};
+ font-weight: 600;
+}}
+#fulsor {{
+ border-bottom: 1px solid {KERET};
+}}
+#allapotcimke {{
+ font-family: "{MONO}";
+ font-size: 11px;
+ padding: 2px 8px;
+ border: 1px solid {KERET_EROS};
+ color: {SZOVEG_MASOD};
+}}
+#allapotcimke[szin="zold"] {{
+ border-color: {ZOLD};
+ color: {ZOLD};
+}}
+#allapotcimke[szin="sarga"] {{
+ border-color: {SARGA};
+ color: {SARGA};
+}}
+#figyelem {{
+ background: {SARGA_PANEL};
+ border: 1px solid {SARGA};
+ color: #f0dcb0;
+ padding: 10px 12px;
+}}
+#uzenet {{
+ background: {FELULET};
+ border: 1px solid {KERET};
+}}
+#uzenet[csapat="true"] {{
+ border-left: 3px solid {ZOLD};
+}}
+#uzenet QWidget {{
+ background: transparent;
+}}
+QTableWidget {{
+ background: {FELULET};
+ border: 1px solid {KERET};
+ gridline-color: {KERET};
+ outline: none;
+}}
+QTableWidget::item {{
+ border-bottom: 1px solid {AKTIV};
+ padding: 0 8px;
+}}
+QHeaderView::section {{
+ background: {FELULET};
+ color: {SZOVEG_HALVANY};
+ border: none;
+ border-bottom: 1px solid {KERET};
+ padding: 8px;
+ font-size: 12px;
+ letter-spacing: 1px;
+}}
+QTableWidget::indicator, QTableView::indicator {{
+ width: 16px;
+ height: 16px;
+ border: 1px solid {KERET_EROS};
+ background: {HATTER};
+}}
+QTableWidget::indicator:checked {{
+ background: {ZOLD};
+ border-color: {ZOLD};
+}}
+#naplo {{
+ background: #111214;
+ border: 1px solid {KERET};
+ font-family: "{MONO}";
+ font-size: 12px;
+ color: {SZOVEG_MASOD};
+}}
+#pill {{
+ background: transparent;
+ color: {SZOVEG_MASOD};
+ border: 1px solid {KERET_EROS};
+ min-height: 34px;
+ padding: 0 14px;
+ font-size: 13px;
+}}
+#pill:hover {{
+ background: {AKTIV};
+}}
+#pill[aktiv="true"] {{
+ background: {ZOLD};
+ color: {ZOLDON_SZOVEG};
+ border-color: {ZOLD};
+ font-weight: 600;
+}}
+#kezdobetu {{
+ background: {AKTIV};
+ border: 1px solid {KERET_EROS};
+ color: {ZOLD};
+ font-size: 20px;
+ font-weight: 700;
+}}
+#kartya #appsor {{
+ border-bottom: 1px solid {AKTIV};
+}}
+#kartya #appsor[utolso="true"] {{
+ border-bottom: none;
+}}
+#kartya #appsor:hover {{
+ background: #27282b;
+}}
+#kijelessav {{
+ background: {FELULET};
+ border-top: 1px solid {ZOLD};
+}}
+#kijelessav QWidget {{
+ background: transparent;
+}}
+#kijelessav QPushButton {{
+ border: 1px solid {KERET_EROS};
+}}
+#kijelessav QPushButton:hover {{
+ background: {AKTIV};
+}}
+#kijelessav QPushButton[fo="true"] {{
+ border: none;
+ background: {ZOLD};
+ color: {ZOLDON_SZOVEG};
+}}
+#kiemelt {{
+ border-left: 3px solid {ZOLD};
+}}
+QComboBox {{
+ background: {HATTER};
+ border: 1px solid {KERET_EROS};
+ padding: 4px 10px;
+ min-height: 26px;
+ font-size: 12px;
+}}
+QComboBox:focus {{
+ border-color: {ZOLD};
+}}
+QComboBox QAbstractItemView {{
+ background: {FELULET};
+ border: 1px solid {KERET_EROS};
+ selection-background-color: {AKTIV};
+ selection-color: {SZOVEG};
+ outline: none;
+}}
+QToolTip {{
+ background: {FELULET};
+ color: {SZOVEG};
+ border: 1px solid {KERET_EROS};
+ padding: 6px;
+}}
+"""