source: remote_control/puzzlebox_brainstorms_client_interface.py @ 107

Last change on this file since 107 was 107, checked in by sc, 11 years ago
  • support for user-configurable NXT bluetooth device port in interface
  • window icon for interface manually set
File size: 16.7 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# Last Update: 2010.07.10
12#
13#####################################################################
14# To Do:
15# - server may not correctly handle multiple clients connected
16#      to an embedded Brainstorms server
17# - disable autorepeating on shortcut keys
18# - update configuration.ini file with settings entered into interface
19#####################################################################
20
21import os, sys
22
23#try:
24        #from PySide import QtCore, QtGui, QtNetwork
25#except:
26        #print "Using PyQt4 module"
27        #from PyQt4 import QtCore, QtGui, QtNetwork
28#else:
29        #print "Using PySide module"
30
31from PyQt4 import QtCore, QtGui, QtNetwork
32#from PySide import QtCore, QtGui, QtNetwork
33
34from puzzlebox_brainstorms_client_interface_design import Ui_Form
35
36import simplejson as json
37
38import puzzlebox_brainstorms_configuration as configuration
39import puzzlebox_brainstorms_network_client as brainstorms_client
40import puzzlebox_brainstorms_network_client_thinkgear as thinkgear_client
41#import puzzlebox_logger
42
43#####################################################################
44# Globals
45#####################################################################
46
47DEBUG = 1
48
49THINKGEAR_POWER_THRESHOLDS = configuration.THINKGEAR_POWER_THRESHOLDS
50
51BLUETOOTH_DEVICE = configuration.NXT_BLUETOOTH_DEVICE
52
53NXT_BLUETOOTH_DEVICE = configuration.NXT_BLUETOOTH_DEVICE
54
55DEFAULT_NXT_POWER_LEVEL = configuration.DEFAULT_NXT_POWER_LEVEL
56
57THINKGEAR_SERVER_HOST = configuration.THINKGEAR_SERVER_HOST
58THINKGEAR_SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
59
60#####################################################################
61# Classes
62#####################################################################
63
64class puzzlebox_brainstorms_client_interface(QtGui.QWidget, Ui_Form):
65       
66        def __init__(self, log, DEBUG=DEBUG, parent = None):
67               
68                self.log = log
69                self.DEBUG = DEBUG
70               
71                QtGui.QWidget.__init__(self, parent)
72                self.setupUi(self)
73               
74                self.configureSettings()
75                self.connectWidgets()
76               
77                self.brainstormsClient = None
78               
79                self.drive_state = 'stop_motors'
80                self.current_speed = 0
81       
82       
83        ##################################################################
84       
85        def configureSettings(self):
86               
87                # Brainstorms Interface
88               
89                icon = QtGui.QIcon()
90                icon.addPixmap(QtGui.QPixmap("images/puzzlebox.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
91                self.setWindowIcon(icon)
92               
93                self.pushButtonTurnLeft.setEnabled(False)
94                self.pushButtonForward.setEnabled(False)
95                self.pushButtonTurnRight.setEnabled(False)
96                self.pushButtonTurnLeftReverse.setEnabled(False)
97                self.pushButtonReverse.setEnabled(False)
98                self.pushButtonTurnRightReverse.setEnabled(False)
99               
100               
101                # LEGO Mindstorms
102               
103                self.textLabelNXTStatus.setText("Status: Disconnected")
104               
105                # Display communication port for LEGO Mindstorms NXT device
106                self.lineEditNXTPort.setText(NXT_BLUETOOTH_DEVICE)
107               
108                # LEGO Mindstorms NXT COM port selection not yet available
109                self.lineEditNXTPort.setEnabled(False)
110               
111               
112                # EEG Headset
113               
114                # Display Host for ThinkGear Connect Socket Server
115                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
116                #self.lineEditThinkGearHost.setEnabled(False)
117               
118                # Display Port for ThinkGear Connect Socket Server
119                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
120                #self.lineEditThinkGearPort.setEnabled(False)
121       
122       
123        ##################################################################
124       
125        def getMinimumThreshold(self, threshold):
126               
127                '''Return the minimum detection level which results
128                in a non-zero power setting'''
129               
130                minimum = 100
131               
132                threshold_keys = threshold.keys()
133                threshold_keys.sort()
134                threshold_keys.reverse()
135               
136                for key in threshold_keys:
137                       
138                        if ((threshold[key] < minimum) and \
139                                 (threshold[key] > 0)):
140                                minimum = key
141               
142               
143                return(minimum)
144       
145       
146        ##################################################################
147       
148        def configureNetworkBrainstorms(self):
149               
150                bluetooth_device = self.lineEditNXTPort.text()
151               
152                self.brainstormsClient = \
153                   brainstorms_client.puzzlebox_brainstorms_network_client( \
154                           self.log, \
155                           bluetooth_device=bluetooth_device, \
156                           parent=self)
157               
158                self.brainstormsClient.sendCommand('connect', \
159                                                   bluetooth_device=bluetooth_device)
160       
161       
162        ##################################################################
163       
164        def connectToBrainstormsServer(self):
165               
166                if self.DEBUG:
167                        print "Connecting to Brainstorms Server"
168               
169                #server_port = int(self.lineEditNXTPort.text())
170               
171                self.configureNetworkBrainstorms()
172               
173                #if (self.brainstormsClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
174                        #QtGui.QMessageBox.information(self, \
175                                                                #self.brainstormsClient.socket.name, \
176                                           #"Failed to connect to Brainstorms socket server")
177               
178                #else:
179                self.disconnect(self.pushButtonNXTConnect, \
180                                                        QtCore.SIGNAL("clicked()"), \
181                                                        self.connectToBrainstormsServer)
182               
183                self.connect(self.pushButtonNXTConnect, \
184                                                        QtCore.SIGNAL("clicked()"), \
185                                                        self.disconnectFromBrainstormsServer)
186               
187                self.textLabelNXTStatus.setText("Status: Connected")
188                self.pushButtonNXTConnect.setText('Disconnect')
189               
190                self.lineEditNXTPort.setEnabled(False)
191               
192                self.pushButtonTurnLeft.setEnabled(True)
193                self.pushButtonForward.setEnabled(True)
194                self.pushButtonTurnRight.setEnabled(True)
195                self.pushButtonTurnLeftReverse.setEnabled(True)
196                self.pushButtonReverse.setEnabled(True)
197                self.pushButtonTurnRightReverse.setEnabled(True)
198       
199       
200        ##################################################################
201       
202        def disconnectFromBrainstormsServer(self):
203               
204                if self.DEBUG:
205                        print "Disconnecting from Brainstorms Server"
206               
207                self.brainstormsClient.socket.disconnectFromHost()
208               
209                self.disconnect(self.pushButtonNXTConnect, \
210                                  QtCore.SIGNAL("clicked()"), \
211                                  self.disconnectFromBrainstormsServer)
212               
213                self.connect(self.pushButtonNXTConnect, \
214                                  QtCore.SIGNAL("clicked()"), \
215                                  self.connectToBrainstormsServer)
216               
217                self.textLabelNXTStatus.setText("Status: Disconnected")
218                self.pushButtonNXTConnect.setText('Connect')
219               
220                self.lineEditNXTPort.setEnabled(True)
221               
222                self.pushButtonTurnLeft.setEnabled(False)
223                self.pushButtonForward.setEnabled(False)
224                self.pushButtonTurnRight.setEnabled(False)
225                self.pushButtonTurnLeftReverse.setEnabled(False)
226                self.pushButtonReverse.setEnabled(False)
227                self.pushButtonTurnRightReverse.setEnabled(False)
228               
229                self.brainstormsClient = None
230       
231       
232        ##################################################################
233       
234        def connectToThinkGearHost(self):
235               
236                if self.DEBUG:
237                        print "Connecting to ThinkGear Host"
238               
239                server_host = str(self.lineEditThinkGearHost.text())
240                server_port = int(self.lineEditThinkGearPort.text())
241               
242                self.thinkgearClient = \
243                   thinkgear_client.puzzlebox_brainstorms_network_client_thinkgear( \
244                           self.log, \
245                           server_host=server_host, \
246                           server_port=server_port, \
247                           DEBUG=0, \
248                           parent=self)
249               
250                if (self.thinkgearClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
251                        QtGui.QMessageBox.information(self, \
252                                                                self.thinkgearClient.socket.name, \
253                                           "Failed to connect to ThinkGear socket server")
254               
255                else:
256                        self.disconnect(self.pushButtonThinkGearConnect, \
257                                                         QtCore.SIGNAL("clicked()"), \
258                                                         self.connectToThinkGearHost)
259                       
260                        self.connect(self.pushButtonThinkGearConnect, \
261                                                         QtCore.SIGNAL("clicked()"), \
262                                                         self.disconnectFromThinkGearHost)
263                       
264                        self.pushButtonThinkGearConnect.setText('Disconnect')
265                       
266                        self.lineEditThinkGearHost.setEnabled(False)
267                        self.lineEditThinkGearPort.setEnabled(False)
268       
269       
270        ##################################################################
271       
272        def disconnectFromThinkGearHost(self):
273               
274                if self.DEBUG:
275                        print "Disconnecting from ThinkGear Host"
276               
277                self.thinkgearClient.disconnectFromHost()
278               
279                self.disconnect(self.pushButtonThinkGearConnect, \
280                                  QtCore.SIGNAL("clicked()"), \
281                                  self.disconnectFromThinkGearHost)
282               
283                self.connect(self.pushButtonThinkGearConnect, \
284                                  QtCore.SIGNAL("clicked()"), \
285                                  self.connectToThinkGearHost)
286               
287                self.pushButtonForward.emit(QtCore.SIGNAL("released()"))
288               
289                self.pushButtonThinkGearConnect.setText('Connect')
290               
291                self.lineEditThinkGearHost.setEnabled(True)
292                self.lineEditThinkGearPort.setEnabled(True)
293               
294                self.progressBarConcentration.setValue(0)
295                self.progressBarRelaxation.setValue(0)
296                self.progressBarSpeed.setValue(0)
297       
298       
299        ##################################################################
300       
301        def connectWidgets(self):
302               
303                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("pressed()"), \
304                             self.turnLeft)
305                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("released()"), \
306                             self.stopMotors)
307               
308                self.connect(self.pushButtonForward, QtCore.SIGNAL("pressed()"), \
309                             self.driveForward)
310                self.connect(self.pushButtonForward, QtCore.SIGNAL("released()"), \
311                             self.stopMotors)
312               
313                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("pressed()"), \
314                             self.turnRight)
315                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("released()"), \
316                             self.stopMotors)
317               
318                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("pressed()"), \
319                             self.turnLeftInReverse)
320                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("released()"), \
321                             self.stopMotors)
322               
323                self.connect(self.pushButtonReverse, QtCore.SIGNAL("pressed()"), \
324                             self.driveReverse)
325                self.connect(self.pushButtonReverse, QtCore.SIGNAL("released()"), \
326                             self.stopMotors)
327               
328                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("pressed()"), \
329                             self.turnRightInReverse)
330                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("released()"), \
331                             self.stopMotors)
332               
333               
334                self.connect(self.pushButtonNXTConnect, \
335                                  QtCore.SIGNAL("clicked()"), \
336                                  self.connectToBrainstormsServer)
337               
338                self.connect(self.pushButtonThinkGearConnect, \
339                                  QtCore.SIGNAL("clicked()"), \
340                                  self.connectToThinkGearHost)
341               
342               
343                #shortcut = QtGui.QShortcut(self)
344                #shortcut.setKey(tr("Down"))
345                #self.connect(shortcut, QtCore.SIGNAL("pressed()"), self.driveReverse)
346               
347               
348                action = QtGui.QAction(self)
349                action.setShortcut(QtGui.QKeySequence("W"))
350                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
351                self.addAction(action)
352               
353                action = QtGui.QAction(self)
354                action.setShortcut(QtGui.QKeySequence("Up"))
355                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
356                self.addAction(action)
357               
358               
359                action = QtGui.QAction(self)
360                action.setShortcut(QtGui.QKeySequence("Left"))
361                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
362                self.addAction(action)
363               
364                action = QtGui.QAction(self)
365                action.setShortcut(QtGui.QKeySequence("A"))
366                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
367                self.addAction(action)
368               
369               
370                action = QtGui.QAction(self)
371                action.setShortcut(QtGui.QKeySequence("S"))
372                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
373                self.addAction(action)
374               
375                action = QtGui.QAction(self)
376                action.setShortcut(QtGui.QKeySequence("Down"))
377                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
378                self.addAction(action)
379               
380               
381                action = QtGui.QAction(self)
382                action.setShortcut(QtGui.QKeySequence("D"))
383                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
384                self.addAction(action)
385               
386                action = QtGui.QAction(self)
387                action.setShortcut(QtGui.QKeySequence("Right"))
388                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
389                self.addAction(action)
390               
391               
392                action = QtGui.QAction(self)
393                action.setShortcut(QtGui.QKeySequence("Z"))
394                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeftReverse, QtCore.SLOT("animateClick()"))
395                self.addAction(action)
396               
397               
398                action = QtGui.QAction(self)
399                action.setShortcut(QtGui.QKeySequence("C"))
400                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRightReverse, QtCore.SLOT("animateClick()"))
401                self.addAction(action)
402               
403               
404                #self.pushButtonForward.setAutoRepeat(False)
405                #self.pushButtonForward.setAutoRepeatDelay(0)
406                #self.pushButtonForward.setAutoRepeatInterval(0)
407
408       
409        ##################################################################
410       
411        def turnLeft(self):
412                self.brainstormsClient.sendCommand('turn_left')
413                self.drive_state = 'turn_left'
414       
415        def driveForward(self):
416                #if self.DEBUG:
417                        #print "driveForward"
418                self.pushButtonForward.setDown(True)
419                if (self.drive_state != 'drive_forward'):
420                        self.updateSpeed(new_speed=DEFAULT_NXT_POWER_LEVEL)
421                self.brainstormsClient.sendCommand('drive_forward', power=self.current_speed)
422                self.drive_state = 'drive_forward'
423       
424        def turnRight(self):
425                self.brainstormsClient.sendCommand('turn_right')
426                self.drive_state = 'turn_right'
427       
428        def turnLeftInReverse(self):
429                self.brainstormsClient.sendCommand('turn_left_in_reverse')
430                self.drive_state = 'turn_left_in_reverse'
431       
432        def driveReverse(self):
433                self.brainstormsClient.sendCommand('drive_reverse')
434                self.drive_state = 'drive_reverse'
435       
436        def turnRightInReverse(self):
437                self.brainstormsClient.sendCommand('turn_right_in_reverse')
438                self.drive_state = 'turn_right_in_reverse'
439       
440        def stopMotors(self):
441                self.pushButtonForward.setDown(False)
442                if (self.current_speed != 0):
443                        self.updateSpeed(new_speed=0)
444                if self.brainstormsClient != None:
445                        self.brainstormsClient.sendCommand('stop_motors')
446                self.drive_state = 'stop_motors'
447       
448       
449        ##################################################################
450       
451        def updateSpeed(self, new_speed=None):
452               
453                if new_speed == None:
454               
455                        concentration=self.progressBarConcentration.value()
456                        relaxation=self.progressBarRelaxation.value()
457                       
458                        new_speed = self.calculateSpeed(concentration, relaxation)
459               
460               
461                # Update GUI
462                self.progressBarSpeed.setValue(new_speed)
463               
464                # GUI button under "Speed" progress bar set to Enabled
465                speed_control = (self.pushButtonSpeedEnable.text() == 'Enabled')
466               
467                # If there is a change between the new and current speeds
468                # and either the robot is currently driving forward
469                # or the "speed control" button is enabled,
470                # then send the updated speed to the robot
471                if ((self.current_speed != new_speed) and \
472                         ((self.drive_state == 'drive_forward') or \
473                          (speed_control))):
474                       
475                        if (new_speed == 0):
476                                self.current_speed = new_speed
477                                self.stopMotors()
478                        else:
479                                if self.brainstormsClient != None:
480                                        self.pushButtonForward.setDown(True)
481                                        self.brainstormsClient.sendCommand('drive_forward', power=new_speed)
482               
483               
484                self.current_speed = new_speed
485       
486       
487        ##################################################################
488       
489        def calculateSpeed(self, concentration, relaxation):
490               
491                speed = 0
492               
493                thresholds = THINKGEAR_POWER_THRESHOLDS
494               
495                match = int(concentration)
496               
497                while ((match not in thresholds['concentration'].keys()) and \
498                            (match >= 0)):
499                        match -= 1
500               
501               
502                if match in thresholds['concentration'].keys():
503                        speed = thresholds['concentration'][match]
504               
505               
506                match = int(relaxation)
507               
508                while ((match not in thresholds['relaxation'].keys()) and \
509                            (match >= 0)):
510                        match -= 1
511               
512                if match in thresholds['relaxation'].keys():
513                        speed = speed + thresholds['relaxation'][match]
514               
515               
516                # LEGO Mindstorms power settings cannot exceed 100
517                # and don't drive well with levels less than 50
518                if (speed > 100):
519                        speed = 100
520                elif (speed < 50):
521                        speed = 0
522               
523               
524                return(speed)
525       
526       
527        ##################################################################
528       
529        def processPacketThinkGear(self, packet):
530               
531                if ('eSense' in packet.keys()):
532                       
533                        if ('attention' in packet['eSense'].keys()):
534                                self.progressBarConcentration.setValue(packet['eSense']['attention'])
535                       
536                        if ('meditation' in packet['eSense'].keys()):
537                                self.progressBarRelaxation.setValue(packet['eSense']['meditation'])
538               
539               
540                self.updateSpeed()
541
542
543#####################################################################
544# Functions
545#####################################################################
546
547#####################################################################
548# Main
549#####################################################################
550
551if __name__ == '__main__':
552       
553        #log = puzzlebox_logger.puzzlebox_logger(logfile='client_interface')
554        log = None
555       
556        app = QtGui.QApplication(sys.argv)
557       
558        window = puzzlebox_brainstorms_client_interface(log, DEBUG)
559        window.show()
560       
561        sys.exit(app.exec_())
562
Note: See TracBrowser for help on using the repository browser.