diff --git a/.gitignore b/.gitignore index 7bd52b3..67be6e8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,5 @@ \#* .emacs* old -test zzz_old -doc/_build \ No newline at end of file +doc/_build diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cdd8e30 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: python +virtualenv: + system_site_packages: true +python: + - "2.7" +before_install: + - sudo apt-get install python-numpy python-pyside python-matplotlib python-h5py + - export PYTHONPATH=$PYTHONPATH:~/taskontrol + +#command to install dependencies +# command to run tests +script: nosetests diff --git a/README.txt b/README.md similarity index 76% rename from README.txt rename to README.md index a600285..d76fc1e 100644 --- a/README.txt +++ b/README.md @@ -1,8 +1,5 @@ - ______________ - - TASKontrol - ______________ - +![](https://travis-ci.org/nickponvert/taskontrol.svg?branch=master) +#TASKontrol TASKontrol is a framework for developing behavioral experiments. @@ -20,10 +17,10 @@ maintained by the Brody Lab) and the state machine for Linux+RTAI, originally developed at Cold Spring Harbor Laboratory. TASKontrol provides the following advantages: -- No need for a Windows license, it runs easily on Linux. -- No need for a Matlab license, it is written in Python. -- No need for multiple computers, when used with the Arduino server. -- An appealing graphical interface, it uses Qt. +* No need for a Windows license, it runs easily on Linux. +* No need for a Matlab license, it is written in Python. +* No need for multiple computers, when used with the Arduino server. +* An appealing graphical interface, it uses Qt. You can find the full documentation at: http://taskontrol.readthedocs.org diff --git a/core/arraycontainer.py b/core/arraycontainer.py index 18cb4ff..0b0b7c3 100644 --- a/core/arraycontainer.py +++ b/core/arraycontainer.py @@ -8,35 +8,45 @@ __version__ = '0.1' __author__ = 'Santiago Jaramillo ' - from taskontrol.core import utils class Container(dict): def __init__(self): - super(Container, self).__init__() + super(Container, self).__init__() self.labels = dict() ###self.currentTrial = 0 - def append_to_file(self, h5file,currentTrial): + + def append_to_file(self, h5file, currentTrial): '''Returns True if successful ''' - if currentTrial<1: - raise UserWarning('WARNING: No trials have been completed or currentTrial not updated.') + if currentTrial < 1: + raise UserWarning('WARNING: No trials have been completed or\ + currentTrial not updated.') resultsDataGroup = h5file.require_group('resultsData') resultsLabelsGroup = h5file.require_group('resultsLabels') - for key,item in self.iteritems(): - dset = resultsDataGroup.create_dataset(key, data=item[:currentTrial]) - for key,item in self.labels.iteritems(): + for key, item in self.iteritems(): + dset = resultsDataGroup.create_dataset(key, + data=item[:currentTrial]) + for key, item in self.labels.iteritems(): # FIXME: Make sure items of self.labels are dictionaries - utils.append_dict_to_HDF5(resultsLabelsGroup,key,item) + utils.append_dict_to_HDF5(resultsLabelsGroup, key, item) + + +def main(): + + '''When executed as a script, create test array container and write the + data to an h5 file''' -if __name__ == "__main__": import h5py import numpy as np c = Container() c['myvar1'] = np.arange(10) - c.labels['myvar2labels'] = {'yes':1,'no':0} - c['myvar2'] = np.array([0,1,1,1,0]) - h5file = h5py.File('/tmp/testh5.h5','w') + c.labels['myvar2labels'] = {'yes': 1, 'no': 0} + c['myvar2'] = np.array([0, 1, 1, 1, 0]) + h5file = h5py.File('/tmp/testh5.h5', 'w') h5file.create_group('resultsData') - c.append_to_file(h5file,4) + c.append_to_file(h5file, 4) h5file.close() + +if __name__ == "__main__": + main() diff --git a/core/dispatcher.py b/core/dispatcher.py index b06746e..4ed0d41 100644 --- a/core/dispatcher.py +++ b/core/dispatcher.py @@ -28,53 +28,62 @@ import sys -from PySide import QtCore -from PySide import QtGui +from PySide import QtCore +from PySide import QtGui import numpy as np #from taskontrol.core import messenger #from taskontrol.core import smclient #reload(smclient) -DEFAULT_PREPARE_NEXT = 0 # State to prepare next trial +DEFAULT_PREPARE_NEXT = 0 # State to prepare next trial + class Dispatcher(QtCore.QObject): + ''' Dispatcher is the trial controller. It is an interface between a trial-structured paradigm and the state machine. It emits the following signals: - 'timerTic' : at every tic of the dispatcher timer. - It sends: serverTime,currentState,eventCount,currentTrial - - 'prepareNextTrial': whenever one of the prepare-next-trial-states is reached. + It sends: + serverTime,currentState,eventCount,currentTrial + - 'prepareNextTrial': whenever one of the prepare-next-trial-states is + reached. It sends: 'nextTrial' REMOVED: - - 'startNewTrial' : whenever READY TO START TRIAL is sent to state machine. + - 'startNewTrial' : whenever READY TO START TRIAL is sent to state + machine. It sends: 'currentTrial' ''' # -- Create signals (they need to be before constructor) -- - timerTic = QtCore.Signal(float,int,int,int) + timerTic = QtCore.Signal(float, int, int, int) prepareNextTrial = QtCore.Signal(int) - startNewTrial = QtCore.Signal(int) ### FIXME: is this really necessary? + startNewTrial = QtCore.Signal(int) # FIXME: is this really necessary? logMessage = QtCore.Signal(str) - def __init__(self, parent=None,serverType='dummy', connectnow=True, interval=0.3, - nInputs=3,nOutputs=3): + def __init__(self, parent=None, serverType='dummy', connectnow=True, + interval=0.3, nInputs=3, nOutputs=3): super(Dispatcher, self).__init__(parent) - if serverType=='arduino_due': + if serverType == 'arduino_due': from taskontrol.core import smclient as smclient - elif serverType=='dummy': + elif serverType == 'dummy': from taskontrol.plugins import smdummy as smclient - elif serverType=='emulator': + elif serverType == 'emulator': from taskontrol.plugins import smemulator as smclient else: pass - + # -- Set trial structure variables -- - self.prepareNextTrialStates = [0] # Default state to prepare next trial - self.preparingNextTrial = False # True while preparing next trial + + # Default state to prepare next trial + self.prepareNextTrialStates = [0] + + # True while preparing next trial + self.preparingNextTrial = False # -- Create a state machine client -- #self.host = host @@ -97,10 +106,10 @@ def __init__(self, parent=None,serverType='dummy', connectnow=True, interval=0.3 self.lastEvents = [] # Matrix with info about last events self.eventsMat = [] # Matrix with info about all events #self._stateMatrixStatus = False # To indicate if a matrix has been set - self.indexLastEventEachTrial = [] # index of last event for each trial + self.indexLastEventEachTrial = [] # index of last event for each trial # -- Create timer -- - self.interval = interval # Polling interval (sec) + self.interval = interval # Polling interval (sec) self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.timeout) @@ -111,7 +120,7 @@ def connect_to_sm(self): '''Connect to state machine server and initialize it.''' self.statemachine.connect() ###self.statemachine.initialize() - self.statemachine.set_sizes(self.nInputs,self.nOutputs,0) + self.statemachine.set_sizes(self.nInputs, self.nOutputs, 0) self.isConnected = True def reset_state_matrix(self): @@ -119,15 +128,16 @@ def reset_state_matrix(self): blankMatrix = [nActions*[0]] blankOutputs = [self.nOutputs*[0]] blankSerial = None - blankTimers = [60] # in sec - self._set_state_matrix(blankMatrix,blankOutputs,blankSerial,blankTimers) + blankTimers = [60] # in sec + self._set_state_matrix(blankMatrix, blankOutputs, blankSerial, + blankTimers) - def set_state_matrix(self,stateMatrix): + def set_state_matrix(self, stateMatrix): ''' Send state transition matrix to server. Args: - stateMatrix (statematrix.StateMatrix): object that contains all information - about the state matrix, outputs and timers. + stateMatrix (statematrix.StateMatrix): object that contains all + information about the state matrix, outputs and timers. ''' self._set_prepare_next_trial_states(stateMatrix.get_ready_states(), stateMatrix.get_states_dict()) @@ -136,15 +146,25 @@ def set_state_matrix(self,stateMatrix): stateMatrix.get_serial_outputs(), stateMatrix.get_state_timers()) - def _set_state_matrix(self,stateMatrix,stateOutputs,serialOutputs,stateTimers,extraTimers=None): + def _set_state_matrix(self, stateMatrix, stateOutputs, serialOutputs, + stateTimers, extraTimers=None): ''' - Send state transition matrix, outputs and timers to server, given python lists. + Send state transition matrix, outputs and timers to server, given + python lists. Data must be python lists (2D), not numpy arrays. - stateMatrix: [nStates][nActions] (where nActions is 2*nInputs+1+nExtraTimers) - stateOutputs: [nStates][nOutputs] specifying it turn on, off, or no change. + + stateMatrix: [nStates][nActions] (where nActions is + 2*nInputs+1+nExtraTimers) + + stateOutputs: [nStates][nOutputs] specifying it turn on, off, or no + change. + 0 (for low), 1 (for high), other (for no change) - serialOutputs: [nStates] (where each value is one byte corresponding to 8 outputs) + + serialOutputs: [nStates] (where each value is one byte corresponding to + 8 outputs) + stateTimers: [nStates] (in sec) ''' # -- Set prepare next trial states -- @@ -154,7 +174,7 @@ def _set_state_matrix(self,stateMatrix,stateOutputs,serialOutputs,stateTimers,ex #if not isinstance(statesmatrix,np.ndarray): # statesmatrix = np.array(statesmatrix) if extraTimers: - #self.statemachine.setScheduledWavesDIO(schedwavesmatrix) + #self.statemachine.setScheduledWavesDIO(schedwavesmatrix) raise 'Sending extra-timers is not implemented yet.' self.statemachine.set_state_matrix(stateMatrix) self.statemachine.set_state_outputs(stateOutputs) @@ -165,11 +185,13 @@ def _set_state_matrix(self,stateMatrix,stateOutputs,serialOutputs,stateTimers,ex else: print 'Call to setStateMatrix, but the client is not connected.\n' - def _set_prepare_next_trial_states(self,prepareNextTrialStatesAsStrings,statesDict): - '''Defines the list of states from which the state machine returns control - to the client to prepare the next trial.''' - if not isinstance(prepareNextTrialStatesAsStrings,list): - raise TypeError('prepareNextTrialStatesAsStrings must be a list of strings') + def _set_prepare_next_trial_states(self, prepareNextTrialStatesAsStrings, + statesDict): + '''Defines the list of states from which the state machine returns + control to the client to prepare the next trial.''' + if not isinstance(prepareNextTrialStatesAsStrings, list): + raise TypeError('prepareNextTrialStatesAsStrings must be a \ + list of strings') self.prepareNextTrialStates = [] for oneState in prepareNextTrialStatesAsStrings: self.prepareNextTrialStates.append(statesDict[oneState]) @@ -181,12 +203,14 @@ def ready_to_start_trial(self): self.currentTrial += 1 self.statemachine.force_state(1) self.preparingNextTrial = False - ### self.startNewTrial.emit(self.currentTrial) # FIXME: is this really necessary? + ### self.startNewTrial.emit(self.currentTrial) + # FIXME: is this really necessary? def timeout(self): ###print '************************ TIMEOUT *********************' self.query_state_machine() - self.timerTic.emit(self.serverTime,self.currentState,self.eventCount,self.currentTrial) + self.timerTic.emit(self.serverTime, self.currentState, self.eventCount, + self.currentTrial) ###print '{0} {1}'.format(self.currentState,self.serverTime) ### DEBUG ###print self.prepareNextTrialStates # -- Check if one of the PrepareNextTrialStates has been reached -- @@ -197,7 +221,8 @@ def timeout(self): self.preparingNextTrial = True self.update_trial_borders() self.prepareNextTrial.emit(self.currentTrial+1) - # FIXME: Should I stop the clock/timeouts here? until everything is processed? + # FIXME: Should I stop the clock/timeouts here? until everything is + # processed? # FIXME: fix what to do for other preparation states ''' if self.currentState and not self.preparingNextTrial) or \ @@ -206,7 +231,8 @@ def timeout(self): for state in self.prepareNextTrialStates: if state in laststates: self.preparingNextTrial = True - #self.emit(QtCore.SIGNAL('PrepareNextTrial'), self.currentTrial+1) + #self.emit(QtCore.SIGNAL('PrepareNextTrial'), + self.currentTrial+1) self.prepareNextTrial.emit(self.currentTrial+1) break ''' @@ -214,10 +240,10 @@ def timeout(self): #@QtCore.Slot() def resume(self): # --- Start timer --- - self.timer.start(1e3*self.interval) # timer takes interval in ms + self.timer.start(1e3*self.interval) # timer takes interval in ms # -- Start state machine -- if self.isConnected: - if 1: #self._stateMatrixStatus: + if 1: # self._stateMatrixStatus: # Prepare next trial when start is pressed #self.prepareNextTrial.emit(self.currentTrial+1) self.statemachine.run() @@ -229,7 +255,8 @@ def resume(self): else: raise Exception('A state matrix has not been set') else: - print 'The dispatcher is not connected to the state machine server.' + print 'The dispatcher is not connected to the state machine\ + server.' #@QtCore.Slot() def pause(self): @@ -242,16 +269,18 @@ def pause(self): # FIXME: self.statemachine.force_output(????) self.logMessage.emit('Stopped') else: - print 'The dispatcher is not connected to the state machine server.' + print 'The dispatcher is not connected to the state machine\ + server.' def query_state_machine(self): '''Request events information to the state machine''' if self.isConnected: - #resultsDict = self.statemachine.getTimeEventsAndState(self.eventCount+1) + #resultsDict = + # self.statemachine.getTimeEventsAndState(self.eventCount+1) self.serverTime = self.statemachine.get_time() self.lastEvents = self.statemachine.get_events() #self.eventsMat = np.vstack((self.eventsMat,self.lastEvents)) - if len(self.lastEvents)>0: + if len(self.lastEvents) > 0: self.eventsMat.extend(self.lastEvents) self.currentState = self.eventsMat[-1][2] self.eventCount = len(self.eventsMat) @@ -259,14 +288,15 @@ def query_state_machine(self): ### print self.eventsMat ### DEBUG def update_trial_borders(self): - '''Find last index of last trial. - It looks for state zero, which corresponds to the last state no each trial. - The first event of all is also state zero, but this one is ignored.''' + '''Find last index of last trial. It looks for state zero, which + corresponds to the last state no each trial. The first event of all is + also state zero, but this one is ignored.''' # FIXME: slow way to find end of trial - if self.currentTrial>=0: # & self.eventCount>0: - for inde in xrange(self.eventCount-1,-1,-1): # This will count from n to 0 + if self.currentTrial >= 0: # & self.eventCount>0: + for inde in xrange(self.eventCount-1, -1, -1): # This will count + # from n to 0 #if self.eventsMat[inde][2] in self.prepareNextTrialStates: - if self.eventsMat[inde][2]==DEFAULT_PREPARE_NEXT: + if self.eventsMat[inde][2] == DEFAULT_PREPARE_NEXT: self.indexLastEventEachTrial.append(inde) break # WARNING: make sure this method is not called before the events @@ -274,48 +304,55 @@ def update_trial_borders(self): # FIXME: this function has not been tested with more than one state # in prepareNextTrialStates. - def events_one_trial(self,trialID): + def events_one_trial(self, trialID): '''Return events for one trial as a numpy array''' #if trialID<0: eventsThisTrial = np.empty((0,3)) # NOTE: hardcoded size indLast = self.indexLastEventEachTrial[-1] - if trialID==0: + if trialID == 0: indPrev = 0 else: indPrev = self.indexLastEventEachTrial[-2] # -- Include the state 0 at the beginning of the trial -- - #eventsThisTrial = self.eventsMat[indPrev:indLast+1] # eventsMat is a list + #eventsThisTrial = self.eventsMat[indPrev:indLast+1] + #eventsMat is a list # -- Do not include the state 0 at the beginning of the trial -- - eventsThisTrial = self.eventsMat[indPrev+1:indLast+1] # eventsMat is a list + eventsThisTrial = self.eventsMat[indPrev+2:indLast+1] + # eventsMat is a list return np.array(eventsThisTrial) - ####### FIXME: this seems inefficient because eventsMat is an array and we - ####### need only a set of trials. Do we need to convert the whole thing? - + ### FIXME: this seems inefficient because eventsMat is an array and we + ### need only a set of trials. Do we need to convert the whole + ### thing? - def append_to_file(self,h5file,currentTrial=None): + def append_to_file(self, h5file, currentTrial=None): '''Add events information to an open HDF5 file. At this point, it ignores the value of 'currentTrial'. ''' - if not (self.indexLastEventEachTrial): # not len(self.eventsMat): - raise UserWarning('WARNING: No trials have been completed. No events were saved.') - eventsGroup = h5file.create_group('/events') # Events that ocurred during the session + if not (self.indexLastEventEachTrial): # not len(self.eventsMat): + raise UserWarning('WARNING: No trials have been completed.\ + No events were saved.') + eventsGroup = h5file.create_group('/events') # Events that ocurred + # during the session eventsMatrixAsArray = np.array(self.eventsMat) - eventsGroup.create_dataset('eventTime', dtype=float, data=eventsMatrixAsArray[:,0]) - eventsGroup.create_dataset('eventCode', dtype=int, data=eventsMatrixAsArray[:,1]) - eventsGroup.create_dataset('nextState', dtype=int, data=eventsMatrixAsArray[:,2]) + eventsGroup.create_dataset('eventTime', dtype=float, + data=eventsMatrixAsArray[:, 0]) + eventsGroup.create_dataset('eventCode', dtype=int, + data=eventsMatrixAsArray[:, 1]) + eventsGroup.create_dataset('nextState', dtype=int, + data=eventsMatrixAsArray[:, 2]) eventsGroup.create_dataset('indexLastEventEachTrial', dtype=int, data=np.array(self.indexLastEventEachTrial)) - ###### FIXME: what happens (on trial 1) when indexLastEventEachTrial is empty? ##### - + ###### FIXME: what happens (on trial 1) + ###### when indexLastEventEachTrial is empty? ##### #rawEventsColumnsLabels = ['eventTime','eventCode','nextState'] - #eventsGroup.create_dataset('rawEvents', dtype=float, data=dispatcherModel.eventsMatrix) + #eventsGroup.create_dataset('rawEvents', dtype=float, + # data=dispatcherModel.eventsMatrix) #dtstr = h5py.special_dtype(vlen=str) #eventsGroup.create_dataset('rawEventsColumnsLabels', dtype=dtstr, # data=rawEventsColumnsLabels) #return True - - + def die(self): '''Make sure timer stops when user closes the dispatcher.''' self.pause() @@ -325,13 +362,13 @@ def die(self): self.statemachine.force_state(0) self.statemachine.close() +BUTTON_COLORS = {'start': 'limegreen', 'stop': 'red'} -BUTTON_COLORS = {'start':'limegreen','stop':'red'} - class DispatcherGUI(QtGui.QGroupBox): resumeSM = QtCore.Signal() pauseSM = QtCore.Signal() + def __init__(self, parent=None, minwidth=220, dummy=False, model=None): super(DispatcherGUI, self).__init__(parent) @@ -369,11 +406,13 @@ def __init__(self, parent=None, minwidth=220, dummy=False, model=None): # -- Create layouts -- layout = QtGui.QGridLayout() - layout.addWidget(self.stateLabel,0,0) - layout.addWidget(self.eventCountLabel,0,1) - layout.addWidget(self.timeLabel,1,0) - layout.addWidget(self.currentTrialLabel,1,1) - layout.addWidget(self.buttonStartStop, 2,0, 1,2) # Span 1 row, 2 cols + layout.addWidget(self.stateLabel, 1, 0) + layout.addWidget(self.eventCountLabel, 0, 1) + layout.addWidget(self.timeLabel, 1, 0) + layout.addWidget(self.currentTrialLabel, 1, 1) + + # Span 1 row, 2 cols + layout.addWidget(self.buttonStartStop, 2, 0, 1, 2) self.setLayout(layout) self.setTitle('Dispatcher') @@ -386,13 +425,14 @@ def __init__(self, parent=None, minwidth=220, dummy=False, model=None): self.stop() #@QtCore.Slot(float,int,int,int) # FIXME: is this really needed? - def update(self,serverTime,currentState,eventCount,currentTrial): + def update(self, serverTime, currentState, eventCount, currentTrial): '''Update display of time and events.''' self.timeLabel.setText(self._timeFormat.format(serverTime)) self.stateLabel.setText(self._stateFormat.format(currentState)) self.eventCountLabel.setText(self._eventCountFormat.format(eventCount)) - if currentTrial>=0: - self.currentTrialLabel.setText(self._currentTrialFormat.format(currentTrial)) + if currentTrial >= 0: + self.currentTrialLabel.setText( + self._currentTrialFormat.format(currentTrial)) #trialToPrint = currentTrial if currentTrial>-1 else '' def startOrStop(self): @@ -405,7 +445,7 @@ def startOrStop(self): def start(self): '''Resume state machine.''' # -- Change button appearance -- - stylestr = 'QWidget { background-color: %s }'%BUTTON_COLORS['stop'] + stylestr = 'QWidget { background-color: %s }' % BUTTON_COLORS['stop'] self.buttonStartStop.setStyleSheet(stylestr) self.buttonStartStop.setText('Stop') @@ -415,7 +455,7 @@ def start(self): def stop(self): '''Pause state machine.''' # -- Change button appearance -- - stylestr = 'QWidget { background-color: %s }'%BUTTON_COLORS['start'] + stylestr = 'QWidget { background-color: %s }' % BUTTON_COLORS['start'] self.buttonStartStop.setStyleSheet(stylestr) self.buttonStartStop.setText('Start') self.pauseSM.emit() @@ -428,31 +468,39 @@ def stop(self): def center(guiObj): '''Place in the center of the screen (NOT TESTED YET)''' screen = QtGui.QDesktopWidget().screenGeometry() - size = guiObj.geometry() - guiObj.move((screen.width()-size.width())/2, (screen.height()-size.height())/2) + size = guiObj.geometry() + guiObj.move((screen.width()-size.width())/2, + (screen.height()-size.height())/2) -if __name__ == "__main__": +def main(TESTCASE): - TESTCASE = 2 - if TESTCASE==1: + '''Create the window and allow closing it with Ctrl-C''' + + if TESTCASE == 1: import signal - # -- Needed for Ctrl-C (otherwise you need to kill with Ctrl-\ + # -- Needed for Ctrl-C (otherwise you need to kill with Ctrl-\ signal.signal(signal.SIGINT, signal.SIG_DFL) app = QtCore.QCoreApplication(sys.argv) - d = Dispatcher(parent=None,serverType='dummy', connectnow=False, interval=1) + d = Dispatcher(parent=None, serverType='dummy', + connectnow=False, interval=1) d.start() sys.exit(app.exec_()) - elif TESTCASE==2: + + elif TESTCASE == 2: import signal - signal.signal(signal.SIGINT, signal.SIG_DFL) # Enable Ctrl-C - app=QtGui.QApplication.instance() # checks if QApplication already exists - if not app: # create QApplication if it doesnt exist + signal.signal(signal.SIGINT, signal.SIG_DFL) # Enable Ctrl-C + # checks if QApplication already exists + app = QtGui.QApplication.instance() + if not app: # create QApplication if it doesnt exist app = QtGui.QApplication(sys.argv) form = QtGui.QDialog() - form.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) - #form.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) - dispatcherModel = Dispatcher(parent=form,serverType='dummy',connectnow=True, interval=0.5) + form.setSizePolicy(QtGui.QSizePolicy.Expanding, + QtGui.QSizePolicy.Expanding) + #form.setSizePolicy(QtGui.QSizePolicy.Minimum, + # QtGui.QSizePolicy.Minimum) + dispatcherModel = Dispatcher(parent=form, serverType='dummy', + connectnow=True, interval=0.5) dispatcherView = DispatcherGUI(parent=form) dispatcherModel.timerTic.connect(dispatcherView.update) dispatcherView.resumeSM.connect(dispatcherModel.resume) @@ -467,7 +515,8 @@ def center(guiObj): app = QtGui.QApplication(sys.argv) form = QtGui.QDialog() form.setFixedSize(180,200) - #form.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) + #form.setSizePolicy(QtGui.QSizePolicy.Expanding, + QtGui.QSizePolicy.Expanding) if TESTCASE==100: dispatcherwidget = Dispatcher(parent=form,connectnow=False) @@ -483,8 +532,12 @@ def center(guiObj): form.show() app.exec_() - + # FIXME: maybe this way is better #sys.exit(app.exec_()) ''' + +TESTCASE = 2 +if __name__ == "__main__": + main(TESTCASE) diff --git a/core/messenger.py b/core/messenger.py index b627ccd..ec46931 100644 --- a/core/messenger.py +++ b/core/messenger.py @@ -10,7 +10,8 @@ import time -from PySide import QtCore +from PySide import QtCore + class Message(object): ''' @@ -18,13 +19,14 @@ class Message(object): It contains the timestamp, the message and the sender. ''' - def __init__(self,text): - self.text=text - self.timestamp=time.localtime() + def __init__(self, text): + self.text = text + self.timestamp = time.localtime() + def __str__(self): '''String representation of the message''' - timeString = time.strftime('[%H:%M:%S] ',self.timestamp) - return '%s%s'%(timeString,self.text) + timeString = time.strftime('[%H:%M:%S] ', self.timestamp) + return '%s%s' % (timeString, self.text) class Messenger(QtCore.QObject): @@ -37,12 +39,13 @@ class Messenger(QtCore.QObject): ''' timedMessage = QtCore.Signal(str) messages = [] + def __init__(self): super(Messenger, self).__init__() #self.messages = [] @QtCore.Slot(str) - def collect(self,text): + def collect(self, text): newMessage = Message(text) Messenger.messages.append(newMessage) self.timedMessage.emit(str(newMessage)) @@ -52,16 +55,20 @@ def stringlist(self): def __str__(self): return '\n'.join(self.stringlist()) - -if __name__ == "__main__": + +def main(): + + '''When executed as a script instead of imported as a module, run some test + message commands''' onemsg = Message('My short message') print onemsg - mess1 = Messenger() mess1.send('One message') mess2 = Messenger() mess2.send('Another message') +if __name__ == "__main__": + main() diff --git a/examples/example003_dispatcher.py b/examples/example003_dispatcher.py index 47ab640..fde28e0 100644 --- a/examples/example003_dispatcher.py +++ b/examples/example003_dispatcher.py @@ -14,6 +14,7 @@ from PySide import QtGui from taskontrol.settings import rigsettings from taskontrol.core import dispatcher +from taskontrol.core import statematrix import signal # -- Create main window -- @@ -33,7 +34,13 @@ [ 2, 2, 1, 2, 2, 2, 1 ] ] stateOutputs = [[0,0,0], [1,1,1], [0,0,0]] stateTimers = [ 0.1, 2 , 2 ] -dispatcherModel.set_state_matrix(stateMatrix, stateOutputs, stateTimers) + +sM=statematrix.StateMatrix() #Create the statematrix object to upload +sM.stateMatrix=stateMatrix +sM.stateOutputs=stateOutputs +sM.stateTimers=stateTimers + +dispatcherModel.set_state_matrix(sM) # -- Create dispatcher GUI and connect signals -- dispatcherView = dispatcher.DispatcherGUI(model=dispatcherModel) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/test_core.py b/test/test_core.py new file mode 100644 index 0000000..b2358ea --- /dev/null +++ b/test/test_core.py @@ -0,0 +1,41 @@ + +def arraycontainer_test(): + + from core.arraycontainer import Container + import h5py + import numpy as np + c = Container() + c['myvar1'] = np.arange(10) + c.labels['myvar2labels'] = {'yes':1,'no':0} + c['myvar2'] = np.array([0,1,1,1,0]) + h5file = h5py.File('testh5.h5','w') + h5file.create_group('resultsData') + c.append_to_file(h5file,4) + h5file.close() +''' +def dispatcher_test_one(): + + from core.dispatcher import * + import sys + from PySide import QtCore + from PySide import QtGui + import numpy as np + import signal + + signal.signal(signal.SIGINT, signal.SIG_DFL) # Enable Ctrl-C + app=QtGui.QApplication.instance() # checks if QApplication already exists + if not app: # create QApplication if it doesnt exist + app = QtGui.QApplication(sys.argv) + form = QtGui.QDialog() + form.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + #form.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + dispatcherModel = Dispatcher(parent=form,serverType='dummy',connectnow=True, interval=0.5) + dispatcherView = DispatcherGUI(parent=form) + dispatcherModel.timerTic.connect(dispatcherView.update) + dispatcherView.resumeSM.connect(dispatcherModel.resume) + dispatcherView.pauseSM.connect(dispatcherModel.pause) + dispatcherModel.resume() + #form.show() + #app.exec_() + #app.quit() +'''