Printing to the Report window (console) --------------------------------------- >>> QUB.Report('yo') Dialog boxes ------------ You can put up a dialog with a message, some fields, and some buttons. QUB.Prompt( message, buttons, fields ) It returns a dictionary mapping field name to value. Fields can be * text-entry, for free response >>> field1 = PromptField( name, caption, initialvalue ) * radio-button groups, for choosing among options >>> field2 = PromptField( name, caption, initialoption, [option1, option2, ...] ) >>> from QUBB import PromptField >>> QUB.Prompt( 'testing this dialog', ['OK', 'Never Mind'], [ PromptField('choice', 'Choose One', 'b', ['a', 'b', 'c']), PromptField('comments', 'Comments: ', '') ] ) --------------------------------------------------------------------------- |===================================================================== X | | testing this dialog | | - Choose One --------------------------------------------------------- | | | O a | | | | O b | | | | * c | | | ---------------------------------------------------------------------- | | comments: | all good | | | ---------- ---------------- -------------------------------------- | | | OK | | Never Mind | | ----------|\--------------------------------------------------------------- | { 'choice' : 'c', 'comments' : 'all good', 'button' : 'OK' } Wizards ------- A wizard is like a dialog with just a message and some buttons, except that it doesn't monopolize the user's attention -- other windows are still clickable. A wizard is ideal for guiding the user through a process that can't be entirely automated. >>> wiz = QUB.CreateWizard() It can show a message while a script is working: >>> wiz.caption = 'Script progress' >>> wiz.message = 'this will take a while ...\r\n... sit tight' >>> wiz.show() >>> ... >>> wiz.hide() It can offer buttons and wait for one to be pressed: >>> wiz.message = 'abnormal conditions detected. blaze ahead anyway?' >>> wiz.buttons = ['Continue', 'Cancel'] >>> wiz.show() >>> choice = wiz.waitForButton() >>> wiz.hide() >>> if choice == 'Cancel': >>> raise Exception Working with the model ---------------------- Model access is being implemented in stages. Currently you can mess with any open model, but features including new/open/save, concurrency control, and metadata storage are not yet available. We can expedite these features if you have a need for them. >>> modelView = QUB.ModelView #\ QUB.ModelView acts like a list of currently open models >>> len(modelView) 1 >>> model = modelView[0] >>> model.copyImage() >>> modelTree = model.getAsTree() >>> model.setAsTree( modelTree ) >>> print modelTree ModelFile { States { State { x = 33.3333 y = 50 Class = 0 #\ classes numbered 0-9 Pr = 0 #\ start probability } State { x = 66.6667 y = 50 Class = 1 Pr = 0 } } Rates { Rate { States = 0 1 #\ states numbered from 0 #\ for subsequent elements, data[0] is for the forward direction #\ and data[1] is for the backward direction k0 = 1000 1000 #\ pre-exponential rates k1 = 0 0 #\ exponential rates dk0 = 0 0 #\ rate error limits (e.g. in MIL result) dk1 = 0 0 P = 0 0 #\ ligand-sensitivity (1 for yes, 0 for no) Q = 0 0 #\ voltage-sensitivity PNames { PName = Ligand PName = Ligand } QNames { QName = Voltage QName = Voltage } PValue = 0 0 #\ ignore PValue and QValue QValue = 0 0 } } Constraints { FixRate = 0 1 #\ state numbers, zero-based ScaleRate = 0 1 1 0 FixExp = 1 0 ScaleExp = 0 1 1 0 LoopBal = 0 1 } ChannelCount = 1 ( Amps Stds NAr #\ these are three nodes: Amps, Stds, NAr; multiplexed for display 7.55192 2.47766 0 #\ ModelFile['Amps'].data[0] is the amp of the first class 0.349307 2.37308 0 #\ ModelFile['NAr'].data[0] is the number of AR coefficients for the first class 2 0.2 0 #\ the coefficients for class i are stored in ModelFile['Ars'][i] (below) 3 0.25 0 4 0.3 0 5 0.35 0 6 0.4 0 7 0.45 0 8 0.5 0 9 0.55 0 ) Ars { ( Ar Ar Ar Ar Ar Ar Ar Ar Ar Ar 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) } } Working with data files ----------------------- >>> len( QUB.DataView ) # how many open files? 1 >>> data = QUB.DataView[0] # or QUB.DataView.currData >>> data.fileName "C:\\noname1.ldt" >>> data.copyImage() ... >>> data.select(0, 9999) Reading data and idealization ---------------------------- The current data source is used by the algorithm buttons on the right. It also determines which data or idealization is available to scripts. Which data is chosen depends on the data source, which data channel is active, which selection list is active, and which data is selected. ------------------------ The 'data source' is the choice on the right between 'selection', 'list', 'file' and 'file list'. >>> QUB.GetDataSource() "file" >>> QUB.SetDataSource('sel') >>> QUB.SetDataSource('list') >>> QUB.SetDataSource('file') >>> QUB.SetDataSource('file list') When requesting idealization, 'file list' refers to the "MIL Input List". When requesting data, 'file list' means 'file'. ----------------------- The 'active channel' is the chosen data channel (trace). It's labeled "Ch" at the upper left. >>> data = QUB.DataView.currData >>> data.activeChannel 0 >>> data.activeChannel = 1 ----------------------- You can get, set, and activate selection lists by name for each data file: >>> data.setList('scraps', [(0, 99), (124, 132), (2889, 3311)]) >>> data.getList('scraps') [(0, 99), (124, 132), (2889, 3311)] >>> data.chooseList('scraps') ----------------------- You can change the data selection: >>> data.select(0, 512) # highlights between point 0 and point 512, inclusive ------------------------------- Access to data happens in two steps * get segment boundaries and metadata for the chosen data source and active channel * fill a segment with data This way you can work on a whole file without keeping it all in RAM. >>> datasets = QUB.getChosenData() #\ get everything but the data points >>> print datasets DataSets { DataSet { FileName = C:\noname1.ldt sampling = 0.01 #\ ms ChannelCount = 1 Segment = 0 9999 { start = 0 #\ ms } Segment = 10000 19999 { start = 100 #\ ms } } } DataSet represents a file or part of it. It has some information about the whole file, and a number of segments. Segment.data contains the first and last point indices of the segment. Segment['Channel'].data contains the actual data, in pA. Segment['Channel'] does not exist until you fill it: >>> seg = datasets['DataSet']['Segment'] >>> QUB.fillData( seg ) >>> channel = seg['Channel'] >>> len(channel.data) 10000 When you're done with the data, save memory by releasing it: >>> seg.remove( channel ) channel is a copy of the actual data: changes will not be reflected anywhere. There is currently no way to edit data from python. You can also partially fill a segment. fillData() retrieves the interval between seg.data[0] and seg.data[1]. Change those bounds before passing the segment to fillData(). ---------------------------- >>> dataset = QUB.getChosenIdealization() #\ get it all (no second step) >>> print dataset DataSets { DataSet { FileName = C:\noname1.ldt sampling = 0.01 #\ ms ChannelCount = 1 Segment = 0 9999 { start = 0 #\ ms amp = 0.12 2.91 sd = 0.08 0.13 DwellCount = 10 ( Classes Durations Firsts Lasts 0 12.1 0 1209 1 8.3 1210 2039 0 0.3 2040 2069 1 30.2 2070 5089 0 3.2 5090 5409 1 15.2 5410 6929 0 0.2 6930 6949 1 3.7 6950 7319 0 8.9 7320 8209 1 17.9 8210 9999 ) } Segment = 10000 19999 { start = 100 #\ ms amp = 0.09 3.1 sd = 0.08 0.13 DwellCount = 7 ( Classes Durations Firsts Lasts 1 17.2 10000 11719 0 12.4 11720 12959 1 30.2 12960 15979 0 21.1 15980 18089 1 15.2 18090 19609 0 0.2 19610 19629 1 3.7 19630 19999 ) } } } This is similar to QUB.getChosenData(), with a few additions: * amp and sd: as in the model, per class * DwellCount: number of dwell intervals, size of Classes.data and Durations.data * Classes: Classes.data[i] is the class (color) index of dwell i * Durations: Durations.data[i] is the length of dwell i in milliseconds * Firsts: Index of the first sample in each dwell * Lasts: Index of the last sample in each dwell Note that Classes, Durations, Firsts, and Lasts are multiplexed for display. Each is a child of Segment with the same length data. >>> QUB.fillData( seg1 ) # is perfectly ok too Invoking buttons ---------------------------------------------------------- The buttons on the right are grouped into categories such as 'Modeling'. To run a button: >>> Buttons.Run('Modeling', 'MIL') To run a button, overriding some settings: (first, what are the settings called?) >>> print Buttons.GetSettings('Modeling', 'MIL') { ChannelIndex =0 SearchLimit =1000 STRING Mode =optimize STRING use segments =together join segments =0 ShowIterations =1 Histograms { BinCount =50 PDFs Each Iteration =1 } DFP { MaxIterations =100 MaxRestarts =0 MaxStep =1 ConvLL =0.0001 ConvGrad =0.01 } Voltage =0 Concentration =1 DeadTime =2.5e-005 DeadTimeSamples =0.5 DeadTimeUsesSamples =1 GroupViterbi =0 } >>> settings = qubtree.Node() >>> settings['use segments'] = 'together' >>> settings['Mode'] = 'check' >>> Buttons.Run('Modeling', 'MIL', settings) Some buttons put up new Results: >>> milResults = QUB.ResultsView.currResults Adding your own buttons --------------------------------------------- (see Buttons.AddTaskButton()) Your button can put up new results: >>> results = qubtree.Node('SDG noname1') >>> results['ResultType'].data = 'SDG' >>> ... >>> QUB.ResultsView.addResults( results ) customizing the results view customizing other menus curve fitting: adding new curves A word on "GraphicalInterrupt". When you click "Stop Tasks", it raises an exception in python. The exception is of type QUBB.GraphicalInterrupt. If you are so foolish (as I am) to write try: # whatever except: pass that is, if you don't specify which exceptions to ignore, you'll end up ignoring the GraphicalInterrupt.