Import eines QT5-Dialogs startet App (Noob-Frage)

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
ran
User
Beiträge: 28
Registriert: Mittwoch 23. April 2025, 14:48

Hallo,
da es unter tkinter keinen fertigen Drucker-Dialog gibt, dachte ich, ich mache es mir einfach und nehme den hübschen QT5-Dialog.

Offenbar geht das nicht so, wie ich dachte. Ich habe mir ein Script aus dem Netz gefischt, das den Zugriff für txt und html zeigte und bastelte das für pdf in meinem Projekt um:

Code: Alles auswählen

# gui.qt5_print_dialog_pdf.py

import sys
from pathlib import Path

import fitz  
from PyQt5.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
from PyQt5.QtGui import QPainter, QImage


def print_pdf_dialog(pdf_path: str = None, parent: QWidget = None) -> bool:
    """
    Öffnet einen PDF-Druck-Dialog.
    - Wenn pdf_path None, öffnet sich ein QFileDialog.
    - parent kann ein QWidget sein (z.B. dein Hauptfenster).
    Returns True, wenn gedruckt oder Dialog abgebrochen wurde, False bei Fehler.
    """
    # 1) QApplication sicherstellen
    app_created = False
    app = QApplication.instance()
    if not app:
        app = QApplication(sys.argv)
        app_created = True

    # 2) PDF-Pfad ermitteln
    if not pdf_path:
        pdf_path, _ = QFileDialog.getOpenFileName(
            parent, "PDF auswählen", "", "PDF-Dateien (*.pdf)"
        )
        if not pdf_path:
            # Nutzer hat abgebrochen
            if app_created:
                app.quit()
            return False

    # 3) PDF laden
    try:
        doc = fitz.open(pdf_path)
    except Exception as e:
        QMessageBox.critical(parent, "Fehler", f"PDF konnte nicht geladen werden:\n{e}")
        if app_created:
            app.quit()
        return False

    # 4) Drucker-Dialog anzeigen
    printer = QPrinter()
    dlg = QPrintDialog(printer, parent)
    if dlg.exec_() != QPrintDialog.Accepted:
        # Abgebrochen, aber kein Fehler
        if app_created:
            app.quit()
        return True

    # 5) Seiten drucken
    painter = QPainter(printer)
    for page_num in range(doc.page_count):
        page = doc.load_page(page_num)
        pix = page.get_pixmap(dpi=printer.resolution())
        img = QImage(
            pix.samples, pix.width, pix.height, pix.stride, QImage.Format_RGB888
        )

        # Bild im Druckbereich skalieren
        rect = painter.viewport()
        size = img.size()
        size.scale(rect.size(), 1)  # 1 == Qt.KeepAspectRatio
        painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
        painter.setWindow(img.rect())
        painter.drawImage(0, 0, img)

        if page_num < doc.page_count - 1:
            printer.newPage()
    painter.end()

    if app_created:
        app.quit()
    return True


if __name__ == "__main__":
    # Nur beim direkten Aufruf wird der Dialog geöffnet
    success = print_pdf_dialog()
    sys.exit(0 if success else 1)

Im Test tut es was es soll, aber nicht beim Aufruf aus dem GUI.
Sobald ich es irgendwie anfasse - also schon beim Import, startet es den Dialog. Auch aus einem python-Terminal heraus, einzige Codezeile '"import qt5_printer..." und Dialog geht auf. Das ist ärgerlich (für mich) aber ich hätte hier auch weiter herum gesucht, aber das wirklich Böse ist, dass, wenn ich den Dialog wegklicke, mein GUI mit stirbt.

Wie im Titel geschrieben, ich habe von QT keine Ahnung, ich weiß nicht, ob das so gehört - aber vielleicht gibt es ja eine Möglichkeit, dass es erst mit Methodenaufruf loslegt? Danke für eure Hilfe vorab
Benutzeravatar
__blackjack__
User
Beiträge: 14027
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das sollte so nicht passieren. Ändert sich etwas wenn die drei letzten Zeilen entfernt werden?

Edit: GUI-Rahmenwerke mischen ist in der Regel keine gute Idee, weil das je nach Plattform nicht funktionieren muss.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
ran
User
Beiträge: 28
Registriert: Mittwoch 23. April 2025, 14:48

okay, ich habe den Import in die aufrufende Methode verlegt und das scheint der Stolperstein gewesen zu sein. Jetzt wird erst bei bereits existierendem GUI der Dialog gestartet und tötet ohne parent nicht das danach erzeugte Hauptfenster.

Code: Alles auswählen

    def _on_print_file(self):
        from gui.qt5_print_dialog_pdf  import print_pdf_dialog

        # drucke eine bestimmte Datei direkt
        print_pdf_dialog()  # ("/home/.../mein_dokument.pdf", parent=self)

Also bisher läuft es rund. Danke für Eure Aufmerksamkeit
Benutzeravatar
__blackjack__
User
Beiträge: 14027
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ran: Der Import sollte aber nicht in der Methode stehen.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

@ran: Nur um das eben zu noch einmal ganz klar zu sagen:

Importe gehören _nicht_ in eien Funktion. Wenn du unerwartetes Verhalten beim Import hast, dann ist das ein Fehler.

Wenn du zwei GUI-Frameworks mischt, also zum Beispiel TK/tkinter und QT, dann ist das ebenfalls falsch.
ran
User
Beiträge: 28
Registriert: Mittwoch 23. April 2025, 14:48

Danke für die Antworten.
Noobfrage: Welche Probleme könnten denn durch Import in einer Methode entstehen?
Benutzeravatar
__blackjack__
User
Beiträge: 14027
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ran: Man versteckt so etwas nicht irgendwo im Modul, weil man dann schwerer sieht von was ein Modul abhängig ist. Manchmal machen Leute das um Seiteneffekte zu vermeiden, die es erst gar nicht geben sollte (im Grunde hier der Fall), oder um zirkuläre Importe zum laufen zu bekommen, was auch eine Hölle für sich ist.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
ran
User
Beiträge: 28
Registriert: Mittwoch 23. April 2025, 14:48

Das mit dem zirkulären Import und dessen Umgehung hatte mir mal ein Freund gezeigt, der aber auch meinte, dass das ein üblicher Weg sei, das zu umgehen. Danke für die Erklärung
Antworten