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 :

QTextEdit lien et mise en forme


Sujet :

PyQt Python

  1. #1
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut QTextEdit lien et mise en forme
    Bonjour,

    j'utilise un Qtextedit en lecture seule pour renseigner des fichiers joints dans une interface.
    pas de souci pour charger le qtextedit avec du texte, en gros à chaque fichier renseigné, j'ajoute une ligne au qtextedit en ne renseignant que le nom du fichier et son extension.
    J'aimerais que ce QTextedit permette d'ouvrir les fichiers chargés en cliquant sur leurs noms (pour que l'utilisateur puisse bien vérifier ce qu'il a chargé).

    J'ai bien réussi à gérer la couleur, l'évènement clic avec la récupération de l'info dans chaque ligne...
    La partie ouverture n'est pas dans les lignes de code ci-dessous mais elle ne me posera pas de pb -> l'info de ligne correspondra à la clé d'un dico et un "os.system" ouvrira la value du dico.

    L'exemple de code ci-dessous fait l'affaire mais il a 2 petits détails qui me chiffonnent!
    J'aimerais que le pointeur de ma souri bascule sur le "doigt" uniquement en zone texte! et là ça bascule uniquement en zone ligne...
    je me suis obligé à ajouter 1 ligne vide pour permettre une condition de retour au pointeur classique en zone de QTextEdit vide... (c'est moche non comme approche?)

    J'ai pas réussi à trouver 1 moyen de basculer le pointeur en mode normal sur les "restes" de ligne... genre à droite du dernier caractère de chaque ligne.
    J'ai essayé de m'en sortir avec les atBlockEnd du QTextCursor avec la position de ma souri mais je commence à tourner en rond... d'où ma présence ici,
    comment vous auriez fait?
    merci!

    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
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
                                 QVBoxLayout, QTextEdit)
    from PyQt5.QtCore import Qt
    from PyQt5.QtGui import QTextCursor
    import sys
     
    class text_edit(QTextEdit):
        def __init__(self):
            super().__init__()
            self.setMouseTracking(True)
            self.setReadOnly(True)
     
        def mouseMoveEvent(self, event):
            position = event.pos()
            self.cursor = self.cursorForPosition(position)
            self.cursor.select(QTextCursor.LineUnderCursor)
            self.line = self.cursor.selectedText()
            if self.line !="":
                self.setCursor(Qt.PointingHandCursor)
                self.viewport().setCursor(Qt.PointingHandCursor)
            else:
                self.setCursor(Qt.ArrowCursor)
                self.viewport().setCursor(Qt.ArrowCursor)    
     
        def mousePressEvent(self, event):
            print(self.line)
     
     
    class Fenetre_Principale(QMainWindow):
     
        def __init__(self):
            super().__init__()
     
            self.edit = text_edit()
            self.link_format = '<span style="color:blue;">{}</span>'
            self.destroy_format = '<span style="color:red;">{}</span>'
     
            self.edit.append(self.link_format.format("première ligne") + "  " + self.destroy_format.format("X"))
            self.edit.append(self.link_format.format("seconde ligne"))
            self.edit.append(self.link_format.format("troisième ligne"))
            self.edit.append(self.link_format.format("")) #ligne pour permettre de retrouver 1 poitneur normal en dehors des lignes saisies
     
            self.setCentralWidget(QWidget(parent=self))        
            layout_vertical_global=QVBoxLayout(self.centralWidget())
            layout_vertical_global.addWidget(self.edit)
     
            self.setFixedSize(600, 200)
     
            self.show()
     
     
    if __name__ == "__main__":
        appli = QApplication(sys.argv)
        fenetre_main = Fenetre_Principale()
        sys.exit(appli.exec())

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Bonjour

    J'aime beaucoup ton code (il m'a appris des trucs). Mais je n'ai malheureusement pas trouvé comment régler. J'ai tenté de regarder des trucs comme cursor.atEnd() ou bien compter les lignes insérées (pour voir si on pouvait dire "est-ce que je dépasse?") mais rien ne marche. Il semble que quand la dernière ligne est écrite, la zone est en quelque sorte "fermée" et ce qu'il y a après n'est pas de la ligne. Pareil pour la sélection d'une ligne (qui continue dans le "blanc" qui se trouve après), j'ai tenté de sélectionner par block au lieu de par ligne mais que dalle...

    Quelques détails
    • pas besoin de mettre des variables de travail en "self". Concrètement, le "cursor" n'étant utilisé que dans le mouseMoveEvent, il n'a pas besoin d'être attribut. Pareil pour "edit" et tes formats divers
    • quand on hérite d'un objet, toujours mettre dans son constructeur les paramètres "*args" et "**args", et les passer à l'objet d'origine dans super(). Cela permet à un utilisateur quelconque d'appeler ton objet exactement de la même façon qu'il appellerait l'objet d'origine (en lui passant par exemple "parent=...") => l'héritage face à l'évolution
    • quand on surcharge une méthode, toujours appeler la méthode d'origine (au cas où celle-ci aurait une action nécessaire)
    • if self.line !="" franchement !!!


    Mon code qui ne fait pas mieux mais qui, au-moins, respecte ces principes...
    Code python : 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
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
    							 QVBoxLayout, QTextEdit)
    from PyQt5.QtCore import Qt
    from PyQt5.QtGui import QTextCursor
    import sys
     
    class text_edit(QTextEdit):
    	def __init__(self, *args, **kwargs):
    		super().__init__(*args, **kwargs)
    		self.setMouseTracking(True)
    		self.setReadOnly(True)
    		self.__nblig=0
     
    	def append(self, s):
    		self.__nblig+=1
    		super().append(s)
     
    	def mouseMoveEvent(self, event):
    		position = event.pos()
    		cursor = self.cursorForPosition(position)
    		cursor.select(QTextCursor.BlockUnderCursor)
    		self.line = cursor.selectedText()
    		print("line=[%s], nb=%d" % (self.line, self.__nblig), cursor.atEnd())
    		if self.line:
    			self.setCursor(Qt.PointingHandCursor)
    			self.viewport().setCursor(Qt.PointingHandCursor)
    		else:
    			self.setCursor(Qt.ArrowCursor)
    			self.viewport().setCursor(Qt.ArrowCursor)	
    		super().mouseMoveEvent(event)
     
    	def mousePressEvent(self, event):
    		print(self.line)
    		super().mousePressEvent(event)
     
     
    class Fenetre_Principale(QMainWindow):
    	def __init__(self, *args, **kwargs):
    		super().__init__(*args, **kwargs)
     
    		link_format = '<span style="color:blue;">{}</span>'
    		destroy_format = '<span style="color:red;">{}</span>'
     
    		self.setCentralWidget(QWidget(parent=self))		
    		edit = text_edit(parent=self.centralWidget())
     
    		edit.append(link_format.format("première ligne") + "  " + destroy_format.format("X"))
    		edit.append(link_format.format("seconde ligne"))
    		edit.append(link_format.format("troisième ligne"))
    		#edit.append(None) #ligne pour permettre de retrouver 1 pointeur normal en dehors des lignes saisies
     
    		layout_vertical_global=QVBoxLayout(self.centralWidget())
    		layout_vertical_global.addWidget(edit)
     
    		self.setFixedSize(600, 200)
     		self.show()
     
    if __name__ == "__main__":
    	appli = QApplication(sys.argv)
    	fenetre_main = Fenetre_Principale()
    	sys.exit(appli.exec())		# Je ne savais pas que exec() fonctionnait, je pensais que c'était exec_() obligatoire !!!
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Merci pour la réponse et les corrections de codes!
    en changeant le QTextEdit pour un QTextBrowser la gestion du lien est simplifié et la dynamique avec la souri parait ok.
    Du coup, il me reste "plus" qu'à voir comment je peux gérer le setHtml avec différentes lignes (dynamiques)... mais c'est un autre sujet


    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
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
    							 QVBoxLayout, QTextEdit, QTextBrowser)
    from PyQt5.QtCore import Qt
    from PyQt5.QtGui import QTextCursor
    import sys
     
    class text_browser(QTextBrowser):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.setOpenExternalLinks(True)
     
    class Fenetre_Principale(QMainWindow):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
     
            link_format = '<span style="color:blue;">{}</span>'
            destroy_format = '<span style="color:red;">{}</span>'
     
            self.setCentralWidget(QWidget(parent=self))		
            edit = text_browser(parent=self.centralWidget())
     
            edit.setHtml('<a href="C:/Users/clement/Desktop/tuto3.pdf">text</a>')        
     
            layout_vertical_global=QVBoxLayout(self.centralWidget())
            layout_vertical_global.addWidget(edit)
     
            self.setFixedSize(600, 200)
            self.show()
     
    if __name__ == "__main__":
    	appli = QApplication(sys.argv)
    	fenetre_main = Fenetre_Principale()
    	sys.exit(appli.exec())

  4. #4
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Bonjour,
    quelques petite interrogations par rapport au message d'hier:
    "quand on surcharge une méthode, toujours appeler la méthode d'origine (au cas où celle-ci aurait une action nécessaire)"
    cela fait bien référence aux super().mouseMoveEvent(event) et super().mousePressEvent(event) situés à la fin de leurs méthodes respectives?
    si oui, je ne comprend pas bien ce que ces lignes permettent de gérer... dans quel cas ça pourrait poser pb si on ne les appelle pas?

    Sinon, ci dessous le code modifié avec ce que je cherchais à faire (pouvoir supprimer des lignes avec des liens + un pointeur qui se met en forme correctement).
    NB: pas certain que la partie html soit propre (j'en ai jamais fait, je commence à peine... je suis en plein tuto).

    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
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
    							 QVBoxLayout, QTextEdit, QTextBrowser)
    from PyQt5.QtCore import Qt
    from PyQt5.QtGui import QTextCursor
    import sys
     
    class text_browser(QTextBrowser):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.setOpenExternalLinks(True)
            self.setMouseTracking(True)
            self.setReadOnly(True)
            self.__nblig=0
     
        def append(self, s):
            self.__nblig+=1
            super().append(s)
     
        def mouseMoveEvent(self, event):
            position = event.pos()
            cursor = self.cursorForPosition(position)
            cursor_word = self.cursorForPosition(position)
     
            cursor.select(QTextCursor.BlockUnderCursor)
            cursor_word.select(QTextCursor.WordUnderCursor)
     
            self.line = cursor.selectedText()
            self.word = cursor_word.selectedText()
            print("line=[%s], nb=%d, word_survolé=[%s]" % (self.line, self.__nblig, self.word), cursor.atEnd())
            super().mouseMoveEvent(event)
     
        def mousePressEvent(self, event):
            position = event.pos()
            cursor = self.cursorForPosition(position)
            cursor.select(QTextCursor.BlockUnderCursor)
            if self.word == "X":
                cursor.select(QTextCursor.LineUnderCursor)
                cursor.removeSelectedText()
                cursor.deleteChar()
            super().mousePressEvent(event)
     
     
     
    class Fenetre_Principale(QMainWindow):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
     
            link_format = ('<a href="C:/Users/clement/Desktop/tuto3.pdf">{}</a>' +
                           '<span style="color:black;">{}</span>' +
                           '<a href="#" role="button">{}</a>')
     
            self.setCentralWidget(QWidget(parent=self))		
            edit = text_browser(parent=self.centralWidget())
     
            edit.append(link_format.format("ligne1", " " ,"X"))
            edit.append(link_format.format("ligne2", " " ,"X"))
            edit.append(link_format.format("ligne3", " " ,"X"))
            edit.append(link_format.format("ligne4", " " ,"X"))
     
            layout_vertical_global=QVBoxLayout(self.centralWidget())
            layout_vertical_global.addWidget(edit)
     
            self.setFixedSize(600, 200)
            self.show()
     
    if __name__ == "__main__":
    	appli = QApplication(sys.argv)
    	fenetre_main = Fenetre_Principale()
    	sys.exit(appli.exec())

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par clement_74 Voir le message
    cela fait bien référence aux super().mouseMoveEvent(event) et super().mousePressEvent(event) situés à la fin de leurs méthodes respectives?
    Exact

    Citation Envoyé par clement_74 Voir le message
    si oui, je ne comprend pas bien ce que ces lignes permettent de gérer... dans quel cas ça pourrait poser pb si on ne les appelle pas?
    Tu n'en sais rien, et moi non plus. Et c'est justement pour ça qu'il faut les appeler. Parce que tu ne sais pas ce que ça ne fera pas si tu ne l'appelles pas.
    Il faut bien comprendre que ton objet, qui hérite d'un QTextEdit, doit offrir à minima à l'appelant les mêmes services qu'un QTextEdit de base. C'est d'ailleurs aussi pour cette raison qu'il faut rajouter "*args" et "**kwargs" dans le __init__() et les passer à super(). Parce que l'appelant peut avoir envie d'appeler ton objet exactement de la même façon qu'il appelle un QTextEdit, avec des paramètres attendus par celui-ci et que tu ne connais pas. Donc tu ne les connais pas mais en fait tu t'en branles, tu les reçois dans *args et **kwargs et tu les passes au parent sans te poser de question.
    Et là c'est pareil. L'appelant qui utilise ton mousePressEvent() a au minimum besoin du mousePressEvent() d'origine ; ce que fait le tien reste "en plus". Mais tu ne sais pas ce que fait le mousePressEvent() d'origine (qui, d'ailleurs, peut lui-même utiliser un mousePressEvent d'un objet de plus haut niveau) donc il te faut l'appeler pour pouvoir obtenir son travail.
    Tu as la même chose avec le append() dans mon exemple. J'ai tenté un compteur de lignes, compteur qui s'incrément quand l'appelant ajoute une ligne. Il fallait bien que quand l'appelant appelle append(), cet ajout se fasse réellement donc j'appelle en final super().append() pour obtenir l'ajout concret de la ligne (sinon le textEdit reste vide).
    En POO on appelle ça la "délégation" (l'héritier qui délègue le travail d'origine à son ancètre). Dans beaucoup de langages objet (C++, php) cette délégation est implicite (elle se fait sans que tu le demandes) mais pas en Python où tu dois la demander manuellement.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 300
    Points : 6 780
    Points
    6 780
    Par défaut
    Citation Envoyé par clement_74 Voir le message
    "quand on surcharge une méthode, toujours appeler la méthode d'origine (au cas où celle-ci aurait une action nécessaire)"
    cela fait bien référence aux super().mouseMoveEvent(event) et super().mousePressEvent(event) situés à la fin de leurs méthodes respectives?
    si oui, je ne comprend pas bien ce que ces lignes permettent de gérer... dans quel cas ça pourrait poser pb si on ne les appelle pas?
    Salut,

    Il est nécessaire de renvoyer l'event au framework pour qu'il termine l'action.

    Lors d'un appui sur un QPushButton, par exemple, celui-ci change d'apparence et l'event est lancé et pour que le widget retrouve son apparence il faut que tu lui renvoie son event.

    Les modifications d'apparence ne peuvent se faire que à l'intérieur d'un paintEvent or ce paintEvent est interrompu si tu réimplémentes l'event.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 8
    Dernier message: 09/09/2007, 12h52
  2. Mise en forme CSS et lien
    Par debie1108 dans le forum Mise en page CSS
    Réponses: 6
    Dernier message: 14/02/2007, 16h01
  3. Mise en forme d'un lien
    Par Blink182 dans le forum Mise en page CSS
    Réponses: 4
    Dernier message: 02/11/2006, 17h24

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