View  Edit  Attributes  History  Attach  Print  Search

PYQTLAB

PyQt Lab' : Dessin : Déplacer une ligne horizontale à l'aide d'un slider vertical sur une image

Par X. HINAULT - Juin 2013

Ce que l'on va faire ici

  • Dans ce code PyQt, je montre comment mobiliser une ligne horizontale sur une image à l'aide d'un slider vertical. Ceci pourra servir de base pour réaliser une interface d'analyse par exemple d'une rangée de pixels d'une image ou ce genre de choses.
  • Ce code montre tout l'intérêt de l'utilisation concomitante d'un QImage et d'un QPixmap :
    • à la base le QImage sert à manipuler l'image au niveau des pixels, etc... et le QPixmap sert à l'affichage dans le QLabel,
    • mais il est possible également de dessiner sur un QPixmap...
    • du coup, ici, nous voyons comment il est possible de recharger le QImage dans le QPixmap avant d'effectuer le dessin transitoire voulu : l'intérêt principal est que le QImage reste inchangé, donnant un effet dynamique intéressant.
  • Remarquer au passage la fluidité obtenue et le peu de ressources systèmes utilisées.

Pré-requis

  • python 2.7
  • pyqt4.x

Le fichier d'interface *.py

  • Fichier obtenu automatiquement avec l'utilitaire pyuic4 à partir du fichier *.ui créé avec QtDesigner :
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file
#
# Created: Wed Jun 12 15:51:19 2013
#      by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName(_fromUtf8("Form"))
        Form.resize(372, 265)
        self.labelImage = QtGui.QLabel(Form)
        self.labelImage.setGeometry(QtCore.QRect(5, 15, 320, 240))
        self.labelImage.setStyleSheet(_fromUtf8("background-color: rgb(180, 180, 180);"))
        self.labelImage.setText(_fromUtf8(""))
        self.labelImage.setObjectName(_fromUtf8("labelImage"))
        self.verticalSlider = QtGui.QSlider(Form)
        self.verticalSlider.setGeometry(QtCore.QRect(335, 10, 31, 251))
        self.verticalSlider.setMaximum(240)
        self.verticalSlider.setOrientation(QtCore.Qt.Vertical)
        self.verticalSlider.setTickPosition(QtGui.QSlider.TicksBothSides)
        self.verticalSlider.setTickInterval(20)
        self.verticalSlider.setObjectName(_fromUtf8("verticalSlider"))

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "PyQt : dessin : Ligne H controlée par slider V", None, QtGui.QApplication.UnicodeUTF8))


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    Form = QtGui.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())
 

Le fichier d'application *Main.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

# par X. HINAULT - Mai 2013 - Tous droits réservés
# GPLv3 - www.mon-club-elec.fr

# modules a importer
from PyQt4.QtGui import *
from PyQt4.QtCore import *  # inclut QTimer..
import os,sys

from  tuto_pyqt_dessin_qpixmap_qimage_ligneH_sliderV import * # fichier obtenu à partir QtDesigner et pyuic4

# +/- variables et objets globaux

class myApp(QWidget, Ui_Form): # la classe reçoit le Qwidget principal ET la classe définie dans test.py obtenu avec pyuic4
        def __init__(self, parent=None):
                QWidget.__init__(self) # initialise le Qwidget principal
                self.setupUi(parent) # Obligatoire

                # --- Variables de classe

                # --- Paramétrage des widgets de l'interface GUI si nécessaire ---

                # --- Connexions entre signaux des widgets et fonctions
                # connecte chaque signal utilisé des objets à l'appel de la fonction voulue

                self.connect(self.verticalSlider, SIGNAL("valueChanged(int)"), self.verticalSliderValueChanged)

                # --- Code actif initial  ---

                # Dessin avec QPixmap (affichage) et QImage (I/O, accès pixels)

                # création d'un QImage permettant l'accès aux pixels
                self.image=QImage(self.labelImage.size(),QImage.Format_RGB32) # crée image RGB 32 bits même taille que label

                #-- opérations de dessin sur le QImage
                self.image.fill(QColor(255,255,255)) # fond blanc

                self.painterImage=QPainter(self.image) # associe QPainter (classe de dessin) au Qimage

                # tracé rectangle vide
                self.painterImage.drawRect(10,10,50,50) # dessin rectangle : drawRect (self, int x, int y, int w, int h)

                # tracé rectangle plein
                self.painterImage.fillRect(150,150,30,30,QColor(255,255,0)) # fillRect (self, int x, int y, int w, int h, QColor b)

                self.painterImage.end() # ferme le painter - n'est plus accessible après

                # le QImage est modifié une fois pour toute : seul le QPixmpa sera affecté ensuite par les mouvements du slider

                #-- initialisation  initiale pixmap
                self.pixmap=QPixmap.fromImage(self.image)

                self.updateLine(0) # initialisation position ligne

        # --- les fonctions appelées, utilisées par les signaux des widgets ---

        def verticalSliderValueChanged(self, valeur): # fonction appelée si changement valeur slider - reçoit la valeur courante
                print("Slider vertical modifié : valeur = " + str(valeur))

                self.updateLine(valeur) # initialisation position ligne


        # --- les fonctions appelées, utilisées par les signaux hors widgets ---

        # --- fonctions de classes autres---

        # fonction de mise à jour de la ligne
        def updateLine(self, valeurIn):

                # chargement qu QImage dans le QPixmap
                #self.pixmap=QPixmap.fromImage(self.image) # cette ligne ne met pas à jour le QPixmap...
                self.pixmap.convertFromImage(self.image) # recharge le QImage dans le QPixmap existant - met à jour le QPixmap

                self.painterPixmap=QPainter(self.pixmap) # associe QPainter (classe de dessin) au QPixmap

                # tracé ligne horizontale sur le Pixmap = laise QImage inchangé ++
                self.painterPixmap.setPen(QPen(QColor(255,0,0),1)) # fixe la couleur du crayon et la largeur pour le dessin - forme compactée          
                valeur=239-valeurIn
                self.painterPixmap.drawLine(0,valeur,self.image.width(),valeur) # dessin rectangle : drawRect (self, int x, int y, int w, int h)

                # il existe d'autres possibilités de dessin (polygone, chemin, etc..) voir : http://pyqt.sourceforge.net/Docs/PyQt4/qpainter.html

                self.painterPixmap.end() # ferme le painter - n'est plus accessible après

                #-- affichage du QPixmap dans QLabel
                self.labelImage.setPixmap(self.pixmap) # met à jour le qpixmap affiché dans le qlabel  

# -- Autres Classes utiles --

# -- Classe principale (lancement)  --
def main(args):
        a=QApplication(args) # crée l'objet application
        f=QWidget() # crée le QWidget racine
        c=myApp(f) # appelle la classe contenant le code de l'application
        f.show() # affiche la fenêtre QWidget
        r=a.exec_() # lance l'exécution de l'application
        return r

if __name__=="__main__": # pour rendre le code exécutable
        main(sys.argv) # appelle la fonction main

 

Utilisation

  • Les 2 fichiers suivants sont à enregistrer dans un même répertoire, l'un en nom.py et l'autre en nomMain.py.
  • Puis lancer l'application depuis Geany ou équivalent, en exécutant le fichier nomMain.py
  • Ensuite, mobiliser le curseur du slider : la ligne se positionne en conséquence.