source: trunk/Puzzlebox/Synapse/Interface.py @ 313

Last change on this file since 313 was 313, checked in by sc, 9 years ago
  • Updates to support plugins in Puzzlebox Jigsaw
  • Property svn:executable set to *
File size: 39.9 KB
Line 
1# -*- coding: utf-8 -*-
2
3# Copyright Puzzlebox Productions, LLC (2010-2011)
4#
5# This code is released under the GNU Pulic License (GPL) version 2
6# For more information please refer to http://www.gnu.org/copyleft/gpl.html
7
8# Old Class Names:
9#       puzzlebox_synapse_interface = QtUI
10
11__changelog__ = """\
12Last Update: 2011.12.07
13"""
14
15__todo__ = """
16- update configuration.ini file with settings entered into interface
17"""
18
19### IMPORTS ###
20import os, sys, time
21import simplejson as json
22
23
24import Configuration as configuration
25
26if configuration.ENABLE_PYSIDE:
27        try:
28                import PySide as PyQt4
29                from PySide import QtCore, QtGui, QtNetwork
30        except Exception, e:
31                print "ERROR: Exception importing PySide:",
32                print e
33                configuration.ENABLE_PYSIDE = False
34        else:
35                print "INFO: [Synapse:Interface] Using PySide module"
36
37if not configuration.ENABLE_PYSIDE:
38        print "INFO: [Synapse:Interface] Using PyQt4 module"
39        from PyQt4 import QtCore, QtGui, QtNetwork
40
41
42try:
43        from Interface_Plot import *
44        MATPLOTLIB_AVAILABLE = True
45except Exception, e:
46        print "ERROR: Exception importing Interface_Plot:",
47        print e
48        MATPLOTLIB_AVAILABLE = False
49
50
51if (sys.platform != 'win32'):
52        import bluetooth
53        DEFAULT_IMAGE_PATH = '/usr/share/puzzlebox_synapse/images'
54else:
55        import _winreg as winreg
56        import itertools
57        import re
58        import serial
59        DEFAULT_IMAGE_PATH = 'images'
60
61#try:
62        #import PySide as PyQt4
63        #from PySide import QtCore, QtGui
64#except:
65        #print "Using PyQt4 module"
66        #from PyQt4 import QtCore, QtGui
67#else:
68        #print "Using PySide module"
69
70try:
71        import cPickle as pickle
72except:
73        import pickle
74
75# from puzzlebox_synapse_interface_design import Ui_Form
76from Interface_Design import Ui_Form as Design
77
78#import Configuration as configuration
79import Server as synapse_server
80import Client as thinkgear_client
81#import puzzlebox_logger
82
83### GLOBALS ###
84
85DEBUG = configuration.DEBUG
86
87THINKGEAR_SERVER_HOST = configuration.THINKGEAR_SERVER_HOST
88THINKGEAR_SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
89
90THINKGEAR_EEG_POWER_BAND_ORDER = configuration.THINKGEAR_EEG_POWER_BAND_ORDER
91
92THINKGEAR_EMULATION_MAX_ESENSE_VALUE = \
93        configuration.THINKGEAR_EMULATION_MAX_ESENSE_VALUE
94THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE = \
95        configuration.THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
96
97PATH_TO_HCITOOL = '/usr/bin/hcitool'
98
99#UPDATE_INTERFACE_VIA_TIMER = True # Alternative is to establish a
100                                  ## ThinkGear Connect client which
101                                  ## updates the interface on demand
102                                  ## as packets are received
103
104UPDATE_INTERFACE_VIA_TIMER = False
105
106#INTERFACE_UPDATE_FREQUENCY = (1 / 512) * 1000 # ms (512 Hz)
107INTERFACE_UPDATE_FREQUENCY = 1000 # ms
108
109INTERFACE_RAW_EEG_UPDATE_FREQUENCY = 512
110
111PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD = 0.75
112
113### CLASSES ###
114
115class QtUI(QtGui.QWidget, Design):
116       
117        def __init__(self, log, server=None, DEBUG=DEBUG, parent = None):
118               
119                self.log = log
120                self.DEBUG = DEBUG
121                self.parent=parent
122               
123                if self.parent == None:
124                        QtGui.QWidget.__init__(self, parent)
125                        self.setupUi(self)
126               
127                        self.configureSettings()
128                        self.connectWidgets()
129               
130                self.name = "Synapse Interface"
131               
132                self.thinkGearConnectServer = None
133                self.thinkgearConnectClient = None
134               
135                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
136               
137                self.debug_console_buffer = ''
138               
139                self.packets = {}
140                self.packets['rawEeg'] = []
141                self.packets['signals'] = []
142               
143                self.customDataHeaders = []
144               
145                if UPDATE_INTERFACE_VIA_TIMER:
146                        self.updateInterfaceTimer = QtCore.QTimer()
147                        QtCore.QObject.connect(self.updateInterfaceTimer, \
148                                                    QtCore.SIGNAL("timeout()"), \
149                                                    self.updateInterface)
150               
151                if (sys.platform == 'win32'):
152                        self.homepath = os.path.join( \
153                           os.environ['HOMEDRIVE'], \
154                           os.environ['HOMEPATH'], \
155                           'Desktop')
156                else:
157                        self.homepath = os.environ['HOME']
158               
159                if not os.path.exists(self.homepath):
160                        self.homepath = os.getcwd()
161       
162       
163        ##################################################################
164       
165        def configureSettings(self):
166               
167                # Synapse Interface
168                image_path = "puzzlebox.ico"
169                if not os.path.exists(image_path):
170                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
171               
172                if os.path.exists(image_path):
173                        icon = QtGui.QIcon()
174                        icon.addPixmap(QtGui.QPixmap(image_path), \
175                                            QtGui.QIcon.Normal, \
176                                            QtGui.QIcon.Off)
177                        self.setWindowIcon(icon)
178               
179                image_path = "puzzlebox_logo.png"
180                if not os.path.exists(image_path):
181                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
182                if os.path.exists(image_path):
183                        self.labelPuzzleboxIcon.setPixmap(QtGui.QPixmap(image_path))
184               
185               
186                if configuration.INTERFACE_TAB_POSITION == 'South':
187                        self.tabWidget.setTabPosition(QtGui.QTabWidget.South)
188                else:
189                        self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
190               
191               
192                # ThinkGear Device
193                self.updateThinkGearDevices()
194               
195               
196                # ThinkGear Connect Server
197                self.textLabelBluetoothStatus.setText("Status: Disconnected")
198               
199                # Display Host for ThinkGear Connect Socket Server
200                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
201               
202                # Display Port for ThinkGear Connect Socket Server
203                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
204               
205               
206                # ThinkgGear Progress Bars
207                self.progressBarEEGDelta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
208                self.progressBarEEGTheta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
209                self.progressBarEEGLowAlpha.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
210                self.progressBarEEGHighAlpha.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
211                self.progressBarEEGLowBeta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
212                self.progressBarEEGHighBeta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
213                self.progressBarEEGLowGamma.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
214                self.progressBarEEGMidGamma.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
215               
216                self.progressBarAttention.setMaximum(THINKGEAR_EMULATION_MAX_ESENSE_VALUE)
217                self.progressBarMeditation.setMaximum(THINKGEAR_EMULATION_MAX_ESENSE_VALUE)
218               
219                self.progressBarSignalContactQuality.setMaximum(200)
220               
221               
222                if MATPLOTLIB_AVAILABLE:
223                        self.rawEEGMatplot = rawEEGMatplotlibCanvas( \
224                                                self.tabEEGSignals, \
225                                                width=8, \
226                                                height=4, \
227                                                dpi=100, \
228                                                title='Raw EEG Waves')
229                        self.chartEEGMatplot = chartEEGMatplotlibCanvas( \
230                                                self.tabCharts, \
231                                                width=8, \
232                                                height=4, \
233                                                dpi=100, \
234                                                title='EEG Brain Signals')
235               
236                else:
237                        self.tabWidget.removeTab(self.tabWidget.indexOf(self.tabEEGSignals))
238                        self.tabWidget.removeTab(self.tabWidget.indexOf(self.tabCharts))
239       
240       
241        ##################################################################
242       
243        def connectWidgets(self):
244               
245                self.connect(self.pushButtonBluetoothSearch, \
246                                  QtCore.SIGNAL("clicked()"), \
247                                  self.updateThinkGearDevices)
248               
249                self.connect(self.pushButtonBluetoothConnect, \
250                                  QtCore.SIGNAL("clicked()"), \
251                                  self.connectToThinkGearDevice)
252               
253                self.connect(self.pushButtonThinkGearConnect, \
254                                  QtCore.SIGNAL("clicked()"), \
255                                  self.startThinkGearConnectServer)
256               
257                self.connect(self.pushButtonSave, \
258                                  QtCore.SIGNAL("clicked()"), \
259                                  self.saveData)
260               
261                self.connect(self.pushButtonExport, \
262                                  QtCore.SIGNAL("clicked()"), \
263                                  self.exportData)
264               
265                self.connect(self.pushButtonReset, \
266                                  QtCore.SIGNAL("clicked()"), \
267                                  self.resetData)
268       
269       
270        ##################################################################
271       
272        def connectToThinkGearDevice(self):
273               
274                device_selection = self.comboBoxDeviceSelect.currentText()
275               
276                self.disconnect(self.pushButtonBluetoothConnect, \
277                                     QtCore.SIGNAL("clicked()"), \
278                                     self.connectToThinkGearDevice)
279               
280                self.connect(self.pushButtonBluetoothConnect, \
281                                  QtCore.SIGNAL("clicked()"), \
282                                  self.disconnectFromThinkGearDevice)
283               
284                self.textLabelBluetoothStatus.setText("Status: Connected")
285               
286                self.pushButtonBluetoothSearch.setEnabled(False)
287               
288                self.pushButtonBluetoothConnect.setText('Disconnect')
289                self.pushButtonBluetoothConnect.setChecked(True)
290               
291                self.comboBoxDeviceSelect.setEnabled(False)
292                self.comboBoxEEGHeadsetModel.setEnabled(False)
293       
294       
295        ##################################################################
296       
297        def disconnectFromThinkGearDevice(self):
298               
299                self.disconnect(self.pushButtonBluetoothConnect, \
300                                     QtCore.SIGNAL("clicked()"), \
301                                     self.disconnectFromThinkGearDevice)
302               
303                self.connect(self.pushButtonBluetoothConnect, \
304                                  QtCore.SIGNAL("clicked()"), \
305                                  self.connectToThinkGearDevice)
306               
307                self.textLabelBluetoothStatus.setText("Status: Disconnected")
308               
309                self.pushButtonBluetoothSearch.setEnabled(True)
310               
311                self.pushButtonBluetoothConnect.setText('Connect')
312                self.pushButtonBluetoothConnect.setChecked(False)
313               
314                self.comboBoxDeviceSelect.setEnabled(True)
315                self.comboBoxEEGHeadsetModel.setEnabled(True)
316               
317               
318                self.progressBarEEGDelta.setValue(0)
319                self.progressBarEEGTheta.setValue(0)
320                self.progressBarEEGLowAlpha.setValue(0)
321                self.progressBarEEGHighAlpha.setValue(0)
322                self.progressBarEEGLowBeta.setValue(0)
323                self.progressBarEEGHighBeta.setValue(0)
324                self.progressBarEEGLowGamma.setValue(0)
325                self.progressBarEEGMidGamma.setValue(0)
326               
327                self.progressBarAttention.setValue(0)
328                self.progressBarMeditation.setValue(0)
329               
330                self.progressBarSignalContactQuality.setValue(0)
331               
332                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
333               
334                # In case the user connects to a MindSet, then disconnects
335                # and re-connects to a MindSet Emulator,
336                # we need to reset the max power values
337                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
338                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
339                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
340                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
341                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
342                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
343                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
344                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
345       
346       
347        ##################################################################
348       
349        def startThinkGearConnectServer(self):
350               
351                # Ensure EEG device is connected first
352               
353                if not self.pushButtonBluetoothConnect.isChecked():
354                        self.connectToThinkGearDevice()
355               
356               
357                self.pushButtonBluetoothSearch.setEnabled(False)
358                self.pushButtonBluetoothConnect.setEnabled(False)
359               
360                server_interface = str(self.lineEditThinkGearHost.text())
361                server_port = int(self.lineEditThinkGearPort.text())
362                device_address = str(self.comboBoxDeviceSelect.currentText())
363                emulate_headset_data = (device_address == 'ThinkGear Emulator')
364               
365               
366                self.thinkGearConnectServer = \
367                        synapse_server.ThinkgearServer( \
368                                self.log, \
369                                server_interface=server_interface, \
370                                server_port=server_port, \
371                                device_address=device_address, \
372                                device_model=None, \
373                                emulate_headset_data=emulate_headset_data, \
374                                DEBUG=DEBUG, \
375                                parent=self)
376               
377                #self.connect(self.thinkGearConnectServer, \
378                             #QtCore.SIGNAL("sendPacket()"), \
379                             #self.thinkGearConnectServer.sendPacketQueue)
380               
381                self.thinkGearConnectServer.start()
382               
383               
384                if UPDATE_INTERFACE_VIA_TIMER:
385                        self.updateInterfaceTimer.start(INTERFACE_UPDATE_FREQUENCY)
386               
387                else:
388                        self.thinkgearConnectClient = \
389                                thinkgear_client.QtClient( \
390                                        self.log, \
391                                        server_host=server_interface, \
392                                        server_port=server_port, \
393                                        DEBUG=0, \
394                                        parent=self)
395                       
396                        self.thinkgearConnectClient.start()
397               
398               
399                self.disconnect(self.pushButtonThinkGearConnect, \
400                                     QtCore.SIGNAL("clicked()"), \
401                                     self.startThinkGearConnectServer)
402               
403                self.connect(self.pushButtonThinkGearConnect, \
404                                  QtCore.SIGNAL("clicked()"), \
405                                  self.stopThinkGearConnectServer)
406               
407                self.lineEditThinkGearHost.setEnabled(False)
408                self.lineEditThinkGearPort.setEnabled(False)
409               
410                self.pushButtonThinkGearConnect.setText('Stop')
411       
412       
413        ##################################################################
414       
415        def stopThinkGearConnectServer(self):
416               
417                if UPDATE_INTERFACE_VIA_TIMER:
418                        self.updateInterfaceTimer.stop()
419                else:
420                        try:
421                                self.thinkgearConnectClient.disconnectFromHost()
422                        except Exception, e:
423                                if self.DEBUG:
424                                        print "Call failed to self.thinkgearConnectClient.disconnectFromHost():",
425                                        print e
426                       
427                        try:
428                                self.thinkGearConnectServer.exitThread()
429                        except Exception, e:
430                                if self.DEBUG:
431                                        print "Call failed to self.thinkGearConnectServer.exitThread():",
432                                        print e
433               
434                self.disconnect(self.pushButtonThinkGearConnect, \
435                                QtCore.SIGNAL("clicked()"), \
436                                self.stopThinkGearConnectServer)
437               
438                self.connect(self.pushButtonThinkGearConnect, \
439                                  QtCore.SIGNAL("clicked()"), \
440                                  self.startThinkGearConnectServer)
441               
442                self.lineEditThinkGearHost.setEnabled(True)
443                self.lineEditThinkGearPort.setEnabled(True)
444               
445                self.pushButtonThinkGearConnect.setText('Start')
446               
447                #self.pushButtonBluetoothSearch.setEnabled(True)
448                self.pushButtonBluetoothConnect.setEnabled(True)
449               
450                self.pushButtonThinkGearConnect.setChecked(False)
451       
452       
453        ##################################################################
454       
455        def updateInterface(self):
456               
457                if not self.thinkGearConnectServer.emulate_headset_data:
458                        self.processPacketThinkGear( \
459                                self.thinkGearConnectServer.protocol.data_packet)
460       
461       
462        ##################################################################
463       
464        def processPacketThinkGear(self, packet):
465               
466                if self.DEBUG > 2:
467                        print packet
468               
469               
470                if ('rawEeg' in packet.keys()):
471                        self.packets['rawEeg'].append(packet['rawEeg'])
472                        value = packet['rawEeg']
473                        if MATPLOTLIB_AVAILABLE and \
474                                (self.tabWidget.currentIndex() == \
475                                 self.tabWidget.indexOf(self.tabEEGSignals)):
476                                self.rawEEGMatplot.update_figure(value)
477                        return
478                else:
479                        self.packets['signals'].append(packet)
480               
481               
482                if ('poorSignalLevel' in packet.keys()):
483                        value = 200 - packet['poorSignalLevel']
484                        self.progressBarSignalContactQuality.setValue(value)
485                        self.textEditDebugConsole.append("")
486                        try:
487                                (date, localtime) = self.parseTimeStamp(packet['timestamp'])
488                                self.textEditDebugConsole.append("Timestamp: %s %s" % (date, localtime))
489                        except:
490                                pass
491                        self.textEditDebugConsole.append("poorSignalLevel: %i" % \
492                                                         packet['poorSignalLevel'])
493               
494               
495                if ('eSense' in packet.keys()):
496                       
497                        if ('attention' in packet['eSense'].keys()):
498                                value = packet['eSense']['attention']
499                                self.progressBarAttention.setValue(value)
500                                self.textEditDebugConsole.append("eSense attention: %i" % value)
501                       
502                        if ('meditation' in packet['eSense'].keys()):
503                                value = packet['eSense']['meditation']
504                                self.progressBarMeditation.setValue(value)
505                                self.textEditDebugConsole.append("eSense meditation: %i" % value)
506                       
507                       
508                        if MATPLOTLIB_AVAILABLE:
509                                self.chartEEGMatplot.update_values('eSense', packet['eSense'])
510                                if (self.tabWidget.currentIndex() == \
511                                    self.tabWidget.indexOf(self.tabCharts)):
512                                        self.chartEEGMatplot.update_figure('eSense', packet['eSense'])
513               
514               
515                if ('eegPower' in packet.keys()):
516                       
517                        # If we are not emulating packets we'll set the maximum EEG Power value
518                        # threshold to the default (or maximum value found within this packet)
519                        if not self.thinkGearConnectServer.emulate_headset_data:
520                                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
521                       
522                        for value in packet['eegPower'].keys():
523                                if packet['eegPower'][value] > self.maxEEGPower:
524                                        self.maxEEGPower = packet['eegPower'][value]
525                       
526                       
527                        if ('delta' in packet['eegPower'].keys()):
528                                value = packet['eegPower']['delta']
529                                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
530                                self.progressBarEEGDelta.setValue(value)
531                                self.textEditDebugConsole.append("delta: %i" % value)
532                       
533                        if ('theta' in packet['eegPower'].keys()):
534                                value = packet['eegPower']['theta']
535                                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
536                                self.progressBarEEGTheta.setValue(value)
537                                self.textEditDebugConsole.append("theta: %i" % value)
538                       
539                        if ('lowAlpha' in packet['eegPower'].keys()):
540                                value = packet['eegPower']['lowAlpha']
541                                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
542                                self.progressBarEEGLowAlpha.setValue(value)
543                                self.textEditDebugConsole.append("lowAlpha: %i" % value)
544                       
545                        if ('highAlpha' in packet['eegPower'].keys()):
546                                value = packet['eegPower']['highAlpha']
547                                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
548                                self.progressBarEEGHighAlpha.setValue(value)
549                                self.textEditDebugConsole.append("highAlpha: %i" % value)
550                       
551                        if ('lowBeta' in packet['eegPower'].keys()):
552                                value = packet['eegPower']['lowBeta']
553                                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
554                                self.progressBarEEGLowBeta.setValue(value)
555                                self.textEditDebugConsole.append("lowBeta: %i" % value)
556                       
557                        if ('highBeta' in packet['eegPower'].keys()):
558                                value = packet['eegPower']['highBeta']
559                                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
560                                self.progressBarEEGHighBeta.setValue(value)
561                                self.textEditDebugConsole.append("highBeta: %i" % value)
562                       
563                        if ('lowGamma' in packet['eegPower'].keys()):
564                                value = packet['eegPower']['lowGamma']
565                                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
566                                self.progressBarEEGLowGamma.setValue(value)
567                                self.textEditDebugConsole.append("lowGamma: %i" % value)
568                       
569                        if ('highGamma' in packet['eegPower'].keys()):
570                                value = packet['eegPower']['highGamma']
571                                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
572                                self.progressBarEEGMidGamma.setValue(value)
573                                self.textEditDebugConsole.append("highGamma: %i" % value)
574                       
575                       
576                        if MATPLOTLIB_AVAILABLE:
577                                self.chartEEGMatplot.update_values('eegPower', packet['eegPower'])
578                                if (self.tabWidget.currentIndex() == \
579                                    self.tabWidget.indexOf(self.tabCharts)):
580                                        self.chartEEGMatplot.update_figure('eegPower', packet['eegPower'])
581               
582               
583                if ((self.thinkGearConnectServer.protocol != None) and
584                    (self.tabWidget.currentIndex() == \
585                     self.tabWidget.indexOf(self.tabControlPanel))):
586                       
587                        self.updateProfileSessionStatus()
588       
589       
590        ##################################################################
591       
592        def updateProfileSessionStatus(self, source=None, target=None):
593               
594                session_time = self.calculateSessionTime()
595               
596                if source == None:
597                        if self.parent == None:
598                                source = self
599                        else:
600                                source = self.parent
601               
602                if target == None:
603                        if self.parent == None:
604                                target = self
605                        else:
606                                target = self.parent
607               
608                target.textLabelSessionTime.setText(session_time)
609               
610                try:
611                        target.textLabelPacketsReceived.setText( "%i" % \
612                                source.thinkGearConnectServer.protocol.packet_count)
613                except:
614                        pass
615               
616                try:
617                        target.textLabelPacketsDropped.setText( "%i" % \
618                                source.thinkGearConnectServer.protocol.bad_packets)
619                except:
620                        pass
621       
622       
623        ##################################################################
624       
625        def calculateSessionTime(self):
626               
627                if self.parent == None:
628                        server = self.thinkGearConnectServer
629                else:
630                        server = self.parent.thinkGearConnectServer
631               
632                session_time = time.time() - \
633                        server.protocol.session_start_timestamp
634               
635                session_time = int(session_time)
636               
637                session_time = self.convert_seconds_to_datetime(session_time)
638               
639                return(session_time)
640       
641       
642        ##################################################################
643       
644        def enumerateSerialPorts(self):
645               
646                """ Uses the Win32 registry to return an
647                iterator of serial (COM) ports
648                existing on this computer.
649               
650                from http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
651                """
652         
653                path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
654                try:
655                        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
656                except WindowsError:
657                        #raise IterationError
658                        return
659               
660                for i in itertools.count():
661                        try:
662                                val = winreg.EnumValue(key, i)
663                                yield str(val[1])
664                        except EnvironmentError:
665                                break
666       
667       
668        ##################################################################
669       
670        def fullPortName(self, portname):
671               
672                """ Given a port-name (of the form COM7,
673                COM12, CNCA0, etc.) returns a full
674                name suitable for opening with the
675                Serial class.
676                """
677               
678                m = re.match('^COM(\d+)$', portname)
679                if m and int(m.group(1)) < 10:
680                        return portname
681               
682                return '\\\\.\\' + portname
683       
684       
685        ##################################################################
686       
687        def searchForSerialDevices(self, devices=[]):
688               
689                if (sys.platform == 'win32'):
690                       
691                        for portname in self.enumerateSerialPorts():
692                               
693                                if portname not in devices:
694                                        #portname = self.fullPortName(portname)
695                                        devices.append(portname)
696               
697                else:
698                       
699                        if os.path.exists('/dev/ttyUSB0'):
700                                devices.append('/dev/ttyUSB0')
701                        if os.path.exists('/dev/ttyUSB1'):
702                                devices.append('/dev/ttyUSB1')
703                        if os.path.exists('/dev/ttyUSB2'):
704                                devices.append('/dev/ttyUSB2')
705                        if os.path.exists('/dev/ttyUSB3'):
706                                devices.append('/dev/ttyUSB3')
707                        if os.path.exists('/dev/ttyUSB4'):
708                                devices.append('/dev/ttyUSB4')
709                        if os.path.exists('/dev/ttyUSB5'):
710                                devices.append('/dev/ttyUSB5')
711                        if os.path.exists('/dev/ttyUSB6'):
712                                devices.append('/dev/ttyUSB6')
713                        if os.path.exists('/dev/ttyUSB7'):
714                                devices.append('/dev/ttyUSB7')
715                        if os.path.exists('/dev/ttyUSB8'):
716                                devices.append('/dev/ttyUSB8')
717                        if os.path.exists('/dev/ttyUSB9'):
718                                devices.append('/dev/ttyUSB9')
719                       
720                        if os.path.exists('/dev/ttyACM0'):
721                                devices.append('/dev/ttyACM0')
722                        if os.path.exists('/dev/ttyACM1'):
723                                devices.append('/dev/ttyACM1')
724                        if os.path.exists('/dev/ttyACM2'):
725                                devices.append('/dev/ttyACM2')
726                        if os.path.exists('/dev/ttyACM3'):
727                                devices.append('/dev/ttyACM3')
728                        if os.path.exists('/dev/ttyACM4'):
729                                devices.append('/dev/ttyACM4')
730               
731               
732                return(devices)
733       
734       
735        ##################################################################
736       
737        def hcitoolScanForRemoteDevices(self, thinkgear_devices=[]):
738               
739                bluetooth_devices = []
740               
741                #command = '%s scan 2> /dev/null' % PATH_TO_HCITOOL
742                command = '%s scan' % PATH_TO_HCITOOL
743               
744                if self.DEBUG > 1:
745                        print 'INFO: Calling "%s"' % command
746               
747                output = os.popen(command, 'r')
748               
749                for line in output.readlines():
750                        line = line.strip()
751                        if line == '' or line == 'Scanning ...':
752                                continue
753                        elif self.DEBUG > 1:
754                                print line
755                        try:
756                                address = line.split('\t')[0]
757                        except:
758                                pass
759                        else:
760                                bluetooth_devices.append(address)
761               
762               
763                for address in bluetooth_devices:
764                       
765                        command = '%s name %s' % (PATH_TO_HCITOOL, address)
766                       
767                        if self.DEBUG:
768                                print 'INFO: Calling "%s"' % command
769                       
770                        output = os.popen(command, 'r')
771                       
772                        for line in output.readlines():
773                                line = line.strip()
774                                if line == '':
775                                        continue
776                                elif self.DEBUG:
777                                        print '\t',
778                                        print line
779                               
780                                device_name = line.strip()
781                       
782                                if ((device_name == 'MindSet' or device_name == 'MindWave Mobile') and \
783                                        (address not in thinkgear_devices)):
784                                        thinkgear_devices.append(address)
785               
786               
787                return (thinkgear_devices)
788       
789       
790        ##################################################################
791       
792        def hcitoolGetActiveConnections(self, thinkgear_devices=[]):
793               
794                bluetooth_devices = []
795               
796                #command = '%s con 2> /dev/null' % PATH_TO_HCITOOL
797                command = '%s con' % PATH_TO_HCITOOL
798               
799                if self.DEBUG > 1:
800                        print 'INFO: Calling "%s"' % command
801               
802                output = os.popen(command, 'r')
803               
804                for line in output.readlines():
805                        line = line.strip()
806                        if line == '' or line == 'Connections:':
807                                continue
808                        elif self.DEBUG > 1:
809                                print line
810                        try:
811                                address = line.split(' ')[2]
812                        except:
813                                pass
814                        else:
815                                bluetooth_devices.append(address)
816               
817               
818                for address in bluetooth_devices:
819                       
820                        command = '%s name %s' % (PATH_TO_HCITOOL, address)
821                       
822                        if self.DEBUG:
823                                print 'INFO: Calling "%s":' % command
824                       
825                        output = os.popen(command, 'r')
826                       
827                        for line in output.readlines():
828                                line = line.strip()
829                                if line == '':
830                                        continue
831                                elif self.DEBUG:
832                                        print '\t',
833                                        print line
834                               
835                                device_name = line.strip()
836                       
837                                if ((device_name == 'MindSet' or device_name == 'MindWave Mobile') and \
838                                        (address not in thinkgear_devices)):
839                                        thinkgear_devices.append(address)
840               
841               
842                return (thinkgear_devices)
843       
844       
845        ##################################################################
846       
847        def searchForThinkGearDevices(self):
848               
849                enable_hcitool = configuration.ENABLE_HCITOOL
850               
851                thinkgear_devices = []
852               
853                #self.pushButtonBluetoothSearch.setText('Searching')
854               
855                if (sys.platform != 'win32'):
856                       
857                        # Bluetooth module doesn't compile properly under WindowsError
858                       
859                        bluetooth_devices = []
860                       
861                        if not enable_hcitool:
862                               
863                                try:
864                                       
865                                        if self.DEBUG:
866                                                print "INFO: Searching for Bluetooth devices using PyBluez module"
867                                       
868                                        bluetooth_devices = bluetooth.discover_devices( \
869                                                               duration=5, \
870                                                               flush_cache=True, \
871                                                               lookup_names=False)
872                                       
873                                        for address in bluetooth_devices:
874                                                device_name = bluetooth.lookup_name(address)
875                                                if ((device_name == 'MindSet' or device_name == 'MindWave Mobile') and \
876                                                        (address not in thinkgear_devices)):
877                                                        thinkgear_devices.append(address)
878                                       
879                                       
880                                        # There is an issue under recent released of Linux
881                                        # in which already-connected Bluetooth ThinkGear devices
882                                        # are not appearing in a bluetooth device scan. However,
883                                        # using "hcitool" connected devices can be listed correctly.
884                                        # There does not appear to be an equivalent PyBluez feature.
885                                        # (http://pybluez.googlecode.com/svn/www/docs-0.7/index.html)
886                                       
887                                        if thinkgear_devices == []:
888                                                if self.DEBUG:
889                                                        print "INFO: No devices found through PyBluez module. Falling back to hcitool."
890                                                thinkgear_devices = self.hcitoolGetActiveConnections(thinkgear_devices)
891                               
892                               
893                                except Exception, e:
894                                        if self.DEBUG:
895                                                print "ERROR: Exception calling Python Bluetooth module. (Is PyBluez installed?):"
896                                                print e
897                                       
898                                        enable_hcitool = True
899                       
900                       
901                        if enable_hcitool:
902                               
903                                thinkgear_devices = self.hcitoolScanForRemoteDevices(thinkgear_devices)
904                                thinkgear_devices = self.hcitoolGetActiveConnections(thinkgear_devices)
905                       
906                       
907                        if self.DEBUG > 2:
908                                print "Bluetooth ThinkGear devices found:",
909                                print thinkgear_devices
910               
911               
912                thinkgear_devices = self.searchForSerialDevices(thinkgear_devices)
913               
914               
915                if self.DEBUG:
916                        print "ThinkGear devices found:",
917                        print thinkgear_devices
918               
919               
920                return(thinkgear_devices)
921       
922       
923        ##################################################################
924       
925        def updateThinkGearDevices(self):
926               
927                devices = self.searchForThinkGearDevices()
928               
929                #if self.parent == None:
930                        #self.comboBoxDeviceSelect.clear()
931                #else:
932                        #self.parent.comboBoxDeviceSelect.clear()
933               
934                self.comboBoxDeviceSelect.clear()
935                devices.insert(0, 'ThinkGear Emulator')
936               
937                for device in devices:
938                        self.comboBoxDeviceSelect.addItem(device)
939       
940       
941        ##################################################################
942       
943        def collectData(self, source=None, target=None):
944               
945                if source == None:
946                        if self == None:
947                                source = self
948                        else:
949                                source = self.parent
950               
951                if target == None:
952                        if self == None:
953                                target = self
954                        else:
955                                target = self.parent
956               
957                data = {}
958               
959                data['rawEeg'] = source.packets['rawEeg']
960                data['signals'] = source.packets['signals']
961               
962                data['sessionTime'] = self.calculateSessionTime()
963               
964                data['profileName'] = str(target.lineEditSessionProfile.text())
965               
966                return(data)
967       
968       
969        ##################################################################
970       
971        def parseTimeStamp(self, timestamp, local_version=False, truncate_time_zone=False):
972               
973                try:
974                        decimal = '%f' % timestamp
975                        decimal = decimal.split('.')[1]
976                except:
977                        decimal = '0'
978               
979                localtime = time.localtime(timestamp)
980               
981                if local_version:
982                        date = time.strftime('%x', localtime)
983                        localtime = time.strftime('%X', localtime)
984               
985                elif truncate_time_zone:
986                        date = time.strftime('%Y-%m-%d', localtime)
987                        localtime = time.strftime('%H:%M:%S', localtime)
988                        localtime = '%s.%s' % (localtime, decimal[:3])
989               
990                else:
991                        date = time.strftime('%Y-%m-%d', localtime)
992                        localtime = time.strftime('%H:%M:%S', localtime)
993                        localtime = '%s.%s %s' % (localtime, decimal, \
994                                       time.strftime('%Z', time.localtime(timestamp)))
995               
996               
997                return(date, localtime)
998       
999       
1000        ##################################################################
1001       
1002        def saveData(self, source=None, target=None):
1003               
1004                if source == None:
1005                        if self == None:
1006                                source = self
1007                        else:
1008                                source = self.parent
1009               
1010                if target == None:
1011                        if self == None:
1012                                target = self
1013                        else:
1014                                target = self.parent
1015               
1016                data = self.collectData(source=source, target=target)
1017               
1018                (date, localtime) = self.parseTimeStamp(time.time())
1019               
1020                default_filename = '%s %s.synapse' % (date, \
1021                                      target.lineEditSessionProfile.text())
1022                                     
1023                default_filename = os.path.join(self.homepath, default_filename)
1024               
1025                output_file = QtGui.QFileDialog.getSaveFileName(parent=target, \
1026                                 caption="Save Session Data to File", \
1027                                 dir=default_filename, \
1028                                 filter="Puzzlebox Synapse Data File (*.synapse)")
1029               
1030                try:
1031                        output_file = output_file[0]
1032                except:
1033                        output_file = ''
1034               
1035                if output_file == '':
1036                        return
1037               
1038                file = open(str(output_file), 'w')
1039                pickle.dump(data, file)
1040                file.close()
1041       
1042       
1043        ##################################################################
1044       
1045        def exportData(self, parent=None, source=None, target=None):
1046               
1047                if parent == None:
1048                        if self == None:
1049                                parent = self
1050                        else:
1051                                parent = self.parent
1052               
1053                if source == None:
1054                        if self == None:
1055                                source = self
1056                        else:
1057                                source = self.parent
1058               
1059                if target == None:
1060                        if self.parent == None:
1061                                target = self
1062                        else:
1063                                target = self.parent
1064               
1065                (date, localtime) = self.parseTimeStamp(time.time())
1066               
1067                default_filename = '%s %s.csv' % (date, \
1068                                      target.lineEditSessionProfile.text())
1069               
1070                default_filename = os.path.join(target.homepath, default_filename)
1071               
1072                output_file = QtGui.QFileDialog.getSaveFileName(parent=target, \
1073                                 caption="Export Session Data to File", \
1074                                 dir=default_filename, \
1075                                 filter="CSV File (*.csv);;Text File (*.txt)")
1076               
1077                try:
1078                        output_file = output_file[0]
1079                except:
1080                        output_file = ''
1081               
1082                if output_file == '':
1083                        return
1084               
1085                if str(output_file).endswith('.csv'):
1086                       
1087                        outputData = self.exportDataToCSV(parent=parent, source=source, target=target)
1088               
1089               
1090                else:
1091                       
1092                        try:
1093                                outputData = self.textEditDebugConsole.toPlainText()
1094                        except:
1095                                outputData = self.exportDataToCSV()
1096                       
1097                       
1098                file = open(str(output_file), 'w')
1099                file.write(outputData)
1100                file.close()
1101       
1102       
1103        ##################################################################
1104       
1105        def exportDataToCSV(self, parent=None, source=None, target=None):
1106               
1107                if parent == None:
1108                        if self == None:
1109                                parent = self
1110                        else:
1111                                parent = self.parent
1112               
1113                if source == None:
1114                        if self == None:
1115                                source = self
1116                        else:
1117                                source = self.parent
1118               
1119                if target == None:
1120                        if self == None:
1121                                target = self
1122                        else:
1123                                target = self.parent
1124               
1125                try:
1126                        truncate_csv_timezone = target.configuration.EXPORT_CSV_TRUNCATE_TIMEZONE
1127                except:
1128                        truncate_csv_timezone = False
1129               
1130                try:
1131                        scrub_data = target.configuration.EXPORT_CSV_SCRUB_DATA
1132                except:
1133                        scrub_data = False
1134               
1135               
1136                headers = 'Date,Time,Delta,Theta,Low Alpha,High Alpha,Low Beta,High Beta,Low Gamma,Mid Gamma,Attention,Meditation,Signal Level'
1137               
1138                customDataHeaders = []
1139                for header in parent.customDataHeaders:
1140                        customDataHeaders.append(header)
1141                for plugin in parent.activePlugins:
1142                        for header in plugin.customDataHeaders:
1143                                customDataHeaders.append(header)
1144               
1145                for each in customDataHeaders:
1146                        headers = headers + ',%s' % each
1147               
1148                headers = headers + '\n'
1149               
1150                csv = {}
1151               
1152                for packet in source.packets['signals']:
1153                       
1154                        if 'rawEeg' in packet.keys():
1155                                continue
1156                       
1157                        if packet['timestamp'] not in csv.keys():
1158                               
1159                                #print packet
1160                                timestamp = packet['timestamp']
1161                                (date, localtime) = self.parseTimeStamp(timestamp, \
1162                                                    truncate_time_zone=truncate_csv_timezone)
1163                               
1164                                csv[timestamp] = {}
1165                                csv[timestamp]['Date'] = date
1166                                csv[timestamp]['Time'] = localtime
1167                                csv[timestamp]['Delta'] = ''
1168                                csv[timestamp]['Theta'] = ''
1169                                csv[timestamp]['Low Alpha'] = ''
1170                                csv[timestamp]['High Alpha'] = ''
1171                                csv[timestamp]['Low Beta'] = ''
1172                                csv[timestamp]['High Beta'] = ''
1173                                csv[timestamp]['Low Gamma'] = ''
1174                                csv[timestamp]['Mid Gamma'] = ''
1175                                csv[timestamp]['Attention'] = ''
1176                                csv[timestamp]['Meditation'] = ''
1177                                csv[timestamp]['Signal Level'] = ''
1178                               
1179                                for header in customDataHeaders:
1180                                        csv[timestamp][header] = ''
1181                       
1182                       
1183                        if 'eSense' in packet.keys():
1184                                if 'attention' in packet['eSense'].keys():
1185                                        csv[timestamp]['Attention'] = packet['eSense']['attention']
1186                                if 'meditation' in packet['eSense'].keys():
1187                                        csv[timestamp]['Meditation'] = packet['eSense']['meditation']
1188                       
1189                        if 'eegPower' in packet.keys():
1190                                if 'delta' in packet['eegPower'].keys():
1191                                        csv[timestamp]['Delta'] = packet['eegPower']['delta']
1192                                if 'theta' in packet['eegPower'].keys():
1193                                        csv[timestamp]['Theta'] = packet['eegPower']['theta']
1194                                if 'lowAlpha' in packet['eegPower'].keys():
1195                                        csv[timestamp]['Low Alpha'] = packet['eegPower']['lowAlpha']
1196                                if 'highAlpha' in packet['eegPower'].keys():
1197                                        csv[timestamp]['High Alpha'] = packet['eegPower']['highAlpha']
1198                                if 'lowBeta' in packet['eegPower'].keys():
1199                                        csv[timestamp]['Low Beta'] = packet['eegPower']['lowBeta']
1200                                if 'highBeta' in packet['eegPower'].keys():
1201                                        csv[timestamp]['High Beta'] = packet['eegPower']['highBeta']
1202                                if 'lowGamma' in packet['eegPower'].keys():
1203                                        csv[timestamp]['Low Gamma'] = packet['eegPower']['lowGamma']
1204                                if 'highGamma' in packet['eegPower'].keys():
1205                                        csv[timestamp]['Mid Gamma'] = packet['eegPower']['highGamma']
1206                       
1207                        if 'poorSignalLevel' in packet.keys():
1208                                csv[timestamp]['Signal Level'] = packet['poorSignalLevel']
1209                       
1210                        for header in customDataHeaders:
1211                                if 'custom' in packet.keys() and \
1212                                   header in packet['custom'].keys():
1213                                        csv[timestamp][header] = packet['custom'][header]
1214               
1215               
1216                if scrub_data:
1217                        csv = self.scrubData(csv, truncate_csv_timezone)
1218               
1219               
1220                output = headers
1221               
1222                csv_keys = csv.keys()
1223                csv_keys.sort()
1224               
1225                for key in csv_keys:
1226                       
1227                        row = '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % \
1228                              (csv[key]['Date'], \
1229                               csv[key]['Time'], \
1230                               csv[key]['Delta'], \
1231                               csv[key]['Theta'], \
1232                               csv[key]['Low Alpha'], \
1233                               csv[key]['High Alpha'], \
1234                               csv[key]['Low Beta'], \
1235                               csv[key]['High Beta'], \
1236                               csv[key]['Low Gamma'], \
1237                               csv[key]['Mid Gamma'], \
1238                               csv[key]['Attention'], \
1239                               csv[key]['Meditation'], \
1240                               csv[key]['Signal Level'])
1241                       
1242                        for header in customDataHeaders:
1243                                row = row + ',%s' % csv[key][header]
1244                       
1245                        row = row + '\n'
1246                       
1247                        output = output + row
1248               
1249               
1250                return(output)
1251       
1252       
1253        ##################################################################
1254       
1255        def scrubData(self, csv, truncate_csv_timezone=False):
1256               
1257                # If there are missing packets, repeat a given packet once per missing
1258                # second until there is a gap between 1 and 2 seconds, in which case
1259                # produce a final duplicate packet at the mid-point between the packets
1260
1261                if self.DEBUG:
1262                        print "INFO: Scrubbing Data"
1263               
1264                last_time = None
1265                last_recorded_time = None
1266               
1267                output = {}
1268               
1269                csv_keys = csv.keys()
1270                csv_keys.sort()
1271               
1272                for key in csv_keys:
1273                       
1274                        timestamp = key
1275
1276                        if csv[key]['Attention'] == '':
1277                                continue
1278                       
1279                        if last_time == None:
1280                                # First entry in log
1281                                last_time = timestamp
1282                                last_recorded_time = timestamp
1283                                output[key] = csv[key]
1284                                continue
1285                       
1286                        else:
1287                               
1288                                #time_difference = timestamp - last_time
1289                                time_difference = timestamp - last_recorded_time
1290                               
1291                                if (time_difference <= 1) and \
1292                                   (time_difference >= PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD):
1293                                        # Skip packets within the correct time threshold
1294                                        last_time = timestamp
1295                                        last_recorded_time = timestamp
1296                                        output[key] = csv[key]
1297                                        continue
1298                               
1299                                else:
1300
1301                                        if self.DEBUG > 1:
1302                                                print "time_difference:",
1303                                                print time_difference
1304                                                print "timestamp:",
1305                                                print self.parseTimeStamp(timestamp)[-1].split(' ')[0]
1306                                                print "last_time:",
1307                                                print self.parseTimeStamp(last_time)[-1].split(' ')[0]
1308                                                print "last_recorded_time:",
1309                                                print self.parseTimeStamp(last_recorded_time)[-1].split(' ')[0]
1310
1311                                       
1312                                        new_packet = csv[key].copy()
1313                                       
1314                                        if time_difference >= 2:
1315                                               
1316                                                ##new_time = last_time + 1
1317                                                #new_time = last_recorded_time + 1
1318
1319                                                count = int(time_difference)
1320                                                while count >= 1:
1321                                                        new_packet = csv[key].copy()
1322                                                        new_time = last_recorded_time + 1
1323                                                        (date, formatted_new_time) = self.parseTimeStamp(new_time, \
1324                                                         truncate_time_zone=truncate_csv_timezone)
1325                                                        new_packet['Time'] = formatted_new_time
1326                                                        last_recorded_time = new_time
1327                                                        last_time = timestamp
1328                                                        output[new_time] = new_packet
1329                                                        count = count - 1
1330                                                continue
1331                                               
1332                                        elif time_difference < PACKET_MINIMUM_TIME_DIFFERENCE_THRESHOLD:
1333                                                # Spread out "bunched up" packets
1334                                                #new_time = last_time + 1
1335                                                new_time = last_recorded_time + 1
1336                                       
1337                                       
1338                                        elif (time_difference < 2) and (time_difference > 1):
1339                                               
1340                                                #new_time = last_time + ((last_time - timestamp) / 2)
1341                                                #new_time = last_recorded_time + ((last_recorded_time - timestamp) / 2)
1342                                                #new_time = last_time + 1
1343                                                new_time = last_recorded_time + 1
1344                                       
1345                                       
1346                                        (date, formatted_new_time) = self.parseTimeStamp(new_time, \
1347                                           truncate_time_zone=truncate_csv_timezone)
1348                                       
1349                                        new_packet['Time'] = formatted_new_time
1350                                       
1351                                        #last_time = new_time
1352                                        last_recorded_time = new_time
1353                                        last_time = timestamp
1354                                        output[new_time] = new_packet
1355                                       
1356                                        if self.DEBUG > 1:
1357                                                print "WARN: Scrubbing new packet:",
1358                                                print new_packet
1359                                                print
1360               
1361               
1362                return(output)
1363       
1364       
1365        ##################################################################
1366       
1367        def resetData(self, source=None):
1368               
1369                if source == None:
1370                        if self == None:
1371                                source = self
1372                        else:
1373                                source = self.parent
1374               
1375                #if target == None:
1376                        #if self == None:
1377                                #target = self
1378                        #else:
1379                                #target = self.parent
1380               
1381                source.packets['rawEeg'] = []
1382                source.packets['signals'] = []
1383               
1384                source.thinkGearConnectServer.protocol.session_start_timestamp = \
1385                        time.time()
1386               
1387                source.thinkGearConnectServer.protocol.packet_count = 0
1388                source.thinkGearConnectServer.protocol.bad_packets = 0
1389               
1390                self.updateProfileSessionStatus()
1391               
1392                try:
1393                        source.textEditDebugConsole.setText("")
1394                except:
1395                        pass
1396       
1397       
1398        #####################################################################
1399       
1400        def convert_seconds_to_datetime(self, duration):
1401               
1402                duration_hours = duration / (60 * 60)
1403                duration_minutes = (duration - (duration_hours * (60 * 60))) / 60
1404                duration_seconds = (duration - (duration_hours * (60 * 60)) - (duration_minutes * 60))
1405               
1406                duration_hours = '%i' % duration_hours
1407                if (len(duration_hours) == 1):
1408                        duration_hours = "0%s" % duration_hours
1409               
1410                duration_minutes = '%i' % duration_minutes
1411                if (len(duration_minutes) == 1):
1412                        duration_minutes = "0%s" % duration_minutes
1413               
1414                duration_seconds = '%i' % duration_seconds
1415                if (len(duration_seconds) == 1):
1416                        duration_seconds = "0%s" % duration_seconds
1417               
1418                datetime = '%s:%s:%s' % (duration_hours, duration_minutes, duration_seconds)
1419               
1420                return(datetime)
1421       
1422       
1423        ##################################################################
1424       
1425        def stop(self):
1426               
1427                if UPDATE_INTERFACE_VIA_TIMER:
1428                        self.updateInterfaceTimer.stop()
1429                else:
1430                        if self.thinkgearConnectClient != None:
1431                                self.thinkgearConnectClient.exitThread()
1432               
1433                if self.thinkGearConnectServer != None:
1434                        self.thinkGearConnectServer.exitThread()
1435       
1436       
1437        ##################################################################
1438       
1439        def closeEvent(self, event):
1440               
1441                quit_message = "Are you sure you want to exit the program?"
1442               
1443                reply = QtGui.QMessageBox.question( \
1444                           self, \
1445                          'Message', \
1446                           quit_message, \
1447                           QtGui.QMessageBox.Yes, \
1448                           QtGui.QMessageBox.No)
1449               
1450                if reply == QtGui.QMessageBox.Yes:
1451                       
1452                        self.stop()
1453                       
1454                        event.accept()
1455               
1456                else:
1457                        event.ignore()
Note: See TracBrowser for help on using the repository browser.