i forgot to commit for maaany hours. its pretty much done i think :D

This commit is contained in:
anne 2023-07-14 15:09:56 +02:00
parent 486f2dad71
commit e6e8f4c1ef
8 changed files with 405 additions and 168 deletions

1
.gitignore vendored
View file

@ -179,3 +179,4 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear # and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/

BIN
img/download_done.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

BIN
img/download_pending.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

BIN
img/download_update.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

View file

@ -12,7 +12,7 @@ except ImportError:
import ostools import ostools
import parsetools import parsetools
from thememanager import ThemeManagerWidget from theme_repo_manager import ThemeManagerWidget
from generic import RightClickList, RightClickTree, MultiTextDialog from generic import RightClickList, RightClickTree, MultiTextDialog
from dataobjs import pesterQuirk, PesterProfile, PesterHistory from dataobjs import pesterQuirk, PesterProfile, PesterHistory
from memos import TimeSlider, TimeInput from memos import TimeSlider, TimeInput
@ -1454,16 +1454,34 @@ class PesterOptions(QtWidgets.QDialog):
if not parent.randhandler.running: if not parent.randhandler.running:
self.randomscheck.setEnabled(False) self.randomscheck.setEnabled(False)
avail_themes = self.config.availableThemes()
self.themeBox = QtWidgets.QComboBox(self) self.themeBox = QtWidgets.QComboBox(self)
notheme = theme.name not in avail_themes def reset_themeBox():
for i, t in enumerate(avail_themes): avail_themes = self.config.availableThemes()
self.themeBox.addItem(t) PchumLog.debug("Resetting themeself.themeBox")
if (not notheme and t == theme.name) or (notheme and t == "pesterchum"): self.themeBox.clear()
self.themeBox.setCurrentIndex(i) notheme = theme.name not in avail_themes
for i, t in enumerate(avail_themes):
self.themeBox.addItem(t)
if (not notheme and t == theme.name) or (notheme and t == "pesterchum"):
self.themeBox.setCurrentIndex(i)
self.themeBox.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
)
reset_themeBox()
self.refreshtheme = QtWidgets.QPushButton("Refresh current theme", self) self.refreshtheme = QtWidgets.QPushButton("Refresh current theme", self)
self.refreshtheme.clicked.connect(parent.themeSelectOverride) self.refreshtheme.clicked.connect(parent.themeSelectOverride)
self.refreshtheme.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Minimum,
)
)
self.themeManager = ThemeManagerWidget(self.config) self.themeManager = ThemeManagerWidget(self.config)
self.themeManager.rebuilt.connect( reset_themeBox )
# This makes it so that the themeBox gets updated when a theme is installed or removed through the repository
self.ghostchum = QtWidgets.QCheckBox("Pesterdunk Ghostchum!!", self) self.ghostchum = QtWidgets.QCheckBox("Pesterdunk Ghostchum!!", self)
self.ghostchum.setChecked(self.config.ghostchum()) self.ghostchum.setChecked(self.config.ghostchum())
@ -1676,8 +1694,12 @@ class PesterOptions(QtWidgets.QDialog):
layout_theme = QtWidgets.QVBoxLayout(widget) layout_theme = QtWidgets.QVBoxLayout(widget)
layout_theme.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_theme.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
layout_theme.addWidget(QtWidgets.QLabel("Pick a Theme:")) layout_theme.addWidget(QtWidgets.QLabel("Pick a Theme:"))
layout_theme.addWidget(self.themeBox)
layout_theme.addWidget(self.refreshtheme) layout_theme_hbox = QtWidgets.QHBoxLayout()
layout_theme_hbox.addWidget(self.themeBox)
layout_theme_hbox.addWidget(self.refreshtheme)
layout_theme.addLayout(layout_theme_hbox)
layout_theme.addWidget(QtWidgets.QLabel("Get new themes:"))
layout_theme.addWidget(self.themeManager) layout_theme.addWidget(self.themeManager)
layout_theme.addWidget(self.ghostchum) layout_theme.addWidget(self.ghostchum)
self.pages.addWidget(widget) self.pages.addWidget(widget)

