mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-10 01:54:15 +02:00
[ui] feature: themes
This commit is contained in:
@@ -1,100 +1,164 @@
|
||||
import os
|
||||
from typing import Tuple
|
||||
import traceback
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QCursor, QIcon
|
||||
from PyQt5.QtWidgets import QInputDialog, QLineEdit, QDialogButtonBox, QApplication
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtWidgets import QLineEdit, QApplication, QDialog, QPushButton, QVBoxLayout, \
|
||||
QSizePolicy, QToolBar, QLabel
|
||||
|
||||
from bauh.api.abstract.context import ApplicationContext
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.commons.system import new_subprocess
|
||||
from bauh.view.core.config import read_config
|
||||
from bauh.view.qt import css
|
||||
from bauh.view.qt.components import QtComponentsManager
|
||||
from bauh.view.qt.dialog import show_message
|
||||
from bauh.view.qt.components import QtComponentsManager, new_spacer
|
||||
from bauh.view.util import util
|
||||
from bauh.view.util.translation import I18n
|
||||
|
||||
ACTION_ASK_ROOT = 99
|
||||
|
||||
|
||||
class ValidatePassword(QThread):
|
||||
|
||||
signal_valid = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, password: Optional[str] = None):
|
||||
super(ValidatePassword, self).__init__()
|
||||
self.password = password
|
||||
|
||||
def run(self):
|
||||
if self.password is not None:
|
||||
try:
|
||||
valid = validate_password(self.password)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
valid = False
|
||||
|
||||
self.signal_valid.emit(valid)
|
||||
|
||||
|
||||
class RootDialog(QDialog):
|
||||
|
||||
def __init__(self, i18n: I18n, max_tries: int = 3):
|
||||
super(RootDialog, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
self.i18n = i18n
|
||||
self.max_tries = max_tries
|
||||
self.tries = 0
|
||||
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
self.setWindowIcon(util.get_default_icon()[1])
|
||||
self.setWindowTitle(i18n['popup.root.title'])
|
||||
self.setLayout(QVBoxLayout())
|
||||
self.setMinimumWidth(300)
|
||||
|
||||
self.label_msg = QLabel(i18n['popup.root.msg'])
|
||||
self.label_msg.setObjectName('message')
|
||||
self.layout().addWidget(self.label_msg)
|
||||
|
||||
self.input_password = QLineEdit()
|
||||
self.input_password.setObjectName('password')
|
||||
self.layout().addWidget(self.input_password)
|
||||
|
||||
self.label_error = QLabel()
|
||||
self.label_error.setProperty('error', 'true')
|
||||
self.layout().addWidget(self.label_error)
|
||||
self.label_error.hide()
|
||||
|
||||
self.lower_bar = QToolBar()
|
||||
self.layout().addWidget(self.lower_bar)
|
||||
|
||||
self.lower_bar.addWidget(new_spacer())
|
||||
self.bt_ok = QPushButton(i18n['popup.root.continue'])
|
||||
self.bt_ok.setDefault(True)
|
||||
self.bt_ok.setAutoDefault(True)
|
||||
self.bt_ok.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_ok.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.bt_ok.setObjectName('ok')
|
||||
self.bt_ok.clicked.connect(self._validate_password)
|
||||
self.lower_bar.addWidget(self.bt_ok)
|
||||
|
||||
self.bt_cancel = QPushButton()
|
||||
self.bt_cancel.setText(i18n['popup.button.cancel'])
|
||||
|
||||
self.bt_cancel.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.bt_cancel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.bt_cancel.setObjectName('bt_cancel')
|
||||
self.bt_cancel.clicked.connect(self.close)
|
||||
self.lower_bar.addWidget(self.bt_cancel)
|
||||
self.lower_bar.addWidget(new_spacer())
|
||||
|
||||
self.valid = False
|
||||
self.password = None
|
||||
self.validate_password = ValidatePassword()
|
||||
self.validate_password.signal_valid.connect(self._handle_password_validated)
|
||||
|
||||
def _validate_password(self):
|
||||
password = self.input_password.text().strip()
|
||||
|
||||
if password:
|
||||
self.password = password
|
||||
self.tries += 1
|
||||
self.bt_ok.setEnabled(False)
|
||||
self.bt_cancel.setEnabled(False)
|
||||
self.input_password.setEnabled(False)
|
||||
self.label_error.setText('')
|
||||
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
self.validate_password.password = password
|
||||
self.validate_password.start()
|
||||
|
||||
def _handle_password_validated(self, valid: bool):
|
||||
self.valid = valid
|
||||
|
||||
QApplication.restoreOverrideCursor()
|
||||
tries_ended = self.tries == self.max_tries
|
||||
|
||||
if not self.valid:
|
||||
self.label_error.show()
|
||||
self.bt_cancel.setEnabled(True)
|
||||
|
||||
if tries_ended:
|
||||
self.bt_cancel.setText(self.i18n['close'].capitalize())
|
||||
self.label_error.setText(self.i18n['popup.root.bad_password.last_try'])
|
||||
self.bt_cancel.setFocus()
|
||||
else:
|
||||
self.label_error.setText(self.i18n['popup.root.bad_password.body'])
|
||||
self.bt_ok.setEnabled(True)
|
||||
self.input_password.setEnabled(True)
|
||||
self.input_password.setFocus()
|
||||
else:
|
||||
self.close()
|
||||
|
||||
@staticmethod
|
||||
def ask_password(context: ApplicationContext, i18n: I18n, app_config: Optional[dict] = None,
|
||||
comp_manager: Optional[QtComponentsManager] = None, tries: int = 3) -> Tuple[bool, Optional[str]]:
|
||||
|
||||
current_config = read_config() if not app_config else app_config
|
||||
|
||||
store_password = bool(current_config['store_root_password'])
|
||||
|
||||
if store_password and context.root_password and validate_password(context.root_password):
|
||||
return True, context.root_password
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.save_states(state_id=ACTION_ASK_ROOT, only_visible=True)
|
||||
comp_manager.disable_visible()
|
||||
|
||||
diag = RootDialog(i18n=i18n, max_tries=tries)
|
||||
diag.exec()
|
||||
password = diag.password
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.restore_state(ACTION_ASK_ROOT)
|
||||
|
||||
if password is not None and store_password:
|
||||
context.root_password = password
|
||||
|
||||
return (True, password) if diag.valid else (False, None)
|
||||
|
||||
|
||||
def is_root():
|
||||
return os.getuid() == 0
|
||||
|
||||
|
||||
def ask_root_password(context: ApplicationContext, i18n: I18n,
|
||||
app_config: dict = None,
|
||||
comp_manager: QtComponentsManager = None) -> Tuple[str, bool]:
|
||||
|
||||
cur_config = read_config() if not app_config else app_config
|
||||
store_password = bool(cur_config['store_root_password'])
|
||||
|
||||
if store_password and context.root_password and validate_password(context.root_password):
|
||||
return context.root_password, True
|
||||
|
||||
diag = QInputDialog(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
diag.setStyleSheet("""QLineEdit { border-radius: 5px; font-size: 16px; border: 1px solid lightblue }""")
|
||||
diag.setInputMode(QInputDialog.TextInput)
|
||||
diag.setTextEchoMode(QLineEdit.Password)
|
||||
diag.setWindowIcon(util.get_default_icon()[1])
|
||||
diag.setWindowTitle(i18n['popup.root.title'])
|
||||
diag.setLabelText('')
|
||||
diag.setOkButtonText(i18n['popup.root.continue'].capitalize())
|
||||
diag.setCancelButtonText(i18n['popup.button.cancel'].capitalize())
|
||||
|
||||
bt_box = [c for c in diag.children() if isinstance(c, QDialogButtonBox)][0]
|
||||
|
||||
for bt in bt_box.buttons():
|
||||
if bt_box.buttonRole(bt) == QDialogButtonBox.AcceptRole:
|
||||
bt.setStyleSheet(css.OK_BUTTON)
|
||||
|
||||
bt.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
bt.setIcon(QIcon())
|
||||
|
||||
diag.resize(400, 200)
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.save_states(state_id=ACTION_ASK_ROOT, only_visible=True)
|
||||
comp_manager.disable_visible()
|
||||
|
||||
for attempt in range(3):
|
||||
|
||||
ok = diag.exec_()
|
||||
|
||||
if ok:
|
||||
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
valid = validate_password(diag.textValue())
|
||||
QApplication.restoreOverrideCursor()
|
||||
|
||||
if not valid:
|
||||
body = i18n['popup.root.bad_password.body']
|
||||
|
||||
if attempt == 2:
|
||||
body += '. ' + i18n['popup.root.bad_password.last_try']
|
||||
|
||||
show_message(title=i18n['popup.root.bad_password.title'],
|
||||
body=body,
|
||||
type_=MessageType.ERROR)
|
||||
ok = False
|
||||
diag.setTextValue('')
|
||||
|
||||
if ok:
|
||||
if store_password:
|
||||
context.root_password = diag.textValue()
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.restore_state(ACTION_ASK_ROOT)
|
||||
|
||||
return diag.textValue(), ok
|
||||
else:
|
||||
break
|
||||
|
||||
if comp_manager:
|
||||
comp_manager.restore_state(ACTION_ASK_ROOT)
|
||||
|
||||
return '', False
|
||||
|
||||
|
||||
def validate_password(password: str) -> bool:
|
||||
clean = new_subprocess(['sudo', '-k']).stdout
|
||||
echo = new_subprocess(['echo', password], stdin=clean).stdout
|
||||
|
||||
Reference in New Issue
Block a user