Package speakeasy :: Module buttonSetPopupSelector_ui
[hide private]
[frames] | no frames]

Source Code for Module speakeasy.buttonSetPopupSelector_ui

  1  #!/usr/bin/env python 
  2   
  3  import sys 
  4   
  5  from speakeasy_ui import SpeakEasyGUI; 
  6   
  7  from python_qt_binding.QtGui import QVBoxLayout, QHBoxLayout, QGridLayout, QDialog, QLabel, QPushButton, QStyleFactory, QApplication 
  8   
  9   
10 -class NextPrev:
11 NEXT = 0; 12 PREVIOUS = 1;
13 14 #----------------------------------------------------- ButtonSetPopupSelector Class ----------------------------- 15
16 -class ButtonSetPopupSelector(QDialog):
17 ''' 18 An interactive dialog that displays successive sets of 19 buttons. When each set is displayed, user may ask for the 20 next available set, the previous. already seen set, or the 21 user may accept the currently displayed set. A cancel is 22 available as well. 23 24 The call and return protocol is as follows: 25 26 - The caller creates a fresh dialog instance of this class, 27 passing an iterator. The iterator's next() method returns 28 an array of ButtonProgram instances each time it is called. (When the 29 iterator is exhausted, the user is able to use a 'Previous' 30 button to view the previously seen sets again). 31 - The caller invokes the exec_() method on the dialog instance. 32 - The exec_() method returns: 33 34 - -1, if the iterator yielded no button label arrays at all. 35 - 0, if the user canceled, and 36 - 1 if the user accepted one of the sets. 37 38 - If the exec_() method returned 1, the caller may obtain an array with the 39 ButtonProgram instances of the currently showing (and therefore accepted) buttons. 40 The array is obtained from the dialog instance via method getCurrentlyShowingSetLabels(self). 41 ''' 42 #------------------------------------------------------ Public Methods --------------------------- 43
44 - def __init__(self, buttonProgramArrayIterator):
45 46 super(ButtonSetPopupSelector, self).__init__(); 47 48 self.setStyleSheet(SpeakEasyGUI.stylesheetAppBG); 49 50 self.programButtonDict = {}; 51 self.buttonProgramArrayIt = buttonProgramArrayIterator; 52 self.buttonProgramArrays = []; # saved ButtonProgram arrays 53 self.shownLabelArrays = []; 54 self.currentlyShowingSetIndex = None; 55 self.rootLayout = None; 56 57 self.cancelButton = QPushButton("Cancel"); 58 self.cancelButton.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet); 59 self.cancelButton.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT); 60 #self.cancelButton.clicked.connect(partial(QDialog.done, self, 0)); 61 self.cancelButton.clicked.connect(self.clickedCancelButton); 62 63 self.OKButton = QPushButton("Pick this One"); 64 self.OKButton.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet); 65 self.OKButton.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT); 66 #self.OKButton.clicked.connect(partial(QDialog.done, self, 1)); 67 self.OKButton.clicked.connect(self.clickedOKButton); 68 69 self.currentNextPrevDirection = NextPrev.NEXT; 70 71 self.nextButton = QPushButton("Next"); 72 self.nextButton.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet); 73 self.nextButton.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT); 74 self.nextButton.clicked.connect(self.clickedNextButton); 75 76 self.prevButton = QPushButton("Previous"); 77 self.prevButton.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet); 78 self.prevButton.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT); 79 self.prevButton.clicked.connect(self.clickedPrevButton); 80 81 self.setNextPrevButtonsEnabledness(); 82 83 self.endOfSetsLabel = QLabel("<b>No more button sets.</b>") 84 self.noSetsLabel = QLabel("<b>No button sets available.</b>") 85 86 self.noAvailableSets = False; 87 self.offerNewButtonSet();
88 89
90 - def exec_(self):
91 ''' 92 If the prior initialization revealed that the caller does not 93 deliver any sets of button labels at all, then return -1, without 94 displaying any dialog. 95 @return: -1 if no button sets available. 1 if user accepted one of the button 96 label sets as the one they want, or 0, if the user cancelled. 97 @rtype: int 98 ''' 99 if self.noAvailableSets: 100 return -1; 101 return super(ButtonSetPopupSelector, self).exec_();
102
103 - def getCurrentlyShowingSet(self):
104 ''' 105 Returns an array of ButtonProgram instances that are currently showing on 106 the dialog, or were showing when the user clicked OK. 107 @return: Array of ButtonProgram instances. 108 @rtype: [ButtonProgram] 109 ''' 110 return self.buttonProgramArrays[self.currentlyShowingSetIndex];
111
113 ''' 114 Returns an array of labels of buttons that are currently showing on 115 the dialog, or were showing when the user clicked OK. 116 @return: Array of labels. 117 @rtype: [string] 118 ''' 119 allLabels = []; 120 for buttonProgram in self.buttonProgramArrays[self.currentlyShowingSetIndex]: 121 allLabels.append(buttonProgram.getLabel()); 122 return allLabels;
123 124 125 126 #------------------------------------------------------ Private Methods --------------------------- 127
128 - def offerNewButtonSet(self):
129 ''' 130 Responsible for displaying the next, or previous set of buttons. 131 During successive calls to this method, the method obtains new 132 button label sets from the caller's iterator until the iterator 133 is exhausted. The sets are collected in <code>self.shownLabelArrays</code>. 134 The method is also responsible for showing previously shown sets again. 135 The direction is controlled by the handlers of the Previous and Next buttons. 136 They set instance variable <code>currentNextPrevDirection</code>. 137 ''' 138 139 # Make sure the 'No more button sets in this direction" 140 # message gets deleted: 141 self.endOfSetsLabel.hide(); 142 self.enableActionButton(self.OKButton); 143 144 # Want to display a previously displayed button set? 145 if self.currentNextPrevDirection == NextPrev.PREVIOUS: 146 # Are we showing the first set, i.e. is there no set 147 # before the current one? 148 if self.currentlyShowingSetIndex == 0: 149 # Show the 'nothing before this one' label, 150 # and switch directions: 151 self.flipNextPrevDirection(); 152 return; 153 self.currentlyShowingSetIndex -= 1; 154 self.buildButtonSet(self.getCurrentlyShowingSetLabels()); 155 #*************** 156 self.setButtonSetTitleInWindow(self.currentlyShowingSetIndex + 1); 157 #*************** 158 self.setNextPrevButtonsEnabledness(); 159 return; 160 161 # Wants next button set. Is there one we haven't shown yet?: 162 try: 163 # Get next set of ButtonProgram instances from caller: 164 buttonProgramArray = self.buttonProgramArrayIt.next(); 165 # Save this ButtonProgram instance: 166 self.buttonProgramArrays.append(buttonProgramArray); 167 168 # From the ButtonProgram objects array, create an 169 # array of just the button labels: 170 buttonLabelArray = []; 171 for buttonProgram in buttonProgramArray: 172 buttonLabelArray.append(buttonProgram.getLabel()); 173 174 # If this is the very first time we pull a button set 175 # from the caller, then our current index will be None 176 # from the initialization. We use that to check for 177 # the case that the caller has no sets at all to feed 178 # out. Since we didn't bomb with StopIteration, we know 179 # that there is at least one available button set: 180 if self.currentlyShowingSetIndex is None: 181 self.currentlyShowingSetIndex = -1; 182 183 # Save that list of button names: 184 self.shownLabelArrays.append(buttonLabelArray); 185 self.currentlyShowingSetIndex += 1; 186 #********** 187 self.setButtonSetTitleInWindow(self.currentlyShowingSetIndex + 1); 188 #********** 189 self.buildButtonSet(buttonLabelArray); 190 self.setNextPrevButtonsEnabledness(); 191 return; 192 except StopIteration: 193 # If we fell through the very first time, there is no 194 # button set available at all: 195 if self.currentlyShowingSetIndex is None: 196 self.terminateWithWarning(); 197 return; 198 # No more button sets available from caller's iterator: 199 self.currentlyShowingSetIndex += 1; 200 #********** 201 self.setButtonSetTitleInWindow(self.currentlyShowingSetIndex + 1); 202 #********** 203 204 if self.currentlyShowingSetIndex >= len(self.shownLabelArrays): 205 # Neither are there more new sets, nor are we going 206 # through the already shown sets going forward a second 207 # time. So reverse direction to go backwards through the 208 # the already shown sets: 209 self.setButtonSetTitleInWindow(-1) 210 self.flipNextPrevDirection(); 211 return; 212 213 self.buildButtonSet(self.getCurrentlyShowingSetLabels()); 214 self.setNextPrevButtonsEnabledness(); 215 216 return;
217
218 - def setButtonSetTitleInWindow(self, buttonSetIndex):
219 ''' 220 Given a button set number, displays a help text in the 'pick button set' dialog. 221 @param buttonSetIndex: number of button set (1-based by convention, but 0 is legal). 222 set to -1 if wish to indicate absence of a relevant set. Will show as 'Showing button set --'. 223 @type buttonSetIndex: int 224 ''' 225 if buttonSetIndex > -1: 226 self.setWindowTitle('Showing button set %d' % buttonSetIndex); 227 else: 228 self.setWindowTitle('Showing button set --');
229
230 - def flipNextPrevDirection(self):
231 ''' 232 Called when no more button label sets are available 233 either in the next, or previous direction. 234 ''' 235 236 self.clearDialogPane(); 237 238 self.rootLayout.addWidget(self.endOfSetsLabel); 239 # Show the 'End of sets' label, and 240 # de-activate the OK button: 241 self.endOfSetsLabel.show(); 242 self.disableActionButton(self.OKButton); 243 244 if (self.currentNextPrevDirection == NextPrev.PREVIOUS): 245 self.currentNextPrevDirection = NextPrev.NEXT; 246 self.currentlyShowingSetIndex = -1; 247 else: 248 self.currentNextPrevDirection = NextPrev.PREVIOUS; 249 self.currentlyShowingSetIndex = len(self.shownLabelArrays); 250 251 self.setNextPrevButtonsEnabledness(); 252 self.addChoiceButtons(self.rootLayout); 253 self.setLayout(self.rootLayout);
254
255 - def clearDialogPane(self):
256 ''' 257 Cleans out the dialog's layout. Does not destroy 258 the control buttons (Next, Previous, Cancel, and OK), 259 but does trigger deletion of all the button objects 260 in the button grid. Attempts to None out references 261 to UI widgets to enable garbage collection. 262 ''' 263 if self.rootLayout is None: 264 return; 265 266 self.rootLayout.removeWidget(self.cancelButton); 267 self.rootLayout.removeWidget(self.OKButton); 268 self.rootLayout.removeWidget(self.nextButton); 269 self.rootLayout.removeWidget(self.prevButton); 270 self.rootLayout.removeItem(self.buttonGridLayout); 271 272 self.buttonGritLayout = None; 273 274 for buttonObj in self.programButtonDict.values(): 275 if buttonObj is not None: 276 buttonObj.hide(); 277 buttonObj.deleteLater() 278 279 for key in self.programButtonDict.keys(): 280 self.programButtonDict[key] = None; 281 282 # Set this dialog's layout to the empty layout: 283 self.setLayout(self.rootLayout);
284
285 - def buildButtonSet(self, buttonLabelArray):
286 ''' 287 Constructs one set of buttons, based on passed in 288 button labels. 289 290 @param buttonLabelArray: Array of button labels. 291 @type buttonLabelArray: [string] 292 ''' 293 294 # If we never made the root layout, make it now: 295 if self.rootLayout is None: 296 self.rootLayout = QVBoxLayout(); 297 else: 298 # A previous button set is being displayed, 299 # Empty out the rootLayout: 300 self.clearDialogPane(); 301 302 # Get a layout filled with the button objects 303 # in the proper styling. Also get a dictionary 304 # mapping button labels to button objects: 305 306 (self.buttonGridLayout, self.programButtonDict) =\ 307 SpeakEasyGUI.buildButtonGrid(buttonLabelArray, 308 SpeakEasyGUI.NUM_OF_PROGRAM_BUTTON_COLUMNS); 309 310 for buttonObj in self.programButtonDict.values(): 311 buttonObj.setStyleSheet(SpeakEasyGUI.programButtonStylesheet); 312 buttonObj.setMinimumHeight(SpeakEasyGUI.BUTTON_MIN_HEIGHT); 313 314 # Place the grid into a V-layout, and add 315 # the next/previous, cancel, and OK buttons; 316 self.rootLayout.addLayout(self.buttonGridLayout); 317 self.addChoiceButtons(self.rootLayout); 318 319 # Set the popup window's layout to the button grid 320 # plus choice button combo: 321 self.setLayout(self.rootLayout);
322
323 - def addChoiceButtons(self, layout):
324 ''' 325 Appends the existing Next/Previous/Cancel/OK buttons 326 into the passed-in layout. 327 @param layout: 328 ''' 329 choiceButtonRowLayout = QHBoxLayout(); 330 choiceButtonRowLayout.addWidget(self.nextButton); 331 choiceButtonRowLayout.addWidget(self.prevButton); 332 choiceButtonRowLayout.addWidget(self.cancelButton); 333 choiceButtonRowLayout.addWidget(self.OKButton); 334 layout.addLayout(choiceButtonRowLayout);
335
337 ''' 338 Examines the instance variables <code>shownLabelArrays</code> 339 and </code>currentlyShowingSetIndex</code> to determine whether 340 the Next or Previous buttons should be enabled. Acts on the result. 341 ''' 342 if self.currentlyShowingSetIndex >= len(self.shownLabelArrays): 343 # Can only go backwards: 344 self.disableActionButton(self.nextButton); 345 self.enableActionButton(self.prevButton); 346 elif self.currentlyShowingSetIndex <= 0: 347 # Can only go forward: 348 self.enableActionButton(self.nextButton); 349 self.disableActionButton(self.prevButton); 350 else: 351 self.enableActionButton(self.nextButton); 352 self.enableActionButton(self.prevButton);
353
354 - def enableActionButton(self, buttonObj):
355 buttonObj.setEnabled(True) 356 buttonObj.setStyleSheet(SpeakEasyGUI.recorderButtonStylesheet);
357
358 - def disableActionButton(self, buttonObj):
359 buttonObj.setEnabled(False) 360 buttonObj.setStyleSheet(SpeakEasyGUI.recorderButtonDisabledStylesheet);
361
362 - def clickedNextButton(self):
363 ''' 364 Handler for Next button clicks. Triggers show of next 365 button set in order. 366 ''' 367 if self.currentNextPrevDirection == NextPrev.PREVIOUS: 368 self.currentNextPrevDirection = NextPrev.NEXT; 369 self.offerNewButtonSet();
370
371 - def clickedPrevButton(self):
372 ''' 373 Handler for Next button clicks. Triggers show of previous 374 button set in order. 375 ''' 376 if self.currentNextPrevDirection == NextPrev.NEXT: 377 self.currentNextPrevDirection = NextPrev.PREVIOUS; 378 self.offerNewButtonSet();
379
380 - def clickedCancelButton(self):
381 self.clearDialogPane(); 382 self.done(0);
383
384 - def clickedOKButton(self):
385 self.clearDialogPane(); 386 self.done(1);
387
388 - def terminateWithWarning(self):
389 ''' 390 Called when no button sets are available from the caller at all. 391 Records this event in instance variable <code>noAvailableSets</code>, 392 and returns. 393 ''' 394 # No buttons at all: 395 self.clearDialogPane(); 396 self.noAvailableSets = True;
397 398 #----------------------------------------------------- Tester Class ----------------------------- 399
400 -class Tester(QDialog):
401 ''' 402 Testing class. Invoked from main() of this file. Shows different 403 button sets. Not automatic, must be run by hand. 404 ''' 405
406 - def __init__(self):
407 super(Tester, self).__init__(); 408 vlayout = QVBoxLayout(); 409 self.testButton = QPushButton("Exactly one row of buttons"); 410 vlayout.addWidget(self.testButton); 411 self.testButton.clicked.connect(self.exactlyOneRow); 412 413 self.setLayout(vlayout); 414 self.show();
415
416 - def exactlyOneRow(self):
417 # Exactly one row even: 418 testButtonSetArray = [ 419 ["Button1.1", "Button2.1", "Button3.1", "Button4.1"], 420 ["Button1.2", "Button2.2", "Button3.2", "Button4.2"], 421 ["Button1.3", "Button2.3", "Button3.3", "Button4.3"] 422 ]; 423 buttonSetChoiceDialog = ButtonSetPopupSelector(iter(testButtonSetArray)); 424 buttonSetChoiceDialog.show(); 425 choiceResult = buttonSetChoiceDialog.exec_(); 426 print "Choice result: " + str(choiceResult); 427 buttonSetChoiceDialog.deleteLater(); 428 429 # Rewire the test button for the next text: 430 self.testButton.setText("One button available"); 431 self.testButton.clicked.disconnect(self.exactlyOneRow); 432 self.testButton.clicked.connect(self.oneButtonAvailable);
433
434 - def oneButtonAvailable(self):
435 # One button available 436 testButtonSetArray = [["Single button."]]; 437 buttonSetChoiceDialog = ButtonSetPopupSelector(iter(testButtonSetArray)); 438 buttonSetChoiceDialog.show(); 439 choiceResult = buttonSetChoiceDialog.exec_(); 440 print "Choice result: " + str(choiceResult); 441 buttonSetChoiceDialog.deleteLater(); 442 443 self.testButton.setText("No button set available"); 444 self.testButton.clicked.disconnect(self.oneButtonAvailable); 445 self.testButton.clicked.connect(self.noSetAvailable);
446
447 - def noSetAvailable(self):
448 testButtonSetArray = []; 449 buttonSetChoiceDialog = ButtonSetPopupSelector(iter(testButtonSetArray)); 450 #buttonSetChoiceDialog.show(); 451 choiceResult = buttonSetChoiceDialog.exec_(); 452 print "Choice result: " + str(choiceResult); 453 buttonSetChoiceDialog.deleteLater(); 454 455 # Rewire the test button for the next text: 456 self.testButton.setText("Exit"); 457 self.testButton.clicked.disconnect(self.noSetAvailable); 458 self.testButton.clicked.connect(self.close);
459
460 - def exitApp(self):
461 self.close();
462 463 if __name__ == "__main__": 464 465 style = QStyleFactory.create("Cleanlooks"); 466 QApplication.setStyle(style); 467 app = QApplication(sys.argv); 468 469 theTester = Tester(); 470 471 472 # Enter Qt application main loop 473 sys.exit(app.exec_()); 474