IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

aperçu avant impression


Sujet :

PyQt Python

  1. #1
    Membre du Club

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Burkina Faso

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 39
    Points : 41
    Points
    41
    Billets dans le blog
    1
    Par défaut aperçu avant impression
    Bonjour,
    Je veux faire de l'aperçu avant impression du texte, disons d'un tableau contenant des données. Le tableau sera tracé et les données du tableau sont dans une liste de liste. Et avec un bouton permettant d'imprimer l'aperçu. Merci. Je sais faire de l'impression avec QPrinter donc si la solution pouvait être avec ça, ça serait mieux.

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Il y a une classe qui fait ça très bien: QPrintPreviewDialog du module QtPrintSupport (version PyQt5).

    Mais, bien sûr, il faut avoir fait la mise en page avant!

    Une des solutions est de transformer la liste de listes en chaine html dans un QTextEdit.

    Mais attention: le QTextEdit ne supporte qu'un sous-ensemble de html appelé "rich text": il faut souvent se reporter à la doc de Qt pour savoir ce qui marche:
    http://doc.qt.io/qt-5/richtext-html-subset.html

    Voilà un petit code de test qui fait tout ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3
     
    import sys
    from PyQt5 import (QtWidgets, QtCore, QtPrintSupport)
     
    #############################################################################
    class Fenetre(QtWidgets.QWidget):
     
        def __init__(self, parent=None):
            super().__init__(parent)
     
            self.tedit = QtWidgets.QTextEdit(self)
     
            # liste de liste à imprimer dans une table
            L = [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]
     
            # préparation de la construction de la table html
            tabledeb = '<table border="1" align="left" width="100%" cellspacing="0" cellpadding="4">'
            tablefin = '</table>'
     
            tablelignedeb = '<tr>'
            tablelignefin = '</tr>'
     
            tablecoldeb = '<td>'
            tablecolfin = '</td>'
     
            # construction de la table html
            html = tabledeb
            for ligne in L:
                html += tablelignedeb
                for elem in ligne:
                    html += tablecoldeb + elem + tablecolfin
                html += tablelignefin  
            html += tablefin  
     
            # enregistrer la table html dans le QTextEdit
            self.tedit.setHtml(html)
     
            # création du bouton pour lancer l'impression
            self.bouton = QtWidgets.QPushButton("imprimer", self)
            self.bouton.clicked.connect(self.imprimer)
     
            # placement des widgets dans la fenêtre
            posit = QtWidgets.QGridLayout()
            posit.addWidget(self.tedit, 0, 0)
            posit.addWidget(self.bouton, 1, 0)
            self.setLayout(posit)
     
        @QtCore.pyqtSlot(bool)
        def imprimer(self):
            printer = QtPrintSupport.QPrinter()
            preview = QtPrintSupport.QPrintPreviewDialog(printer)
            preview.paintRequested.connect(self.tedit_printPreview)
            preview.exec_()
     
        @QtCore.pyqtSlot("QPrinter")
        def tedit_printPreview(self, printer):
            self.tedit.print_(printer)
     
    #############################################################################
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
    Une autre solution plus "lourde" que d'utiliser QPrintPreviewDialog est de créer un fichier pdf avec QPrinter, de le visualiser avec adobe reader (ou équivalent) et de l'imprimer avec ce même visualiseur.

    Une autre solution est de transformer la liste de listes en fichier csv et de le donner en importation à un tableur type Excel.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  3. #3
    Membre du Club

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Burkina Faso

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 39
    Points : 41
    Points
    41
    Billets dans le blog
    1
    Par défaut
    Bonjour tyrtamos, j'ai beaucoup aimé la solution que tu m'as proposé
    Citation Envoyé par tyrtamos Voir le message

    Une des solutions est de transformer la liste de listes en chaine html dans un QTextEdit.
    et je pense qu'elle peut satisfaire mes attentes. Juste une ou deux choses :
    1. Comment faire pour inserér du texte et de l'image dans l'entête et le pied de page
    2. Ajouter un titre au tableau


    Merci pour le temps que vous m'accordez

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    S'il s'agit de construire une page complète, c'est facile: on dispose des ressources du html (ou plutôt du sous-ensemble "rich text"). Il est donc possible d'insérer des images ainsi que de faire le choix et l'embellissement des polices de caractères (taille, gras, italique, couleur, ...). Etc...

    A titre d'exemple, il suffit de consulter la démo "textedit.py" qui se trouve ici sous Windows: C:\Python34\Lib\site-packages\PyQt5\examples\richtext\textedit. Et le texte html affiché est dans le fichier "example.html". A noter que si le texte proposé dépasse une page A4, le programme ne gère pas les entêtes et bas de pages.

    A noter aussi qu'un saut de page à un endroit précis du code html peut être ajouté avec: <div style="page-break-before:always;"></div>


    Si par contre il s'agit de gérer un texte html long en multi-pages automatique avec en-têtes et bas de pages, comme le fait MSWord, c'est possible mais c'est BEAUCOUP plus compliqué...
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  5. #5
    Membre du Club

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Burkina Faso

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 39
    Points : 41
    Points
    41
    Billets dans le blog
    1
    Par défaut
    Merci pour cette réponse, selon les propos suivants
    A noter que si le texte proposé dépasse une page A4, le programme ne gère pas les entêtes et bas de pages.
    dois-je comprendre qu'il y a la possibilité lorsque mon impression ne dépasse pas une page A4, je peux obtenir la zone d'entête et pied de page pour y effectuer mes opérations? Si ce n'est pas possible, je vais me repencher sur la solution avec QPrintPreviewDialog pour tout "dessiner à la main" aux positions que je veux. Comme je l'ai dit dans l'introduction, j'ai déjà fait une chose pareille en "dessinant" avec QPrinter et QPainter mais j'envoyais le résultat directement à l'imprimante. Maintenant que je veux essayer l'aperçu, j'arrive plus à afficher. Est-ce cela peut être le fait d'utiliser QPrintPreviewWidget au lieu de QPrintPreviewDialog? Je vais essayer les deux et voir la différence et je vous reviens. En attendant, si vous avez une astuce sous la main, je suis preneur!!!!!

    Pour vous donner idée de mon blocage; voici dans votre code ce que je veux faire et que j'arrive pas.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self.tedit.print_(printer)
    Etant donné que moi, je dessine directement sur un QPainter, alors je ne sais pas comment afficher ce QPainter sur le QPrintPreviewDialog ou widget et ensuite l'envoyer pour impression comme vous l'avez fait. Merci mais je ne sais pas si j'ai bien exprimé le problème.

  6. #6
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Dans le cadre d'un de mes programmes, je crée des courriers multi-pages avec en-têtes et bas de page mais en pdf. Je n'utilise donc pas QPrintPreviewDialog et je n'en ai pas besoin: c'est l'affichage du pdf par adobe reader qui me sert de preview et de fonction d'impression. Un avantage supplémentaire de cette solution, c'est que je dispose ainsi du fichier pdf, et que je peux l'envoyer par email et le sauvegarder quelque part sur un disque. Je fais d'ailleurs un truc en plus: j'encapsule tous les fichiers pdf d'une même période (env. 500) dans un seul (gros) fichier pdf avec le module Python "PyPDF2" (voir pypi).

    Et comme toi, en imprimant avec QPainter pour gérer mes pages, je ne sais pas utiliser QPrintPreviewDialog en même temps.

    Je n'ai jamais utilisé QPrintPreviewWidget: peut-être y a-t-il une solution avec?

    Désolé de ne pouvoir pas t'aider plus, mais si quelqu'un trouve une solution, ça m'intéresse aussi!
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  7. #7
    Membre du Club

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Burkina Faso

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 39
    Points : 41
    Points
    41
    Billets dans le blog
    1
    Par défaut
    Bonsoir, voici ce que j'ai pu faire pour atteindre mes objectifs et avec QPrintPreviewWidget.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    import os
    import sys
    import datetime
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class Printer(QWidget):
        def __init__(self):
            QWidget.__init__(self)
     
            __file = QFile("lipsum.txt")
     
            if (__file.open(QIODevice.ReadOnly)):
     
                self.m_lipsum = QTextStream(__file)
     
     
     
            self.toPrint = ""
     
     
     
            while(not self.m_lipsum.atEnd()):
     
                self.toPrint += self.m_lipsum.readLine()
     
                self.toPrint += " \n"
     
            self.previewDialog = QPrintPreviewWidget(self)
            self.previewDialog.paintRequested.connect(self.doPaint)
     
            self.btnPrint = QPushButton('Imprimer', self)
            self.btnPrint.clicked.connect(self.handlePrint)
            self.btnCancel = QPushButton('Annuler', self)
            self.btnCancel.clicked.connect(self.handleCancel)
            layout = QVBoxLayout(self)
            layout.addWidget(self.previewDialog)
            btnLayout = QHBoxLayout(self)
            btnLayout.addStretch()
            btnLayout.addWidget(self.btnPrint)
            btnLayout.addWidget(self.btnCancel)
            layout.addLayout(btnLayout)
            self.setLayout(layout)
     
     
        def doPaint(self, printer):
            small = QFont("Arial", 14)
     
            large = QFont("Arial", 32)
     
            painter = QPainter()
     
            if painter.begin(printer):
     
                for i in range(150):
     
                    painter.save()
     
                    painter.setFont(large)
     
                    painter.drawText(50, 100, str(i))
     
                    painter.translate(50, 200)
     
                    painter.drawText(painter.window(), 1,self.toPrint)
     
                    printer.newPage()
     
                    painter.restore()
     
        def handlePrint(self):
            self.previewDialog.print_()
     
        def handleCancel(self):
            self.close()
     
     
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = Printer()
        window.resize(640, 480)
        window.show()
        sys.exit(app.exec_())
    Je crois qu'il reste à faire beaucoup de mathématiques pour pouvoir placer les différents éléments où il faut et même gérer les entêtes et pied de page. Si quelqu'un à une idée meilleure, je suis preneur.
    Fichiers attachés Fichiers attachés

  8. #8
    Membre du Club

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Burkina Faso

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 39
    Points : 41
    Points
    41
    Billets dans le blog
    1
    Par défaut
    Une solution que je viens de trouver avec QPrintPreviewDialog mais avec la possibilité de placer les éléments où l'on desire avec les calculs qui s'imposent est la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Copyright (c) 2014 G Group. All rights reserved.
     
    import os
    import sys
     
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class Printer(QPrintPreviewDialog):
        def __init__(self, parent=None):
            super(Printer,self).__init__(parent, flags=Qt.WindowMinMaxButtonsHint|Qt.WindowCloseButtonHint)
            __file = QFile("lipsum.txt")
            if (__file.open(QIODevice.ReadOnly)):
                self.m_lipsum = QTextStream(__file)
            self.toPrint = ""
            while(not self.m_lipsum.atEnd()):
                self.toPrint += self.m_lipsum.readLine()
                self.toPrint += " \n"
            self.paintRequested.connect(self.doPaint)
     
        def doPaint(self, printer):
            small = QFont("Arial", 14)
            large = QFont("Arial", 32)
     
            painter = QPainter()
            if painter.begin(printer):
                for i in range(150):
                    painter.save()
                    painter.setFont(large)
                    painter.drawText(50, 100, str(i))
                    painter.translate(50, 200)
                    painter.drawText(painter.window(), 1,self.toPrint)
                    printer.newPage()
                    painter.restore()
     
     
    if __name__ == '__main__':
     
        app = QApplication(sys.argv)
        window = Printer()
        window.resize(640, 480)
        window.show()
        sys.exit(app.exec_())
    J'espère que j'ai pu aidé. Merci tyrtamos pour l'inspiration que tu m'as donné

  9. #9
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Citation Envoyé par kekule10 Voir le message
    J'espère que j'ai pu aidé. Merci tyrtamos pour l'inspiration que tu m'as donné
    Oui, tu m'as aidé! Et il y a longtemps que je sais qu'en aidant les autres, je m'aide moi-même!

    La preuve: sur la base de ton dernier code, voilà un complément qui fonctionne complètement!

    Pour l'exemple, j'ai construit un texte html dans un fichier appelé "texte.html" et qui contient plusieurs fois (pour avoir plusieurs pages) le texte d'exemple en rich text que j'ai déjà signalé (C:\Python34\Lib\site-packages\PyQt5\examples\richtext\textedit\example.html). Je l'ai mis en pièce jointe.

    C'est en PyQt5, mais il devrait suffire de modifier PyQt5=>PyQt4 et de supprimer la ligne "from PyQt5.QtPrintSupport import *"

    A noter que j'ai repris les importations "attrape-tout" pour partir de ton exemple et aller plus vite, mais il faudrait faire mieux que ça pour la version de production: ce n'est pas de la bonne programmation.

    Le code est largement commenté (je le fais aussi pour moi!).

    Il reste à imprimer les en-têtes et bas de page si c'est demandé. Le principe est assez simple: il faut augmenter les marges haute et basse pour ménager de la place et faire fonctionner correctement la pagination du texte. Puis, pour chaque page dans la boucle, écrire les en-têtes et bas de page aux bons endroits de la page. On peut mettre des images et des textes, ce qui permet de faire de jolis courriers d'entreprise. A noter qu'on peut parfaitement avoir des en-têtes et bas-de-pages différents pour la 1ère page et pour les suivantes. Si tu es intéressé, je peux aller plus loin.

    Avec l'exemple, tout a l'air de fonctionner dans le preview! La seule petite anomalie que j'ai trouvée, c'est que la marge gauche se déplace en marge haute quand on change l'orientation.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3 PyQt5
     
    import os
    import sys
     
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtPrintSupport import *
     
    #############################################################################
    class PrinterDialog(QPrintPreviewDialog):
     
        #========================================================================    
        def __init__(self, texte="", parent=None):
            super().__init__(parent, flags=Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint)
     
            self.resize(800, 600)
     
            #--------------------------------------------------------------------
            # possibilité de modifier la configuration du printer par défaut
            # ex: mettre des marges
            margeG = 20.5 # marge gauche (mm)
            margeH = 10.5 # marge haute (mm)
            margeD = 10.5 # marge droite (mm)
            margeB = 10.5 # marge basse (mm)
            self.printer().setPageMargins(margeG, margeH, margeD, margeB, QPrinter.Millimeter)
            # ex: mettre l'orientation paysage
            self.printer().setOrientation(QPrinter.Landscape)
     
            # charge le texte dans un QTextDocument
            self.doc = QTextDocument()
            self.doc.setHtml(texte)
     
            #--------------------------------------------------------------------
            # lancement du preview
            self.paintRequested.connect(self.doPaint)
     
        #========================================================================    
        def doPaint(self, printer):
     
            # initialise le rectangle d'impression du texte (coordonnées du papier)
            self.rect = printer.pageRect()
     
            # met pour le document le même rectangle d'impression que printer
            self.doc.setPageSize(QSizeF(QSize(self.rect.size().width(), self.rect.size().height())))
     
            # initialise le rectangle d'impression courant de la page
            self.currentRect = QRectF(QRect(QPoint(0, 0), self.rect.size()))
     
            # crée le painter lié au printer
            self.painter = QPainter(printer)
     
            # sauvegarde le painter dans la pile
            self.painter.save()
     
            # calcule le nombre total de pages du document à imprimer
            self.nbpages = self.doc.pageCount()
     
            #--------------------------------------------------------------------
            # imprime toutes les pages
            for numpage in range(0, self.nbpages):
     
                #----------------------------------------------------------------
                # imprime le corps du texte sur la page courante
                self.doc.drawContents(self.painter, QRectF(self.currentRect))
                self.currentRect.translate(0, self.currentRect.height())
                self.painter.restore()
     
                #----------------------------------------------------------------
                # imprime l'en-tête et le bas de page si c'est demandé
                """
                impression de l'en-tête
                impression du bas de page
                """
                #----------------------------------------------------------------
                # sauvegarde le painter dans la pile
                self.painter.save()
     
                # décale la partie à imprimer de la hauteur de la zone d'impression
                self.painter.translate(0, -self.currentRect.height() * (numpage+1))
     
                #----------------------------------------------------------------
                # provoque un changement de page s'il reste quelque chose à imprimer
                if numpage<self.nbpages-1:
                    printer.newPage()
     
            #----------------------------------------------------------------
            # restaure le painter de la pile
            self.painter.restore()
     
            # fin d'impression
            self.painter.end()
     
    #############################################################################
    if __name__ == '__main__':
     
        app = QApplication(sys.argv)
     
        # charge le texte html
        with open("texte.html", 'r') as fs:
            textehtml = fs.read()
     
        window = PrinterDialog(textehtml) # avec utilisation du printer par défaut
        window.show()
     
        sys.exit(app.exec_())
    Donne-moi ton avis!
    Fichiers attachés Fichiers attachés
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  10. #10
    Membre du Club

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    39
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Burkina Faso

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2014
    Messages : 39
    Points : 41
    Points
    41
    Billets dans le blog
    1
    Par défaut
    Merci vraiment pour ce code, quant à savoir si ça m'interesse pour les entêtes différentes, je dis oui!
    A noter qu'on peut parfaitement avoir des en-têtes et bas-de-pages différents pour la 1ère page et pour les suivantes. Si tu es intéressé, je peux aller plus loin.
    Pour être franc, je ne peux pas me concentrer avant lundi car je participe à un forum catholique des laïcs qui me prend mon weekend, donc je vais me contenter de recevoir de votre part cette fois-ci. désolé. Pour les entêtes avec image, si tu pouvais aussi m'en donner, je suis pas trop à l'aise avec les balises avancées du html ou c'est la paresse...

  11. #11
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 461
    Points : 9 248
    Points
    9 248
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Ça y est, j'ai réussi! Ça a été difficile, parce que les principes que j'avais adoptés pour ma solution pdf ne marchaient pas correctement avec le QPrintPreviewDialog.

    Pour l'instant, les en-têtes et bas-de-pages sont les mêmes sur la 1ère page et les suivantes, mais le code a été préparé pour accepter ce complément. Je n'aurai cependant pas le temps de le faire pour l'instant.

    Le code est écrit pour PyQt5 mais contient en commentaires les changements à faire pour PyQt4.

    Les en-têtes et bas-de-pages sont, comme le texte lui-même en html, ou plutôt en "rich text". Il faudra apprendre un peu à s'en servir! Pour simplifier, on peut approcher le texte voulu avec un éditeur html comme Kompozer, et ajuster ensuite le code html en fonction de ce que le "rich text" permet (voir sur le site de Qt).

    Cependant, pour les en-têtes et bas-de-pages, il est toujours possible d'écrire directement du texte et des images avec le painter, en utilisant l'adressage direct (x,y). Ce sera seulement plus complexe et surtout cela nécessitera du code Python spécifique à chaque document. C'est ce que je fais actuellement, et c'est pour ça que j'essaie d'en sortir.

    Pour le code html du présent test, j'ai utilisé le code de l'exemple de PyQt, doublé pour avoir plus de pages. J'ai aussi modifié l'une des images pour en avoir une plus grande. Voilà le code html d'intégration de l'image:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <img src="images/cameleon.jpg" width="300" height="200"/>
    Voilà ce que donne la 2ème page d'aperçu:

    Nom : ecran_600x600.jpg
