Outils pour utilisateurs

Outils du site


stu:python_gui:pyqt_faq

FAQ PySide

- Graphisme

- Comment faire un rectangle semi-transparent (par dessus une autre image) ?

Un widget (héritant de QWidget) peut être peint avec un fond transparent :

class ZoneDessin(QWidget) :
   ...
   def paintEvent(self,e) :
       p=QtGui.QPainter(self)
       p.setBrush(QtGui.QColor(0,0,255,200))
       p.drawRect(0,0,self.width(),self.height())

Les 4 paramètres donnés pour la couleur correspondent respectivement au rouge, vertn bleu, canal alpha. Chaque valeur est entre 0 et 255. Pour le canal alpha, 255 correspond à une couleur opaque. Dans l'exemple, nous avons don du bleu vif légèrement transparent.

- Mettre une image en fond de la fenêtre

L'image doit être accessible au programme python. Dans l'exemple qui suit, elle devra être placée dans le même répertoire que le fichier .py. Attention, si la fenêtre principale hérite de QWidget, au lieu de QMainWindow, ça ne fonctionnera pas.

fond_fenetre.py
from PySide import QtGui
import sys
 
class Fenetre(QtGui.QMainWindow): 
    def __init__(self) :
        super().__init__()
        self.resize(400,70)
        self.setWindowTitle("Fenêtre avec fond")
        self.setStyleSheet('Fenetre {background-image : url(back.png)}')
        b=QtGui.QPushButton("Bouton",self)
        b.setGeometry(200-50,20,100,40)
        self.show()
 
app=QtGui.QApplication(sys.argv)
frame=Fenetre()
sys.exit(app.exec_())

- Mettre un bouton par dessus une zone de dessin contenant une image (et une imag esur le bouton...)

Pour tester ce code, il vous faudra des images. Vous pouvez télécharger les 2 qui ont été utilisées ici :

images_boutons.py
# Dessiner une image en utilisant paintEvent
from PySide import QtGui,QtCore
import sys
 
# La zone de dessin 
    class ZoneDessin(QtGui.QWidget) :
    def __init__(self,parent=None) :
        super().__init__(parent)
        # L'image qui servira de fond
        self.pixmap=QtGui.QPixmap("back.png")
        # On ajoute unbouton dans la zone de dessin
        bouton=QtGui.QPushButton("",self)
        bouton.setGeometry(20,20,70,70)
        # On choisit une image à mettre sur le bouton
        bouton.setStyleSheet("background-image: url(imgbout.png);");
 
# On peint la zone de dessin avec l'image de fond
# On pourrait même faire une animation, ou des zones cliquables
    def paintEvent(self,e) :
        p=QtGui.QPainter(self)
        p.drawPixmap(10,10,200,200,self.pixmap)
 
class Fenetre(QtGui.QMainWindow):
    def __init__(self,parent=None) :
        super().__init__(parent)
        self.resize(420,420)
        self.setWindowTitle("Dessin / Bouton")
        dessin=ZoneDessin(self)
        dessin.setGeometry(10,10,400,400)
 
app=QtGui.QApplication(sys.argv)
frame=Fenetre()
frame.show()
sys.exit(app.exec_())

- Utilisation des dégradés (gradient)

Le code suivant affiche un disque rempli avec un dégradé radial lorsqu'on clique à la souris. Le point le plus important à comprendre est probablement que le dégradé n'est pas relatif à la position du disque. Il faut considérer le disque comme une zone qui permet de découvrir le dégradé. En conséquence, le dégradé doit être repositionné en même temps qu'on positionne l'objet.

from PySide import QtGui,QtCore
import sys
 
class ZoneDessin(QtGui.QWidget) :
 
    def __init__(self,parent=None) :
        super().__init__(parent)
        self.listepoints=[]
 
    def mousePressEvent(self,e) :
        self.listepoints.append((e.x(),e.y()))
        self.repaint()
        print(e.x(), e.y())
 
    def paintEvent(self,e) :
        p=QtGui.QPainter(self)
        for l in self.listepoints :
            gradient = QtGui.QRadialGradient(l[0]-5, l[1]-5, 35)                                                                     
            gradient.setColorAt(0, QtCore.Qt.yellow)
            gradient.setColorAt(0.3, QtCore.Qt.red)
            gradient.setColorAt(1, QtCore.Qt.blue)                                      
            p.setBrush(QtGui.QBrush(gradient))                                                                         
            p.drawEllipse(l[0]-20,l[1]-20,40,40)
 
class Fenetre(QtGui.QMainWindow):
    def __init__(self,parent=None) :
        super().__init__(parent)
        self.resize(420,420)
        self.setWindowTitle("Dessin gradients")
        dessin=ZoneDessin(self)
        dessin.setGeometry(10,10,400,400)
 
app=QtGui.QApplication(sys.argv)
frame=Fenetre()
frame.show()
sys.exit(app.exec_())

Pour constater la difficulté de positionnement du dégradé, il suffit de retirer les lignes :

gradient = QtGui.QRadialGradient(l[0]-5, l[1]-5, 35)                                                                     
gradient.setColorAt(0, QtCore.Qt.yellow)
gradient.setColorAt(0.3, QtCore.Qt.red)
gradient.setColorAt(1, QtCore.Qt.blue)                                      
p.setBrush(QtGui.QBrush(gradient))

de la boucle for et de placer avant la boucle for:

gradient = QtGui.QRadialGradient(200, 200, 35) # <= Ligen modifiée
gradient.setColorAt(0, QtCore.Qt.yellow)
gradient.setColorAt(0.3, QtCore.Qt.red)
gradient.setColorAt(1, QtCore.Qt.blue)                                      
p.setBrush(QtGui.QBrush(gradient))
for l in self....

Puis cliquer sur le bord de la fenêtre et s'approcher du centre en continuant d'afficher des disques…

- Jouer une animation

#!/usr/bin/env python
 
import sys
from PyQt4 import QtGui, QtCore
 
FSCREEN = (1024, 768)
 
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(0,0, FSCREEN[0], FSCREEN[1])
        self.setWindowTitle("Play Movie")
        self.movie_screen = QtGui.QLabel(self)
        self.movie_screen.setGeometry(0,0,self.width(), self.height())
        self.movie_screen.setAlignment(QtCore.Qt.AlignCenter) 
        self.movie = QtGui.QMovie("anim_01.gif", QtCore.QByteArray(), self)
        #self.movie.setCacheMode(QtGui.QMovie.CacheNone) 
        self.movie_screen.setMovie(self.movie) 
        self.start()
 
 
    def start(self):
        self.movie.start()
        self.movie.finished.connect(self.stop)
 
        # Pour redimensionner :
        img = self.movie.currentImage()
        #print("======>", img.size())
        w = img.size().width()
        h = img.size().height()
        rapport = min((self.width())/ w, (self.height())/ h)
        #print("-----------")
        #print(self.width(), self.height(), w, h ,rapport)
        w, h = int(w * rapport), int(h * rapport)
        #print(w, h)
        self.movie.setScaledSize(QtCore.QSize(w, h))
 
    def stop(self):
        print("Stop Movie")
 
 
if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = MainWindow()
    win.show()
    app.exec_()

Vous devez télécharger aussi la gif :

- Fenêtres et Widgets

- Comment créer une page d'aide, de règles, de bienvenue... avec du texte mis en forme ?

from PySide import QtGui,QtCore,QtWebKit
import sys
 
class Fenetre(QtGui.QMainWindow): 
    def __init__(self,parent=None) :
        super().__init__(parent)
        self.resize(500,500)
        self.setWindowTitle("Blabla")
        self.page=QtWebKit.QWebView(self)
        self.page.setGeometry(10,10,480,480)
        self.page.setHtml("""<h1>Titre</h1>
        <p> Ceci est une bien belle page</p>
        """)
        # On peut aussi charger un fichier 
        #self.page.load("page.html")
 
app=QtGui.QApplication(sys.argv)
frame=Fenetre()
frame.show()
sys.exit(app.exec_())

