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.
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.
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_())
Pour tester ce code, il vous faudra des images. Vous pouvez télécharger les 2 qui ont été utilisées ici :
# 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_())
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…
#!/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 :
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.
QtGui.QColor(255,255,255)
<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
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()
En premier, il fau configurer un nouvel intrepéteur :
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à…
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_())