Affichages : 2006
Taille : 101,8 Ko

    J'ai ajouté un entourage des en-têtes, textes et bas-de-pages, surtout pour vérifier que j’obtiens bien les marges papier voulues. On peut bien sûr les supprimer en mettant le code correspondant en commentaires.

    On peut, bien sûr, avoir une en-tête et pas de bas-de-page, un bas-de-page et pas d'en-tête ou ni l'un ni l'autre.

    Et voilà (enfin) le code Python:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3 PyQt5
     
    import os
    import sys
     
    #----------------------------------------------------------------------------
    # version PyQt5
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtPrintSupport import *
     
    """
    # version PyQt4
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    """
    #----------------------------------------------------------------------------
     
    #############################################################################
    class PrinterDialog(QPrintPreviewDialog):
     
        #========================================================================
        def __init__(self, printer=None, texte="", entete="", basdepage="", parent=None):
     
            if printer == None:
                # On laisse le printer par défaut de Qt
                super(PrinterDialog, self).__init__(parent, flags=Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint)
            else:
                # On utilise le printer donné en argument
                super(PrinterDialog, self).__init__(printer, parent, flags=Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint)
     
            self.resize(800, 800)
            self.setWindowTitle("Aperçu avant impression")
     
            #--------------------------------------------------------------------
            # Charge le texte html dans un QTextDocument
            self.doc = QTextDocument()
            self.doc.setHtml(texte)
     
            # Charge l'en-tête html dans un QTextDocument
            self.entete = None
            if entete!="":
                self.entete = QTextDocument()
                self.entete.setHtml(entete)
     
            # Charge le bas-de-page html dans un QTextDocument
            self.basdepage = None
            if basdepage!="":
                self.basdepage = QTextDocument()
                self.basdepage.setHtml(basdepage)
     
            # Espace de séparation entre le texte et les en-tête et bas-de-page (mm)
            self.separation = 2.0
     
            if printer == None:
                # Met des marges à l'imprimante par défaut
                margeG = 20.0  # marge gauche (mm)
                margeH = 10.0  # marge haute (mm)
                margeD = 10.0  # marge droite (mm)
                margeB = 10.0  # marge basse (mm)
                self.printer().setPageMargins(margeG, margeH, margeD, margeB, QPrinter.Millimeter)
     
            #--------------------------------------------------------------------
            # Lance l'aperçu
            self.paintRequested.connect(self.doPaint)
     
        #========================================================================
        def doPaint(self, printer):
     
            # Fonction pour convertir les millimètres en pixels
            mm2px = lambda mm: int(mm / 25.4 * printer.resolution())
     
            # Séparation en pixels entre le texte et les en-tête et bas-de-page
            separation = mm2px(self.separation)
     
            #--------------------------------------------------------------------
            # Met les marges après conversion en pixels
     
            # version PyQt5
            margeG = mm2px(printer.margins().left)  # marge gauche
            margeH = mm2px(printer.margins().top)  # marge haute
            margeD = mm2px(printer.margins().right)  # marge droite
            margeB = mm2px(printer.margins().bottom)  # marge basse
     
            """
            # version PyQt4
            margeG = mm2px(printer.getPageMargins(QPrinter.Millimeter)[0])  # marge gauche
            margeH = mm2px(printer.getPageMargins(QPrinter.Millimeter)[1])  # marge haute
            margeD = mm2px(printer.getPageMargins(QPrinter.Millimeter)[2])  # marge droite
            margeB = mm2px(printer.getPageMargins(QPrinter.Millimeter)[3])  # marge basse
            """
     
            #--------------------------------------------------------------------
            printer.setPageMargins(margeG, margeH, margeD, margeB, QPrinter.DevicePixel)
     
            # Rectangle d'impression du texte en tenant compte des marges
            rect = printer.pageRect(QPrinter.DevicePixel)
            largeurimpression = rect.size().width()
            hauteurimpression = rect.size().height()
     
            # Calcule la hauteur à réserver pour l'en-tête
            entete_H = 0
            sep_entete = 0
            if self.entete!=None:
                self.entete.setTextWidth(largeurimpression)
                entete_H = self.entete.size().height()
                sep_entete = separation
                # Affecte le même rectangle d'impression au texte de l'en-tête
                self.entete.setPageSize(QSizeF(largeurimpression, entete_H))
     
            # Calcule la hauteur à réserver pour le bas-de-page
            basdepage_H = 0
            sep_basdepage = 0
            if self.basdepage!=None:
                self.basdepage.setTextWidth(largeurimpression)
                basdepage_H = self.basdepage.size().height()
                sep_basdepage = separation
                # Affecte le même rectangle d'impression au texte du bas-de-page
                self.basdepage.setPageSize(QSizeF(largeurimpression, basdepage_H))
     
            # Affecte le même rectangle d'impression au document
            self.doc.setPageSize(QSizeF(largeurimpression, hauteurimpression - entete_H - sep_entete - basdepage_H - sep_basdepage))
     
            # Grand rectangle d'impression de tout le document
            contentRect = QRectF(QPointF(0, 0), QSizeF(self.doc.size().toSize()))
     
            # Rectangle courant d'impression de chaque page
            currentRect = QRectF(0, 0, largeurimpression, hauteurimpression - entete_H - sep_entete - basdepage_H - sep_basdepage)
     
            # Rayon des coins arrondis des rectangles d'entourage si c'est demandé
            rayon = float(mm2px(2))
     
            # Crée le painter et le relie au printer
            painter = QPainter(printer)
     
            # Sauvegarde le painter dans la pile
            painter.save()
     
            #--------------------------------------------------------------------
            # Imprime toutes les pages
            numpage = 0
            while True:
     
                # Met à jour le numéro de page
                numpage += 1
     
                #----------------------------------------------------------------
                # Imprime le corps du texte
                # Atteint le début d'impression du texte
                painter.translate(0, entete_H + sep_entete)
                # Imprime le texte
                self.doc.drawContents(painter, currentRect)
                painter.restore()
     
                #----------------------------------------------------------------
                # Imprime l'en-tête si elle existe
                if self.entete!=None:
                    painter.save()
                    # Imprime le texte de l'en-tête
                    rect = QRectF(QPointF(0, 0), QSizeF(largeurimpression, entete_H))
                    self.entete.drawContents(painter, rect)
                    painter.restore()
     
                #----------------------------------------------------------------
                # Imprime le bas-de-page s'il existe
                if self.basdepage!=None:
                    painter.save()
                    # Atteint le début d'impression du bas-de-page
                    painter.translate(0, entete_H + sep_entete + currentRect.height() + sep_basdepage)
                    # Imprime le texte du bas de page
                    rect = QRectF(QPointF(0, 0), QSizeF(largeurimpression, basdepage_H))
                    self.basdepage.drawContents(painter, rect)
                    painter.restore()
     
                #----------------------------------------------------------------
                # Dessine des entourages si c'est demandé
     
                # Dessine un entourage autour de l'entête
                if self.entete!=None:
                    painter.save()
                    rect = QRectF(0, 0, largeurimpression, entete_H)
                    painter.drawRoundedRect(rect, rayon, rayon)
                    painter.restore()
     
                # Dessine un entourage autour du corps du texte
                painter.save()
                painter.translate(0, entete_H + sep_entete)
                rect = QRectF(0, 0, largeurimpression, currentRect.height())
                painter.drawRoundedRect(rect, rayon, rayon)
                painter.restore()
     
                # Dessine un entourage autour du bas-de-page
                if self.basdepage!=None:
                    painter.save()
                    painter.translate(0, entete_H + sep_entete + currentRect.height() + sep_basdepage)
                    rect = QRectF(0, 0, largeurimpression, basdepage_H)
                    painter.drawRoundedRect(rect, rayon, rayon)
                    painter.restore()
     
                #----------------------------------------------------------------
                # Traite la fin d'impression de chaque page
     
                # Sauvegarde le painter dans la pile
                painter.save()
     
                # Décale le rectange du texte à afficher de la hauteur déjà affichée
                currentRect.translate(0, currentRect.height())
     
                # Revient au début de la zone d'impression
                painter.translate(0, -currentRect.height() * numpage)
     
                #----------------------------------------------------------------
                # Provoque un changement de page s'il reste quelque chose à imprimer
                if currentRect.intersects(contentRect):
                    printer.newPage()
                else:
                    break  # C'est fini!
     
            # Restaure le painter de la pile
            painter.restore()
     
            # Fin d'impression
            painter.end()
     
    #############################################################################
    if __name__ == '__main__':
     
        app = QApplication(sys.argv)
     
        # Crée et configure un QPrinter. Ce n'est pas obligatoire:
        # => si printer=None, QPrintPreviewDialog utilise un QPrinter par défaut
        printer = QPrinter()
        printer.setOrientation(QPrinter.Portrait)  # QPrinter.Landscape
        printer.setPaperSize(QPrinter.A4)
        margeG = 20.0  # marge gauche (mm)
        margeH = 10.0  # marge haute (mm)
        margeD = 10.0  # marge droite (mm)
        margeB = 10.0  # marge basse (mm)
        printer.setPageMargins(margeG, margeH, margeD, margeB, QPrinter.Millimeter)
     
        # Charge le texte html
        with open("texte.html", 'r', encoding='utf-8') as fs:
            texte = fs.read().rstrip()
     
        # Charge l'en-tête html s'il y en a une
        entete = ""
        fic_entete = "entete.html"
        if os.path.exists(fic_entete):
            with open(fic_entete, 'r', encoding='utf-8') as fs:
                entete = fs.read().rstrip()
     
        # Charge le bas de page html s'il y en a un
        basdepage = ""
        fic_basdepage = "basdepage.html"
        if os.path.exists(fic_basdepage):
            with open(fic_basdepage, 'r', encoding='utf-8') as fs:
                basdepage = fs.read().rstrip()
     
        # Lance l'aperçu avant impression 
        # ici, parent=None, mais dans un autre contextre, donner le vrai parent
        window = PrinterDialog(printer, texte, entete, basdepage, None)
        window.exec_()
     
        # inutile ici puisque le QPrintPreviewDialog a sa propre boucle
        #     de traitement des évènements avec sa méthode .exec_()
        # sys.exit(app.exec_())
    Avec ça, tout fonctionne, y compris le changement de format à la volée (portrait / paysage) ou le changement des marges également à la volée! C'est impressionnant! Les zooms fonctionnent également très bien.

    Et merci pour l'idée!

    Ouf...

    [edit] à noter que si on peut imprimer sur une "vraie" imprimante, on peut aussi imprimer dans une "fausse" imprimante "pdf" comme pdfcreator (ou équivalente), pour créer un fichier pdf!
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

Discussions similaires

  1. Concepteur RAVE sous DELPHI 7 : aperçu avant impression
    Par tarbala dans le forum Composants VCL
    Réponses: 4
    Dernier message: 01/06/2020, 00h37
  2. Aperçu avant impression d'un TRichEdit
    Par PoOky dans le forum Composants VCL
    Réponses: 2
    Dernier message: 31/01/2016, 23h19
  3. Réponses: 6
    Dernier message: 04/10/2005, 20h18
  4. Aperçu avant impression
    Par Zebulon777 dans le forum Access
    Réponses: 18
    Dernier message: 15/09/2005, 10h46
  5. [Débutante] Aperçu avant impression d'un composant
    Par gwendo dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 09/07/2004, 09h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo