source: trunk/papywizard/controller/shootController.py @ 1120

Revision 1120, 22.1 KB checked in by fma, 4 years ago (diff)

Added new data file and timer buttons

  • Property svn:keywords set to Id
Line 
1# -*- coding: utf-8 -*-
2
3""" Panohead remote control.
4
5License
6=======
7
8 - B{papywizard} (U{http://trac.gbiloba.org/papywizard}) is Copyright:
9  - (C) 2007-2008 Frédéric Mantegazza
10
11This software is governed by the B{CeCILL} license under French law and
12abiding by the rules of distribution of free software.  You can  use,
13modify and/or redistribute the software under the terms of the CeCILL
14license as circulated by CEA, CNRS and INRIA at the following URL
15U{http://www.cecill.info}.
16
17As a counterpart to the access to the source code and  rights to copy,
18modify and redistribute granted by the license, users are provided only
19with a limited warranty  and the software's author,  the holder of the
20economic rights,  and the successive licensors  have only  limited
21liability.
22
23In this respect, the user's attention is drawn to the risks associated
24with loading,  using,  modifying and/or developing or reproducing the
25software by the user in light of its specific status of free software,
26that may mean  that it is complicated to manipulate,  and  that  also
27therefore means  that it is reserved for developers  and  experienced
28professionals having in-depth computer knowledge. Users are therefore
29encouraged to load and test the software's suitability as regards their
30requirements in conditions enabling the security of their systems and/or
31data to be ensured and,  more generally, to use and operate it in the
32same conditions as regards security.
33
34The fact that you are presently reading this means that you have had
35knowledge of the CeCILL license and that you accept its terms.
36
37Module purpose
38==============
39
40Graphical toolkit controller
41
42Implements
43==========
44
45- ShootController
46
47@author: Frédéric Mantegazza
48@copyright: (C) 2007-2008 Frédéric Mantegazza
49@license: CeCILL
50"""
51
52__revision__ = "$Id$"
53
54import os.path
55import time
56import threading
57
58import pygtk
59pygtk.require("2.0")
60import gtk
61import gtk.gdk
62import gobject
63import pango
64
65from papywizard.common.loggingServices import Logger
66from papywizard.common.configManager import ConfigManager
67from papywizard.controller.messageController import ErrorMessageController
68from papywizard.controller.abstractController import AbstractController
69#from papywizard.controller.generalInfoController import GeneralInfoController
70from papywizard.controller.configController import ConfigController
71from papywizard.controller.spy import Spy
72from papywizard.view.shootingArea import MosaicArea, PresetArea
73
74
75class ShootController(AbstractController):
76    """ Shoot controller object.
77    """
78    def _init(self):
79        self._gladeFile = "shootDialog.glade"
80        self._signalDict = {"on_rewindButton_clicked": self.__onRewindButtonclicked,
81                            "on_forwardButton_clicked": self.__onForwardButtonclicked,
82                            "on_stepByStepCheckbutton_toggled": self.__onStepByStepCheckbuttonToggled,
83                            "on_dataFileButton_clicked": self.__onDataFileButtonclicked,
84                            "on_timerButton_clicked": self.__onTimerButtonClicked,
85                            "on_startButton_clicked": self.__onStartButtonClicked,
86                            "on_pauseResumeButton_clicked": self.__onPauseResumeButtonClicked,
87                            "on_stopButton_clicked": self.__onStopButtonClicked,
88                            "on_doneButton_clicked": self.__onDoneButtonClicked,
89                        }
90
91        self.__keyPressedDict = {'Right': False,
92                                 'Left': False,
93                                 'Up': False,
94                                 'Down': False,
95                                 'Return': False,
96                                 'Escape': False
97                             }
98        self.__key = {'Right': gtk.keysyms.Right,
99                      'Left': gtk.keysyms.Left,
100                      'Up': gtk.keysyms.Up,
101                      'Down': gtk.keysyms.Down,
102                      'Return': gtk.keysyms.Return,
103                      'Escape': gtk.keysyms.Escape
104                      }
105
106        self.__thread = None
107
108    def _retreiveWidgets(self):
109        super(ShootController, self)._retreiveWidgets()
110
111        # Load graphical shooting area
112        self.viewport = self.wTree.get_widget("viewport")
113        if self._model.mode == 'mosaic':
114            self.shootingArea = MosaicArea()
115        else:
116            self.shootingArea = PresetArea()
117        self.viewport.add(self.shootingArea)
118        self.shootingArea.show()
119
120        self.rewindButton = self.wTree.get_widget("rewindButton")
121        self.forwardButton = self.wTree.get_widget("forwardButton")
122        self.progressbar = self.wTree.get_widget("progressbar")
123        self.stepByStepCheckbutton = self.wTree.get_widget("stepByStepCheckbutton")
124        self.dataFileButton = self.wTree.get_widget("dataFileButton")
125        self.dataFileButtonImage = self.wTree.get_widget("dataFileButtonImage")
126        self.timerButton = self.wTree.get_widget("timerButton")
127        self.timerButtonImage = self.wTree.get_widget("timerButtonImage")
128        self.startButton = self.wTree.get_widget("startButton")
129        self.pauseResumeButton = self.wTree.get_widget("pauseResumeButton")
130        self.pauseResumeLabel = self.wTree.get_widget("pauseResumeLabel")
131        self.pauseResumeImage = self.wTree.get_widget("pauseResumeImage")
132        self.stopButton = self.wTree.get_widget("stopButton")
133        self.doneButton = self.wTree.get_widget("doneButton")
134
135        # Populate shooting area with preview positions
136        if self._model.mode == 'mosaic':
137
138            # todo: give args in shootingArea.__init__()
139            self.shootingArea.init(self._model.mosaic.yawStart, self._model.mosaic.yawEnd,
140                                   self._model.mosaic.pitchStart, self._model.mosaic.pitchEnd,
141                                   self._model.mosaic.yawFov, self._model.mosaic.pitchFov,
142                                   self._model.camera.getYawFov(self._model.cameraOrientation),
143                                   self._model.camera.getPitchFov(self._model.cameraOrientation),
144                                   self._model.mosaic.yawRealOverlap, self._model.mosaic.pitchRealOverlap)
145            self._model.mosaic.generatePositions()
146            for (index, yawIndex, pitchIndex), (yaw, pitch) in self._model.mosaic.iterPositions():
147                self.shootingArea.add_pict(yaw, pitch, status='preview')
148        else:
149            self.shootingArea.init(440., 220., # visible fov
150                                    16.,  16.) # camera fov
151            self._model.preset.generatePositions()
152            for index, (yaw, pitch) in self._model.preset.iterPositions():
153                self.shootingArea.add_pict(yaw, pitch, status='preview')
154        yaw, pitch = self._model.hardware.readPosition()
155        self.shootingArea.set_current_head_position(yaw, pitch)
156
157    def _connectSignals(self):
158        super(ShootController, self)._connectSignals()
159
160        self.dialog.connect("key-press-event", self.__onKeyPressed)
161        self.dialog.connect("key-release-event", self.__onKeyReleased)
162        self.dialog.connect("delete-event", self.__onDelete)
163
164        self.shootingArea.connect("button-press-event", self.__onMouseButtonPressed)
165        #self.shootingArea.connect("motion-notify-event", self.__onMotionNotify)
166
167        Spy().newPosSignal.connect(self.__refreshPos)
168        self._model.newPositionSignal.connect(self.__shootingNewPosition)
169        self._model.startedSignal.connect(self.__shootingStarted)
170        self._model.pausedSignal.connect(self.__shootingPaused)
171        self._model.resumedSignal.connect(self.__shootingResumed)
172        self._model.stoppedSignal.connect(self.__shootingStopped)
173        self._model.updateInfoSignal.connect(self.__shootingUpdateInfo)
174
175    # Callbacks GTK
176    def __onKeyPressed(self, widget, event, *args):
177
178        # 'Right' key
179        if event.keyval == self.__key['Right']:
180            if not self.__keyPressedDict['Right'] and not self.__keyPressedDict['Left']:
181                Logger().debug("MainController.__onKeyPressed(): 'Right' key pressed; forward shooting position")
182                self.__keyPressedDict['Right'] = True
183                self.__forwardShootingPosition()
184            return True
185
186        # 'Left' key
187        elif event.keyval == self.__key['Left']:
188            if not self.__keyPressedDict['Left'] and not self.__keyPressedDict['Right']:
189                Logger().debug("MainController.__onKeyPressed(): 'Left' key pressed; rewind shooting position")
190                self.__keyPressedDict['Left'] = True
191                self.__rewindShootingPosition()
192            return True
193
194        # 'Up' key
195        elif event.keyval == self.__key['Up']:
196            if not self.__keyPressedDict['Up'] and not self.__keyPressedDict['Down']:
197                Logger().debug("MainController.__onKeyPressed(): 'Up' key pressed; rewind shooting position")
198                self.__keyPressedDict['Up'] = True
199                self.__rewindShootingPosition()
200            return True
201
202        # 'Down' key
203        elif event.keyval == self.__key['Down']:
204            if not self.__keyPressedDict['Down'] and not self.__keyPressedDict['Up']:
205                Logger().debug("MainController.__onKeyPressed(): 'Down' key pressed; forward shooting position")
206                self.__keyPressedDict['Down'] = True
207                self.__forwardShootingPosition()
208            return True
209
210        # 'Return' key
211        if event.keyval == self.__key['Return']:
212            if not self.__keyPressedDict['Return']:
213                Logger().debug("shootController.__onKeyPressed(): 'Return' key pressed")
214                self.__keyPressedDict['Return'] = True
215
216                # Pressing 'Return' while not shooting starts shooting
217                if not self._model.isShooting():
218                    Logger().debug("shootController.__onKeyPressed(): start shooting")
219                    self.__startShooting()
220
221                # Pressing 'Return' while shooting...
222                else:
223
224                    # ...and not paused pauses shooting
225                    if not self._model.isPaused():
226                        Logger().debug("shootController.__onKeyPressed(): pause shooting")
227                        self.__pauseShooting()
228
229                    #... and paused resumes shooting
230                    else:
231                        Logger().debug("shootController.__onKeyPressed(): resume shooting")
232                        self.__resumeShooting()
233                return True
234
235        # 'Escape' key
236        elif event.keyval == self.__key['Escape']:
237           if not self.__keyPressedDict['Escape']:
238               Logger().debug("shootController.__onKeyPressed(): 'Escape' key pressed")
239               self.__keyPressedDict['Escape'] = True
240
241               # Pressing 'Escape' while not shooting exit shoot dialog
242               if not self._model.isShooting():
243                   Logger().debug("shootController.__onKeyPressed(): close shooting dialog")
244                   self.dialog.response(0)
245
246               # Pressing 'Escape' while shooting stops shooting
247               else:
248                   Logger().debug("shootController.__onKeyPressed(): stop shooting")
249                   self.__stopShooting()
250               return True
251
252        else:
253            Logger().warning("MainController.__onKeyPressed(): unbind '%s' key" % event.keyval)
254
255    def __onKeyReleased(self, widget, event, *args):
256
257        # 'Right' key
258        if event.keyval == self.__key['Right']:
259            if self.__keyPressedDict['Right']:
260                Logger().debug("MainController.__onKeyReleased(): 'Right' key released")
261                self.__keyPressedDict['Right'] = False
262            return True
263
264        # 'Left' key
265        if event.keyval == self.__key['Left']:
266            if self.__keyPressedDict['Left']:
267                Logger().debug("MainController.__onKeyReleased(): 'Left' key released")
268                self.__keyPressedDict['Left'] = False
269            return True
270
271        # 'Up' key
272        if event.keyval == self.__key['Up']:
273            if self.__keyPressedDict['Up']:
274                Logger().debug("MainController.__onKeyReleased(): 'Up' key released;")
275                self.__keyPressedDict['Up'] = False
276            return True
277
278        # 'Down' key
279        if event.keyval == self.__key['Down']:
280            if self.__keyPressedDict['Down']:
281                Logger().debug("MainController.__onKeyReleased(): 'Down' key released;")
282                self.__keyPressedDict['Down'] = False
283            return True
284
285        # 'Return' key
286        if event.keyval == self.__key['Return']:
287            if self.__keyPressedDict['Return']:
288                Logger().debug("MainController.__onKeyReleased(): 'Return' key released")
289                self.__keyPressedDict['Return'] = False
290            return True
291
292        # 'Escape' key
293        if event.keyval == self.__key['Escape']:
294            if self.__keyPressedDict['Escape']:
295                Logger().debug("MainController.__onKeyReleased(): 'Escape' key released")
296                self.__keyPressedDict['Escape'] = False
297            return True
298
299        else:
300            Logger().warning("MainController.__onKeyReleased(): unbind '%s' key" % event.keyval)
301
302    def __onDelete(self, widget, event):
303        Logger().trace("ShootController.__onDelete()")
304        self.__stopShooting() # Freeze if shooting!
305
306    def __onMouseButtonPressed(self, widget, event):
307        Logger().trace("ShootController.__onMouseButtonPressed()")
308        if self._model.isPaused():
309            if event.button == 1:
310                index = self.shootingArea.get_selected_image_index(event.x, event.y)
311                if index is not None:
312                    Logger().debug("ShootController.__onMouseButtonPressed(): x=%d, y=%d, index=%d" % (event.x, event.y, index))
313                    self._model.setShootingIndex(index)
314
315    def __onMotionNotify(self, widget, event):
316        #Logger().trace("ShootController.__onMotionNotify()")
317        if self._model.isPaused():
318            if event.is_hint:
319                Logger().trace("ShootController.__onMotionNotify(): is_hint")
320                x, y, state = event.window.get_pointer()
321            else:
322                x = event.x
323                y = event.y
324                state = event.state
325
326            if state & gtk.gdk.BUTTON1_MASK:
327                Logger().debug("ShootController.__onMotionNotify(): drag x=%d, y=%d" % (x, y))
328
329    def __onRewindButtonclicked(self, widget):
330        Logger().trace("ShootController.__onRewindButtonclicked()")
331        self.__rewindShootingPosition()
332
333    def __onForwardButtonclicked(self, widget):
334        Logger().trace("ShootController.__onForwardButtonclicked()")
335        self.__forwardShootingPosition()
336
337    def __onStepByStepCheckbuttonToggled(self, widget):
338        Logger().trace("ShootController.____onStepByStepCheckbuttonToggled()")
339        switch = self.stepByStepCheckbutton.get_active()
340        self._model.setManualShoot(switch)
341
342    def __onDataFileButtonclicked(self, widget):
343        Logger().trace("ShootController.__onDataFileButtonclicked()")
344        controller = ConfigController(self, self._model, self._serializer)
345        controller.notebook.set_current_page(5)
346        response = controller.run()
347        controller.shutdown()
348        self.refreshView()
349
350    def __onTimerButtonClicked(self, widget):
351        controller = ConfigController(self, self._model, self._serializer)
352        controller.notebook.set_current_page(6)
353        response = controller.run()
354        controller.shutdown()
355        self.refreshView()
356
357    def __onStartButtonClicked(self, widget):
358        Logger().trace("ShootController.__startButtonClicked()")
359        self.__startShooting()
360
361    def __onPauseResumeButtonClicked(self, widget):
362        Logger().trace("ShootController.__onPauseResumeButtonClicked()")
363        if self._model.isShooting(): # Should always be true here, but...
364            if not self._model.isPaused():
365                self.__pauseShooting() # Not used
366            else:
367                self.__resumeShooting()
368
369    def __onStopButtonClicked(self, widget):
370        Logger().trace("ShootController.__stopButtonClicked()")
371        self.__stopShooting()
372
373    def __onDoneButtonClicked(self, widget):
374        Logger().trace("ShootController.__onDoneButtonClicked()")
375
376    # Callback model (all GUI calls must be done via the serializer)
377    def __shootingNewPosition(self, yaw, pitch, status=None, next=False):
378        Logger().trace("ShootController.__shootingNewPosition()")
379        self.shootingArea.add_pict(yaw, pitch, status, next)
380        self._serializer.addWork(self.shootingArea.refresh)
381
382    def __shootingStarted(self):
383        Logger().trace("ShootController.__shootingStarted()")
384        self._serializer.addWork(self.shootingArea.clear)
385        self._serializer.addWork(self.dataFileButton.set_sensitive, False)
386        self._serializer.addWork(self.timerButton.set_sensitive, False)
387        self._serializer.addWork(self.startButton.set_sensitive, False)
388        self._serializer.addWork(self.pauseResumeButton.set_sensitive, True)
389        self._serializer.addWork(self.stopButton.set_sensitive, True)
390        self._serializer.addWork(self.doneButton.set_sensitive, False)
391        self._serializer.addWork(self.rewindButton.set_sensitive, False)
392        self._serializer.addWork(self.forwardButton.set_sensitive, False)
393
394    def __shootingPaused(self):
395        Logger().trace("ShootController.__shootingPaused()")
396        self.pauseResumeButton.set_sensitive(True)
397        self._serializer.addWork(self.pauseResumeLabel.set_text, _("Resume"))
398        self._serializer.addWork(self.rewindButton.set_sensitive, True)
399        self._serializer.addWork(self.forwardButton.set_sensitive, True)
400        self._serializer.addWork(self.progressbar.set_text, _("Idle"))
401
402    def __shootingResumed(self):
403        Logger().trace("ShootController.__shootingResumed()")
404        self._serializer.addWork(self.pauseResumeLabel.set_text, _("Pause"))
405        self._serializer.addWork(self.rewindButton.set_sensitive, False)
406        self._serializer.addWork(self.forwardButton.set_sensitive, False)
407
408    def __shootingStopped(self, status):
409        Logger().debug("ShootController.__shootingStopped(): status=%s" % status)
410        if status == 'ok':
411            self._serializer.addWork(self.progressbar.set_text, _("Finished"))
412        elif status == 'cancel':
413            self._serializer.addWork(self.progressbar.set_text, _("Canceled"))
414        elif status == 'fail':
415            self._serializer.addWork(self.progressbar.set_text, _("Failed"))
416        self._serializer.addWork(self.dataFileButton.set_sensitive, True)
417        self._serializer.addWork(self.timerButton.set_sensitive, True)
418        self._serializer.addWork(self.startButton.set_sensitive, True)
419        #self._serializer.addWork(self.pauseResumeLabel.set_text, _("Pause"))
420        self._serializer.addWork(self.pauseResumeButton.set_sensitive, False)
421        self._serializer.addWork(self.stopButton.set_sensitive, False)
422        self._serializer.addWork(self.doneButton.set_sensitive, True)
423        #self._serializer.addWork(self.rewindButton.set_sensitive, False)
424        #self._serializer.addWork(self.forwardButton.set_sensitive, False)
425
426    def __shootingUpdateInfo(self, info):
427        Logger().debug("ShootController.__shootingUpdateInfo(): info=%s" % info)
428        if info.has_key('sequence'):
429            self._serializer.addWork(self.progressbar.set_text, info['sequence'])
430        elif info.has_key('progress'):
431            self._serializer.addWork(self.progressbar.set_fraction, info['progress'])
432
433    def __refreshPos(self, yaw, pitch):
434        """ Refresh position according to new pos.
435
436        @param yaw: yaw axis value
437        @type yaw: float
438
439        @param pitch: pitch axix value
440        @type pitch: float
441        """
442        Logger().trace("ShootController.__refreshPos()")
443        self.shootingArea.set_current_head_position(yaw, pitch)
444        self._serializer.addWork(self.shootingArea.refresh)
445
446    # Helpers
447    def __rewindShootingPosition(self):
448        """
449        """
450        index = self._model.getShootingIndex()
451        Logger().debug("ShootController.__rewindShootingPosition(): old index=%d" % index)
452        try:
453            self._model.setShootingIndex(index - 1)
454            self.shootingArea.set_selected_image_index(index - 1)
455            Logger().debug("ShootController.__rewindShootingPosition():new index=%d" % (index - 1))
456        except IndexError:
457            Logger().exception("ShootController.__rewindShootingPosition()", debug=True)
458
459    def __forwardShootingPosition(self):
460        """
461        """
462        index = self._model.getShootingIndex()
463        Logger().debug("ShootController.__forwardShootingPosition(): old index=%d" % index)
464        try:
465            self._model.setShootingIndex(index + 1)
466            self.shootingArea.set_selected_image_index(index + 1)
467            Logger().debug("ShootController.__forwardShootingPosition(): new index=%d" % (index + 1))
468        except IndexError:
469            Logger().exception("ShootController.__forwardShootingPosition()", debug=True)
470
471    def __startShooting(self):
472
473        # Join previous thread, if any
474        if self.__thread is not None:
475            self.__thread.join()
476
477        # Start new shooting thread
478        self.__thread = threading.Thread(target=self._model.start, name="Shooting")
479        self.__thread.start()
480
481    def __pauseShooting(self):
482        self._model.pause()
483        self.pauseResumeButton.set_sensitive(False)
484
485    def __resumeShooting(self):
486        self._model.resume()
487
488    def __stopShooting(self):
489        self._model.stop()
490
491    # Interface
492    def shutdown(self):
493        super(ShootController, self).shutdown()
494        if self.__thread is not None:
495            self.__thread.join()
496        self._model.newPositionSignal.disconnect(self.__shootingNewPosition)
497        self._model.startedSignal.disconnect(self.__shootingStarted)
498        self._model.pausedSignal.disconnect(self.__shootingPaused)
499        self._model.resumedSignal.disconnect(self.__shootingResumed)
500        self._model.stoppedSignal.disconnect(self.__shootingStopped)
501        self._model.updateInfoSignal.disconnect(self.__shootingUpdateInfo)
502        Spy().newPosSignal.disconnect(self.__refreshPos)
503
504    def refreshView(self):
505        self.dataFileButtonImage.set_sensitive(ConfigManager().getBoolean('Preferences', 'DATA_FILE_ENABLE'))
506        self.timerButtonImage.set_sensitive(ConfigManager().getBoolean('Preferences', 'TIMER_EVERY_ENABLE'))
Note: See TracBrowser for help on using the repository browser.