Pymecavideo 8.0
Étude cinématique à l'aide de vidéos
trajectoireWidget.py
1# -*- coding: utf-8 -*-
2
3"""
4 trajectoireWidget, a module for pymecavideo:
5 a program to track moving points in a video frameset
6
7 Copyright (C) 2007 Jean-Baptiste Butet <ashashiwa@gmail.com>
8 Copyright (C) 2023 Georges Khaznadar <georgesk@debian.org>
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22"""
23
24from PyQt6.QtCore import QThread, pyqtSignal, QLocale, QTranslator, Qt, \
25 QSize, QTimer, QObject, QRect, QPoint, QPointF, QEvent
26from PyQt6.QtGui import QKeySequence, QIcon, QPixmap, QImage, QPainter, \
27 QCursor, QPen, QColor, QFont, QResizeEvent, QShortcut
28from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QLayout, \
29 QFileDialog, QTableWidgetItem, QInputDialog, QLineEdit, QMessageBox, \
30 QTableWidgetSelectionRange
31
32import os, time, re, sys
33import locale
34
35from version import Version
36from vecteur import vecteur
37from image_widget import ImageWidget
38from globdef import cible_icon, DOCUMENT_PATH, inhibe, pattern_float
39from toQimage import toQImage
40from suivi_auto import SelRectWidget
41from detect import filter_picture
42from cadreur import Cadreur, openCvReader
43from dbg import Dbg
44
45import interfaces.icon_rc
46
47from interfaces.Ui_trajectoire import Ui_trajectoire
48from etatsTraj import Etats
49
51 """
52 Le widget principal de l'onglet des trajectoires
53
54 Paramètres du constructeur :
55 @param parent l'onglet des trajectoires
56 """
57 def __init__(self, parent):
58 QWidget.__init__(self, parent)
59 Ui_trajectoire.__init__(self)
60 Etats.__init__(self)
61 self.setupUi(self)
62 self.connecte_ui()
63 self.trace.connect(self.traceTrajectoires)
64 return
65
66 ############ les signaux spéciaux #####################
67 trace = pyqtSignal(str)
68
69 def setApp(self, app):
70 """
71 Crée des pointeurs locaux vers les widgets importants, le débogueur
72 et les préférences ; connecte aussi les widgets importants dans
73 le sous-widget self.trajW
74 """
75 self.app = app
76 self.dbg = app.dbg
77 self.prefs = app.prefs
78 self.pointage = app.pointage
79 self.video = app.pointage.video
80 ###
81 self.trajW.pointage = self.pointage
82 self.trajW.video = self.video
83 self.trajW.trajectoire = self
84 return
85
86 def connecte_ui(self):
87 """
88 Connecte les signaux des sous-widgets
89 """
90 self.pushButton_save.clicked.connect(self.enregistreChrono)
91 self.spinBox_chrono.valueChanged.connect(self.changeChronoImg)
92 self.comboBox_referentiel.currentIndexChanged.connect(
94 self.checkBoxScale.currentIndexChanged.connect(self.enableSpeed)
95 self.checkBoxScale.currentTextChanged.connect(self.enableSpeed)
96 self.checkBoxVectorSpeed.stateChanged.connect(self.enableSpeed)
97 self.radioButtonSpeedEveryWhere.clicked.connect(self.enableSpeed)
98 self.radioButtonNearMouse.clicked.connect(self.enableSpeed)
99 self.button_video.clicked.connect(self.montre_video)
100 self.comboBoxChrono.currentIndexChanged.connect(self.chronoPhoto)
101 return
102
103 def apply_preferences(self):
104 """
105 Récupère les préférences sauvegardées, et en applique les données
106 ici on s'occupe de ce qui se gère facilement au niveau de la
107 fenêtre principale
108 """
109 self.dbg.p(2, "rentre dans 'trajectoireWidget.apply_preferences'")
110 d = self.prefs.config["DEFAULT"]
111 self.radioButtonNearMouse.setChecked(d["proximite"] == "True")
112 return
113
114 def enregistreChrono(self):
115 self.pixmapChrono = QPixmap(self.trajW.size())
116 self.trajW.render(self.pixmapChrono)
117 base_name = os.path.splitext(os.path.basename(self.pointage.filename))[0]
118 defaultName = os.path.join(DOCUMENT_PATH, base_name)
119 fichier = QFileDialog.getSaveFileName(self,
120 self.tr("Enregistrer comme image"),
121 defaultName, self.tr("fichiers images(*.png *.jpg)"))
122 try :
123 self.pixmapChrono.save(fichier[0])
124 except Exception as err:
125 self.dbg.p(3, f"***Exception*** {err} at line {get_linenumber()}")
126 QMessageBox.critical(None, self.tr("Erreur lors de l'enregistrement"), self.tr("Echec de l'enregistrement du fichier:<b>\n{0}</b>").format(
127 fichier[0]))
128
129 def chronoPhoto(self):
130 """lance la sauvegarde du trajW.
131 Si chronophotographie, on ajoute l'image et la trace de l'échelle comme pointée.
132 Si chronophotogramme, on ne met pas l'image et la trace est en haut.
133 """
134 # Configure l'UI en fonction du mode
135 if self.comboBoxChrono.currentIndex() == 0 :
136 self.widget_chronophoto.setEnabled(False)
137 self.trajW.setEnabled(True)
138 self.widget_speed.setEnabled(True)
139 elif self.comboBoxChrono.currentIndex() == 1 :
140 self.widget_chronophoto.setEnabled(True)
141 self.trajW.setEnabled(False)
142 self.widget_speed.setEnabled(False)
143 self.checkBoxVectorSpeed.setChecked(False)
144 self.spinBox_chrono.setMaximum(int(self.pointage.image_max))
145 self.spinBox_chrono.setMinimum(1)
146
147 elif self.comboBoxChrono.currentIndex() == 2 :
148 self.widget_chronophoto.setEnabled(False)
149 self.trajW.setEnabled(False)
150 self.widget_speed.setEnabled(False)
151 self.checkBoxVectorSpeed.setChecked(False)
152 self.dbg.p(2, "rentre dans 'chronoPhoto'")
153 # ajoute la première image utilisée pour le pointage sur le fond du vidget
154 liste_types_photos = ['chronophotographie', 'chronophotogramme']
155
156 if self.comboBoxChrono.currentIndex() != 0:
157 photo_chrono = liste_types_photos[self.comboBoxChrono.currentIndex(
158 )-1]
159 self.dbg.p(2, "dans 'chronoPhoto, on a choisi le type %s'" %
160 (photo_chrono))
161 if photo_chrono == 'chronophotographie': # on extrait le première image que l'on rajoute au widget
162 self.trajW.chrono = 1 # 1 pour chronophotographie
163 ok, img = self.pointage.cvReader.getImage(
164 self.chronoImg, self.pointage.video.rotation)
165 self.imageChrono = toQImage(img).scaled(
166 self.pointage.video.image_w, self.pointage.video.image_h) #, Qt.KeepAspectRatio)
167 self.trajW.setImage(
168 QPixmap.fromImage(self.imageChrono))
169 else:
170 self.trajW.chrono = 2 # 2 pour chronophotogramme
171 self.trajW.setImage(QPixmap())
172 #self.enregistreChrono()
173 else:
174 self.trajW.setImage(QPixmap())
175 self.trajW.chrono = 0
176 self.app.redimensionneFenetre()
177 self.update()
178 return
179
180 def changeChronoImg(self,img):
181 self.chronoImg = img
182 self.chronoPhoto()
183
184 def enableSpeed(self, secondParam=None):
185 """
186 Quand on veut afficher le vecteur vitesse,
187 on active le spinbox qui permet de choisir une échelle.
188 Quand on ne veut plus, on peut cacher le spinbox.
189 @param secondParam peu utile mais nécessaire : certains modes
190 de rappel de cette fonction ont un paramètre supplémentaire
191 """
192 self.dbg.p(2, "rentre dans 'enableSpeed'")
193 if self.checkBoxVectorSpeed.isChecked():
194 self.dbg.p(2, "In enableSpeed")
195 self.checkBoxScale.setEnabled(1)
196 if self.checkBoxScale.count() < 1:
197 self.checkBoxScale.insertItem(0, "1")
198 self.radioButtonNearMouse.show()
200 self.trajW.prepare_vecteurs_pour_paint()
201 self.trajW.update()
202 else:
203 self.checkBoxScale.setEnabled(0)
204 self.radioButtonNearMouse.hide()
206 self.trajW.update()
207 return
208
209 def montre_video(self):
210 self.dbg.p(2, "rentre dans 'montre_video'")
211 ref = self.comboBox_referentiel.currentText().split(" ")[-1]
212 if len(ref) == 0 or ref == "camera":
213 return
214 c = Cadreur(int(ref), self)
215 c.montrefilm()
216 return
217
218 def traceTrajectoires(self, laquelle):
219 """
220 fonction de rappel du signal "trace"
221 Cette fonction est appelée par un changement de référentiel.
222 On peut aussi appeler cette fonction directement, auquel cas on
223 donne la valeur "absolu" à newValue pour reconnaître ce cas.
224 efface les trajectoires anciennes, puis
225 trace les trajectoires en fonction du référentiel choisi.
226
227 @param laquelle désignation de la trajectoire ("absolu" = réf. camera)
228 """
229 self.dbg.p(2, "rentre dans 'traceTrajectoire'")
230 self.trajW.origine_mvt = self.pointage.origine
231 if laquelle == "absolu":
232 ref = 0 # la caméra
233 # mets à jour le comboBox referentiel :
234 self.comboBox_referentiel.setCurrentIndex(
235 self.comboBox_referentiel.count()-1)
236 self.comboBox_referentiel.update()
237 else:
238 choix_ref = self.comboBox_referentiel.currentText()
239 # on évite le cas où le combobox a été vidé, entre deux sessions
240 if choix_ref == "":
241 return
242 elif choix_ref == "camera":
243 ref = 0
244 else:
245 ref = int(choix_ref.split(" ")[-1])
246 if ref != 0:
247 self.button_video.setEnabled(1)
248 self.trajW.chrono = False
249 origine = vecteur(self.pointage.width() // 2, self.pointage.height() // 2)
250 self.trajW.origine = origine
251 self.trajW.origine_mvt = origine
252 self.trajW.referentiel = ref
253 else: # si le référentiel est la caméra, aucune translation !
254 self.trajW.referentiel = 0
255 self.trajW.origine = vecteur(0, 0)
256 self.dbg.p(3, "origine %s, ref %s" %
257 (str(self.trajW.origine), str(ref)))
258 self.trajW.prepare_vecteurs_pour_paint()
259 self.trajW.update()
260 return
261
Un objet capable de recadrer une vidéo en suivant le déplacement d'un point donné.
Definition: cadreur.py:48
Une classe qui permet de définir les états pour le ccordWidget debut, A, AB, B, C,...
Definition: etatsTraj.py:34
Le widget principal de l'onglet des trajectoires.
def setApp(self, app)
Crée des pointeurs locaux vers les widgets importants, le débogueur et les préférences ; connecte aus...
def chronoPhoto(self)
lance la sauvegarde du trajW.
def traceTrajectoires(self, laquelle)
fonction de rappel du signal "trace" Cette fonction est appelée par un changement de référentiel.
trace
les signaux spéciaux #####################
def apply_preferences(self)
Récupère les préférences sauvegardées, et en applique les données ici on s'occupe de ce qui se gère f...
def enableSpeed(self, secondParam=None)
Quand on veut afficher le vecteur vitesse, on active le spinbox qui permet de choisir une échelle.
def connecte_ui(self)
Connecte les signaux des sous-widgets.
une classe pour des vecteurs 2D ; les coordonnées sont flottantes, et on peut accéder à celles-ci par...
Definition: vecteur.py:44