source: trunk/brainstorms/Puzzlebox/Brainstorms/Interface.py @ 258

Last change on this file since 258 was 258, checked in by sc, 10 years ago

trunk/synapse/puzzlebox_synapse.desktop:

  • Updated for Python 2.7 site-packages path

trunk/brainstorms/Puzzlebox/Brainstorms/Helicopter_Control.py:

  • being dissection of protocol specification

trunk/brainstorms/Puzzlebox/Brainstorms/Interface.py:

  • device access crash bug added to todo list
File size: 53.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Puzzlebox - Brainstorms - Client Interface - Qt
5#
6# Copyright Puzzlebox Productions, LLC (2010)
7#
8# This code is released under the GNU Pulic License (GPL) version 2
9# For more information please refer to http://www.gnu.org/copyleft/gpl.html
10
11__changelog__ = """\
12Last Update: 2010.12.04
13
14"""
15
16__todo__ = """
17 - ERROR: could not open port /dev/ttyUSB0: [Errno 13] Permission denied: '/dev/ttyUSB0' /dev/ttyUSB0
18 - server may not correctly handle multiple clients connected
19      to an embedded Brainstorms server
20 - disable autorepeating on shortcut keys
21 - update configuration.ini file with settings entered into interface
22
23"""
24
25import os, sys
26import urllib
27
28if (sys.platform == 'win32'):
29        DEFAULT_IMAGE_PATH = 'images'
30        import _winreg as winreg
31        import itertools
32        import re
33        import serial
34else:
35        DEFAULT_IMAGE_PATH = '/usr/share/puzzlebox_brainstorms/images'
36        import bluetooth
37        os.chdir('/usr/share/puzzlebox_brainstorms')
38
39try:
40        import PySide as PyQt4
41        from PySide import QtCore, QtGui, QtNetwork
42except:
43        print "Using PyQt4 module"
44        from PyQt4 import QtCore, QtGui, QtNetwork
45else:
46        print "Using PySide module"
47
48#from PyQt4 import QtCore, QtGui, QtNetwork
49#from PySide import QtCore, QtGui, QtNetwork
50
51from Interface_Design import Ui_Form as Design
52
53import simplejson as json
54
55import Configuration as configuration
56import Client as brainstorms_client
57import Puzzlebox.Brainstorms.ThinkGear.Client as thinkgear_client
58import Helicopter_Control as helicopter_control
59import Wheelchair_Control as wheelchair_control
60#import puzzlebox_logger
61
62#####################################################################
63# Globals
64#####################################################################
65
66DEBUG = 1
67
68THINKGEAR_POWER_THRESHOLDS = configuration.THINKGEAR_POWER_THRESHOLDS
69
70BLUETOOTH_DEVICE = configuration.NXT_BLUETOOTH_DEVICE
71
72NXT_BLUETOOTH_DEVICE = configuration.NXT_BLUETOOTH_DEVICE
73
74DEFAULT_NXT_POWER_LEVEL = configuration.DEFAULT_NXT_POWER_LEVEL
75
76THINKGEAR_SERVER_HOST = configuration.THINKGEAR_SERVER_HOST
77THINKGEAR_SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
78
79BRAINSTORMS_FEEDBACK_URL = 'http://brainstorms.puzzlebox.info/contact_cgi.php'
80
81DEVICE_PATH = '/dev'
82PATH_TO_HCITOOL = '/usr/bin/hcitool'
83
84#####################################################################
85# Classes
86#####################################################################
87
88class puzzlebox_brainstorms_client_interface(QtGui.QWidget, Design):
89       
90        def __init__(self, log, server=None, DEBUG=DEBUG, parent = None):
91               
92                self.log = log
93                self.DEBUG = DEBUG
94               
95                QtGui.QWidget.__init__(self, parent)
96                self.setupUi(self)
97               
98                self.configureSettings()
99                self.connectWidgets()
100               
101                self.name = "Brainstorms Interface"
102               
103                self.brainstormsServer = server
104                self.brainstormsClient = None
105               
106                self.helicopter = None
107                self.wheelchair = None
108               
109                self.drive_state = 'stop_motors'
110                self.current_speed = 0
111               
112                self.current_helicopter_state = 'neutral'
113       
114       
115        ##################################################################
116       
117        def configureSettings(self):
118               
119                # Brainstorms Interface
120               
121                image_path = "puzzlebox.ico"
122                if not os.path.exists(image_path):
123                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
124               
125                if os.path.exists(image_path):
126                        icon = QtGui.QIcon()
127                        icon.addPixmap(QtGui.QPixmap(image_path), \
128                                            QtGui.QIcon.Normal, \
129                                            QtGui.QIcon.Off)
130                        self.setWindowIcon(icon)
131               
132                image_path = "puzzlebox_logo.png"
133                if not os.path.exists(image_path):
134                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
135                if os.path.exists(image_path):
136                        self.labelPuzzlebox.setPixmap(QtGui.QPixmap(image_path))
137               
138                self.pushButtonTurnLeft.setEnabled(False)
139                self.pushButtonForward.setEnabled(False)
140                self.pushButtonTurnRight.setEnabled(False)
141                self.pushButtonTurnLeftReverse.setEnabled(False)
142                self.pushButtonReverse.setEnabled(False)
143                self.pushButtonTurnRightReverse.setEnabled(False)
144               
145                self.pushButtonConcentrationEnable.setDown(True)
146                self.pushButtonRelaxationEnable.setDown(True)
147                self.pushButtonSpeedEnable.setDown(True)
148               
149               
150                # Search for available Serial and Bluetooth devices
151                self.searchForDevices()
152               
153               
154                # LEGO Mindstorms
155                self.textLabelNXTStatus.setText("Status: Disconnected")
156               
157                # Display communication port for LEGO Mindstorms NXT device
158                #self.lineEditNXTPort.setText(NXT_BLUETOOTH_DEVICE)
159                self.comboBoxNXTPortSelect.setEnabled(True)
160               
161               
162                # RC Helicopter
163                self.textLabelHelicopterStatus.setText("Status: Disconnected")
164               
165               
166                # Wheelchair
167                self.textLabelWheelchairStatus.setText("Status: Disconnected")
168               
169               
170                # Control Panel
171               
172                # Display Host for ThinkGear Connect Socket Server
173                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
174                #self.lineEditThinkGearHost.setEnabled(False)
175               
176                # Display Port for ThinkGear Connect Socket Server
177                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
178                #self.lineEditThinkGearPort.setEnabled(False)
179               
180               
181                self.lineEditLeftMotorPort.setText( \
182                   configuration.NXT_MOTOR_PORT_LEFT.upper() )
183                self.lineEditRightMotorPort.setText(   \
184                   configuration.NXT_MOTOR_PORT_RIGHT.upper() )
185                self.checkBoxMotorSpinReversed.setChecked( \
186                   configuration.NXT_MOTORS_MOUNTED_BACKWARDS)
187       
188       
189        ##################################################################
190       
191        def getMinimumThreshold(self, threshold):
192               
193                '''Return the minimum detection level which results
194                in a non-zero power setting'''
195               
196                minimum = 100
197               
198                threshold_keys = threshold.keys()
199                threshold_keys.sort()
200                threshold_keys.reverse()
201               
202                for key in threshold_keys:
203                       
204                        if ((threshold[key] < minimum) and \
205                                 (threshold[key] > 0)):
206                                minimum = key
207               
208               
209                return(minimum)
210       
211       
212        ##################################################################
213       
214        def configureNetworkBrainstorms(self):
215               
216                bluetooth_device = str(self.comboBoxNXTPortSelect.currentText())
217               
218                self.brainstormsClient = \
219                   brainstorms_client.puzzlebox_brainstorms_network_client( \
220                           self.log, \
221                           bluetooth_device=bluetooth_device, \
222                           parent=self)
223               
224                self.brainstormsClient.sendCommand('connect', \
225                                                   bluetooth_device=bluetooth_device)
226       
227       
228        ##################################################################
229       
230        def connectToBrainstormsServer(self):
231               
232                # Prevent attempting to connect to a device which does not exist
233                device = str(self.comboBoxNXTPortSelect.currentText())
234                if device == 'N/A':
235                        self.pushButtonNXTConnect.setChecked(False)
236                        return
237                if (sys.platform != 'win32'):
238                        if ((not device.startswith(DEVICE_PATH)) or \
239                            (not os.path.exists(device))):
240                                self.searchForDevices()
241                                self.pushButtonNXTConnect.setChecked(False)
242                                return
243               
244               
245                if self.DEBUG:
246                        print "<---- [%s] Connecting to Brainstorms Server" % self.name
247               
248                self.configureNetworkBrainstorms()
249               
250                #if (self.brainstormsClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
251                        #QtGui.QMessageBox.information(self, \
252                                                                #self.brainstormsClient.socket.name, \
253                                           #"Failed to connect to Brainstorms socket server")
254               
255                #else:
256                self.disconnect(self.pushButtonNXTConnect, \
257                                                        QtCore.SIGNAL("clicked()"), \
258                                                        self.connectToBrainstormsServer)
259               
260                self.connect(self.pushButtonNXTConnect, \
261                                                        QtCore.SIGNAL("clicked()"), \
262                                                        self.disconnectFromBrainstormsServer)
263               
264                self.textLabelNXTStatus.setText("Status: Connected")
265                self.pushButtonNXTConnect.setText('Disconnect')
266               
267                self.comboBoxNXTPortSelect.setEnabled(False)
268                self.pushButtonNXTSearch.setEnabled(False)
269               
270                self.pushButtonTurnLeft.setEnabled(True)
271                self.pushButtonForward.setEnabled(True)
272                self.pushButtonTurnRight.setEnabled(True)
273                self.pushButtonTurnLeftReverse.setEnabled(True)
274                self.pushButtonReverse.setEnabled(True)
275                self.pushButtonTurnRightReverse.setEnabled(True)
276               
277                self.pushButtonNXTMessageOne.setEnabled(True)
278                self.pushButtonNXTMessageTwo.setEnabled(True)
279                self.pushButtonNXTMessageThree.setEnabled(True)
280                self.pushButtonNXTMessageFour.setEnabled(True)
281                self.pushButtonNXTMessageFive.setEnabled(True)
282                self.pushButtonNXTMessageSix.setEnabled(True)
283               
284                self.pushButtonConcentrationEnable.setEnabled(True)
285                self.pushButtonRelaxationEnable.setEnabled(True)
286                self.pushButtonSpeedEnable.setEnabled(True)
287               
288                self.pushButtonMessageOne.setEnabled(True)
289                self.pushButtonMessageTwo.setEnabled(True)
290                self.pushButtonMessageThree.setEnabled(True)
291                self.pushButtonMessageFour.setEnabled(True)
292                self.pushButtonMessageFive.setEnabled(True)
293                self.pushButtonMessageSix.setEnabled(True)
294       
295       
296        ##################################################################
297       
298        def disconnectFromBrainstormsServer(self):
299               
300                if self.DEBUG:
301                        print "- - [%s] Disconnecting from Brainstorms Server" % self.name
302               
303                self.stopMotors()
304               
305                # Ensure the stopMotors command has been received by the server
306                # so the NXT robot will stop before the client disconnects
307                self.brainstormsClient.socket.flush()
308               
309                self.brainstormsClient.socket.disconnectFromHost()
310               
311                self.disconnect(self.pushButtonNXTConnect, \
312                                  QtCore.SIGNAL("clicked()"), \
313                                  self.disconnectFromBrainstormsServer)
314               
315                self.connect(self.pushButtonNXTConnect, \
316                                  QtCore.SIGNAL("clicked()"), \
317                                  self.connectToBrainstormsServer)
318               
319                self.textLabelNXTStatus.setText("Status: Disconnected")
320                self.pushButtonNXTConnect.setText('Connect')
321               
322                self.comboBoxNXTPortSelect.setEnabled(True)
323                self.pushButtonNXTSearch.setEnabled(True)
324               
325                self.pushButtonTurnLeft.setEnabled(False)
326                self.pushButtonForward.setEnabled(False)
327                self.pushButtonTurnRight.setEnabled(False)
328                self.pushButtonTurnLeftReverse.setEnabled(False)
329                self.pushButtonReverse.setEnabled(False)
330                self.pushButtonTurnRightReverse.setEnabled(False)
331               
332                self.pushButtonNXTMessageOne.setEnabled(False)
333                self.pushButtonNXTMessageTwo.setEnabled(False)
334                self.pushButtonNXTMessageThree.setEnabled(False)
335                self.pushButtonNXTMessageFour.setEnabled(False)
336                self.pushButtonNXTMessageFive.setEnabled(False)
337                self.pushButtonNXTMessageSix.setEnabled(False)
338               
339                self.pushButtonConcentrationEnable.setEnabled(False)
340                self.pushButtonRelaxationEnable.setEnabled(False)
341                self.pushButtonSpeedEnable.setEnabled(False)
342               
343                self.pushButtonMessageOne.setEnabled(False)
344                self.pushButtonMessageTwo.setEnabled(False)
345                self.pushButtonMessageThree.setEnabled(False)
346                self.pushButtonMessageFour.setEnabled(False)
347                self.pushButtonMessageFive.setEnabled(False)
348                self.pushButtonMessageSix.setEnabled(False)
349               
350                self.brainstormsClient = None
351               
352                self.searchForDevices()
353       
354       
355        ##################################################################
356       
357        def connectToThinkGearHost(self):
358               
359                if self.DEBUG:
360                        print "Connecting to ThinkGear Host"
361               
362                server_host = str(self.lineEditThinkGearHost.text())
363                server_port = int(self.lineEditThinkGearPort.text())
364               
365                self.thinkgearClient = \
366                   thinkgear_client.puzzlebox_brainstorms_network_client_thinkgear( \
367                           self.log, \
368                           server_host=server_host, \
369                           server_port=server_port, \
370                           DEBUG=0, \
371                           parent=self)
372               
373                if (self.thinkgearClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
374                        QtGui.QMessageBox.information(self, \
375                                                                self.thinkgearClient.socket.name, \
376                                           "Failed to connect to ThinkGear socket server")
377               
378                else:
379                        self.disconnect(self.pushButtonThinkGearConnect, \
380                                                         QtCore.SIGNAL("clicked()"), \
381                                                         self.connectToThinkGearHost)
382                       
383                        self.connect(self.pushButtonThinkGearConnect, \
384                                                         QtCore.SIGNAL("clicked()"), \
385                                                         self.disconnectFromThinkGearHost)
386                       
387                        self.pushButtonThinkGearConnect.setText('Disconnect')
388                       
389                        self.comboBoxEEGHeadsetModel.setEnabled(False)
390                        self.comboBoxEEGSource.setEnabled(False)
391                        self.lineEditThinkGearHost.setEnabled(False)
392                        self.lineEditThinkGearPort.setEnabled(False)
393       
394       
395        ##################################################################
396       
397        def disconnectFromThinkGearHost(self):
398               
399                if self.DEBUG:
400                        print "Disconnecting from ThinkGear Host"
401               
402                self.thinkgearClient.disconnectFromHost()
403               
404                self.disconnect(self.pushButtonThinkGearConnect, \
405                                  QtCore.SIGNAL("clicked()"), \
406                                  self.disconnectFromThinkGearHost)
407               
408                self.connect(self.pushButtonThinkGearConnect, \
409                                  QtCore.SIGNAL("clicked()"), \
410                                  self.connectToThinkGearHost)
411               
412                self.pushButtonForward.emit(QtCore.SIGNAL("released()"))
413               
414                self.pushButtonThinkGearConnect.setText('Connect')
415               
416                self.comboBoxEEGHeadsetModel.setEnabled(True)
417                self.comboBoxEEGSource.setEnabled(True)
418                self.lineEditThinkGearHost.setEnabled(True)
419                self.lineEditThinkGearPort.setEnabled(True)
420               
421                self.progressBarConcentration.setValue(0)
422                self.progressBarRelaxation.setValue(0)
423                self.progressBarSpeed.setValue(0)
424       
425                self.progressBarHelicopterConcentration.setValue(0)
426                self.progressBarHelicopterRelaxation.setValue(0)
427                self.progressBarHelicopterSpeed.setValue(0)
428       
429                self.progressBarWheelchairConcentration.setValue(0)
430                self.progressBarWheelchairRelaxation.setValue(0)
431                self.progressBarWheelchairSpeed.setValue(0)
432       
433       
434        ##################################################################
435       
436        def connectToRCHelicopter(self):
437
438                # Prevent attempting to connect to a device which does not exist
439                device = str(self.comboBoxHelicopterPortSelect.currentText())
440                if device == 'N/A':
441                        self.pushButtonHelicopterConnect.setChecked(False)
442                        return
443                if (sys.platform != 'win32'):
444                        if ((not device.startswith(DEVICE_PATH)) or \
445                            (not os.path.exists(device))):
446                                self.searchForDevices()
447                                self.pushButtonHelicopterConnect.setChecked(False)
448                                return
449
450                self.helicopter = \
451                   helicopter_control.puzzlebox_brainstorms_helicopter_control( \
452                      device_address=device,
453                      command='neutral', \
454                      DEBUG=self.DEBUG)
455               
456                self.helicopter.start()
457               
458                self.disconnect(self.pushButtonHelicopterConnect, \
459                                  QtCore.SIGNAL("clicked()"), \
460                                  self.connectToRCHelicopter)
461               
462                self.connect(self.pushButtonHelicopterConnect, \
463                                  QtCore.SIGNAL("clicked()"), \
464                                  self.disconnectFromRCHelicopter)
465               
466                self.pushButtonHelicopterConnect.setText('Disconnect')
467               
468                self.comboBoxHelicopterTransmitter.setEnabled(False)
469                self.comboBoxHelicopterPortSelect.setEnabled(False)
470                self.pushButtonHelicopterSearch.setEnabled(False)
471               
472                self.pushButtonHelicopterHover.setEnabled(True)
473                self.pushButtonHelicopterFlyForward.setEnabled(True)
474                self.pushButtonHelicopterLand.setEnabled(True)
475                self.pushButtonHelicopterThrottle.setEnabled(True)
476                self.verticalSliderHelicopterThrottle.setEnabled(True)
477                self.pushButtonHelicopterElevatorForward.setEnabled(True)
478                self.verticalSliderHelicopterElevatorForward.setEnabled(True)
479                self.pushButtonHelicopterElevatorReverse.setEnabled(True)
480                self.verticalSliderHelicopterElevatorReverse.setEnabled(True)
481                self.pushButtonHelicopterRudderLeft.setEnabled(True)
482                self.horizontalSliderHelicopterRudderLeft.setEnabled(True)
483                self.pushButtonHelicopterRudderRight.setEnabled(True)
484                self.horizontalSliderHelicopterRudderRight.setEnabled(True)
485                self.pushButtonHelicopterAileronLeft.setEnabled(True)
486                self.horizontalSliderHelicopterAileronLeft.setEnabled(True)
487                self.pushButtonHelicopterAileronRight.setEnabled(True)
488                self.horizontalSliderHelicopterAileronRight.setEnabled(True)
489               
490                self.pushButtonHelicopterConcentrationEnable.setEnabled(True)
491                self.pushButtonHelicopterRelaxationEnable.setEnabled(True)
492                self.pushButtonHelicopterSpeedEnable.setEnabled(True)
493       
494       
495        ##################################################################
496       
497        def disconnectFromRCHelicopter(self):
498               
499                self.helicopter.neutral()
500                self.current_helicopter_state = 'neutral'
501               
502                self.helicopter.stop()
503               
504                self.disconnect(self.pushButtonHelicopterConnect, \
505                                  QtCore.SIGNAL("clicked()"), \
506                                  self.disconnectFromRCHelicopter)
507               
508                self.connect(self.pushButtonHelicopterConnect, \
509                                  QtCore.SIGNAL("clicked()"), \
510                                  self.connectToRCHelicopter)
511               
512                self.pushButtonHelicopterConnect.setText('Connect')
513               
514                self.comboBoxHelicopterTransmitter.setEnabled(True)
515                self.comboBoxHelicopterPortSelect.setEnabled(True)
516                self.pushButtonHelicopterSearch.setEnabled(True)
517               
518                self.pushButtonHelicopterHover.setEnabled(False)
519                self.pushButtonHelicopterFlyForward.setEnabled(False)
520                self.pushButtonHelicopterLand.setEnabled(False)
521                self.pushButtonHelicopterThrottle.setEnabled(False)
522                self.verticalSliderHelicopterThrottle.setEnabled(False)
523                self.pushButtonHelicopterElevatorForward.setEnabled(False)
524                self.verticalSliderHelicopterElevatorForward.setEnabled(False)
525                self.pushButtonHelicopterElevatorReverse.setEnabled(False)
526                self.verticalSliderHelicopterElevatorReverse.setEnabled(False)
527                self.pushButtonHelicopterRudderLeft.setEnabled(False)
528                self.horizontalSliderHelicopterRudderLeft.setEnabled(False)
529                self.pushButtonHelicopterRudderRight.setEnabled(False)
530                self.horizontalSliderHelicopterRudderRight.setEnabled(False)
531                self.pushButtonHelicopterAileronLeft.setEnabled(False)
532                self.horizontalSliderHelicopterAileronLeft.setEnabled(False)
533                self.pushButtonHelicopterAileronRight.setEnabled(False)
534                self.horizontalSliderHelicopterAileronRight.setEnabled(False)
535               
536                self.pushButtonHelicopterConcentrationEnable.setEnabled(False)
537                self.pushButtonHelicopterRelaxationEnable.setEnabled(False)
538                self.pushButtonHelicopterSpeedEnable.setEnabled(False)
539       
540       
541        ##################################################################
542       
543        def connectToWheelchair(self):
544               
545                # Prevent attempting to connect to a device which does not exist
546                device = str(self.comboBoxWheelchairPortSelect.currentText())
547                if device == 'N/A':
548                        self.pushButtonWheelchairConnect.setChecked(False)     
549                        return
550                if (sys.platform != 'win32'):
551                        if ((not device.startswith(DEVICE_PATH)) or \
552                            (not os.path.exists(device))):
553                                self.searchForDevices()
554                                self.pushButtonWheelchairConnect.setChecked(False)
555                                return
556               
557                self.wheelchair = \
558                   wheelchair_control.puzzlebox_brainstorms_wheelchair_control( \
559                      device_address=device,
560                      command=None, \
561                      DEBUG=self.DEBUG)
562               
563                #self.wheelchair.start()
564               
565                self.disconnect(self.pushButtonWheelchairConnect, \
566                                  QtCore.SIGNAL("clicked()"), \
567                                  self.connectToWheelchair)
568               
569                self.connect(self.pushButtonWheelchairConnect, \
570                                  QtCore.SIGNAL("clicked()"), \
571                                  self.disconnectFromWheelchair)
572               
573                self.pushButtonWheelchairConnect.setText('Disconnect')
574               
575                self.comboBoxWheelchairTransmitter.setEnabled(False)
576                self.comboBoxWheelchairPortSelect.setEnabled(False)
577                self.pushButtonWheelchairSearch.setEnabled(False)
578               
579                self.pushButtonWheelchairForward.setEnabled(True)
580                self.pushButtonWheelchairReverse.setEnabled(True)
581                self.pushButtonWheelchairLeft.setEnabled(True)
582                self.pushButtonWheelchairRight.setEnabled(True)
583                self.pushButtonWheelchairStop.setEnabled(True)
584                self.dialWheelchairSpeed.setEnabled(True)
585               
586                self.pushButtonWheelchairConcentrationEnable.setEnabled(True)
587                self.pushButtonWheelchairRelaxationEnable.setEnabled(True)
588                self.pushButtonWheelchairSpeedEnable.setEnabled(True)
589               
590                # Safety Measure: Explicitely require wheelchair speed control
591                # to be enabled each time it wheelchair is connected
592                self.pushButtonWheelchairSpeedEnable.setChecked(False)
593                self.pushButtonWheelchairSpeedEnable.setText('Disabled')
594                self.progressBarWheelchairSpeed.setValue(0)
595       
596       
597        ##################################################################
598       
599        def disconnectFromWheelchair(self):
600               
601                self.stopWheelchair()
602               
603                #self.wheelchair.stop()
604               
605                self.disconnect(self.pushButtonWheelchairConnect, \
606                                  QtCore.SIGNAL("clicked()"), \
607                                  self.disconnectFromWheelchair)
608               
609                self.connect(self.pushButtonWheelchairConnect, \
610                                  QtCore.SIGNAL("clicked()"), \
611                                  self.connectToWheelchair)
612               
613                self.pushButtonWheelchairConnect.setText('Connect')
614               
615                self.comboBoxWheelchairTransmitter.setEnabled(True)
616                self.comboBoxWheelchairPortSelect.setEnabled(True)
617                self.pushButtonWheelchairSearch.setEnabled(True)
618               
619                self.pushButtonWheelchairForward.setEnabled(False)
620                self.pushButtonWheelchairReverse.setEnabled(False)
621                self.pushButtonWheelchairLeft.setEnabled(False)
622                self.pushButtonWheelchairRight.setEnabled(False)
623                self.pushButtonWheelchairStop.setEnabled(False)
624                self.dialWheelchairSpeed.setEnabled(False)
625               
626                self.pushButtonWheelchairConcentrationEnable.setEnabled(False)
627                self.pushButtonWheelchairRelaxationEnable.setEnabled(False)
628                self.pushButtonWheelchairSpeedEnable.setEnabled(False)
629               
630                # Safety Measure: Explicitely require wheelchair speed control
631                # to be enabled each time it wheelchair is connected
632                self.pushButtonWheelchairSpeedEnable.setChecked(False)
633                self.pushButtonWheelchairSpeedEnable.setText('Disabled')
634                self.progressBarWheelchairSpeed.setValue(0)
635       
636       
637        ##################################################################
638       
639        def updateConcentrationButton(self):
640               
641                if self.pushButtonConcentrationEnable.isChecked():
642                       
643                        self.pushButtonConcentrationEnable.setText('Enabled')
644               
645                else:
646                       
647                        self.pushButtonConcentrationEnable.setText('Disabled')
648                        self.progressBarConcentration.setValue(0)
649               
650               
651                self.updateSpeed()
652       
653       
654        ##################################################################
655       
656        def updateRelaxationButton(self):
657               
658                if self.pushButtonRelaxationEnable.isChecked():
659               
660                        self.pushButtonRelaxationEnable.setText('Enabled')
661               
662                else:
663                       
664                        self.pushButtonRelaxationEnable.setText('Disabled')
665                        self.progressBarRelaxation.setValue(0)
666               
667               
668                self.updateNXTSpeed()
669       
670       
671        ##################################################################
672       
673        def updateSpeedButton(self):
674               
675                if self.pushButtonSpeedEnable.isChecked():
676               
677                        self.pushButtonSpeedEnable.setText('Enabled')
678                        self.updateSpeed()
679               
680                else:
681                       
682                        self.pushButtonSpeedEnable.setText('Disabled')
683                        self.progressBarSpeed.setValue(0)
684                        self.stopMotors()
685       
686       
687        ##################################################################
688       
689        def updateHelicopterConcentrationButton(self):
690               
691                if self.pushButtonHelicopterConcentrationEnable.isChecked():
692                       
693                        self.pushButtonHelicopterConcentrationEnable.setText('Enabled')
694               
695                else:
696                       
697                        self.pushButtonHelicopterConcentrationEnable.setText('Disabled')
698                        self.progressBarHelicopterConcentration.setValue(0)
699               
700               
701                self.updateHelicopterSpeed()
702       
703       
704        ##################################################################
705       
706        def updateHelicopterRelaxationButton(self):
707               
708                if self.pushButtonHelicopterRelaxationEnable.isChecked():
709               
710                        self.pushButtonHelicopterRelaxationEnable.setText('Enabled')
711               
712                else:
713                       
714                        self.pushButtonHelicopterRelaxationEnable.setText('Disabled')
715                        self.progressBarHelicopterRelaxation.setValue(0)
716               
717               
718                self.updateHelicopterSpeed()
719       
720       
721        ##################################################################
722       
723        def updateHelicopterSpeedButton(self):
724               
725                if self.pushButtonHelicopterSpeedEnable.isChecked():
726               
727                        self.pushButtonHelicopterSpeedEnable.setText('Enabled')
728                        self.updateHelicopterSpeed()
729               
730                else:
731                       
732                        self.pushButtonHelicopterSpeedEnable.setText('Disabled')
733                        self.progressBarHelicopterSpeed.setValue(0)
734                        self.landHelicopter()
735       
736       
737        ##################################################################
738       
739        def updateWheelchairConcentrationButton(self):
740               
741                if self.pushButtonWheelchairConcentrationEnable.isChecked():
742                       
743                        self.pushButtonWheelchairConcentrationEnable.setText('Enabled')
744               
745                else:
746                       
747                        self.pushButtonWheelchairConcentrationEnable.setText('Disabled')
748                        self.progressBarWheelchairConcentration.setValue(0)
749               
750               
751                self.updateWheelchairSpeed()
752       
753       
754        ##################################################################
755       
756        def updateWheelchairRelaxationButton(self):
757               
758                if self.pushButtonWheelchairRelaxationEnable.isChecked():
759               
760                        self.pushButtonWheelchairRelaxationEnable.setText('Enabled')
761               
762                else:
763                       
764                        self.pushButtonWheelchairRelaxationEnable.setText('Disabled')
765                        self.progressBarWheelchairRelaxation.setValue(0)
766               
767               
768                self.updateWheelchairSpeed()
769       
770       
771        ##################################################################
772       
773        def updateWheelchairSpeedButton(self):
774               
775                if self.pushButtonWheelchairSpeedEnable.isChecked():
776               
777                        self.pushButtonWheelchairSpeedEnable.setText('Enabled')
778                        self.updateWheelchairSpeed()
779               
780                else:
781                       
782                        self.pushButtonWheelchairSpeedEnable.setText('Disabled')
783                        self.progressBarWheelchairSpeed.setValue(0)
784                        self.stopWheelchair()
785       
786       
787        ##################################################################
788       
789        def connectWidgets(self):
790               
791                # LEGO Mindstorms Buttons
792                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("pressed()"), \
793                             self.turnLeft)
794                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("released()"), \
795                             self.stopMotors)
796               
797                self.connect(self.pushButtonForward, QtCore.SIGNAL("pressed()"), \
798                             self.driveForward)
799                self.connect(self.pushButtonForward, QtCore.SIGNAL("released()"), \
800                             self.stopMotors)
801               
802                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("pressed()"), \
803                             self.turnRight)
804                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("released()"), \
805                             self.stopMotors)
806               
807                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("pressed()"), \
808                             self.turnLeftInReverse)
809                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("released()"), \
810                             self.stopMotors)
811               
812                self.connect(self.pushButtonReverse, QtCore.SIGNAL("pressed()"), \
813                             self.driveReverse)
814                self.connect(self.pushButtonReverse, QtCore.SIGNAL("released()"), \
815                             self.stopMotors)
816               
817                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("pressed()"), \
818                             self.turnRightInReverse)
819                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("released()"), \
820                             self.stopMotors)
821               
822               
823                self.connect(self.pushButtonNXTSearch, \
824                                  QtCore.SIGNAL("clicked()"), \
825                                  self.searchForDevices)
826               
827                self.connect(self.pushButtonNXTConnect, \
828                                  QtCore.SIGNAL("clicked()"), \
829                                  self.connectToBrainstormsServer)
830               
831               
832                self.connect(self.pushButtonConcentrationEnable, \
833                                  QtCore.SIGNAL("clicked()"), \
834                                  self.updateConcentrationButton)
835               
836                self.connect(self.pushButtonRelaxationEnable, \
837                                  QtCore.SIGNAL("clicked()"), \
838                                  self.updateRelaxationButton)
839               
840                self.connect(self.pushButtonSpeedEnable, \
841                                  QtCore.SIGNAL("clicked()"), \
842                                  self.updateSpeedButton)
843               
844               
845                self.connect(self.pushButtonNXTMessageOne, QtCore.SIGNAL("pressed()"), \
846                             self.sendMessageOne)
847               
848                self.connect(self.pushButtonNXTMessageTwo, QtCore.SIGNAL("pressed()"), \
849                             self.sendMessageTwo)
850               
851                self.connect(self.pushButtonNXTMessageThree, QtCore.SIGNAL("pressed()"), \
852                             self.sendMessageThree)
853               
854                self.connect(self.pushButtonNXTMessageFour, QtCore.SIGNAL("pressed()"), \
855                             self.sendMessageFour)
856               
857                self.connect(self.pushButtonNXTMessageFive, QtCore.SIGNAL("pressed()"), \
858                             self.sendMessageFive)
859               
860                self.connect(self.pushButtonNXTMessageSix, QtCore.SIGNAL("pressed()"), \
861                             self.sendMessageSix)
862               
863               
864               
865                # RC Helicopter Buttons
866                self.connect(self.pushButtonHelicopterSearch, \
867                                  QtCore.SIGNAL("clicked()"), \
868                                  self.searchForDevices)
869               
870                self.connect(self.pushButtonHelicopterConnect, \
871                                  QtCore.SIGNAL("clicked()"), \
872                                  self.connectToRCHelicopter)
873               
874               
875                self.connect(self.pushButtonHelicopterConcentrationEnable, \
876                                  QtCore.SIGNAL("clicked()"), \
877                                  self.updateHelicopterConcentrationButton)
878               
879                self.connect(self.pushButtonHelicopterRelaxationEnable, \
880                                  QtCore.SIGNAL("clicked()"), \
881                                  self.updateHelicopterRelaxationButton)
882               
883                self.connect(self.pushButtonHelicopterSpeedEnable, \
884                                  QtCore.SIGNAL("clicked()"), \
885                                  self.updateHelicopterSpeedButton)
886               
887               
888                self.connect(self.pushButtonHelicopterHover, \
889                                  QtCore.SIGNAL("clicked()"), \
890                                  self.enableHelicopterHover)
891               
892                self.connect(self.pushButtonHelicopterFlyForward, \
893                                  QtCore.SIGNAL("clicked()"), \
894                                  self.enableHelicopterFlyForward)
895               
896                self.connect(self.pushButtonHelicopterLand, \
897                                  QtCore.SIGNAL("clicked()"), \
898                                  self.landHelicopter)
899               
900               
901               
902                # Wheelchair Buttons
903                self.connect(self.pushButtonWheelchairSearch, \
904                                  QtCore.SIGNAL("clicked()"), \
905                                  self.searchForDevices)
906               
907                self.connect(self.pushButtonWheelchairConnect, \
908                                  QtCore.SIGNAL("clicked()"), \
909                                  self.connectToWheelchair)
910               
911               
912                self.connect(self.pushButtonWheelchairConcentrationEnable, \
913                                  QtCore.SIGNAL("clicked()"), \
914                                  self.updateWheelchairConcentrationButton)
915               
916                self.connect(self.pushButtonWheelchairRelaxationEnable, \
917                                  QtCore.SIGNAL("clicked()"), \
918                                  self.updateWheelchairRelaxationButton)
919               
920                self.connect(self.pushButtonWheelchairSpeedEnable, \
921                                  QtCore.SIGNAL("clicked()"), \
922                                  self.updateWheelchairSpeedButton)
923               
924               
925                self.connect(self.pushButtonWheelchairForward, \
926                                  QtCore.SIGNAL("pressed()"), \
927                                  self.driveWheelchairForward)
928##              self.connect(self.pushButtonWheelchairForward, \
929##                           QtCore.SIGNAL("released()"), \
930##                           self.stopWheelchair)
931               
932                self.connect(self.pushButtonWheelchairReverse, \
933                                  QtCore.SIGNAL("pressed()"), \
934                                  self.driveWheelchairReverse)
935##              self.connect(self.pushButtonWheelchairReverse, \
936##                           QtCore.SIGNAL("released()"), \
937##                           self.stopWheelchair)
938               
939                self.connect(self.pushButtonWheelchairLeft, \
940                                  QtCore.SIGNAL("pressed()"), \
941                                  self.driveWheelchairLeft)
942##              self.connect(self.pushButtonWheelchairLeft, \
943##                           QtCore.SIGNAL("released()"), \
944##                           self.stopWheelchair)
945               
946                self.connect(self.pushButtonWheelchairRight, \
947                                  QtCore.SIGNAL("pressed()"), \
948                                  self.driveWheelchairRight)
949##              self.connect(self.pushButtonWheelchairRight, \
950##                           QtCore.SIGNAL("released()"), \
951##                           self.stopWheelchair)
952               
953                self.connect(self.pushButtonWheelchairStop, \
954                                  QtCore.SIGNAL("pressed()"), \
955                                  self.stopWheelchair)
956               
957               
958               
959                # Control Panel Buttons
960                self.connect(self.pushButtonMessageOne, QtCore.SIGNAL("pressed()"), \
961                             self.sendMessageOne)
962               
963                self.connect(self.pushButtonMessageTwo, QtCore.SIGNAL("pressed()"), \
964                             self.sendMessageTwo)
965               
966                self.connect(self.pushButtonMessageThree, QtCore.SIGNAL("pressed()"), \
967                             self.sendMessageThree)
968               
969                self.connect(self.pushButtonMessageFour, QtCore.SIGNAL("pressed()"), \
970                             self.sendMessageFour)
971               
972                self.connect(self.pushButtonMessageFive, QtCore.SIGNAL("pressed()"), \
973                             self.sendMessageFive)
974               
975                self.connect(self.pushButtonMessageSix, QtCore.SIGNAL("pressed()"), \
976                             self.sendMessageSix)
977               
978               
979                self.connect(self.pushButtonThinkGearConnect, \
980                                  QtCore.SIGNAL("clicked()"), \
981                                  self.connectToThinkGearHost)
982               
983                self.connect(self.pushButtonSendFeedback, \
984                                  QtCore.SIGNAL("clicked()"), \
985                                  self.sendFeedback)
986               
987               
988                #shortcut = QtGui.QShortcut(self)
989                #shortcut.setKey(tr("Down"))
990                #self.connect(shortcut, QtCore.SIGNAL("pressed()"), self.driveReverse)
991               
992               
993                action = QtGui.QAction(self)
994                action.setShortcut(QtGui.QKeySequence("W"))
995                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
996                self.addAction(action)
997               
998                action = QtGui.QAction(self)
999                action.setShortcut(QtGui.QKeySequence("Up"))
1000                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
1001                self.addAction(action)
1002               
1003               
1004                action = QtGui.QAction(self)
1005                action.setShortcut(QtGui.QKeySequence("Left"))
1006                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
1007                self.addAction(action)
1008               
1009                action = QtGui.QAction(self)
1010                action.setShortcut(QtGui.QKeySequence("A"))
1011                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
1012                self.addAction(action)
1013               
1014               
1015                action = QtGui.QAction(self)
1016                action.setShortcut(QtGui.QKeySequence("S"))
1017                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
1018                self.addAction(action)
1019               
1020                action = QtGui.QAction(self)
1021                action.setShortcut(QtGui.QKeySequence("Down"))
1022                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
1023                self.addAction(action)
1024               
1025               
1026                action = QtGui.QAction(self)
1027                action.setShortcut(QtGui.QKeySequence("D"))
1028                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
1029                self.addAction(action)
1030               
1031                action = QtGui.QAction(self)
1032                action.setShortcut(QtGui.QKeySequence("Right"))
1033                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
1034                self.addAction(action)
1035               
1036               
1037                action = QtGui.QAction(self)
1038                action.setShortcut(QtGui.QKeySequence("Z"))
1039                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeftReverse, QtCore.SLOT("animateClick()"))
1040                self.addAction(action)
1041               
1042               
1043                action = QtGui.QAction(self)
1044                action.setShortcut(QtGui.QKeySequence("C"))
1045                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRightReverse, QtCore.SLOT("animateClick()"))
1046                self.addAction(action)
1047               
1048               
1049               
1050                # RC Helicopter Buttons
1051               
1052                action = QtGui.QAction(self)
1053                action.setShortcut(QtGui.QKeySequence("Home"))
1054                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonHelicopterHover, QtCore.SLOT("animateClick()"))
1055                self.addAction(action)
1056                action = QtGui.QAction(self)
1057                action.setShortcut(QtGui.QKeySequence("["))
1058                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonHelicopterHover, QtCore.SLOT("animateClick()"))
1059                self.addAction(action)
1060               
1061                action = QtGui.QAction(self)
1062                action.setShortcut(QtGui.QKeySequence("PgUp"))
1063                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonHelicopterFlyForward, QtCore.SLOT("animateClick()"))
1064                self.addAction(action)
1065                action = QtGui.QAction(self)
1066                action.setShortcut(QtGui.QKeySequence("]"))
1067                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonHelicopterFlyForward, QtCore.SLOT("animateClick()"))
1068                self.addAction(action)
1069               
1070                action = QtGui.QAction(self)
1071                action.setShortcut(QtGui.QKeySequence("End"))
1072                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonHelicopterLand, QtCore.SLOT("animateClick()"))
1073                self.addAction(action)
1074                action = QtGui.QAction(self)
1075                action.setShortcut(QtGui.QKeySequence("\\"))
1076                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonHelicopterLand, QtCore.SLOT("animateClick()"))
1077                self.addAction(action)
1078               
1079               
1080               
1081                # Wheelchair Buttons
1082               
1083                action = QtGui.QAction(self)
1084                action.setShortcut(QtGui.QKeySequence("i"))
1085                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonWheelchairForward, QtCore.SLOT("animateClick()"))
1086                self.addAction(action)
1087               
1088                action = QtGui.QAction(self)
1089                action.setShortcut(QtGui.QKeySequence("k"))
1090                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonWheelchairReverse, QtCore.SLOT("animateClick()"))
1091                self.addAction(action)
1092                action = QtGui.QAction(self)
1093                action.setShortcut(QtGui.QKeySequence("m"))
1094                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonWheelchairReverse, QtCore.SLOT("animateClick()"))
1095                self.addAction(action)
1096               
1097                action = QtGui.QAction(self)
1098                action.setShortcut(QtGui.QKeySequence("j"))
1099                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonWheelchairLeft, QtCore.SLOT("animateClick()"))
1100                self.addAction(action)
1101               
1102                action = QtGui.QAction(self)
1103                action.setShortcut(QtGui.QKeySequence("l"))
1104                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonWheelchairRight, QtCore.SLOT("animateClick()"))
1105                self.addAction(action)
1106               
1107                action = QtGui.QAction(self)
1108                action.setShortcut(QtGui.QKeySequence("Space"))
1109                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonWheelchairStop, QtCore.SLOT("animateClick()"))
1110                self.addAction(action)
1111               
1112               
1113               
1114                # Control Panel Buttons
1115               
1116                action = QtGui.QAction(self)
1117                action.setShortcut(QtGui.QKeySequence("1"))
1118                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonNXTMessageOne, QtCore.SLOT("animateClick()"))
1119                self.addAction(action)
1120               
1121                action = QtGui.QAction(self)
1122                action.setShortcut(QtGui.QKeySequence("2"))
1123                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonNXTMessageTwo, QtCore.SLOT("animateClick()"))
1124                self.addAction(action)
1125               
1126                action = QtGui.QAction(self)
1127                action.setShortcut(QtGui.QKeySequence("3"))
1128                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonNXTMessageThree, QtCore.SLOT("animateClick()"))
1129                self.addAction(action)
1130               
1131                action = QtGui.QAction(self)
1132                action.setShortcut(QtGui.QKeySequence("4"))
1133                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonNXTMessageFour, QtCore.SLOT("animateClick()"))
1134                self.addAction(action)
1135               
1136                action = QtGui.QAction(self)
1137                action.setShortcut(QtGui.QKeySequence("5"))
1138                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonNXTMessageFive, QtCore.SLOT("animateClick()"))
1139                self.addAction(action)
1140               
1141                action = QtGui.QAction(self)
1142                action.setShortcut(QtGui.QKeySequence("6"))
1143                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonNXTMessageSix, QtCore.SLOT("animateClick()"))
1144                self.addAction(action)
1145               
1146               
1147                #self.pushButtonForward.setAutoRepeat(False)
1148                #self.pushButtonForward.setAutoRepeatDelay(0)
1149                #self.pushButtonForward.setAutoRepeatInterval(0)
1150       
1151       
1152        ##################################################################
1153       
1154        def searchForDevices(self):
1155               
1156                nxt_devices = []
1157                rc_helicopter_devices = []
1158                wheelchair_devices = []
1159               
1160                #if (sys.platform != 'win32'):
1161                if False: # temporarily disabled
1162                       
1163                        # Bluetooth module doesn't compile properly under Windows
1164                       
1165                        bluetooth_devices = []
1166                       
1167                        try:
1168                                bluetooth_devices = bluetooth.discover_devices( \
1169                                                       duration=5, \
1170                                                       flush_cache=True, \
1171                                                       lookup_names=False)
1172                        except:
1173                                #command = '%s con' % PATH_TO_HCITOOL
1174                                command = '%s scan' % PATH_TO_HCITOOL
1175                               
1176                                output = os.popen(command, 'r')
1177                               
1178                                for line in output.readlines():
1179                                        print line
1180                                        try:
1181                                                address = line.split(' ')[2]
1182                                        except:
1183                                                pass
1184                                        else:
1185                                                bluetooth_devices.append(address)
1186                       
1187                        for address in bluetooth_devices:
1188                                device_name = bluetooth.lookup_name(address)
1189                                if ((device_name == 'NXT') and \
1190                                    (address not in nxt_devices)):
1191                                        nxt_devices.append(address)
1192                       
1193                       
1194                        if self.DEBUG:
1195                                print "Bluetooth NXT devices found:",
1196                                print nxt_devices
1197               
1198               
1199                # List all serial devices
1200                serial_devices = self.enumerateSerialPorts()
1201               
1202                for serial_device in serial_devices:
1203                        #serial_device = self.fullPortName(serial_device)
1204                        nxt_devices.append(serial_device)
1205                        rc_helicopter_devices.append(serial_device)
1206                        wheelchair_devices.append(serial_device)
1207               
1208               
1209                # Configure combo boxes
1210                if nxt_devices == []:
1211                        nxt_devices.append('N/A')
1212               
1213                if rc_helicopter_devices == []:
1214                        rc_helicopter_devices.append('N/A')
1215               
1216                if wheelchair_devices == []:
1217                        wheelchair_devices.append('N/A')
1218               
1219               
1220                # Don't reset combo boxes if already connected
1221                if self.pushButtonNXTConnect.text != 'Disconnect':
1222                       
1223                        self.comboBoxNXTPortSelect.clear()
1224                       
1225                        #nxt_devices.reverse()
1226                        for nxt_device in nxt_devices:
1227                                self.comboBoxNXTPortSelect.addItem(nxt_device)
1228               
1229               
1230                if self.pushButtonHelicopterConnect.text != 'Disconnect':
1231                       
1232                        self.comboBoxHelicopterPortSelect.clear()
1233                       
1234                        #rc_helicopter_devices.reverse()
1235                        for rc_helicopter in rc_helicopter_devices:
1236                                self.comboBoxHelicopterPortSelect.addItem(rc_helicopter)
1237       
1238       
1239                if self.pushButtonWheelchairConnect.text != 'Disconnect':
1240                       
1241                        self.comboBoxWheelchairPortSelect.clear()
1242                       
1243                        #rc_helicopter_devices.reverse()
1244                        for wheelchair in wheelchair_devices:
1245                                self.comboBoxWheelchairPortSelect.addItem(wheelchair)
1246       
1247       
1248        ##################################################################
1249       
1250        def enumerateSerialPorts(self):
1251               
1252                """ Uses the Win32 registry to return an
1253                iterator of serial (COM) ports
1254                existing on this computer.
1255               
1256                from http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
1257                """
1258               
1259                serial_ports = []
1260               
1261                if (sys.platform == 'win32'):
1262                       
1263                        path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
1264                        try:
1265                                key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
1266                        except WindowsError:
1267                                #raise IterationError
1268                                return []
1269                                #pass
1270                       
1271                        for i in itertools.count():
1272                                try:
1273                                        val = winreg.EnumValue(key, i)
1274                                        #yield str(val[1])
1275                                        serial_ports.append( str(val[1]) )
1276                                except EnvironmentError:
1277                                        break
1278               
1279               
1280                else:
1281                       
1282                        if os.path.exists(DEVICE_PATH):
1283                                device_list = os.listdir(DEVICE_PATH)
1284                               
1285                                device_list.sort()
1286                       
1287                                for device in device_list:
1288                                        if device.startswith('ttyUSB'):
1289                                                serial_ports.append( DEVICE_PATH + '/' + device )
1290                                for device in device_list:
1291                                        if device.startswith('rfcomm'):
1292                                                serial_ports.append( DEVICE_PATH + '/' + device )
1293                                for device in device_list:
1294                                        if device.startswith('ttyACM'):
1295                                                serial_ports.append( DEVICE_PATH + '/' + device )
1296                                for device in device_list:
1297                                        if device.startswith('ttyS'):
1298                                                serial_ports.append( DEVICE_PATH + '/' + device )
1299               
1300               
1301                return(serial_ports)
1302       
1303       
1304        ##################################################################
1305       
1306        def fullPortName(self, portname):
1307               
1308                """ Given a port-name (of the form COM7,
1309                COM12, CNCA0, etc.) returns a full
1310                name suitable for opening with the
1311                Serial class.
1312                """
1313               
1314                m = re.match('^COM(\d+)$', portname)
1315                if m and int(m.group(1)) < 10:
1316                        return portname
1317               
1318                return '\\\\.\\' + portname
1319       
1320       
1321        ##################################################################
1322       
1323        # LEGO Mindstorms Controls
1324       
1325        def turnLeft(self):
1326                self.brainstormsClient.sendCommand('turn_left')
1327                self.drive_state = 'turn_left'
1328       
1329        def driveForward(self):
1330                #if self.DEBUG:
1331                        #print "driveForward"
1332                self.pushButtonForward.setDown(True)
1333                if (self.drive_state != 'drive_forward'):
1334                        self.updateNXTSpeed(new_speed=DEFAULT_NXT_POWER_LEVEL)
1335                self.brainstormsClient.sendCommand('drive_forward', power=self.current_speed)
1336                self.drive_state = 'drive_forward'
1337       
1338        def turnRight(self):
1339                self.brainstormsClient.sendCommand('turn_right')
1340                self.drive_state = 'turn_right'
1341       
1342        def turnLeftInReverse(self):
1343                self.brainstormsClient.sendCommand('turn_left_in_reverse')
1344                self.drive_state = 'turn_left_in_reverse'
1345       
1346        def driveReverse(self):
1347                self.brainstormsClient.sendCommand('drive_reverse')
1348                self.drive_state = 'drive_reverse'
1349       
1350        def turnRightInReverse(self):
1351                self.brainstormsClient.sendCommand('turn_right_in_reverse')
1352                self.drive_state = 'turn_right_in_reverse'
1353       
1354        def stopMotors(self):
1355                self.pushButtonForward.setDown(False)
1356                if (self.current_speed != 0):
1357                        self.updateNXTSpeed(new_speed=0)
1358                if self.brainstormsClient != None:
1359                        self.brainstormsClient.sendCommand('stop_motors')
1360                self.drive_state = 'stop_motors'
1361       
1362        def sendMessageOne(self):
1363                message = str(self.lineEditMessageOne.text())
1364                self.brainstormsClient.sendCommand('send_message_1')
1365       
1366        def sendMessageTwo(self):
1367                message = str(self.lineEditMessageTwo.text())
1368                self.brainstormsClient.sendCommand('send_message_2')
1369       
1370        def sendMessageThree(self):
1371                message = str(self.lineEditMessageThree.text())
1372                self.brainstormsClient.sendCommand('send_message_3')
1373       
1374        def sendMessageFour(self):
1375                message = str(self.lineEditMessageFour.text())
1376                self.brainstormsClient.sendCommand('send_message_4')
1377       
1378        def sendMessageFive(self):
1379                message = str(self.lineEditMessageFive.text())
1380                self.brainstormsClient.sendCommand('send_message_5')
1381       
1382        def sendMessageSix(self):
1383                message = str(self.lineEditMessageSix.text())
1384                self.brainstormsClient.sendCommand('send_message_6')
1385       
1386       
1387        ##################################################################
1388       
1389        def enableHelicopterHover(self):
1390               
1391                if self.pushButtonHelicopterFlyForward.isChecked():
1392                        self.pushButtonHelicopterFlyForward.setChecked(False)
1393                        self.disableHelicopterFlyForward()
1394               
1395                self.helicopter.hover(duration=None)
1396                self.current_helicopter_state = 'hover'
1397               
1398                self.disconnect(self.pushButtonHelicopterHover, \
1399                                                        QtCore.SIGNAL("clicked()"), \
1400                                                        self.enableHelicopterHover)
1401               
1402                self.connect(self.pushButtonHelicopterHover, \
1403                                  QtCore.SIGNAL("clicked()"), \
1404                                  self.disableHelicopterHover)
1405               
1406                #if self.pushButtonHelicopterFlyForward.isChecked():
1407                        #self.pushButtonHelicopterFlyForward.toggle()
1408                        ##self.disableHelicopterFlyForward()
1409       
1410       
1411        ##################################################################
1412       
1413        def disableHelicopterHover(self):
1414               
1415                self.helicopter.neutral()
1416                self.current_helicopter_state = 'neutral'
1417               
1418                self.disconnect(self.pushButtonHelicopterHover, \
1419                                                        QtCore.SIGNAL("clicked()"), \
1420                                                        self.disableHelicopterHover)
1421               
1422                self.connect(self.pushButtonHelicopterHover, \
1423                                  QtCore.SIGNAL("clicked()"), \
1424                                  self.enableHelicopterHover)
1425       
1426       
1427        ##################################################################
1428       
1429        def enableHelicopterFlyForward(self):
1430               
1431                if self.pushButtonHelicopterHover.isChecked():
1432                        self.pushButtonHelicopterHover.setChecked(False)
1433                        self.disableHelicopterHover()
1434               
1435                self.helicopter.fly_forward(duration=None)
1436                self.current_helicopter_state = 'fly_forward'
1437               
1438                self.disconnect(self.pushButtonHelicopterFlyForward, \
1439                                                        QtCore.SIGNAL("clicked()"), \
1440                                                        self.enableHelicopterFlyForward)
1441               
1442                self.connect(self.pushButtonHelicopterFlyForward, \
1443                                  QtCore.SIGNAL("clicked()"), \
1444                                  self.disableHelicopterFlyForward)
1445               
1446                #if self.pushButtonHelicopterHover.isChecked():
1447                        #self.pushButtonHelicopterHover.toggle()
1448                        ##self.disableHelicopterHover()
1449       
1450       
1451        ##################################################################
1452       
1453        def disableHelicopterFlyForward(self):
1454               
1455                self.helicopter.neutral()
1456                self.current_helicopter_state = 'neutral'
1457               
1458                self.disconnect(self.pushButtonHelicopterFlyForward, \
1459                                                        QtCore.SIGNAL("clicked()"), \
1460                                                        self.disableHelicopterFlyForward)
1461               
1462                self.connect(self.pushButtonHelicopterFlyForward, \
1463                                  QtCore.SIGNAL("clicked()"), \
1464                                  self.enableHelicopterFlyForward)
1465       
1466       
1467        ##################################################################
1468       
1469        def landHelicopter(self):
1470               
1471                self.helicopter.neutral()
1472                self.current_helicopter_state = 'neutral'
1473               
1474                if self.pushButtonHelicopterHover.isChecked():
1475                        self.pushButtonHelicopterHover.setChecked(False)
1476                        self.disableHelicopterHover()
1477                if self.pushButtonHelicopterFlyForward.isChecked():
1478                        self.pushButtonHelicopterFlyForward.setChecked(False)
1479                        self.disableHelicopterFlyForward()
1480       
1481       
1482        ##################################################################
1483       
1484        def driveWheelchairForward(self):
1485                #print "WheelchairForward"
1486                speed = self.dialWheelchairSpeed.value()
1487                self.wheelchair.sendCommand(speed, 'forward')
1488       
1489        def driveWheelchairReverse(self):
1490                print "WheelchairReverse"
1491                speed = self.dialWheelchairSpeed.value()
1492                self.wheelchair.sendCommand(speed, 'reverse')
1493       
1494        def driveWheelchairLeft(self):
1495                print "WheelchairLeft"
1496                speed = self.dialWheelchairSpeed.value()
1497                self.wheelchair.sendCommand(speed, 'left')
1498       
1499        def driveWheelchairRight(self):
1500                print "WheelchairRight"
1501                speed = self.dialWheelchairSpeed.value()
1502                self.wheelchair.sendCommand(speed, 'right')
1503       
1504        def stopWheelchair(self):
1505                print "stopWheelchair"
1506                speed = self.dialWheelchairSpeed.value()
1507                self.wheelchair.sendCommand(speed, 'stop')
1508       
1509       
1510        ##################################################################
1511       
1512        def updateNXTSpeed(self, new_speed=None):
1513               
1514                if new_speed == None:
1515               
1516                        concentration=self.progressBarConcentration.value()
1517                        relaxation=self.progressBarRelaxation.value()
1518                       
1519                        new_speed = self.calculateSpeed(concentration, relaxation)
1520               
1521               
1522                # Update GUI
1523                if self.pushButtonSpeedEnable.isChecked():
1524                        self.progressBarSpeed.setValue(new_speed)
1525               
1526               
1527                # If there is a change between the new and current speeds
1528                # and either the robot is currently driving forward
1529                # or the "speed control" button is enabled,
1530                # then send the updated speed to the robot
1531                if ((self.current_speed != new_speed) and \
1532                         ((self.drive_state == 'drive_forward') or \
1533                          (self.pushButtonSpeedEnable.isChecked()))):
1534                       
1535                        if (new_speed == 0):
1536                                self.current_speed = new_speed
1537                                self.stopMotors()
1538                        else:
1539                                if ((self.brainstormsClient != None) and \
1540                                    (self.pushButtonSpeedEnable.isChecked())):
1541                                        self.pushButtonForward.setDown(True)
1542                                        self.brainstormsClient.sendCommand('drive_forward', power=new_speed)
1543               
1544               
1545                self.current_speed = new_speed
1546       
1547       
1548        ##################################################################
1549       
1550        def updateHelicopterSpeed(self, new_speed=None):
1551               
1552                if new_speed == None:
1553               
1554                        concentration=self.progressBarHelicopterConcentration.value()
1555                        relaxation=self.progressBarHelicopterRelaxation.value()
1556                       
1557                        new_speed = self.calculateSpeed(concentration, relaxation)
1558               
1559               
1560                # Update GUI
1561                if self.pushButtonHelicopterSpeedEnable.isChecked():
1562                        self.progressBarHelicopterSpeed.setValue(new_speed)
1563                       
1564                        if ((new_speed > 0) and \
1565                            (self.current_helicopter_state == 'neutral') and \
1566                            (self.helicopter != None)):
1567                                self.enableHelicopterHover()
1568                       
1569                        elif ((new_speed == 0) and \
1570                              (self.current_helicopter_state != 'neutral') and \
1571                              (self.helicopter != None)):
1572                                self.landHelicopter()
1573               
1574               
1575                self.current_speed = new_speed
1576       
1577       
1578        ##################################################################
1579       
1580        def updateWheelchairSpeed(self, new_speed=None):
1581               
1582                if new_speed == None:
1583               
1584                        concentration=self.progressBarWheelchairConcentration.value()
1585                        relaxation=self.progressBarWheelchairRelaxation.value()
1586                       
1587                        new_speed = self.calculateSpeed(concentration, relaxation)
1588               
1589               
1590                # Update GUI
1591                if self.pushButtonWheelchairSpeedEnable.isChecked():
1592                        self.progressBarWheelchairSpeed.setValue(new_speed)
1593               
1594               
1595                self.current_speed = new_speed
1596       
1597       
1598        ##################################################################
1599       
1600        def calculateSpeed(self, concentration, relaxation):
1601               
1602                speed = 0
1603               
1604                thresholds = THINKGEAR_POWER_THRESHOLDS
1605               
1606                match = int(concentration)
1607               
1608                while ((match not in thresholds['concentration'].keys()) and \
1609                            (match >= 0)):
1610                        match -= 1
1611               
1612               
1613                if match in thresholds['concentration'].keys():
1614                        speed = thresholds['concentration'][match]
1615               
1616               
1617                match = int(relaxation)
1618               
1619                while ((match not in thresholds['relaxation'].keys()) and \
1620                            (match >= 0)):
1621                        match -= 1
1622               
1623                if match in thresholds['relaxation'].keys():
1624                        speed = speed + thresholds['relaxation'][match]
1625               
1626               
1627                # LEGO Mindstorms power settings cannot exceed 100
1628                # and don't drive well with levels less than 50
1629                if (speed > 100):
1630                        speed = 100
1631                elif (speed < 50):
1632                        speed = 0
1633               
1634               
1635                return(speed)
1636       
1637       
1638        ##################################################################
1639       
1640        def processPacketThinkGear(self, packet):
1641               
1642                if ('eSense' in packet.keys()):
1643                       
1644                        if ('attention' in packet['eSense'].keys()):
1645                                if self.pushButtonConcentrationEnable.isChecked():
1646                                        self.progressBarConcentration.setValue(packet['eSense']['attention'])
1647                                if self.pushButtonHelicopterConcentrationEnable.isChecked():
1648                                        self.progressBarHelicopterConcentration.setValue(packet['eSense']['attention'])
1649                                if self.pushButtonWheelchairConcentrationEnable.isChecked():
1650                                        self.progressBarWheelchairConcentration.setValue(packet['eSense']['attention'])
1651                       
1652                        if ('meditation' in packet['eSense'].keys()):
1653                                if self.pushButtonRelaxationEnable.isChecked():
1654                                        self.progressBarRelaxation.setValue(packet['eSense']['meditation'])
1655                                if self.pushButtonHelicopterRelaxationEnable.isChecked():
1656                                        self.progressBarHelicopterRelaxation.setValue(packet['eSense']['meditation'])
1657                                if self.pushButtonWheelchairRelaxationEnable.isChecked():
1658                                        self.progressBarWheelchairRelaxation.setValue(packet['eSense']['meditation'])
1659               
1660               
1661                self.updateNXTSpeed()
1662                self.updateHelicopterSpeed()
1663                self.updateWheelchairSpeed()
1664       
1665       
1666        ##################################################################
1667
1668        def sendFeedback(self):
1669               
1670                values = {}
1671
1672                values['name'] = str(self.lineEditFeedbackName.text())
1673                values['email'] = str(self.lineEditFeedbackEmail.text())
1674                values['comment'] = str(self.textEditFeedback.toPlainText())
1675               
1676                values['subject'] = '[brainstorms feedback]'
1677                values['capcha_contact'] = 'brainstorms'
1678               
1679               
1680                url_data = urllib.urlencode(values)
1681               
1682                try:
1683                        page = urllib.urlopen(BRAINSTORMS_FEEDBACK_URL, url_data)
1684                       
1685                        reply = QtGui.QMessageBox.information( \
1686                              self, \
1687                              'Feedback Sent', \
1688                              'Thank you for your feedback', \
1689                              'OK')
1690                       
1691                        self.lineEditFeedbackName.setText('')
1692                        self.lineEditFeedbackEmail.setText('')
1693                        self.textEditFeedback.setText('')
1694               
1695                except:
1696                        reply = QtGui.QMessageBox.information( \
1697                              self, \
1698                              'Feedback Sent', \
1699                              'We\'re sorry but there was an error submitting your feedback.\nPlease email contact@puzzlebox.info instead.', \
1700                              'OK')
1701       
1702       
1703        ##################################################################
1704       
1705        def closeEvent(self, event):
1706               
1707                quit_message = "Are you sure you want to exit the program?"
1708               
1709                reply = QtGui.QMessageBox.question( \
1710                           self, \
1711                          'Quit Puzzlebox Brainstorms', \
1712                           quit_message, \
1713                           QtGui.QMessageBox.Yes, \
1714                           QtGui.QMessageBox.No)
1715               
1716                if reply == QtGui.QMessageBox.Yes:
1717                       
1718                        if self.brainstormsClient != None:
1719                                self.stopMotors()
1720                                self.brainstormsClient.socket.flush()
1721                               
1722                                if self.brainstormsServer != None:
1723                                       
1724                                        if self.brainstormsServer.rc == None:
1725                                               
1726                                                bluetooth_device = str(self.comboBoxNXTPortSelect.currentText())
1727                                                self.brainstormsServer.executeCommand( \
1728                                                        'stop_motors', \
1729                                                        bluetooth_device=bluetooth_device)
1730                                               
1731                                        else:
1732                                                self.brainstormsServer.rc.run('stop_motors')
1733                       
1734                       
1735                        event.accept()
1736               
1737                else:
1738                        event.ignore()
1739
1740
1741#####################################################################
1742# Functions
1743#####################################################################
1744
1745#####################################################################
1746# Main
1747#####################################################################
1748
1749if __name__ == '__main__':
1750       
1751        #log = puzzlebox_logger.puzzlebox_logger(logfile='client_interface')
1752        log = None
1753       
1754        app = QtGui.QApplication(sys.argv)
1755       
1756        window = puzzlebox_brainstorms_client_interface(log, DEBUG)
1757        window.show()
1758       
1759        sys.exit(app.exec_())
1760
Note: See TracBrowser for help on using the repository browser.