View file

@ -51,7 +51,13 @@ def validateDataDir():
logs = os.path.join(datadir, "logs") logs = os.path.join(datadir, "logs")
errorlogs = os.path.join(datadir, "errorlogs") errorlogs = os.path.join(datadir, "errorlogs")
backup = os.path.join(datadir, "backup") backup = os.path.join(datadir, "backup")
themes = os.path.join(datadir, "themes")
# ~lisanne Datadir/themes for repository themes
# Apparently everything checks this folder for themes already
# So hopefully im not plugging into an existng system on accident
js_pchum = os.path.join(datadir, "pesterchum.js") js_pchum = os.path.join(datadir, "pesterchum.js")
js_manifest = os.path.join(datadir, "manifest.js")
dirs = [datadir, profile, quirks, logs, errorlogs, backup] dirs = [datadir, profile, quirks, logs, errorlogs, backup]
for d in dirs: for d in dirs:
@ -59,9 +65,10 @@ def validateDataDir():
os.makedirs(d, exist_ok=True) os.makedirs(d, exist_ok=True)
# pesterchum.js # pesterchum.js
if not os.path.exists(js_pchum): for filepath in [js_pchum, js_manifest]:
with open(js_pchum, "w") as f: if not os.path.exists(filepath):
f.write("{}") with open(filepath, "w") as f:
f.write("{}")
def getDataDir(): def getDataDir():

363
theme_repo_manager.py Normal file
View file