Pour afficher des images (comme fond de page ou dasn le texte), il faut utiliser son chemin complet (dans le cas où la page est offline : ''file:… car le paramètre baseURL ne semble pas fonctionner correctement. Voici une manière de procéder pour afficher une image : <code python> # récupérer le path actuel pour rester portable path=os.getcwd() urlprefix = 'file:'+path … self.page.setHtml(“”“ <p> Ceci est une bien belle page</p> <img src=”{prefix}/Image_1.png“/> ”“”.format(prefix=urlprefix)) … </code> Le fond de la fenêtre peut être fixé en chageant la feuille de style embarquée : <code python> # récupérer le path actuel pour rester portable path=os.getcwd() urlprefix = 'file:'+path … self.page.setHtml(“”“ <html> <head> <style> body </style> </head> <body> <p> Ceci est une bien belle page</p> <img src=”{prefix}/Image_1.png“/> </body></html> ”“”.format(prefix=urlprefix)) … </code>

Attention aux double ''{{ utilisés avec format. </WRAP> ==== - Comment faire un bouton qui change d'apparence lors du survol par le curseur ? ==== Il faut écrire une classe qui hérite de QPushButton et réécrire les méthodes enterEvent et leaveEvent : <code python> “”“ Utilisation de enterEvent ”“” from PySide import QtGui,QtCore import sys import threading class BoutonSurvol(QtGui.QPushButton) : def enterEvent(self,e) : self.setStyleSheet('*{background-color:#f00}') def leaveEvent(self,e) : self.setStyleSheet('*{}') class Fenetre(QtGui.QMainWindow): def init(self,parent=None) : super().init(parent) self.resize(400,70) self.setWindowTitle(“Détection survol bouton”) self.createInterface() def clic(self) : self.button.setText(“Ouille”) def createInterface(self) : self.button=BoutonSurvol(“Survolez-moi….”,self) self.button.setGeometry(200-80,20,160,40) self.button.clicked.connect(self.clic) self.button.clicked.connect(self.clic) app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() sys.exit(app.exec_()) </code> On peut parvenir au même résultat en utilisant uniquement les feuilles de style: <code python> “”“ Utilisation de enterEvent ”“” from PySide import QtGui,QtCore import sys import threading class Fenetre(QtGui.QMainWindow): def init(self,parent=None) : super().init(parent) self.resize(400,70) self.setWindowTitle(“Détection survol bouton”) self.createInterface() def clic(self) : self.button.setText(“Ouille”) def createInterface(self) : self.button=QtGui.QPushButton(“Survolez-moi….”,self) self.button.setStyleSheet('*:hover{background-color:#f00}') self.button.setGeometry(200-80,20,160,40) self.button.clicked.connect(self.clic) app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() sys.exit(app.exec_()) </code> ==== - Comment ajouter un menu à une fenêtre ? ==== <code python> import sys from PySide import QtGui class WinMenu(QtGui.QMainWindow): def init(self): super().init() # Récupération barre de menu menubar = self.menuBar() # Nouveau menu fileMenu = menubar.addMenu('&File') formatMenu = menubar.addMenu('F&ormat') # Nouvel item dans le menu exitAction = QtGui.QAction( '&Exit', self) exitAction.triggered.connect(self.close) fileMenu.addAction(exitAction) self.setGeometry(300, 300, 250, 150) self.setWindowTitle('Menubar') app = QtGui.QApplication(sys.argv) ex = WinMenu() ex.show() sys.exit(app.exec_()) </code> ==== - Comment affecter une icone à une application ? ==== Si le widget principal est self : <code python> self.setWindowIcon(QtGui.QIcon(“icon.jpg”)) </code> ==== - Comment “swapper” entre deux affichages dans une fenêtre ? ==== Le premier contenu est placé dans un QFrame. Le second contenu est placé dans un autre QFrame. Puis on cache/montre l'un ou l'autre des 2 QFrames. <code python> # Deux “écrans” pour la même fenêtre from PySide import QtGui,QtCore import sys class Fenetre(QtGui.QWidget): def init(self,parent=None) : super().init(parent) self.resize(400,400) self.widget1=QtGui.QFrame(self) self.widget2=QtGui.QFrame(self) self.widget1.setGeometry(0,0,400,400) self.widget2.setGeometry(0,0,400,400) self.bouton1=QtGui.QPushButton(“Bouton1”,self.widget1) self.bouton1.setGeometry(0,0,200,20) self.bouton2=QtGui.QPushButton(“Bouton2”,self.widget2) self.bouton2.setGeometry(200,0,200,20) self.bouton1.clicked.connect(self.ecran2) self.bouton2.clicked.connect(self.ecran1) def ecran2(self) : print(“Vers ecran2”) self.widget1.hide() self.widget2.show() def ecran1(self) : print(“Vers ecran 1”) self.widget2.hide() self.widget1.show() app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() frame.ecran1() sys.exit(app.exec_()) </code> ==== - Comment faire s'enchaîner plusieurs fenêtres ==== La même méthode que précédemment peut être utilisée avec plusieurs fenêtres : <code python> # Deux fenêtres pour une même appli from PySide import QtGui,QtCore import sys class Fenetre1(QtGui.QWidget): def init(self,parent=None) : super().init(parent) self.resize(400,400) self.bouton=QtGui.QPushButton(“Aller fenêtre 2”,self) self.bouton.setGeometry(0,0,200,20) self.bouton.clicked.connect(vers_fenetre_2) class Fenetre2(QtGui.QWidget): def init(self,parent=None) : super().init(parent) self.resize(400,400) self.bouton=QtGui.QPushButton(“Aller fenêtre 1”,self) self.bouton.setGeometry(200,0,200,20) self.bouton.clicked.connect(vers_fenetre_1) def vers_fenetre_1() : frame2.hide() frame1.show() def vers_fenetre_2() : frame1.hide() frame2.show() app=QtGui.QApplication(sys.argv) frame1=Fenetre1() frame1.show() frame2=Fenetre2() sys.exit(app.exec_()) </code> ==== - Comment faire un bouton qui permet de quitter l'application ? ==== Le slot QApplication.quit est prédéfini, ainsi que le slot QWidget.close. Il suffit de connecter ce dernier au signal clicked d'un bouton : <code python> # self est la fenêtre principale b=QtGui.QPushButton(“Quit”,self) b.clicked.connect(self.close) </code> ==== - Comment fixer la taille et la position d'un Widget ? ==== La méthode setGeometry permet de fixer à la fois la taille et la position. On précise dans l'ordre (x,y) pour le coijn haut/gauche du widget, puis largeur et hauteur. L'origine du repère est le coin dupérieur gauche du widget parent. L'axe des ordonnées est dirigé vers le bas. <code python> w.setGeometry(10,10,200,100) </code> On peut aussi déplacer un widget sans modifier sa taille : <code python> w.move(20,10) </code> ==== - Comment modifier la police et sa taille sur un bouton ? ==== La méthode setFont permet de modifier la police associée à un Widget. On lui passe en paramètres un objet de type QFont qui permet de choisir la famille de police, la taille, la graisse etc… : <code python> bouton.setFont(QtGui.QFont(“Verdana”,25)) </code> ==== - Comment faire une liste déroulante ? ==== Les ComboBox sont des listes de choix. <file python qt_sample_combo.py> import sys from PySide import QtGui class Fen(QtGui.QWidget) : def choix(self,i) : print(“Vous avez choisi l'item numéro”,i) print(“Il s'agit de ”,self.combo.itemText(i)) def init(self,title) : super().init() self.resize(200,50) self.combo=QtGui.QComboBox(self) self.combo.insertItems(0,(“Dollars”,“Euros”,“Yen”)) self.combo.setGeometry(0,0,180,40) self.combo.activated.connect(self.choix) self.show() app = QtGui.QApplication(sys.argv) f=Fen(“Samble Combo”) sys.exit(app.exec_()) </file> ==== - Comment faire une boîte de dialogue ? ==== === Dialogues standards === Voici un exemple d'utilisation de boîtes de dialogue informatives, ou demandant un retour de l'utilisateur : <file python dialogboxes.py> from PySide import QtGui import sys app=QtGui.QApplication(sys.argv) msgBox = QtGui.QMessageBox() msgBox.setText(“Vous avez perdu.”) msgBox.exec_() msgBox = QtGui.QMessageBox() msgBox.setText(“Voulez-vous rejouer ?”) msgBox.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) msgBox.setDefaultButton(QtGui.QMessageBox.Yes) retour=msgBox.exec_() if retour==QtGui.QMessageBox.Yes : stri=“Vous avez choisi Oui. Merci d'avoir choisi Oui” else : stri=“Vous avez choisi Non. Merci d'avoir choisi Non” msgBox = QtGui.QMessageBox() msgBox.setText(stri) msgBox.exec_() sys.exit() </file> === Dialogues personnalisées === <code python> from PySide import QtGui,QtCore import sys class Fenetre(QtGui.QMainWindow): def init(self,parent=None) : super().init(parent) bouton=QtGui.QPushButton(“Parle moi…”,self) bouton.setGeometry(10,10,100,50) bouton.clicked.connect(self.dialogue) def dialogue(self) : msgBox = QtGui.QMessageBox() connectButton1 = msgBox.addButton(self.tr(“PasBavard”), QtGui.QMessageBox.ActionRole) connectButton2 = msgBox.addButton(self.tr(“Bavard”), QtGui.QMessageBox.ActionRole) abortButton = msgBox.addButton(QtGui.QMessageBox.Abort) msgBox.exec_() if msgBox.clickedButton() == connectButton1: print(“bla….”) elif msgBox.clickedButton() == connectButton2: print(“bla bla bla bla bla bla….”) elif msgBox.clickedButton() == abortButton: pass app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() sys.exit(app.exec_()) </code> === - Boîte de dialogue avec une image de fond === <code python> from PySide import QtGui,QtCore import sys app = QtGui.QApplication(sys.argv) msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Information, “QMessageBox avec Image de fond”, “Une boîte de dialogue avec une image de fond”) palette = QtGui.QPalette() palette.setBrush(QtGui.QPalette.Background, QtGui.QBrush(QtGui.QImage(“back.png”))) msgBox.setPalette(palette) msgBox.show() sys.exit(app.exec_()) </code> === - Comment faire une boîte de sélection de couleur === <code python> from PySide import QtGui,QtCore import sys class ZoneDessin(QtGui.QWidget) : pass class Fenetre(QtGui.QMainWindow): def init(self,parent=None) : super().init(parent) self.resize(200,60) self.setWindowTitle(“Dialogue Color”) b = QtGui.QPushButton(“Choisir une couleur”, self) b.setGeometry(10,10,180,40) b.clicked.connect(self.choixcouleur) def choixcouleur(self): color = QtGui.QColorDialog.getColor(QtGui.QColor(255,255,255), self) print(“Vous avez choisi la couleur : ”,color) app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() sys.exit(app.exec_()) </code> La boîte de dialogue de sélectiond e couleur est ouverte par : <code> QtGui.QColorDialog.getColor(QtGui.QColor(255,255,255), self) </code> Elle renvoie la couleur sélectionnée. La valeur QtGui.QColor(255,255,255) est la couleur sélectionnée par défaut dans la boîte. === - Boite de sélection de fichier ==== <code python> import sys from PySide import QtGui class Example(QtGui.QMainWindow): def init(self): super().init() openFile = QtGui.QAction('Open', self) openFile.triggered.connect(self.openFileDialog) menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(openFile) self.label = QtGui.QLabel(“…”, self) self.label.setGeometry(10,10,400,100) self.resize(420,120) self.show() def openFileDialog(self): fname, _ = QtGui.QFileDialog.getOpenFileName(self, 'Open file') print(fname) self.label.setText(fname) app = QtGui.QApplication(sys.argv) ex = Example() #sys.exit(app.exec_()) </code> ==== - Comment récupérer la résolution de l'écran ==== L'objet représentant l'application permet d'accéder à la résolution des différents écrans. Dans le cas où on n'utilise qu'un seul écran : <code python> import sys from PySide import QtGui app = QtGui.QApplication(sys.argv) print(app.desktop().screenGeometry()) r = app.desktop().screenGeometry() print(r.width()) </code> affichera : <code> PySide.QtCore.QRect(0, 0, 1366, 768) 1366 </code> screenGeometry peut prendre en paramètre un entier, qui correspond au numéro de l'écran dans le cas multi-écrans. ==== - Utiliser les feuilles de style pour personnaliser les contrôles ==== Dans la doc de Qt (à adapter pour PySide) : http://doc.qt.io/qt-4.8/stylesheet-examples.html ===== - Gestion des événements ===== ==== - Comment gérer les événements clavier (déplacer un objet au clavier) (Méthode 1) ? ==== Les différents codes des touches (comme QtCore.Qt.Key_Left) sont sur la page : PySide/QtCore/Qt.html#PySide.QtCore.PySide.QtCore.Qt.Key <code python> from PySide import QtGui,QtCore import sys class ZoneDessin(QtGui.QWidget) : def init(self,parent=None) : super().init(parent) self.position=[100,100] # Attention à ne pas oublier ça, nécessaire à la réception # des événements clavier self.setFocusPolicy(QtCore.Qt.StrongFocus) def move(self,direction) : self.position[0]+=direction[0] self.position[1]+=direction[1] self.repaint() def keyPressEvent(self,e) : if e.key() not in (QtCore.Qt.Key_Left,QtCore.Qt.Key_Right,\ QtCore.Qt.Key_Up,QtCore.Qt.Key_Down) : return super().keyPressEvent(e) if QtCore.Qt.Key_Left==e.key() : self.move1) if QtCore.Qt.Key_Right==e.key() : self.move2) if QtCore.Qt.Key_Up==e.key() : self.move3) if QtCore.Qt.Key_Down==e.key() : self.move4) def paintEvent(self,e) : p=QtGui.QPainter(self) p.setBrush(QtGui.QBrush(QtCore.Qt.SolidPattern)) p.drawEllipse(QtCore.QPoint(self.position[0],self.position[1]),15,15) class Fenetre(QtGui.QMainWindow): def init(self,parent=None) : super().init(parent) self.resize(420,420) self.setWindowTitle(“Keyboard”) dessin=ZoneDessin(self) dessin.setGeometry(10,10,400,400) app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() msg=QtGui.QMessageBox() msg.setText(“Utilisez les flèches du clavier”) msg.exec_() sys.exit(app.exec_()) </code> ==== - Comment gérer les événements clavier (déplacer un objet au clavier) (Méthode 2) ? ==== La méthode précédente, dans le cas d'un jeu par exemple, présente plusieurs défauts. D'une part le délai avant l'autorépétition de la touche peut être jugé trop long. D'autre part, l'appui sur deux touches simultanément pose problèmé (pour voir ce que verra votre application, ouvrez un traitement de texte, laissez la touche A appuyée, puis, pendant l'autorépétition, appuyez sur P. Vous constaterez que l'appui sur la touche A n'est plus traité…). Pour régler ce problème, une solution est de maintenir à jour un ensemble contenant les touches actuellement enfoncées. Cet ensemble sera mis à jour à chaque événement KeyPressEvent ou KeyReleaseEvent. Puis, un timer viendra régulièrement consulter cette table pour savoir quel est l'ensemble des touches appuyées. <code python> from PySide import QtGui,QtCore import sys # Var Globale contenant les touches actuellement enfoncées key_map=set() class ZoneDessin(QtGui.QWidget) : def init(self,parent=None) : super().init(parent) self.pos=[100,100] # Attention à ne pas oublier ça, nécessaire à la réception # des événements clavier self.setFocusPolicy(QtCore.Qt.StrongFocus) self.timer=QtCore.QBasicTimer() self.timer.start(20,self) def move(self,direction) : self.pos[0]+=direction[0] self.pos[1]+=direction[1] def keyPressEvent(self,e) : key_map.add(e.key()) def keyReleaseEvent(self,e) : key_map.discard(e.key()) def timerEvent(self,e) : print(key_map) if len(key_map)>0 : if QtCore.Qt.Key_Left in key_map : self.move5) if QtCore.Qt.Key_Right in key_map : self.move6) if QtCore.Qt.Key_Up in key_map : self.move7) if QtCore.Qt.Key_Down in key_map : self.move8) self.repaint() def paintEvent(self,e) : p=QtGui.QPainter(self) p.setBrush(QtGui.QBrush(QtCore.Qt.SolidPattern)) p.drawEllipse(QtCore.QPoint(self.pos[0],self.pos[1]),15,15) class Fenetre(QtGui.QMainWindow): def init(self,parent=None) : super().init(parent) self.resize(420,420) self.setWindowTitle(“Keyboard”) dessin=ZoneDessin(self) dessin.setGeometry(10,10,400,400) app=QtGui.QApplication(sys.argv) frame=Fenetre() frame.show() msg=QtGui.QMessageBox() msg.setText(“Utilisez les flèches du clavier”) msg.exec_() sys.exit(app.exec_()) </code> En appuyant simultanément sur deux flèches, vous pourrez déplacer l'objet en diagonale (ce qui ne fonctionne pas avec la version précédente). ===== - Audio ===== ==== - Comment jouer une musique de fond ==== === Solution avec Phonon === <code python> import sys from PySide import QtGui from PySide.phonon import Phonon class MainWindow(QtGui.QMainWindow): def init(self): super().init() startbutton = QtGui.QPushButton(“Start”, self) startbutton.setGeometry(10,10,100,40) self.resize(120,60) startbutton.clicked.connect(self.play) # Création des deux objets MediaObject et AudioAutput, qu'on connecte self.m_media = Phonon.MediaObject(self) self.m_media.setCurrentSource(Phonon.MediaSource(“1.mp3”)) audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self) Phonon.createPath(self.m_media, audioOutput) def play(self): self.m_media.play() app = QtGui.QApplication(sys.argv) mw = MainWindow() mw.show() sys.exit(app.exec_()) </code> === Solution avec PySide === La classe QSound permet de jouer un fichier wav sous Windows sans utiliser un module externe à PySide. Cette classe ne fonctionne toutefois pas avec une config Unix standard (sans Network Audio System). <code python> import sys from PySide import QtGui class MaFenetre(QtGui.QMainWindow) : def init(self) : super().init() self.creationInterface() self.show() self.compteur=0 # Création du slot def go_sound(self) : if self.sound.isFinished() : print(“Le son démarre”) self.sound.setLoops(-1) self.sound.play() else : # Le son ne s'arrête pas instantanément print(“Le son s'arrête”) self.sound.stop() def creationInterface(self) : self.setWindowTitle(“Son”) self.resize(100,40) bouton=QtGui.QPushButton(“Son ON/OFF”,self) bouton.setGeometry(10,10,80,20) bouton.clicked.connect(self.go_sound) print(QtGui.QSound.isAvailable()) # Il est fondamental que le son soit un attribut de # la classe (avec self). Dans le cas contraire, la variable # qui référence le son est détruite à la sortie de la méthode # qui l'a créée… et le son s'arrête. self.sound = QtGui.QSound(“sound.wav”) app=QtGui.QApplication(sys.argv) fen=MaFenetre() sys.exit(app.exec_()) </code> === Solution avec Pygame === Pour utiliser cette solution, le module pygame soit être installé. <code python> #Jouer un son en fond… # Utilise le module pygame (SDL) from PySide import QtGui,QtCore import sys import pygame import pygame.mixer class Fenetre(QtGui.QWidget): def init(self) : super().init() self.createInterface() self.show() def play(self) : self.zic.play() def stop(self) : self.zic.stop() def timerEvent(self,e) : print(“Timer func…”) def createInterface(self) : self.resize(400,30) bplay=QtGui.QPushButton(“Play”,self) bplay.setGeometry(0,0,200,30) bplay.clicked.connect(self.play) bstop=QtGui.QPushButton(“Stop”,self) bstop.setGeometry(200,0,200,30) bstop.clicked.connect(self.stop) self.zic = pygame.mixer.Sound(“musique.wav”) self.timer=QtCore.QBasicTimer() self.timer.start(2000,self) self.show() pygame.init() pygame.mixer.init() app=QtGui.QApplication(sys.argv) frame=Fenetre() sys.exit(app.exec_()) </code> ===== - Autres Questions ===== ==== - Lire des informations dans un fichier (par exemple une map pour un jeu) et la transformer en matrice ==== Données à entrer dans un fichier texte : <file - plan.txt> .* *..*.*…* *..*.*.* *..*…*.* *..*.*.* *..*.* *.*….*.* *..* *……..* </file> On veut lire ce fichier et créer une matrice qui contiendra des 0 ou des 1 dans chaque case (selon que le fichier contenait * ou .. Les matrices n'existent pas en Python (en revanche, elles existent dans le module Numpy, mais ne vous seront pas nécessaires sauf si vous gérez des données de grande taille). À la place on utilise des listes de listes. Voici un programme, peut être un peu cryptique, qui lit le fichier ci-dessus et crée une liste de listes : <file python fichier_matrice.py> def fictomat(nom_fichier) : “”“ Lit nom_fichier et renvoie une matrice de même dimension que celle lue. Aucune vérification n'est faite (caractère invalides, fichier inexistant etc…) ”“” with open(nom_fichier,“r”) as f : s = f.readlines() for i,l in enumerate(s) : val = {'.' : 0, '*' : 1} s[i] = [val[c] for c in l.strip()] return s import pprint matrice = fictomat(“plan.txt”) pprint.pprint(matrice) </file> Le programme affiche : <code> 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 0, 0, 1, 0, 1, 0, 0, 0, 1], [1, 0, 0, 1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 1, 0, 0, 0, 1, 0, 1], [1, 0, 0, 1, 1, 1, 0, 1, 0, 1], [1, 0, 1, 1, 1, 1, 0, 1, 0, 1], [1, 0, 1, 0, 0, 0, 0, 1, 0, 1], [1, 0, 1, 1, 1, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1 </code> Notez le dictionnaire val qui permet de faire la correspondance entre les caractères du fichier et les valeurs qu'on souhaite stocker dans la matrice. ==== Problème des timers (et plantages) avec Iep/Pyzo ==== Pyzo permet de lancer des programmes utilisant Pyside en gérant lui même la boucle des événements de Pyside. Cela permet dans certains cas de débugger efficacement un programme. Toutefois, si vous utilisez des timers, il se peut que lors d'un plantage, ou lorsque vous quittez, le timer continue de tourner. Pour vous en rentre compte, mettez un simple print('Salut') dans timerEvent et voyez si le timer s'arrête quand vous quittez. Si ce n'est pas le cas, 2 solution : - quitter proprement - demander à Pyzo de ne plus gérer la boucle des événements === - Quitter proprement === Lorsque l'utilisateur quitte le programme, la fonction closeEvent'' de la fenêtre principale est appelée. Si vous écrivez cette fonction, vous pourrez exécuter le code de votre choix automatiquement lors de la fermeture de la fenêtre. Vous pourrez par exemple arrêter le timer.

class Zonedessin(....)
   # possède un attribut self.timer
 
class Fenetre(QtGui.QMaintWindow):
    def __init__(self, parent=None):
        self.dessin = Zonedessin(...)
 
    def closeEvent(self, e):
        self.dessin.timer.stop()

Demander à Pyzo de ne plus gérer la boucle des événements

En premier, il fau configurer un nouvel intrepéteur :

  • Edit shell configuration
  • Ajouter une configuration
  • Choisir : GUI - None
  • Valider

Puis exécutez cet interpréteur.

Dans le programme principal, apportez quelques modifications. Si vous aviez :

app=QtGui.QApplication(sys.argv)
frame=Fenetre()
frame.show()
sys.exit(app.exec_()

Modifiez en :

app = QtGui.QApplication.instance()
if app is None: 
    app=QtGui.QApplication(sys.argv)
frame=Fenetre()
frame.show()
app.exec_()

Et voilà…

- Deux timers en parallèle

Dasn l'exemple suivant, on lance 2 timers avec des délais différents. L'un passe le fond de fenêtre en rouge et l'autre en bleu.

from PySide import QtGui, QtCore    
import sys                                                                                             
 
class Fenetre(QtGui.QMainWindow):                                                                      
    def __init__(self) :                                                                               
        super().__init__()                                                                             
        self.resize(400,70)                                                                            
        self.setWindowTitle("Fenêtre 2 timers")                                                        
        self.setStyleSheet('Fenetre {background-color : #eee;}')
        self.timer1 = QtCore.QBasicTimer()                                                             
        self.timer2 = QtCore.QBasicTimer()                                                             
        self.timer1.start(5000,self)                                                                   
        self.timer2.start(8000,self)                                                                   
        print("==>",self.timer1.timerId())                                                             
        print("==>",self.timer2.timerId())                                                             
        self.show()                                                                                    
 
    def timerEvent(self,e):                                                                            
        print(e.timerId())                                                                             
        if e.timerId() == self.timer1.timerId() :                                                      
            self.setStyleSheet('Fenetre {background-color: #e00;}')                                    
            print("Exec timer 1")                                                                      
        if e.timerId() == self.timer2.timerId() :                                                      
            self.setStyleSheet('Fenetre {background-color: #00e;}')                                    
            print("Exec timer 2")                                                                      
 
 
app=QtGui.QApplication(sys.argv)                                                                       
frame=Fenetre()                                                                                        
sys.exit(app.exec_())    
1) , 5)
-5,0
2) , 6)
5,0
3) , 7)
0,-5
4) , 8)
0,5
stu/python_gui/pyqt_faq.txt · Dernière modification: 2016/03/29 06:43 (modification externe)