1
0
Fork 0
mirror of synced 2024-06-29 19:51:02 +12:00
Rare/rare/launcher/console_dialog.py
loathingKernel 0ea4b1a824 Dialogs: Re-implement all dialogs on top of a few common super-classes
Also add a dialog to select optional downloads before verifying
and refactor the move widget into a full-fledged dialog.

To keep dialogs in a common format and allow them to share the same
properties, three classes of dialogs have been implemented inheriting from
each other.

The classes are `BaseDialog` -> `ButtonDialog` -> `ActionDialog`

* Basedialog: is the basis of all dialogs and is responsible for
rejecting close requests from the window manager and the keyboard.
It also restricts access to `exec()` and `exec_()` because they are harmful.
It serves as the basis of Launch and Login dialogs

* ButtonDialog: is offering buttons for accepting or rejecting the presented
option. It implements its own buttons and exposes abstract methods to
implement handling in them. It restricts access to `close()` because these
dialogs should always product a result.
It is the basis of Uninstall, Selective dialogs.

* ActionDialog: in addition to the ButtonDialog, it offers an action buttom
with to validate the form or to make the dialog unable to close. It serves
as the basis of Install and Move dialogs.

Accordingly all dialogs in Rare have been updated to use these classes.
2024-02-12 21:52:07 +02:00

186 lines
6 KiB
Python

from typing import Union
from PyQt5.QtCore import QProcessEnvironment, pyqtSignal, QSize, Qt
from PyQt5.QtGui import QTextCursor, QFont, QCursor, QCloseEvent
from PyQt5.QtWidgets import (
QPlainTextEdit,
QDialog,
QPushButton,
QFileDialog,
QVBoxLayout,
QHBoxLayout,
QSpacerItem,
QSizePolicy, QTableWidgetItem, QHeaderView, QApplication,
)
from rare.ui.launcher.console_env import Ui_ConsoleEnv
class ConsoleDialog(QDialog):
term = pyqtSignal()
kill = pyqtSignal()
env: QProcessEnvironment
def __init__(self, parent=None):
super(ConsoleDialog, self).__init__(parent=parent)
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowTitle("Rare - Console")
self.setGeometry(0, 0, 640, 480)
layout = QVBoxLayout()
self.console = ConsoleEdit(self)
layout.addWidget(self.console)
button_layout = QHBoxLayout()
self.env_button = QPushButton(self.tr("Show environment"))
button_layout.addWidget(self.env_button)
self.env_button.clicked.connect(self.show_env)
self.save_button = QPushButton(self.tr("Save output to file"))
button_layout.addWidget(self.save_button)
self.save_button.clicked.connect(self.save)
self.clear_button = QPushButton(self.tr("Clear console"))
button_layout.addWidget(self.clear_button)
self.clear_button.clicked.connect(self.console.clear)
button_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Fixed))
self.terminate_button = QPushButton(self.tr("Terminate"))
# self.terminate_button.setVisible(platform.system() == "Windows")
button_layout.addWidget(self.terminate_button)
self.terminate_button.clicked.connect(lambda: self.term.emit())
self.kill_button = QPushButton(self.tr("Kill"))
# self.kill_button.setVisible(platform.system() == "Windows")
button_layout.addWidget(self.kill_button)
self.kill_button.clicked.connect(lambda: self.kill.emit())
layout.addLayout(button_layout)
self.setLayout(layout)
self.env_variables = ConsoleEnv(self)
self.env_variables.hide()
self.accept_close = False
def show(self) -> None:
super(ConsoleDialog, self).show()
self.center_window()
def center_window(self):
# get the margins of the decorated window
margins = self.windowHandle().frameMargins()
# get the screen the cursor is on
current_screen = QApplication.screenAt(QCursor.pos())
if not current_screen:
current_screen = QApplication.primaryScreen()
# get the available screen geometry (excludes panels/docks)
screen_rect = current_screen.availableGeometry()
decor_width = margins.left() + margins.right()
decor_height = margins.top() + margins.bottom()
window_size = QSize(self.width(), self.height()).boundedTo(
screen_rect.size() - QSize(decor_width, decor_height)
)
self.resize(window_size)
self.move(
screen_rect.center()
- self.rect().adjusted(0, 0, decor_width, decor_height).center()
)
def save(self):
file, ok = QFileDialog.getSaveFileName(
self, "Save output", "", "Log Files (*.log);;All Files (*)"
)
if ok:
if "." not in file:
file += ".log"
with open(file, "w") as f:
f.write(self.console.toPlainText())
f.close()
self.save_button.setText(self.tr("Saved"))
def set_env(self, env: QProcessEnvironment):
self.env = env
def show_env(self):
self.env_variables.setTable(self.env)
self.env_variables.show()
def log(self, text: str, end: str = "\n"):
self.console.log(text + end)
def error(self, text, end: str = "\n"):
self.console.error(text + end)
def on_process_exit(self, app_title: str, status: Union[int, str]):
self.error(
self.tr("Application \"{}\" finished with \"{}\"\n").format(app_title, status)
)
self.accept_close = True
def reject(self) -> None:
self.close()
def accept(self) -> None:
self.close()
def closeEvent(self, a0: QCloseEvent) -> None:
if self.accept_close:
super(ConsoleDialog, self).closeEvent(a0)
a0.accept()
else:
self.showMinimized()
a0.ignore()
class ConsoleEnv(QDialog):
def __init__(self, parent=None):
super(ConsoleEnv, self).__init__(parent=parent)
self.setAttribute(Qt.WA_DeleteOnClose, False)
self.ui = Ui_ConsoleEnv()
self.ui.setupUi(self)
def setTable(self, env: QProcessEnvironment):
self.ui.table.clearContents()
self.ui.table.setRowCount(len(env.keys()))
for idx, key in enumerate(env.keys()):
self.ui.table.setItem(idx, 0, QTableWidgetItem(env.keys()[idx]))
self.ui.table.setItem(idx, 1, QTableWidgetItem(env.value(env.keys()[idx])))
self.ui.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
class ConsoleEdit(QPlainTextEdit):
def __init__(self, parent=None):
super(ConsoleEdit, self).__init__(parent=parent)
self.setReadOnly(True)
font = QFont("Monospace")
font.setStyleHint(QFont.Monospace)
self.setFont(font)
self._cursor_output = self.textCursor()
def log(self, text):
html = f"<p style=\"color:#BBB;white-space:pre\">{text}</p>"
self._cursor_output.insertHtml(html)
self.scroll_to_last_line()
def error(self, text):
html = f"<p style=\"color:#eee;white-space:pre\">{text}</p>"
self._cursor_output.insertHtml(html)
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(
QTextCursor.Up if cursor.atBlockStart() else QTextCursor.StartOfLine
)
self.setTextCursor(cursor)