@ -0,0 +1,363 @@
import os
import io
import json
import zipfile
import logging
from shutil import rmtree
from datetime import datetime
from ostools import getDataDir
try:
from PyQt6 import QtCore, QtGui, QtWidgets, QtNetwork
from PyQt6.QtGui import QAction
_flag_selectable = QtCore.Qt.TextInteractionFlag.TextSelectableByMouse
_flag_topalign = QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop
except ImportError:
print("PyQt5 fallback (thememanager.py)")
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
from PyQt5.QtWidgets import QAction
_flag_selectable = QtCore.Qt.TextSelectableByMouse
_flag_topalign = QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop
PchumLog = logging.getLogger("pchumLogger")
themeManager = None
# ~Lisanne
# This file has all the stuff needed to use a theme repository
# - ThemeManagerWidget, a GUI widget that lets the user install, update, & delete repository themes
# - ThemeManager, the class that the widget hooks up to. Handles fetching, downloading, installing, & keeps the manifest updated
# Manifest is a local file that tracks the metadata of themes downloaded from the repository
# Its a themename: {database entry} dict
class ThemeManager(QtCore.QObject):
# signals
theme_installed = QtCore.pyqtSignal(str) # theme name
zip_downloaded = QtCore.pyqtSignal(str, str) # theme name, zip location
database_refreshed = QtCore.pyqtSignal(dict) # self.manifest
manifest_updated = QtCore.pyqtSignal(dict) # self.manifest
errored = QtCore.pyqtSignal(str) # error_text
# variables
manifest = {}
database = {}
database_entries = {}
config = None
manifest_path = os.path.join(getDataDir(), "manifest.js")
NAManager = None
downloads = {}
def __init__(self, config):
super().__init__()
with open(self.manifest_path, 'r') as f:
self.manifest = json.load(f)
PchumLog.debug("Manifest.js loaded with: %s" % self.manifest)
self.config = config
self.NAManager = QtNetwork.QNetworkAccessManager()
self.NAManager.finished[QtNetwork.QNetworkReply].connect(self._on_reply)
self.validate_manifest()
self.refresh_database()
@QtCore.pyqtSlot()
def refresh_database(self):
PchumLog.debug("Refreshing theme repo database @ %s" % self.config.theme_repo_url())
promise = self.NAManager.get(QtNetwork.QNetworkRequest(QtCore.QUrl(self.config.theme_repo_url())))
def delete_theme(self, theme_name):
theme = self.manifest[theme_name]
directory = os.path.join(getDataDir(), 'themes', theme['name'])
if os.path.isdir(directory):
rmtree(directory)
self.manifest.pop(theme_name)
self.save_manifest()
self.manifest_updated.emit(self.manifest)
def save_manifest(self):
with open(self.manifest_path, 'w') as f:
json.dump(self.manifest, f)
PchumLog.debug("Saved manifes.js to %s" % self.manifest_path)
def validate_manifest(self):
all_themes = self.config.availableThemes()
for theme_name in self.manifest:
if not theme_name in all_themes:
PchumLog.warning("Theme %s from the manifest seems to have been deleted" % theme_name)
def download_theme(self, theme_name):
PchumLog.info("Downloading %s" % theme_name)
promise = self.NAManager.get(QtNetwork.QNetworkRequest(QtCore.QUrl(self.database_entries[theme_name]['download'])))
self.downloads[self.database_entries[theme_name]['download']] = self.database_entries[theme_name]
def has_update(self, theme_name):
if self.is_installed(theme_name) and theme_name in self.database_entries:
return self.manifest[theme_name]['version'] != self.database_entries[theme_name]['version']
return False
def is_installed(self, theme_name):
return theme_name in self.manifest
def is_database_valid(self):
return 'entries' in self.database and isinstance(self.database.get('entries'), list)
# using a slot here raises `ERROR - <class 'TypeError'>, connect() failed between finished(QNetworkReply*) and _on_reply()``
# maybe because of the underscore?
# @QtCore.pyqtSlot(QtNetwork.QNetworkReply)
def _on_reply(self, reply):
if reply.error() != QtNetwork.QNetworkReply.NetworkError.NoError:
PchumLog.error("An error occured contacting the repository: %s" % reply.error())
self.errored.emit("An error occured contacting the repository: %s" % reply.error())
print([reply.error()])
return
try:
original_url = reply.request().url().url()
# Check if zipfile or database fetch
if original_url in self.downloads:
theme = self.downloads[original_url]
self._handle_downloaded_zip(bytes(reply.readAll()), theme)
self.downloads.pop(original_url)
self.manifest[theme['name']] = theme
self.save_manifest()
self.manifest_updated.emit(self.manifest)
else:
as_json = bytes(reply.readAll()).decode("utf-8")
self.database = json.loads(as_json)
self.database_entries = {}
if not self.is_database_valid():
PchumLog.error('Incorrect database format, missing "entries"')
self.errored.emit('Incorrect database format, missing "entries"')
return
self.database_entries = {}
for dbitem in self.database['entries']:
self.database_entries[dbitem['name']] = dbitem
self.database_refreshed.emit(self.database)
except json.decoder.JSONDecodeError as e:
PchumLog.error("Could not decode JSON: %s" % e)
self.errored.emit("Could not decode JSON: %s" % e)
return
def _handle_downloaded_zip(self, zip_buffer, theme):
# ~liasnne TODO: i dont know if this is inside a thread or not so
# probably check to make sure it is
# when its not 5 am
directory = os.path.join(getDataDir(), 'themes', theme['name'])
with zipfile.ZipFile(io.BytesIO(zip_buffer)) as z:
if os.path.isdir(directory):
rmtree(directory)
# Deletes old files that have been removed in an update
z.extractall(directory)
class ThemeManagerWidget(QtWidgets.QWidget):
icons = None
config = None
rebuilt = QtCore.pyqtSignal()
def __init__(self, config, parent=None):
super().__init__(parent)
self.icons = [QtGui.QIcon("img/download_pending.png"), QtGui.QIcon("img/download_done.png"), QtGui.QIcon("img/download_update.png"), ]
self.config = config
global themeManager
if themeManager == None or not themeManager.is_database_valid():
themeManager = ThemeManager(config)
self.setupUI()
else:
self.setupUI()
self.rebuild()
themeManager.database_refreshed.connect(self._on_database_refreshed)
themeManager.manifest_updated.connect(self._on_database_refreshed)
def setupUI(self):
self.layout_main = QtWidgets.QVBoxLayout(self)
self.setLayout(self.layout_main)
# Search bar
# TODO: implement searching
# self.line_search = QtWidgets.QLineEdit()
# self.line_search.setPlaceholderText("Search for themes")
# self.layout_main.addWidget(self.line_search)
# Main layout
# [list of themes/results] | [selected theme details]
layout_hbox_list_and_details = QtWidgets.QHBoxLayout()
# This is the list of database themes
self.list_results = QtWidgets.QListWidget()
self.list_results.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Expanding,
)
)
self.list_results.itemClicked.connect(self._on_theme_selected)
layout_hbox_list_and_details.addWidget(self.list_results)
# This is the right side, has the install buttons & all the theme details of the selected item
layout_vbox_details = QtWidgets.QVBoxLayout()
# The theme details are inside a scroll container in case of small window
self.frame_scroll = QtWidgets.QScrollArea()
self.frame_scroll.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Expanding,
)
)
# The vbox that the detail labels will rest in
layout_vbox_scroll_insides = QtWidgets.QVBoxLayout()
# here starts the actual detail labels
# Selected theme's name
self.lbl_theme_name = QtWidgets.QLabel("Click a theme to get started")
self.lbl_theme_name.setTextInteractionFlags(_flag_selectable)
self.lbl_theme_name.setStyleSheet("QLabel { font-size: 16px; font-weight:bold;}")
self.lbl_theme_name.setWordWrap(False)
layout_vbox_scroll_insides.addWidget(self.lbl_theme_name)
# Author name
self.lbl_author_name = QtWidgets.QLabel("")
self.lbl_author_name.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_author_name)
# description. this needs to be the biggest
self.lbl_description = QtWidgets.QLabel("")
self.lbl_description.setTextInteractionFlags(_flag_selectable)
self.lbl_description.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
)
)
self.lbl_description.setAlignment(_flag_topalign)
self.lbl_description.setWordWrap(True)
layout_vbox_scroll_insides.addWidget(self.lbl_description)
# requires. shows up if a theme has "inherits" set & we dont have it installed
self.lbl_requires = QtWidgets.QLabel("")
self.lbl_requires.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_requires)
# Version number. this will also show the current installed one if there is an update
self.lbl_version = QtWidgets.QLabel("")
self.lbl_version.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_version)
# Last update time
self.lbl_last_update = QtWidgets.QLabel("")
self.lbl_last_update.setWordWrap(True)
self.lbl_last_update.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_last_update)
# Theme details done, so we wont need the scroll after this
self.frame_scroll.setLayout(layout_vbox_scroll_insides)
layout_vbox_details.addWidget(self.frame_scroll)
# Insta//uninstall buttons
# "Uninstall" button. Only visisble when the selected thene is installed
self.btn_uninstall = QtWidgets.QPushButton("Uninstall", self)
self.btn_uninstall.setHidden(True)
self.btn_uninstall.clicked.connect(self._on_uninstall_clicked)
layout_vbox_details.addWidget(self.btn_uninstall)
# "Install" button. can also say "Update" if an update is availible
# Only visible when not installed or if theres an update
self.btn_install = QtWidgets.QPushButton("Install", self)
self.btn_install.clicked.connect(self._on_install_clicked)
self.btn_install.setDisabled(True)
layout_vbox_details.addWidget(self.btn_install)
# Done with details
layout_hbox_list_and_details.addLayout(layout_vbox_details)
self.layout_main.addLayout(layout_hbox_list_and_details)
self.btn_refresh = QtWidgets.QPushButton("Refresh", self)
self.btn_refresh.clicked.connect(themeManager.refresh_database) # Conneced to themeManager!
self.layout_main.addWidget(self.btn_refresh)
self.lbl_error = QtWidgets.QLabel("")
self.lbl_error.setVisible(False)
self.lbl_error.setWordWrap(True)
themeManager.errored.connect(self._on_fetch_error)
self.lbl_error.setTextInteractionFlags(_flag_selectable)
self.lbl_error.setStyleSheet(" QLabel { background-color:black; color:red; font-size: 16px;}")
self.layout_main.addWidget(self.lbl_error)
def _on_fetch_error(self, text):
self.lbl_error.setText(text)
self.lbl_error.setVisible(True)
def _on_uninstall_clicked(self):
theme = themeManager.database['entries'][self.list_results.currentRow()]
themeManager.delete_theme(theme['name'])
def _on_install_clicked(self):
theme = themeManager.database['entries'][self.list_results.currentRow()]
themeManager.download_theme(theme['name'])
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def _on_theme_selected(self, item):
index = self.list_results.currentRow()
theme = themeManager.database['entries'][index]
theme_name = theme['name']
is_installed = themeManager.is_installed(theme_name)
has_update = themeManager.has_update(theme_name)
self.btn_install.setDisabled(False)
self.btn_install.setText( "Update" if has_update else "Install" )
self.btn_install.setVisible( (is_installed and has_update) or not is_installed )
self.btn_uninstall.setVisible( themeManager.is_installed(theme_name) )
self.lbl_theme_name.setText(theme_name)
self.lbl_author_name.setText("By %s" % theme['author'])
self.lbl_description.setText(theme['description'])
version_text = "Version %s" % theme['version']
if has_update:
version_text += " (Installed: %s)" % themeManager.manifest[theme_name]['version']
self.lbl_version.setText( version_text )
self.lbl_requires.setText( ("Requires %s" % theme['inherits']) if theme['inherits'] else "")
self.lbl_last_update.setText( "Released on: " + datetime.fromtimestamp(theme['updated']).strftime("%d/%m/%Y, %H:%M") )
@QtCore.pyqtSlot(dict)
def _on_database_refreshed(self,_):
self.rebuild()
def rebuild(self):
database = themeManager.database
self.list_results.clear()
self.lbl_error.setText("")
self.lbl_error.setVisible(False)
if not themeManager.is_database_valid():
self.lbl_error.setText("")
self.lbl_error.setVisible(True)
# Repopulate the list
for dbitem in database['entries']:
# Determine the suffix
icon = self.icons[0]
status = ''
if themeManager.is_installed(dbitem['name']):
if themeManager.has_update(dbitem['name']):
status = '~ (update available)'
icon = self.icons[2]
else:
status = '~ (installed)'
icon = self.icons[1]
text = "%s by %s %s" % (dbitem['name'], dbitem['author'], status)
item = QtWidgets.QListWidgetItem(icon, text)
self.list_results.addItem(item)
self.btn_install.setDisabled(True)
for lbl in [self.lbl_author_name, self.lbl_description, self.lbl_version, self.lbl_requires, self.lbl_last_update]:
lbl.setText("")
self.lbl_theme_name.setText("Click a theme to get started")
self.btn_uninstall.setVisible(False)
self.btn_install.setVisible(True)
self.btn_install.setDisabled(True)
self.rebuilt.emit()
PchumLog.debug("Rebuilt emitted")

