C'est plus une question de goûts, personnellement je découpe quasiment toutes mes classes dans des fichiers séparés organisés dans des modules de manière logique (ex: gui.layout). C'est plus une habitude qu'autre chose.
Version imprimable
C'est plus une question de goûts, personnellement je découpe quasiment toutes mes classes dans des fichiers séparés organisés dans des modules de manière logique (ex: gui.layout). C'est plus une habitude qu'autre chose.
D'accord, je pense aussi que cela est mieux donc je vais essayer de tendre vers cela.
Actuellement, j'ai réussi à avoir ma gomme qui efface correctement les choses malheureusement, je ne peux pas récupérer la couleur que j'initialise dans le main .
Je vais essayer de trouver une solution peut - être aurais -je encore besoin de ta précieuse aide.
Bonne journée.^
Edit : J'ai résolu mon problème finalement maintenant je vais gérer la taille de ma gomme aussi tout seul si je peux.
J'ai suivi ton conseil, je refais l'architecture de mon programme dans ce but j'ai séparer la vue, la scène et la mainWindow du Main.
Malheureusement, j'ai un problème, je ne peux plus ouvrir mon Image :s
Main.py
MainWindow.pyCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 # -*- coding: utf-8 -*- #from ImageView.ImageView import ImageView from PySide.QtGui import QApplication from MainWindow.MainWindow import MainWindow import sys """ Class calling just the main window and showing it """ def main(args): app = QApplication(args) mainWindow = MainWindow() mainWindow.resize(600,400) mainWindow.show() sys.exit(app.exec_()) # Qt Main loop if __name__ == "__main__": main(sys.argv)
GraphicsScene.pyCode:
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 from PySide.QtCore import QSize, QRect, QRectF, Qt,QFile from PySide.QtGui import QApplication, QWidget, QPainter, QKeySequence, QColor, \ QCheckBox, QStatusBar, QIcon, QMessageBox, QImage, QRubberBand, QMainWindow, \ QAction, QMenuBar, QFileDialog, QScrollArea, QColorDialog, QPalette, QBrush, \ QPixmap, QGraphicsScene, QGraphicsView, QGraphicsPixmapItem, QPushButton # We call the class graphicsScene and we import it from Scene.GraphicsScene import GraphicsScene from View.GraphicsView import GraphicsView class MainWindow(QMainWindow): def __init__(self, *args): QMainWindow.__init__(self, *args) self.setWindowTitle("Freehand Line Test") self._fileName="-1" self.image = QImage() #Init of the Menu with the menubar and the icon self._createMenu() #Init of the toolbar with all tools rubber etc.. self._createToolBar() #Init of the status bar (at bottom) with ckbox and colorPicker self._createStatusBar() def _createMenu(self): """ Adding of actions for the tool bar """ self.iconToolBar = self.addToolBar("iconBar") """ Add of the Menu which contains all the features like of Open/Save/.. and the edit options """ menu = QMenuBar() self.setMenuBar(menu) _file = menu.addMenu('File') _edit = menu.addMenu("Edit") # Menu Open _action = QAction('Open', _file, shortcut=QKeySequence.Open) _action.triggered.connect(self.__actionOpen) _file.addAction(_action) def _createToolBar(self): #Adding of actions for the tool bar self.iconToolBar = self.addToolBar("iconBar") #About _actionAbout = QAction(self) _actionAbout.triggered.connect(self.__actionAbout) _actionAbout.setIcon(QIcon("Pics\info-icon.jpg")) _actionAbout.setStatusTip("Pop up the About dialog.") self.iconToolBar.addAction(_actionAbout) #Rubber _actionRubber = QAction(self) _actionRubber.triggered.connect(self.__actionRubber) _actionRubber.setIcon(QIcon("Pics\eraser2.png")) _actionRubber.setStatusTip("Enable or Disable eraser tool") self.iconToolBar.addAction(_actionRubber) #ColorPicker _actionColorPicker = QAction(self) _actionColorPicker.triggered.connect(self.__actionColorPicker) _actionColorPicker.setIcon(QIcon("Pics\Palette2.png")) _actionColorPicker.setStatusTip("Chosen color") self.iconToolBar.addAction(_actionColorPicker) def _createStatusBar(self): #Check Box rub statusbar = QStatusBar(self) self.setStatusBar(statusbar) self._ckbox = QCheckBox("Rubber On/Off") statusbar.addWidget(self._ckbox) if self._ckbox.isChecked(): self._mode = "rub" else : self._mode = "select" #Init of the boolean to know whether we select or rub self.image.mode = self._mode self._ckbox.stateChanged.connect(self.__rubberBoxChanged) #ColorPicker self._buttonColorPicker = QPushButton() self.__setColor(QColor(255,255,255)) self._buttonColorPicker.clicked.connect(self.__actionColorPicker) self._buttonColorPicker.setText("Color Picker") self._buttonColorPicker.setToolTip("Choose your color") #Bottom left #statusbar.addWidget(self._buttonColorPicker) #Bottom right statusbar.addPermanentWidget(self._buttonColorPicker) def getFileName(self): return self._fileName def setFileName(self,fileName): self._fileName = fileName def __actionOpen(self): self.__fileName = QFileDialog.getOpenFileName(self, "Open Image", "", "Image Files (*.png *.jpg *.bmp)") #self.setFileName(self._fileName) if self.__fileName: self.setFileName(self.__fileName[0]) self.scene = GraphicsScene(self.getFileName()) self.image = self.scene.img self.view = GraphicsView(self.scene) self.setCentralWidget(self.view) #self.image.setWorkingImage(QImage(self._fileName[0])) else: print "Invalid Image" QMessageBox.about(self, "Opening State", """ <p>Opening this image failed </p> <p> Check your extension and make sure it is .bmp </p>""") def __actionSave(self): if self._fileName == "-1" : self.__actionSaveAs() else : _result = self.image.workingImage().save(self. _fileName[0], "BMP", -1) # Test to know if it's working if _result: print "Saved successfully" QMessageBox.information(self, "Saving State", """<p> Saved with success </p>""" ) else : print "Saving failed" def __actionSaveAs(self): self._fileName = QFileDialog.getSaveFileName(parent=None, caption="Save image as") """ Must check if the .bmp is well written if not message box to show an error """ _result = self.image.workingImage().save(self._fileName[0], "BMP", -1) # Test to know if it's working if _result: print "Saved successfully" QMessageBox.about(self, "Saving State", """<p> Saved with success </p>""" ) else : print "Saving failed" QMessageBox.about(self, "Saving State", """ <p>Saving this image failed </p> <p> Check your extension and make sure it is .bmp </p>""" ) def getFileName(self): return self._fileName def setFileName(self,newFileName): self._fileName = newFileName def __actionClose(self): self.close() def __actionRubber(self): if self._ckbox.isChecked(): print"uncheck" self._ckbox.setChecked(False) else : print "Set check" self._ckbox.setChecked(self._ckbox.isCheckable()) def __actionAbout(self): '''Popup a box with about message.''' QMessageBox.about(self, "About PySide, Platform and the like", """<b> About this program </b> <p>Copyright 2012 Maugin Guillaume. All rights reserved in accordance with GPL v2 or later <p>This application can be used for displaying OS and platform details. <p>Python %s - PySide version %s - Qt version %s on %s""" ) def __actionColorPicker(self): dlg = QColorDialog(self) if dlg.exec_(): self.__setColor(dlg.selectedColor()) #self.image.setPenColor(dlg.selectedColor()) def __setColor(self, color): _img = QPixmap(16, 16) _p = QPainter(_img) _p.fillRect(_img.rect(), QBrush(color)) #self.image.setPenColor(color) _p.end() self._buttonColorPicker.setIcon(QIcon(_img)) def getPaletteColor(self): return self._color def setPaletteColor(self,colour): self._color=colour self.update() #Init of the property for mode color = property(getPaletteColor, setPaletteColor) def __rubberBoxChanged(self, state): if self._ckbox.isChecked(): self._mode = 'rub' else: self._mode = 'select' self.image.mode = self._mode def _initStyleFile(self): styleFile = QFile("CSS\style.qss") styleFile.open(QFile().ReadOnly) styleSheet = unicode(styleFile.readAll()) return styleSheet
Je passe à la scène le nom du fichier à metttre dans l'image et rien ne s'affiche
GraphicsView.pyCode:
1
2
3
4
5
6
7
8
9 from PySide.QtGui import QGraphicsScene, QImage, QPixmap class GraphicsScene(QGraphicsScene): def __init__(self,fileName, parent=None ): super(GraphicsScene, self).__init__(parent=parent) print "Filename", fileName self.img = QImage(fileName) self.addPixmap(QPixmap.fromImage(self.img)) self.setSceneRect(self.img.rect())
Code:
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 from PySide.QtGui import QGraphicsView,QPainter from PySide.QtCore import Qt from Tools.freehand import FreehandTool class GraphicsView(QGraphicsView): def __init__(self, scene, parent=None): super(GraphicsView, self).__init__(scene, parent=parent) assert self.dragMode() is QGraphicsView.NoDrag self.setRenderHint(QPainter.Antialiasing) self.setRenderHint(QPainter.TextAntialiasing) self.setMouseTracking(True); # Enable mouseMoveEvent without clicking__init__ self.freehandTool = FreehandTool(self.scene(), self) # not working because don't cal the segString where the color is def #self.freehandTool.setSegmentColor(colour) """ Delegate events to FreehandTool. """ def mouseMoveEvent(self, event): # print "GV mouse moved" self.freehandTool.pointerMoveEvent(event) def mousePressEvent(self, event): # print "GV mouse pressed" self.freehandTool.pointerPressEvent(event) self.freehandTool.path.penStyle.setWidth(20) self.freehandTool.path.penStyle.setColor(Qt.red) def mouseReleaseEvent(self, event): self.freehandTool.pointerReleaseEvent(event)
J'ai testé en mettant tout dans un seul fichier et ça marche, essaye donc de séparer élément par élément en testant entre chaque modification.
Sinon tu regardes les messages d'exceptions dans la console ou non ? (s'il y en a).
Quand tu fait une grosse modification, penses à tester de temps en temps et pas tout faire d'un coup, puis si un élément bien spécifique te pose problème et que la documentation et google ne t'aident pas, demandes.
Débugger fait partie de la programmation, et c'est la même méthode peu importe le langage. Tu fait de l'orienté objet depuis combien de temps ? (je suis juste curieux).
Depuis peu de temps, j'en fais 1 ans sérieusement avant j'ai juste fais quelques cours.
Et en plus, je débute vraiment sur Python et Qt genre depuis 2 semaines lol.
Je suis si nul lol ?
Edit :
J'ai fais exactement ce que tu as dis je viens de tester dans un fichier pareil, c'est vrai cela marche, c'est étrange, je vais réessayer dans l'autre.
Pas nul, mais je pense que tu n'as peut-être pas tous les réflexes de programmation. Avant de faire des projets conséquents, il faut maîtriser la méthodologie (pas forcément le langage) car se jeter dans du code en ayant 1 an d'expérience (autodidacte je présume ?) c'est un peu comme se jeter contre un mûr la tête la première et sans casque.
Je te conseillerai de regarder d'abord des cours sur Python (developpez.net possède une section) puis de faire quelques tests pour te faire la main.
Pour debug, ma méthode (en python et d'autres):
- analyser le problème pour savoir d'où ça peut venir
- tester en commentant des bouts de code qui pourraient être mauvais
- si ce n'est pas ça, retour à la première étape, sinon
- analyser le code incriminé
- commenter les parties de code que l'on juge être responsables, tester, recommencer l'analyse jusqu'à conclusion.
Tu peux aussi utiliser un debugger si ton environnement de développement est adapté (le mien ne l'est pas).
Je ne suis pas vraiment autoditacte, je suis en école d'électronique et informatique donc je programme réellement depuis 4 ans mais de l'objet (Java,J2EE) depuis un an on va dire.
J'ai déjà réalisé quelques projets déjà donc je connais la méthodologie seulement je ne connais pas le langage et cela me pose quelques soucis mais j'ai déjà fais des tutos sur Python depuis 3 semaines.
EDIT :
Mon environnement est fonctionnel, j'ai eclipse avec PyDev.
De plus, j'ai testé tes méthodes malgré tout, j'ai pas encore trouvé mon erreur, je vais continuer à chercher.
J'ai copié-collé tes bouts de code dans les bons fichiers, lancé, et ... ça fonctionne ...
Quand tu créés tes modules, tu créés bien un fichier '__init__.py' vide dedans ?
PS: joint au post un rar avec les fichiers
Oui , cela se fait automatiquement par mon IDE lorsque je le lance.
En tout cas merci de ton aide, je vais continuer à professionnaliser ma démarche, cela n'est pas facile en étant seul mais c'est des personnes comme toi qui donne envie de persévérer .
J'avoue que ton fichier rar marche bien, c'est étonnant.
Es- tu magicien ? Sérieusement, je ne vois pas trop ce que tu as changé sauf les move ou tu as mis x devant. Merci de me dire ton secret .
Ps : sinon demain, je te dirais pourquoi je dois réaliser mon programme en 3 mois.
Bonne soirée.
Les x c'est pour empêcher que les fonctions soient appelées (j'avais la flemme de supprimer le code) et c'est uniquement parce-que je n'avais pas le code du free hand tool.
Sinon pour les 3 mois je suppose que c'est un projet d'été ou quelque chose dans le genre ?
PS: Je ne suis pas magicien, je programme juste depuis longtemps. Ce que je ferai c'est prendre ton code, le faire ressembler au mien, puis ré-implémenter ce que j'ai commenté/renommé jusqu'à ce que ça soit le code que tu as actuellement. Tu trouveras forcément ce qui cloche.
Oui , c'est un projet d'été pour mon école , un genre de paint mais surtout une gomme perfectionnée pour les images techniques pour enlever les annotations ou autres.
Je vais reprendre ce que tu m'as passé et ajouté le freehandtool pour tester.
EDIT :
J'ai compris mon erreur, je n'appelai pas bien le bon d'image, quelques images ont du mal à s'afficher mais je pense que cela est un problème d'extension donc cela n'est pas trop grave, je me concentre sur le .bmp.
Tu trouveras ci-joint les fichiers du freehand pour comprendre comment je l'ai intégré.
Je ne l'ai pas développé (surement je le reprendrais de façon plus simple moi-même) mais étant libre, je peux l'utiliser sans problème.
De plus, si je suis si pressé, c'est parce que ce projet compte pour la moitié de ma note annuelle pour mon cursus donc tu comprendras que je veuille le réussir.
C'est pourquoi ton aide est si précieuse.
EDIT: avec la pj, c'est mieux. Dedans, tu as tout les fichiers pour le freehand et aussi une classe perso , "penStyle" qui permet de passer la couleur , la largeur et après le style de la gomme.
EDIT 2 :
J'ai réussi à externaliser la classe Pixmap comme je souhaitais pour découper réellement la vue, la scène et l'item.
Du coup, voici mes nouveaux fichiers scène et item.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 from PySide.QtGui import QGraphicsScene, QImage, QPixmap from Item.PixmapItem import PixmapItem class GraphicsScene(QGraphicsScene): def __init__(self,fileName,parent=None ): super(GraphicsScene, self).__init__(parent=parent) print "Filename", fileName self.img = QImage(fileName) #We give the image to have a pixItem in return self.pixItem = PixmapItem(self.img).getPixmapItem() self.addItem(self.pixItem) self.setSceneRect(self.img.rect())
Code:
1
2
3
4
5
6
7
8
9
10 from PySide.QtGui import QGraphicsPixmapItem,QPixmap class PixmapItem(QGraphicsPixmapItem): def __init__(self,image,parent=None ): self.pixItem = QGraphicsPixmapItem() self.pixMap = QPixmap.fromImage(image) self.pixItem.setPixmap(self.pixMap) def getPixmapItem(self): return self.pixItem
Pour changer la couleur de ton free hand, dans ta GraphicsView, ajoute une variable self.maCouleur = Qt.white dans le __init__, utilise là à la place du Qt.red dans le mousePressEvent, et dans ta MainWindow, si self.view est initialisée, dans __setColor change self.view.maCouleur par le paramètre color de __setColor.
Ça devrait le faire.
Très bien, je l'ai déjà fais en faite lol.
Et cela marche bien, j'arrive à changer la couleur.
J'ai du mal à sauvegarder dorénavant :s
EDIT :
En effet, j'ai plus le workingImage() du coup il ne sauvegarde plus les changements effectués par dans la vue donc je me demande si je dois sauvegarder l'image ou la vue ?
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 self._fileName = QFileDialog.getSaveFileName(parent=None, caption="Save image as") """ Must check if the .bmp is well written if not message box to show an error """ # ou self.view :s _result = self.image.save(self._fileName[0], "BMP", -1) # Test to know if it's working if _result: print "Saved successfully" QMessageBox.about(self, "Saving State", """<p> Saved with success </p>""" ) else : print "Saving failed" QMessageBox.about(self, "Saving State", """ <p>Saving this image failed </p> <p> Check your extension and make sure it is .bmp </p>""" )
Bonjour, je suis toujours bloqué sur la sauvegarde, je vois mal comment faire avec ma view car si je la sauvegarde, je perds le contenu dessiné dessus :s.
Pourrais - tu m'indiquer dans quelle direction aller ?
Je te remercie d'avance ^^.
Les changements sont dans la GraphicsScene ? Dans ce cas, QGraphicsScene possède une méthode render(...) qui te permettrait d'obtenir un résultat final.
Euh , comment dire ... , maintenant je réalise les événements dans l'item GraphicsPixmapItem, je ne sais pas si cela est bien.
C'est la seule façon qui marchait donc du coup j'ai changé mon freehandTool et je l'ai simplifié.
Donc je ne sais pas si je peux toujours sauvegarder cet item comme tu as dis ;s ?
Donc voilà à quoi ressemble mon PixmapItem.py
Code:
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 from PySide.QtGui import QGraphicsPixmapItem,QGraphicsScene, QImage, QPixmap, QBrush, QPen,\ QPainter,QColor,QRubberBand from PySide.QtCore import QSize, QRect,QRectF,Qt ''' PixmapItem handles : + Mouses events + Drawing of shapes ''' class PixmapItem(QGraphicsPixmapItem): def __init__(self,scene,pixmap=None,parent=None ): super(PixmapItem,self).__init__() # We make the scene of proper attribute of the class self.scene = scene self.x, self.y = -1, -1 ''' Init of the mode FIXME: Get/set for the different mode : + select , allows you to select the area to delete + rub , allows you to rub with the chosen color + autorub, allows you to rub automatically the color selected from the pixel + pipette , allows you to select the color you want to ''' #By default , we presume selection self.mode = 'select' ''' #Init of the color,width FIXME: maybe need of the style of the pen, .. ''' self.pen = QPen(Qt.SolidLine) self.brush = QBrush() self.color = QColor() self.width = 1 #Init of selection self.__band = QRubberBand(QRubberBand.Rectangle,None) self.__origin = None def getColor(self): return self.color def setColor(self,_color): self.color = _color self.update() def getWidth(self): return self.width def setWidth(self,_witdh): self.width = _witdh self.update() def getMode(self): return self.mode def setMode(self,_mode): self.mode = _mode self.update() def paint(self, painter, option, widget=None): painter.drawPixmap(0, 0, self.pixmap()) self.pen.setColor(self.getColor()) self.pen.setWidth(self.getWidth()) painter.setPen(self.pen) painter.setBrush(self.brush) if self.getMode()== 'rub': if self.x >= 0 and self.y >= 0: painter.drawEllipse(self.x-self.width, self.y-self.width, 2*self.width, 2*self.width) def paintEvent(self,event): ''' FIXME: + have an shape button to choose if you want an ellipse or a rectangle ''' self.x=event.pos().x() self.y=event.pos().y() if self.getMode()== 'rub': if self.x >= 0 and self.y >= 0: self.scene.addEllipse(self.x-self.width, self.y-self.width, 2*self.width, 2*self.width,self.getColor(),self.getColor()) def mousePressEvent (self, event): self.__origin = event.pos() if self.getMode() =="select": #__ private variable self.__band.setGeometry(QRect(self.__origin, QSize())) self.__band.show() self.x=event.pos().x() self.y=event.pos().y() ''' FIXME : Have an option to see when you click on it or not the size of the rub ''' #self.paintEvent(event) self.update() def mouseMoveEvent (self, event): #self.__band.setGeometry(QRect(self.__origin, event.pos()).normalized()) self.x=event.pos().x() self.y=event.pos().y() self.paintEvent(event) self.update() def mouseReleaseEvent(self, event): self.__band.hide() if self.getMode()=="select": print "Selected Zone : ", QRect.intersect(self.__band.geometry(), QRect()) self.x=0 self.y=0 self.update()
De ce que j'ai compris du fonctionnement de QGraphicsScene, c'est elle qui contient les QGraphicsItem non ? De toute manière un simple test isolé devrait dissoudre toute confusion:
résultat -> ça fonctionne.
-Créé une QImage des dimensions de ta scene
-Créé un QPainter sur cette image
-Invoque la méthode render de ta scene en spécifiant le painter
-Invoque save de la QImage
Oui, c'est cela. De ce que j'ai compris aussi, les Items sont contenus dans la scène.
Je vais tester ce que tu me dis, je reviendrais si nécessaire, merci.
Bon week end.
PS : Merci pour tes conseils, j'ai réussi facilement du coup, je vais essayer d'améliorer la sauvegarde mais ça devrait marcher pareil.