from PyQt5.QtWidgets import ( QAbstractScrollArea, QDialog, QHBoxLayout, QHeaderView, QPlainTextEdit, QTableWidget, QTableWidgetItem, QVBoxLayout, QLabel, QPushButton, ) from logging import error from pprint import pformat class QuestionBoxDetails(QDialog): """ Dialog box primitive that displays arbitrary `data`, and prompts user for a `reply` as a QPushButton Args: - `reply`: An integer representing a user choice based on key of `choices` - `choices`: A list of tuples, where each tuple represents a valid reply e.g. [(reply: int, name: str),] Notes: You may supply an arbitrary number of choices, but here are my suggestions: 2 = The alternative 1 = The default 0 = Cancel -1 = Cancel action (non-response) """ def __init__(self, title: str, description: str, data: str | list | dict, choices: list[tuple[int, str]]): super(QuestionBoxDetails, self).__init__() self.title: str = title self.description: str = description self.data: str | list | dict = data self.reply: int = -1 self.setWindowTitle(title) self.setMinimumSize(600, 400) self.setMaximumSize(600, 400) layout = QVBoxLayout() h_layout = QHBoxLayout() # Labels & input fields label = QLabel(description) layout.addWidget(label) if isinstance(self.data, str): self.input_field = QPlainTextEdit(pformat(self.data)) layout.addWidget(self.input_field) else: table: QTableWidget = QTableWidget() table.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) table.horizontalHeader().setStretchLastSection(True) # type: ignore table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) # type: ignore if isinstance(self.data, list): table.setRowCount(len(data)) table.setColumnCount(1) for i, item in enumerate(self.data): table.setItem(i, 0, QTableWidgetItem(str(item))) layout.addWidget(table) elif isinstance(self.data, dict): try: # | TIT2 | title goes here | # | TDRC | 2025-05-05 | table.setRowCount(len(data.keys())) # type: ignore table.setColumnCount(2) table.setHorizontalHeaderLabels(['Key', 'Value']) for i, (k, v) in enumerate(data.items()): # type: ignore table.setItem(i, 0, QTableWidgetItem(str(k))) table.setItem(i, 1, QTableWidgetItem(str(v))) layout.addWidget(table) except Exception as e: data = str(self.data) self.input_field = QPlainTextEdit(pformat(data + "\n\n" + str(e))) layout.addWidget(self.input_field) error(f'Tried to load self.data as dict but could not. {e}') # dynamically load the button choices def create_button(choice): def func(): self.reply = choice[0] self.close() button = QPushButton(choice[1]) button.clicked.connect(func) return button for idx, choice in enumerate(choices): button = create_button(choice) h_layout.addWidget(button) if idx == 0: button.setFocus() # # ok # ok_button = QPushButton("Confirm") # ok_button.clicked.connect(self.ok) # h_layout.addWidget(ok_button) # # cancel # cancel_button = QPushButton("no") # cancel_button.clicked.connect(self.cancel) # h_layout.addWidget(cancel_button) layout.addLayout(h_layout) self.setLayout(layout) def execute(self): """ Returns when `self` is closed """ self.exec_() return self.reply def cancel(self): self.reply = False self.close() def ok(self): self.reply = True self.close()