View file

@ -1,156 +0,0 @@
import logging
from os import remove
try:
from PyQt6 import QtCore, QtGui, QtWidgets, QtNetwork
from PyQt6.QtGui import QAction
_flag_selectable = QtCore.Qt.TextInteractionFlag.TextSelectableByMouse
_flag_topalign = QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop
except ImportError:
print("PyQt5 fallback (menus.py)")
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
from PyQt5.QtWidgets import QAction
_flag_selectable = QtCore.Qt.TextSelectableByMouse
_flag_topalign = QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop
# ~Lisanne
# This file has all the stuff needed to use a theme repository
# - ThemeManagerWidget, a GUI widget that
# - class for fetching & parsing database
# - class for handling installs & uninstalls. it also updates the manifest
# - manifest variable / json which keeps track of installed themes & their version
def unzip_file(path_zip, path_destination):
pass
class ThemeManagerWidget(QtWidgets.QWidget):
manifest = {}
database = {}
config = None
promise = None
def __init__(self, config, parent=None):
super().__init__(parent)
self.config = config
self.setupUI()
self.refresh()
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def themeSelected(self, item):
print("Done!")
print(item)
pass
@QtCore.pyqtSlot(QtNetwork.QNetworkReply)
def receiveReply(self, reply):
print(reply)
breakpoint
@QtCore.pyqtSlot()
def refresh(self):
print("Starting refresh @ ",self.config.theme_repo_url())
request = QtNetwork.QNetworkRequest()
request.setUrl(QtCore.QUrl(self.config.theme_repo_url()))
manager = QtNetwork.QNetworkAccessManager()
promise = manager.get(request)
manager.finished[QtNetwork.QNetworkReply].connect(self.receiveReply)
def setupUI(self):
self.layout_main = QtWidgets.QVBoxLayout(self)
self.setLayout(self.layout_main)
# Search bar
self.line_search = QtWidgets.QLineEdit()
self.line_search.setPlaceholderText("Search for themes")
self.layout_main.addWidget(self.line_search)
# Main layout
# [list of themes/results] | [selected theme details]
layout_hbox_list_and_details = QtWidgets.QHBoxLayout()
# This is the list of database themes
self.list_results = QtWidgets.QListWidget()
self.list_results.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
)
)
self.list_results.itemActivated.connect(self.themeSelected)
layout_hbox_list_and_details.addWidget(self.list_results)
# This is the right side, has the install buttons & all the theme details of the selected item
layout_vbox_details = QtWidgets.QVBoxLayout()
# The theme details are inside a scroll container in case of small window
self.frame_scroll = QtWidgets.QScrollArea()
self.frame_scroll.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.Expanding,
)
)
# The vbox that the detail labels will rest in
layout_vbox_scroll_insides = QtWidgets.QVBoxLayout()
# here starts the actual detail labels
# Selected theme's name
self.lbl_theme_name = QtWidgets.QLabel("Theme name here")
self.lbl_theme_name.setTextInteractionFlags(_flag_selectable)
self.lbl_theme_name.setStyleSheet("QLabel { font-size: 16px; font-weight:bold;}")
self.lbl_theme_name.setWordWrap(True)
layout_vbox_scroll_insides.addWidget(self.lbl_theme_name)
# Author name
self.lbl_author_name = QtWidgets.QLabel("Author: ?")
self.lbl_author_name.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_author_name)
# description. this needs to be the biggest
self.lbl_description = QtWidgets.QLabel("Description")
self.lbl_description.setTextInteractionFlags(_flag_selectable)
self.lbl_description.setSizePolicy(
QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Preferred,
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
)
)
self.lbl_description.setAlignment(_flag_topalign)
self.lbl_description.setWordWrap(True)
layout_vbox_scroll_insides.addWidget(self.lbl_description)
# requires. shows up if a theme has "inherits" set & we dont have it installed
self.lbl_requires = QtWidgets.QLabel("Requires: pibby")
self.lbl_requires.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_requires)
# Version number. this will also show the current installed one if there is an update
self.lbl_version = QtWidgets.QLabel("Version: 2 (installed: 0)")
self.lbl_version.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_version)
# Last update time
self.lbl_last_update = QtWidgets.QLabel("DD/MM/YYYY HH:MM")
self.lbl_last_update.setTextInteractionFlags(_flag_selectable)
layout_vbox_scroll_insides.addWidget(self.lbl_last_update)
# Theme details done, so we wont need the scroll after this
self.frame_scroll.setLayout(layout_vbox_scroll_insides)
layout_vbox_details.addWidget(self.frame_scroll)
# Insta//uninstall buttons
# "Uninstall" button. Only visisble when the selected thene is installed
self.btn_uninstall = QtWidgets.QPushButton("Uninstall", self)
self.btn_uninstall.setHidden(True)
layout_vbox_details.addWidget(self.btn_uninstall)
# "Install" button. can also say "Update" if an update is availible
self.btn_install = QtWidgets.QPushButton("Install", self)
layout_vbox_details.addWidget(self.btn_install)
# Done with details
layout_hbox_list_and_details.addLayout(layout_vbox_details)
self.layout_main.addLayout(layout_hbox_list_and_details)
self.btn_refresh = QtWidgets.QPushButton("Refresh", self)
self.btn_refresh.clicked.connect(self.refresh)
self.layout_main.addWidget(self.btn_refresh)