generated from dopt-python/py311
286 lines
9.8 KiB
Python
286 lines
9.8 KiB
Python
import sys
|
|
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtGui import QAction # WICHTIG: QAction wird für Menüeinträge gebraucht!
|
|
from PySide6.QtWidgets import (
|
|
QApplication,
|
|
QDialog,
|
|
QDialogButtonBox,
|
|
QFormLayout,
|
|
QFrame,
|
|
QGridLayout,
|
|
QHBoxLayout,
|
|
QLabel,
|
|
QLineEdit,
|
|
QMainWindow,
|
|
QScrollArea,
|
|
QSizePolicy,
|
|
QVBoxLayout,
|
|
QWidget,
|
|
)
|
|
|
|
|
|
# 1. Wir definieren unsere eigene klickbare "Zelle"
|
|
class ClickableCell(QFrame):
|
|
def __init__(self, text):
|
|
super().__init__()
|
|
|
|
# Qt Style Sheets (QSS) für das Design (Rahmen, Hintergrund, Hover)
|
|
self.setStyleSheet("""
|
|
ClickableCell {
|
|
background-color: white;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 8px;
|
|
}
|
|
ClickableCell:hover {
|
|
background-color: #eff6ff;
|
|
border: 1px solid #60a5fa;
|
|
}
|
|
""")
|
|
|
|
# Layout in der Zelle, um den Text zu zentrieren
|
|
layout = QVBoxLayout(self)
|
|
|
|
label = QLabel(text)
|
|
label.setWordWrap(True) # Textumbruch erzwingen!
|
|
label.setAlignment(Qt.AlignCenter)
|
|
label.setStyleSheet("border: none; background: transparent;")
|
|
|
|
layout.addWidget(label)
|
|
|
|
# Klick-Event abfangen
|
|
def mousePressEvent(self, event):
|
|
if event.button() == Qt.LeftButton:
|
|
print("Zelle wurde geklickt!")
|
|
|
|
|
|
class HeaderCell(QLabel):
|
|
def __init__(self, text):
|
|
super().__init__(text)
|
|
|
|
# Textausrichtung zentrieren
|
|
self.setAlignment(Qt.AlignCenter)
|
|
|
|
# Styling: Fetter Text, grauer Hintergrund (entspricht slate-200), leicht abgerundet
|
|
self.setStyleSheet("""
|
|
HeaderCell {
|
|
background-color: #e2e8f0;
|
|
color: #475569;
|
|
font-weight: bold;
|
|
padding: 10px;
|
|
border-radius: 6px;
|
|
}
|
|
""")
|
|
|
|
|
|
class NewEntryDialog(QDialog):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle("Neuer Eintrag")
|
|
self.setMinimumWidth(350)
|
|
|
|
# Ein FormLayout richtet Labels und Eingabefelder automatisch sauber aus
|
|
layout = QFormLayout(self)
|
|
|
|
# Eingabefelder erstellen
|
|
self.input_c1 = QLineEdit()
|
|
self.input_c2 = QLineEdit()
|
|
self.input_c3 = QLineEdit()
|
|
self.input_c4 = QLineEdit()
|
|
self.input_date = QLineEdit()
|
|
|
|
# Felder zum Layout hinzufügen
|
|
layout.addRow("Name:", self.input_c1)
|
|
layout.addRow("Beschreibung:", self.input_c2)
|
|
layout.addRow("Abteilung (optional):", self.input_c3)
|
|
layout.addRow("Status:", self.input_c4)
|
|
layout.addRow("Datum:", self.input_date)
|
|
|
|
# Standard-Buttons (OK und Abbrechen)
|
|
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
buttons.accepted.connect(self.accept) # Schließt Dialog und meldet "Erfolg"
|
|
buttons.rejected.connect(self.reject) # Schließt Dialog und meldet "Abbruch"
|
|
layout.addWidget(buttons)
|
|
|
|
def get_data(self):
|
|
# Liest die Textfelder aus und gibt sie als Dictionary zurück
|
|
return {
|
|
"c1": self.input_c1.text(),
|
|
"c2": self.input_c2.text(),
|
|
# Wenn das Feld leer ist, speichern wir None (für unseren Platzhalter-Effekt)
|
|
"c3": self.input_c3.text() if self.input_c3.text().strip() else None,
|
|
"c4": self.input_c4.text(),
|
|
"date": self.input_date.text(),
|
|
}
|
|
|
|
|
|
# 2. Das Hauptfenster mit dem Grid-Layout
|
|
class MainWindow(QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setWindowTitle("Native PySide Tabelle")
|
|
self.resize(1800, 200)
|
|
|
|
# --- 1. DAS MENÜ ERSTELLEN ---
|
|
self.create_menu()
|
|
|
|
# --- 2. DAS ZENTRALE WIDGET ---
|
|
# Da das QMainWindow den Rahmen vorgibt, brauchen wir ein Container-Widget für die Mitte
|
|
central_widget = QWidget()
|
|
self.setCentralWidget(central_widget)
|
|
# Das Haupt-Layout des Fensters (Horizontal)
|
|
outer_layout = QHBoxLayout(central_widget)
|
|
|
|
# Ein Container-Widget für deine Tabelle/Grid
|
|
container = QWidget()
|
|
# 2. NEU: Dem Container sagen: "Dehne dich so weit aus, wie du darfst!"
|
|
# container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
|
|
|
# --- NEU: DER SCROLL-BEREICH ---
|
|
scroll_area = QScrollArea()
|
|
scroll_area.setWidgetResizable(
|
|
True
|
|
) # WICHTIG: Erlaubt dem Grid im Inneren, sich an die Breite anzupassen
|
|
scroll_area.setMinimumWidth(700)
|
|
scroll_area.setMaximumWidth(
|
|
1500
|
|
) # Die Breiten-Begrenzung wandert nun auf die ScrollArea
|
|
scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
# Optional: Rahmen der ScrollArea entfernen, damit es "flacher" und moderner aussieht
|
|
scroll_area.setFrameShape(QFrame.NoFrame)
|
|
# --- WICHTIG: VERKNÜPFUNG ---
|
|
# 1. Den Container in die ScrollArea stecken
|
|
scroll_area.setWidget(container)
|
|
|
|
# 3. Zentrierung durch "Stretches" (wie mx-auto)
|
|
# Wir fügen links und rechts vom Container Platzhalter ein
|
|
outer_layout.addStretch(1)
|
|
outer_layout.addWidget(scroll_area, stretch=100)
|
|
outer_layout.addStretch(1)
|
|
# Optional: Damit der Container oben am Rand klebt
|
|
outer_layout.setAlignment(Qt.AlignTop)
|
|
|
|
# Wir geben dem Container ein vertikales Layout
|
|
container_layout = QVBoxLayout(container)
|
|
container_layout.setContentsMargins(0, 0, 0, 0) # Entfernt unnötige Ränder
|
|
# Das Grid für unsere Tabelle
|
|
# Das Grid-Layout kommt in den Container
|
|
# self.grid = QGridLayout(container)
|
|
# Das Grid wird nun OHNE direkten Container erstellt
|
|
self.grid = QGridLayout()
|
|
self.grid.setSpacing(10) # Entspricht gap-2
|
|
self.grid.setColumnStretch(0, 1)
|
|
self.grid.setColumnStretch(1, 1)
|
|
self.grid.setColumnStretch(2, 1)
|
|
self.grid.setColumnStretch(3, 1)
|
|
|
|
# 3. Wir fügen das Grid oben in das vertikale Layout ein
|
|
container_layout.addLayout(self.grid)
|
|
# 4. DER ZAUBERTRICK: Wir setzen eine Feder unter das Grid.
|
|
# Diese Feder drückt das gesamte Grid nach oben und absorbiert den leeren Raum!
|
|
container_layout.addStretch()
|
|
|
|
# Zeilen-Zähler (0 ist für die Überschriften)
|
|
self.current_row = 0
|
|
# Beispieldaten (Projekt Gamma hat kein 'c3')
|
|
headers = ["Name", "Beschreibung", "Abteilung", "Status", "Datum"]
|
|
for col_idx, title in enumerate(headers):
|
|
self.grid.addWidget(HeaderCell(title), self.current_row, col_idx)
|
|
|
|
self.current_row += 1
|
|
|
|
data = [
|
|
{
|
|
"c1": "Projekt Alpha",
|
|
"c2": "Alles komplett.",
|
|
"c3": "Teamleitung",
|
|
"c4": "Hoch",
|
|
"date": "17.04.",
|
|
},
|
|
{
|
|
"c1": "Projekt Beta",
|
|
"c2": "Abteilung fehlt.",
|
|
"c4": "Wartend",
|
|
"date": "21.04.",
|
|
},
|
|
{
|
|
"c1": "Projekt Gamma",
|
|
"c2": "Alles komplett.",
|
|
"c3": "Teamleitung",
|
|
"c4": "Mittel",
|
|
"date": "30.04.",
|
|
},
|
|
{
|
|
"c1": "Projekt Delta",
|
|
"c2": "Abteilung fehlt.",
|
|
"c4": "Wartend",
|
|
"date": "05.05.",
|
|
},
|
|
]
|
|
|
|
# Wir gehen die Datenliste mit enumerate durch, um den row_index für das Grid zu bekommen
|
|
for entry in data:
|
|
self.add_row_to_grid(entry)
|
|
|
|
# --- HILFSMETHODE UM EINE ZEILE EINZUFÜGEN ---
|
|
def add_row_to_grid(self, entry):
|
|
row = self.current_row
|
|
|
|
self.grid.addWidget(ClickableCell(entry.get("c1", "")), row, 0)
|
|
self.grid.addWidget(ClickableCell(entry.get("c2", "")), row, 1)
|
|
|
|
c3_value = entry.get("c3")
|
|
if c3_value:
|
|
self.grid.addWidget(ClickableCell(c3_value), row, 2)
|
|
else:
|
|
empty_box = QFrame()
|
|
empty_box.setStyleSheet(
|
|
"QFrame { background-color: #f8fafc; border: 2px dashed #e2e8f0; border-radius: 8px; }"
|
|
)
|
|
self.grid.addWidget(empty_box, row, 2)
|
|
|
|
self.grid.addWidget(ClickableCell(entry.get("c4", "")), row, 3)
|
|
self.grid.addWidget(ClickableCell(entry.get("date", "")), row, 4)
|
|
|
|
self.current_row += 1 # Zähler für den nächsten Eintrag erhöhen
|
|
|
|
# --- MENÜ LOGIK ---
|
|
def create_menu(self):
|
|
menu_bar = self.menuBar()
|
|
file_menu = menu_bar.addMenu("Datei")
|
|
|
|
new_action = QAction("Neuer Eintrag...", self)
|
|
new_action.setShortcut("Ctrl+N")
|
|
# VERKNÜPFUNG: Wenn geklickt, rufe die Methode zum Öffnen des Dialogs auf
|
|
new_action.triggered.connect(self.open_new_entry_dialog)
|
|
file_menu.addAction(new_action)
|
|
|
|
file_menu.addSeparator()
|
|
|
|
exit_action = QAction("Beenden", self)
|
|
exit_action.setShortcut("Ctrl+Q")
|
|
exit_action.triggered.connect(self.close)
|
|
file_menu.addAction(exit_action)
|
|
|
|
# 2. Hauptmenü-Punkt: "Hilfe"
|
|
help_menu = menu_bar.addMenu("Hilfe")
|
|
about_action = QAction("Über", self)
|
|
help_menu.addAction(about_action)
|
|
|
|
# --- DIALOG AUFRUFEN & DATEN ÜBERNEHMEN ---
|
|
def open_new_entry_dialog(self):
|
|
dialog = NewEntryDialog(self)
|
|
|
|
# .exec() pausiert das Programm, bis der Dialog geschlossen wird.
|
|
# Es gibt True zurück, wenn der Nutzer "OK" geklickt hat.
|
|
if dialog.exec():
|
|
new_data = dialog.get_data() # Dictionary aus dem Dialog holen
|
|
self.add_row_to_grid(new_data) # Ins Grid zeichnen
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = QApplication(sys.argv)
|
|
window = MainWindow()
|
|
window.show()
|
|
sys.exit(app.exec())
|