class StatusBar(QStatusBar): def __init__(self, win): QStatusBar.__init__(self, win) self._progressLabel = QLabel() self._progressLabel.setMinimumWidth(200) self._progressLabel.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.addPermanentWidget(self._progressLabel) self.progressBar = QProgressBar(win) self.progressBar.setMaximumWidth(250) qt4todo('StatusBar.progressBar.setCenterIndicator(True)') self.addPermanentWidget(self.progressBar) self.progressBar.hide() self.simAbortButton = QToolButton(win) self.simAbortButton.setIcon( geticon("ui/actions/Simulation/Stopsign.png")) self.simAbortButton.setMaximumWidth(32) self.addPermanentWidget(self.simAbortButton) self.connect(self.simAbortButton, SIGNAL("clicked()"), self.simAbort) self.simAbortButton.hide() self.dispbarLabel = QLabel(win) #self.dispbarLabel.setFrameStyle( QFrame.Panel | QFrame.Sunken ) self.dispbarLabel.setText("Global display style:") self.addPermanentWidget(self.dispbarLabel) # Global display styles combobox self.globalDisplayStylesComboBox = GlobalDisplayStylesComboBox(win) self.addPermanentWidget(self.globalDisplayStylesComboBox) # Selection lock button. It always displays the selection lock state # and it is available to click. self.selectionLockButton = QToolButton(win) self.selectionLockButton.setDefaultAction(win.selectLockAction) self.addPermanentWidget(self.selectionLockButton) self.abortableCommands = {} #bruce 081230 debug code: ## self.connect(self, SIGNAL('messageChanged ( const QString &)'), ## self.slotMessageChanged ) ## def slotMessageChanged(self, message): # bruce 081230 debug code ## print "messageChanged: %r" % str(message) def showMessage(self, text): #bruce 081230 """ [extends superclass method] """ ## QStatusBar.showMessage(self, " ") QStatusBar.showMessage(self, text) ## print "message was set to %r" % str(self.currentMessage()) return def _f_progress_msg(self, text): #bruce 081229 refactoring """ Friend method for use only by present implementation of env.history.progress_msg. Display text in our private label widget dedicated to progress messages. """ self._progressLabel.setText(text) return def makeCommandNameUnique(self, commandName): index = 1 trial = commandName while (self.abortableCommands.has_key(trial)): trial = "%s [%d]" % (commandName, index) index += 1 return trial def addAbortableCommand(self, commandName, abortHandler): uniqueCommandName = self.makeCommandNameUnique(commandName) self.abortableCommands[uniqueCommandName] = abortHandler return uniqueCommandName def removeAbortableCommand(self, commandName): del self.abortableCommands[commandName] def simAbort(self): """ Slot for Abort button. """ if debug_flags.atom_debug and self.sim_abort_button_pressed: #bruce 060106 print "atom_debug: self.sim_abort_button_pressed is already True before we even put up our dialog" # Added confirmation before aborting as part of fix to bug 915. Mark 050824. # Bug 915 had to do with a problem if the user accidently hit the space bar or espace key, # which would call this slot and abort the simulation. This should no longer be an issue here # since we aren't using a dialog. I still like having this confirmation anyway. # IMHO, it should be kept. Mark 060106. ret = QMessageBox.warning( self, "Confirm", "Please confirm you want to abort.\n", "Confirm", "Cancel", "", 1, # The "default" button, when user presses Enter or Return (1 = Cancel) 1) # Escape (1= Cancel) if ret == 0: # Confirmed for abortHandler in self.abortableCommands.values(): abortHandler.pressed() def show_indeterminate_progress(self): value = self.progressBar.value() self.progressBar.setValue(value) def possibly_hide_progressbar_and_stop_button(self): if (len(self.abortableCommands) <= 0): self.progressBar.reset() self.progressBar.hide() self.simAbortButton.hide() def show_progressbar_and_stop_button(self, progressReporter, cmdname="<unknown command>", showElapsedTime=False): """ Display the statusbar's progressbar and stop button, and update it based on calls to the progressReporter. When the progressReporter indicates completion, hide the progressbar and stop button and return 0. If the user first presses the Stop button on the statusbar, hide the progressbar and stop button and return 1. Parameters: progressReporter - See potential implementations below. cmdname - name of command (used in some messages and in abort button tooltip) showElapsedTime - if True, display duration (in seconds) below progress bar Return value: 0 if file reached desired size, 1 if user hit abort button. """ updateInterval = .1 # seconds startTime = time.time() elapsedTime = 0 displayedElapsedTime = 0 ###e the following is WRONG if there is more than one task at a time... [bruce 060106 comment] self.progressBar.reset() self.progressBar.setMaximum(progressReporter.getMaxProgress()) self.progressBar.setValue(0) self.progressBar.show() abortHandler = AbortHandler(self, cmdname) # Main loop while progressReporter.notDoneYet(): self.progressBar.setValue(progressReporter.getProgress()) env.call_qApp_processEvents() # Process queued events (e.g. clicking Abort button, # but could be anything -- no modal dialog involved anymore). if showElapsedTime: elapsedTime = int(time.time() - startTime) if (elapsedTime != displayedElapsedTime): displayedElapsedTime = elapsedTime env.history.progress_msg("Elapsed Time: " + hhmmss_str(displayedElapsedTime)) # note: it's intentional that this doesn't directly call # self._f_progress_msg. [bruce 081229 comment] if abortHandler.getPressCount() > 0: env.history.statusbar_msg("Aborted.") abortHandler.finish() return 1 time.sleep(updateInterval) # Take a rest # End of Main loop (this only runs if it ended without being aborted) self.progressBar.setValue(progressReporter.getMaxProgress()) time.sleep( updateInterval) # Give the progress bar a moment to show 100% env.history.statusbar_msg("Done.") abortHandler.finish() return 0
class StatusBar(QStatusBar): def __init__(self, win): QStatusBar.__init__(self, win) self.statusMsgLabel = QLabel() self.statusMsgLabel.setMinimumWidth(200) self.statusMsgLabel.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.addPermanentWidget(self.statusMsgLabel) self.progressBar = QProgressBar(win) self.progressBar.setMaximumWidth(250) qt4todo("StatusBar.progressBar.setCenterIndicator(True)") self.addPermanentWidget(self.progressBar) self.progressBar.hide() self.simAbortButton = QToolButton(win) self.simAbortButton.setIcon(geticon("ui/actions/Simulation/Stopsign.png")) self.simAbortButton.setMaximumWidth(32) self.addPermanentWidget(self.simAbortButton) self.connect(self.simAbortButton, SIGNAL("clicked()"), self.simAbort) self.simAbortButton.hide() self.dispbarLabel = QLabel(win) # self.dispbarLabel.setFrameStyle( QFrame.Panel | QFrame.Sunken ) self.dispbarLabel.setText("Global display style:") self.addPermanentWidget(self.dispbarLabel) # Global display styles combobox self.globalDisplayStylesComboBox = GlobalDisplayStylesComboBox(win) self.addPermanentWidget(self.globalDisplayStylesComboBox) # Selection lock button. It always displays the selection lock state # and it is available to click. self.selectionLockButton = QToolButton(win) self.selectionLockButton.setDefaultAction(win.selectLockAction) self.addPermanentWidget(self.selectionLockButton) # Only use of this appears to be commented out in MWsemantics as of 2007/12/14 self.modebarLabel = QLabel(win) self.addPermanentWidget(self.modebarLabel) self.abortableCommands = {} def makeCommandNameUnique(self, commandName): index = 1 trial = commandName while self.abortableCommands.has_key(trial): trial = "%s [%d]" % (commandName, index) index += 1 return trial def addAbortableCommand(self, commandName, abortHandler): uniqueCommandName = self.makeCommandNameUnique(commandName) self.abortableCommands[uniqueCommandName] = abortHandler return uniqueCommandName def removeAbortableCommand(self, commandName): del self.abortableCommands[commandName] def simAbort(self): """ Slot for Abort button. """ if debug_flags.atom_debug and self.sim_abort_button_pressed: # bruce 060106 print "atom_debug: self.sim_abort_button_pressed is already True before we even put up our dialog" # Added confirmation before aborting as part of fix to bug 915. Mark 050824. # Bug 915 had to do with a problem if the user accidently hit the space bar or espace key, # which would call this slot and abort the simulation. This should no longer be an issue here # since we aren't using a dialog. I still like having this confirmation anyway. # IMHO, it should be kept. Mark 060106. ret = QMessageBox.warning( self, "Confirm", "Please confirm you want to abort.\n", "Confirm", "Cancel", "", 1, # The "default" button, when user presses Enter or Return (1 = Cancel) 1, ) # Escape (1= Cancel) if ret == 0: # Confirmed for abortHandler in self.abortableCommands.values(): abortHandler.pressed() def show_indeterminate_progress(self): value = self.progressBar.value() self.progressBar.setValue(value) def possibly_hide_progressbar_and_stop_button(self): if len(self.abortableCommands) <= 0: self.progressBar.reset() self.progressBar.hide() self.simAbortButton.hide() def show_progressbar_and_stop_button(self, progressReporter, cmdname="<unknown command>", showElapsedTime=False): """ Display the statusbar's progressbar and stop button, and update it based on calls to the progressReporter. When the progressReporter indicates completion, hide the progressbar and stop button and return 0. If the user first presses the Stop button on the statusbar, hide the progressbar and stop button and return 1. Parameters: progressReporter - See potential implementations below. cmdname - name of command (used in some messages and in abort button tooltip) showElapsedTime - if True, display duration (in seconds) below progress bar Return value: 0 if file reached desired size, 1 if user hit abort button. """ updateInterval = 0.1 # seconds startTime = time.time() elapsedTime = 0 displayedElapsedTime = 0 ###e the following is WRONG if there is more than one task at a time... [bruce 060106 comment] self.progressBar.reset() self.progressBar.setMaximum(progressReporter.getMaxProgress()) self.progressBar.setValue(0) self.progressBar.show() abortHandler = AbortHandler(self, cmdname) # Main loop while progressReporter.notDoneYet(): self.progressBar.setValue(progressReporter.getProgress()) env.call_qApp_processEvents() # Process queued events (e.g. clicking Abort button, # but could be anything -- no modal dialog involved anymore). if showElapsedTime: elapsedTime = int(time.time() - startTime) if elapsedTime != displayedElapsedTime: displayedElapsedTime = elapsedTime env.history.progress_msg("Elapsed Time: " + hhmmss_str(displayedElapsedTime)) if abortHandler.getPressCount() > 0: env.history.statusbar_msg("Aborted.") abortHandler.finish() return 1 time.sleep(updateInterval) # Take a rest # End of Main loop (this only runs if it ended without being aborted) self.progressBar.setValue(progressReporter.getMaxProgress()) time.sleep(updateInterval) # Give the progress bar a moment to show 100% env.history.statusbar_msg("Done.") abortHandler.finish() return 0
class ImageController(QFrame): """An ImageController is a widget for controlling the display of one image. It can emit the following signals from the image: raise raise button was clicked center center-on-image option was selected unload unload option was selected slice image slice has changed, need to redraw (emitted by SkyImage automatically) repaint image display range or colormap has changed, need to redraw (emitted by SkyImage automatically) """ def __init__(self, image, parent, imgman, name=None, save=False): QFrame.__init__(self, parent) self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) # init state self.image = image self._imgman = imgman self._currier = PersistentCurrier() self._control_dialog = None # create widgets self._lo = lo = QHBoxLayout(self) lo.setContentsMargins(0, 0, 0, 0) lo.setSpacing(2) # raise button self._wraise = QToolButton(self) lo.addWidget(self._wraise) self._wraise.setIcon(pixmaps.raise_up.icon()) self._wraise.setAutoRaise(True) self._can_raise = False QObject.connect(self._wraise, SIGNAL("clicked()"), self._raiseButtonPressed) self._wraise.setToolTip("""<P>Click here to raise this image above other images. Hold the button down briefly to show a menu of image operations.</P>""") # center label self._wcenter = QLabel(self) self._wcenter.setPixmap(pixmaps.center_image.pm()) self._wcenter.setToolTip( "<P>The plot is currently centered on (the reference pixel %d,%d) of this image.</P>" % self.image.referencePixel()) lo.addWidget(self._wcenter) # name/filename label self.name = image.name self._wlabel = QLabel(self.name, self) self._number = 0 self.setName(self.name) self._wlabel.setToolTip("%s %s" % (image.filename, "\u00D7".join(map(str, image.data().shape)))) lo.addWidget(self._wlabel, 1) # if 'save' is specified, create a "save" button if save: self._wsave = QToolButton(self) lo.addWidget(self._wsave) self._wsave.setText("save") self._wsave.setAutoRaise(True) self._save_dir = save if isinstance(save, str) else "." QObject.connect(self._wsave, SIGNAL("clicked()"), self._saveImage) self._wsave.setToolTip("""<P>Click here to write this image to a FITS file.</P>""") # render control dprint(2, "creating RenderControl") self._rc = RenderControl(image, self) dprint(2, "done") # selectors for extra axes self._wslicers = [] curslice = self._rc.currentSlice(); # this may be loaded from config, so not necessarily 0 for iextra, axisname, labels in self._rc.slicedAxes(): if axisname.upper() not in ["STOKES", "COMPLEX"]: lbl = QLabel("%s:" % axisname, self) lo.addWidget(lbl) else: lbl = None slicer = QComboBox(self) self._wslicers.append(slicer) lo.addWidget(slicer) slicer.addItems(labels) slicer.setToolTip("""<P>Selects current slice along the %s axis.</P>""" % axisname) slicer.setCurrentIndex(curslice[iextra]) QObject.connect(slicer, SIGNAL("activated(int)"), self._currier.curry(self._rc.changeSlice, iextra)) # min/max display ranges lo.addSpacing(5) self._wrangelbl = QLabel(self) lo.addWidget(self._wrangelbl) self._minmaxvalidator = FloatValidator(self) self._wmin = QLineEdit(self) self._wmax = QLineEdit(self) width = self._wmin.fontMetrics().width("1.234567e-05") for w in self._wmin, self._wmax: lo.addWidget(w, 0) w.setValidator(self._minmaxvalidator) w.setMaximumWidth(width) w.setMinimumWidth(width) QObject.connect(w, SIGNAL("editingFinished()"), self._changeDisplayRange) # full-range button self._wfullrange = QToolButton(self) lo.addWidget(self._wfullrange, 0) self._wfullrange.setIcon(pixmaps.zoom_range.icon()) self._wfullrange.setAutoRaise(True) QObject.connect(self._wfullrange, SIGNAL("clicked()"), self.renderControl().resetSubsetDisplayRange) rangemenu = QMenu(self) rangemenu.addAction(pixmaps.full_range.icon(), "Full subset", self.renderControl().resetSubsetDisplayRange) for percent in (99.99, 99.9, 99.5, 99, 98, 95): rangemenu.addAction("%g%%" % percent, self._currier.curry(self._changeDisplayRangeToPercent, percent)) self._wfullrange.setPopupMode(QToolButton.DelayedPopup) self._wfullrange.setMenu(rangemenu) # update widgets from current display range self._updateDisplayRange(*self._rc.displayRange()) # lock button self._wlock = QToolButton(self) self._wlock.setIcon(pixmaps.unlocked.icon()) self._wlock.setAutoRaise(True) self._wlock.setToolTip("""<P>Click to lock or unlock the intensity range. When the intensity range is locked across multiple images, any changes in the intensity range of one are propagated to the others. Hold the button down briefly for additional options.</P>""") lo.addWidget(self._wlock) QObject.connect(self._wlock, SIGNAL("clicked()"), self._toggleDisplayRangeLock) QObject.connect(self.renderControl(), SIGNAL("displayRangeLocked"), self._setDisplayRangeLock) QObject.connect(self.renderControl(), SIGNAL("dataSubsetChanged"), self._dataSubsetChanged) lockmenu = QMenu(self) lockmenu.addAction(pixmaps.locked.icon(), "Lock all to this", self._currier.curry(imgman.lockAllDisplayRanges, self.renderControl())) lockmenu.addAction(pixmaps.unlocked.icon(), "Unlock all", imgman.unlockAllDisplayRanges) self._wlock.setPopupMode(QToolButton.DelayedPopup) self._wlock.setMenu(lockmenu) self._setDisplayRangeLock(self.renderControl().isDisplayRangeLocked()) # dialog button self._wshowdialog = QToolButton(self) lo.addWidget(self._wshowdialog) self._wshowdialog.setIcon(pixmaps.colours.icon()) self._wshowdialog.setAutoRaise(True) self._wshowdialog.setToolTip("""<P>Click for colourmap and intensity policy options.</P>""") QObject.connect(self._wshowdialog, SIGNAL("clicked()"), self.showRenderControls) tooltip = """<P>You can change the currently displayed intensity range by entering low and high limits here.</P> <TABLE> <TR><TD><NOBR>Image min:</NOBR></TD><TD>%g</TD><TD>max:</TD><TD>%g</TD></TR> </TABLE>""" % self.image.imageMinMax() for w in self._wmin, self._wmax, self._wrangelbl: w.setToolTip(tooltip) # create image operations menu self._menu = QMenu(self.name, self) self._qa_raise = self._menu.addAction(pixmaps.raise_up.icon(), "Raise image", self._currier.curry(self.image.emit, SIGNAL("raise"))) self._qa_center = self._menu.addAction(pixmaps.center_image.icon(), "Center plot on image", self._currier.curry(self.image.emit, SIGNAL("center"))) self._qa_show_rc = self._menu.addAction(pixmaps.colours.icon(), "Colours && Intensities...", self.showRenderControls) if save: self._qa_save = self._menu.addAction("Save image...", self._saveImage) self._menu.addAction("Export image to PNG file...", self._exportImageToPNG) self._export_png_dialog = None self._menu.addAction("Unload image", self._currier.curry(self.image.emit, SIGNAL("unload"))) self._wraise.setMenu(self._menu) self._wraise.setPopupMode(QToolButton.DelayedPopup) # connect updates from renderControl and image self.image.connect(SIGNAL("slice"), self._updateImageSlice) QObject.connect(self._rc, SIGNAL("displayRangeChanged"), self._updateDisplayRange) # default plot depth of image markers self._z_markers = None # and the markers themselves self._image_border = QwtPlotCurve() self._image_label = QwtPlotMarker() # subset markers self._subset_pen = QPen(QColor("Light Blue")) self._subset_border = QwtPlotCurve() self._subset_border.setPen(self._subset_pen) self._subset_border.setVisible(False) self._subset_label = QwtPlotMarker() text = QwtText("subset") text.setColor(self._subset_pen.color()) self._subset_label.setLabel(text) self._subset_label.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom) self._subset_label.setVisible(False) self._setting_lmrect = False self._all_markers = [self._image_border, self._image_label, self._subset_border, self._subset_label] def close(self): if self._control_dialog: self._control_dialog.close() self._control_dialog = None def __del__(self): self.close() def __eq__(self, other): return self is other def renderControl(self): return self._rc def getMenu(self): return self._menu def getFilename(self): return self.image.filename def setName(self, name): self.name = name self._wlabel.setText("%s: %s" % (chr(ord('a') + self._number), self.name)) def setNumber(self, num): self._number = num self._menu.menuAction().setText("%s: %s" % (chr(ord('a') + self._number), self.name)) self._qa_raise.setShortcut(QKeySequence("Alt+" + chr(ord('A') + num))) self.setName(self.name) def getNumber(self): return self._number def setPlotProjection(self, proj): self.image.setPlotProjection(proj) sameproj = proj == self.image.projection self._wcenter.setVisible(sameproj) self._qa_center.setVisible(not sameproj) if self._image_border: (l0, l1), (m0, m1) = self.image.getExtents() path = numpy.array([l0, l0, l1, l1, l0]), numpy.array([m0, m1, m1, m0, m0]) self._image_border.setData(*path) if self._image_label: self._image_label.setValue(path[0][2], path[1][2]) def addPlotBorder(self, border_pen, label, label_color=None, bg_brush=None): # make plot items for image frame # make curve for image borders (l0, l1), (m0, m1) = self.image.getExtents() self._border_pen = QPen(border_pen) self._image_border.show() self._image_border.setData([l0, l0, l1, l1, l0], [m0, m1, m1, m0, m0]) self._image_border.setPen(self._border_pen) self._image_border.setZ(self.image.z() + 1 if self._z_markers is None else self._z_markers) if label: self._image_label.show() self._image_label_text = text = QwtText(" %s " % label) text.setColor(label_color) text.setBackgroundBrush(bg_brush) self._image_label.setValue(l1, m1) self._image_label.setLabel(text) self._image_label.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter) self._image_label.setZ(self.image.z() + 2 if self._z_markers is None else self._z_markers) def setPlotBorderStyle(self, border_color=None, label_color=None): if border_color: self._border_pen.setColor(border_color) self._image_border.setPen(self._border_pen) if label_color: self._image_label_text.setColor(label_color) self._image_label.setLabel(self._image_label_text) def showPlotBorder(self, show=True): self._image_border.setVisible(show) self._image_label.setVisible(show) def attachToPlot(self, plot, z_markers=None): for item in [self.image] + self._all_markers: if item.plot() != plot: item.attach(plot) def setImageVisible(self, visible): self.image.setVisible(visible) def showRenderControls(self): if not self._control_dialog: dprint(1, "creating control dialog") self._control_dialog = ImageControlDialog(self, self._rc, self._imgman) dprint(1, "done") if not self._control_dialog.isVisible(): dprint(1, "showing control dialog") self._control_dialog.show() else: self._control_dialog.hide() def _changeDisplayRangeToPercent(self, percent): if not self._control_dialog: self._control_dialog = ImageControlDialog(self, self._rc, self._imgman) self._control_dialog._changeDisplayRangeToPercent(percent) def _updateDisplayRange(self, dmin, dmax): """Updates display range widgets.""" self._wmin.setText("%.4g" % dmin) self._wmax.setText("%.4g" % dmax) self._updateFullRangeIcon() def _changeDisplayRange(self): """Gets display range from widgets and updates the image with it.""" try: newrange = float(str(self._wmin.text())), float(str(self._wmax.text())) except ValueError: return self._rc.setDisplayRange(*newrange) def _dataSubsetChanged(self, subset, minmax, desc, subset_type): """Called when the data subset changes (or is reset)""" # hide the subset indicator -- unless we're invoked while we're actually setting the subset itself if not self._setting_lmrect: self._subset = None self._subset_border.setVisible(False) self._subset_label.setVisible(False) def setLMRectSubset(self, rect): self._subset = rect l0, m0, l1, m1 = rect.getCoords() self._subset_border.setData([l0, l0, l1, l1, l0], [m0, m1, m1, m0, m0]) self._subset_border.setVisible(True) self._subset_label.setValue(max(l0, l1), max(m0, m1)) self._subset_label.setVisible(True) self._setting_lmrect = True self.renderControl().setLMRectSubset(rect) self._setting_lmrect = False def currentSlice(self): return self._rc.currentSlice() def _updateImageSlice(self, slice): dprint(2, slice) for i, (iextra, name, labels) in enumerate(self._rc.slicedAxes()): slicer = self._wslicers[i] if slicer.currentIndex() != slice[iextra]: dprint(3, "setting widget", i, "to", slice[iextra]) slicer.setCurrentIndex(slice[iextra]) def setMarkersZ(self, z): self._z_markers = z for i, elem in enumerate(self._all_markers): elem.setZ(z + i) def setZ(self, z, top=False, depthlabel=None, can_raise=True): self.image.setZ(z) if self._z_markers is None: for i, elem in enumerate(self._all_markers): elem.setZ(z + i + i) # set the depth label, if any label = "%s: %s" % (chr(ord('a') + self._number), self.name) # label = "%s %s"%(depthlabel,self.name) if depthlabel else self.name if top: label = "%s: <B>%s</B>" % (chr(ord('a') + self._number), self.name) self._wlabel.setText(label) # set hotkey self._qa_show_rc.setShortcut(Qt.Key_F9 if top else QKeySequence()) # set raise control self._can_raise = can_raise self._qa_raise.setVisible(can_raise) self._wlock.setVisible(can_raise) if can_raise: self._wraise.setToolTip( "<P>Click here to raise this image to the top. Click on the down-arrow to access the image menu.</P>") else: self._wraise.setToolTip("<P>Click to access the image menu.</P>") def _raiseButtonPressed(self): if self._can_raise: self.image.emit(SIGNAL("raise")) else: self._wraise.showMenu() def _saveImage(self): filename = QFileDialog.getSaveFileName(self, "Save FITS file", self._save_dir, "FITS files(*.fits *.FITS *fts *FTS)") filename = str(filename) if not filename: return busy = BusyIndicator() self._imgman.showMessage("""Writing FITS image %s""" % filename, 3000) QApplication.flush() try: self.image.save(filename) except Exception as exc: busy = None traceback.print_exc() self._imgman.showErrorMessage("""Error writing FITS image %s: %s""" % (filename, str(sys.exc_info()[1]))) return None self.renderControl().startSavingConfig(filename) self.setName(self.image.name) self._qa_save.setVisible(False) self._wsave.hide() busy = None def _exportImageToPNG(self, filename=None): if not filename: if not self._export_png_dialog: dialog = self._export_png_dialog = QFileDialog(self, "Export image to PNG", ".", "*.png") dialog.setDefaultSuffix("png") dialog.setFileMode(QFileDialog.AnyFile) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setModal(True) QObject.connect(dialog, SIGNAL("filesSelected(const QStringList &)"), self._exportImageToPNG) return self._export_png_dialog.exec_() == QDialog.Accepted busy = BusyIndicator() if isinstance(filename, QStringList): filename = filename[0] filename = str(filename) # make QPixmap nx, ny = self.image.imageDims() (l0, l1), (m0, m1) = self.image.getExtents() pixmap = QPixmap(nx, ny) painter = QPainter(pixmap) # use QwtPlot implementation of draw canvas, since we want to avoid caching xmap = QwtScaleMap() xmap.setPaintInterval(0, nx) xmap.setScaleInterval(l1, l0) ymap = QwtScaleMap() ymap.setPaintInterval(ny, 0) ymap.setScaleInterval(m0, m1) self.image.draw(painter, xmap, ymap, pixmap.rect()) painter.end() # save to file try: pixmap.save(filename, "PNG") except Exception as exc: self.emit(SIGNAL("showErrorMessage"), "Error writing %s: %s" % (filename, str(exc))) return self.emit(SIGNAL("showMessage"), "Exported image to file %s" % filename) def _toggleDisplayRangeLock(self): self.renderControl().lockDisplayRange(not self.renderControl().isDisplayRangeLocked()) def _setDisplayRangeLock(self, locked): self._wlock.setIcon(pixmaps.locked.icon() if locked else pixmaps.unlocked.icon()) def _updateFullRangeIcon(self): if self._rc.isSubsetDisplayRange(): self._wfullrange.setIcon(pixmaps.zoom_range.icon()) self._wfullrange.setToolTip( """<P>The current intensity range is the full range. Hold this button down briefly for additional options.</P>""") else: self._wfullrange.setIcon(pixmaps.full_range.icon()) self._wfullrange.setToolTip( """<P>Click to reset to a full intensity range. Hold the button down briefly for additional options.</P>""")
class PM_Dialog(QDialog, SponsorableMixin): """ The PM_Dialog class is the base class for Property Manager dialogs. [To make a PM class from this superclass, subclass it to customize the widget set and add behavior. You must also provide certain methods that used to be provided by GeneratorBaseClass, including ok_btn_clicked and several others, including at least some defined by SponsorableMixin (open_sponsor_homepage, setSponsor). This set of requirements may be cleaned up.] """ headerTitleText = "" # The header title text. _widgetList = [] # A list of all group boxes in this PM dialog, # including the message group box # (but not header, sponsor button, etc.) _groupBoxCount = 0 # Number of PM_GroupBoxes in this PM dialog _lastGroupBox = None # The last PM_GroupBox in this PM dialog # (i.e. the most recent PM_GroupBox added) def __init__(self, name, iconPath="", title=""): """ Property Manager constructor. @param name: the name to assign the property manager dialog object. @type name: str @param iconPath: the relative path for the icon (PNG image) that appears in the header. @type iconPath: str @param title: the title that appears in the header. @type title: str """ QDialog.__init__(self) self.setObjectName(name) self._widgetList = [] # Main pallete for PropMgr. self.setPalette(QPalette(pmColor)) # Main vertical layout for PropMgr. self.vBoxLayout = QVBoxLayout(self) self.vBoxLayout.setMargin(PM_MAINVBOXLAYOUT_MARGIN) self.vBoxLayout.setSpacing(PM_MAINVBOXLAYOUT_SPACING) # Add PropMgr's header, sponsor button, top row buttons and (hidden) # message group box. self._createHeader(iconPath, title) self._createSponsorButton() self._createTopRowBtns() # Create top buttons row self.MessageGroupBox = PM_MessageGroupBox(self) # Keep the line below around; it might be useful. # I may want to use it now that I understand it. # Mark 2007-05-17. #QMetaObject.connectSlotsByName(self) self._addGroupBoxes() try: self._addWhatsThisText() except: print_compact_traceback("Error loading whatsthis text for this " "property manager: ") try: self._addToolTipText() except: print_compact_traceback("Error loading tool tip text for this " "property manager: ") #The following attr is used for comparison in method #'_update_UI_wanted_as_something_changed' self._previous_all_change_indicators = None def update_UI(self): """ Update whatever is shown in this PM based on current state of the rest of the system, especially the state of self.command and of the model it shows. This is part of the PM API required by baseCommand, since it's called by baseCommand.command_update_UI. @note: Overridden in Command_PropertyManager, but should not be overridden in its subclasses. See that method's docstring for details. """ #bruce 081125 defined this here, to fix bugs in example commands return def keyPressEvent(self, event): """ Handles keyPress event. @note: Subclasses should carefully override this. Note that the default implementation doesn't permit ESC key as a way to close the PM_dialog. (This is typically desirable for Property Managers.) If any subclass needs to implement the key press, they should first call this method (i.e. superclass.keyPressEvent) and then implement specific code that closes the dialog when ESC key is pressed. """ key = event.key() # Don't use ESC key to close the PM dialog. Fixes bug 2596 if key == Qt.Key_Escape: pass else: QDialog.keyPressEvent(self, event) return def _addGroupBoxes(self): """ Add various group boxes to this PM. Subclasses should override this method. """ pass def _addWhatsThisText(self): """ Add what's this text. Subclasses should override this method. """ pass def _addToolTipText(self): """ Add Tool tip text. Subclasses should override this method. """ pass def show(self): """ Shows the Property Manager. """ self.setSponsor() # Show or hide the sponsor logo based on whether the user gave # permission to download sponsor logos. if env.prefs[sponsor_download_permission_prefs_key]: self.sponsorButtonContainer.show() else: self.sponsorButtonContainer.hide() if not self.pw or self: self.pw = self.win.activePartWindow() self.pw.updatePropertyManagerTab(self) self.pw.pwProjectTabWidget.setCurrentIndex( self.pw.pwProjectTabWidget.indexOf(self)) # Show the default message whenever we open the Property Manager. self.MessageGroupBox.MessageTextEdit.restoreDefault() def open(self, pm): """ Closes the current property manager (if any) and opens the property manager I{pm}. @param pm: The property manager to open. @type pm: L{PM_Dialog} or QDialog (of legacy PMs) @attention: This method is a temporary workaround for "Insert > Plane". The current command should always be responsible for (re)opening its own PM via self.show(). @see: L{show()} """ if 1: commandSequencer = self.win.commandSequencer #bruce 071008 commandName = commandSequencer.currentCommand.commandName # that's an internal name, but this is just for a debug print print "PM_Dialog.open(): Reopening the PM for command:", commandName # The following line of code is buggy when you, for instance, exit a PM # and reopen the previous one. It sends the disconnect signal twice to # the PM that is just closed.So disabling this line -- Ninad 2007-12-04 ##self.close() # Just in case there is another PM open. self.pw = self.win.activePartWindow() self.pw.updatePropertyManagerTab(pm) try: pm.setSponsor() except: print """PM_Dialog.open(): pm has no attribute 'setSponsor()' ignoring.""" self.pw.pwProjectTabWidget.setCurrentIndex( self.pw.pwProjectTabWidget.indexOf(pm)) def close(self): """ Closes the Property Manager. """ if not self.pw: self.pw = self.win.activePartWindow() self.pw.pwProjectTabWidget.setCurrentIndex(0) ## try: [bruce 071018 moved this lower, since errmsg only covers attr] pmWidget = self.pw.propertyManagerScrollArea.widget() if debug_flags.atom_debug: #bruce 071018 "atom_debug fyi: %r is closing %r (can they differ?)" % \ (self, pmWidget) try: pmWidget.update_props_if_needed_before_closing except AttributeError: if 1 or debug_flags.atom_debug: msg1 = "Last PropMgr %r doesn't have method" % pmWidget msg2 = " update_props_if_needed_before_closing. That's" msg3 = " OK (for now, only implemented for Plane PM). " msg4 = "Ignoring Exception: " print_compact_traceback(msg1 + msg2 + msg3 + msg4) #bruce 071018: I'll define that method in PM_Dialog # so this message should become rare or nonexistent, # so I'll make it happen whether or not atom_debug. else: pmWidget.update_props_if_needed_before_closing() self.pw.pwProjectTabWidget.removeTab( self.pw.pwProjectTabWidget.indexOf( self.pw.propertyManagerScrollArea)) if self.pw.propertyManagerTab: self.pw.propertyManagerTab = None def update_props_if_needed_before_closing(self): # bruce 071018 default implem """ Subclasses can override this to update some cosmetic properties of their associated model objects before closing self (the Property Manager). """ pass def updateMessage(self, msg=''): """ Updates the message box with an informative message @param msg: Message to be displayed in the Message groupbox of the property manager @type msg: string """ self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault=False, minLines=5) def _createHeader(self, iconPath, title): """ Creates the Property Manager header, which contains an icon (a QLabel with a pixmap) and white text (a QLabel with text). @param iconPath: The relative path for the icon (PNG image) that appears in the header. @type iconPath: str @param title: The title that appears in the header. @type title: str """ # Heading frame (dark gray), which contains # a pixmap and (white) heading text. self.headerFrame = QFrame(self) self.headerFrame.setFrameShape(QFrame.NoFrame) self.headerFrame.setFrameShadow(QFrame.Plain) self.headerFrame.setPalette(QPalette(pmHeaderFrameColor)) self.headerFrame.setAutoFillBackground(True) # HBox layout for heading frame, containing the pixmap # and label (title). HeaderFrameHLayout = QHBoxLayout(self.headerFrame) # 2 pixels around edges -- HeaderFrameHLayout.setMargin(PM_HEADER_FRAME_MARGIN) # 5 pixel between pixmap and label. -- HeaderFrameHLayout.setSpacing(PM_HEADER_FRAME_SPACING) # PropMgr icon. Set image by calling setHeaderIcon(). self.headerIcon = QLabel(self.headerFrame) self.headerIcon.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Fixed), QSizePolicy.Policy(QSizePolicy.Fixed))) self.headerIcon.setScaledContents(True) HeaderFrameHLayout.addWidget(self.headerIcon) # PropMgr header title text (a QLabel). self.headerTitle = QLabel(self.headerFrame) headerTitlePalette = self._getHeaderTitlePalette() self.headerTitle.setPalette(headerTitlePalette) self.headerTitle.setAlignment(PM_LABEL_LEFT_ALIGNMENT) # Assign header title font. self.headerTitle.setFont(self._getHeaderFont()) HeaderFrameHLayout.addWidget(self.headerTitle) self.vBoxLayout.addWidget(self.headerFrame) # Set header icon and title text. self.setHeaderIcon(iconPath) self.setHeaderTitle(title) def _getHeaderFont(self): """ Returns the font used for the header. @return: the header font @rtype: QFont """ font = QFont() font.setFamily(PM_HEADER_FONT) font.setPointSize(PM_HEADER_FONT_POINT_SIZE) font.setBold(PM_HEADER_FONT_BOLD) return font def setHeaderTitle(self, title): """ Set the Property Manager header title to string <title>. @param title: the title to insert in the header. @type title: str """ self.headerTitleText = title self.headerTitle.setText(title) def setHeaderIcon(self, iconPath): """ Set the Property Manager header icon. @param iconPath: the relative path to the PNG file containing the icon image. @type iconPath: str """ if not iconPath: return self.headerIcon.setPixmap(getpixmap(iconPath)) def _createSponsorButton(self): """ Creates the Property Manager sponsor button, which contains a QPushButton inside of a QGridLayout inside of a QFrame. The sponsor logo image is not loaded here. """ # Sponsor button (inside a frame) self.sponsorButtonContainer = QWidget(self) SponsorFrameGrid = QGridLayout(self.sponsorButtonContainer) SponsorFrameGrid.setMargin(PM_SPONSOR_FRAME_MARGIN) SponsorFrameGrid.setSpacing(PM_SPONSOR_FRAME_SPACING) # Has no effect. self.sponsor_btn = QToolButton(self.sponsorButtonContainer) self.sponsor_btn.setAutoRaise(True) self.connect(self.sponsor_btn, SIGNAL("clicked()"), self.open_sponsor_homepage) SponsorFrameGrid.addWidget(self.sponsor_btn, 0, 0, 1, 1) self.vBoxLayout.addWidget(self.sponsorButtonContainer) button_whatsthis_widget = self.sponsor_btn #bruce 070615 bugfix -- put tooltip & whatsthis on self.sponsor_btn, # not self. # [self.sponsorButtonContainer might be another possible place to put them.] button_whatsthis_widget.setWhatsThis("""<b>Sponsor Button</b> <p>When clicked, this sponsor logo will display a short description about a NanoEngineer-1 sponsor. This can be an official sponsor or credit given to a contributor that has helped code part or all of this command. A link is provided in the description to learn more about this sponsor.</p>""") button_whatsthis_widget.setToolTip("NanoEngineer-1 Sponsor Button") return def _createTopRowBtns(self): """ Creates the Done, Cancel, Preview, Restore Defaults and What's This buttons row at the top of the Property Manager. """ topBtnSize = QSize(22, 22) # button images should be 16 x 16, though. # Main "button group" widget (but it is not a QButtonGroup). self.pmTopRowBtns = QHBoxLayout() # This QHBoxLayout is (probably) not necessary. Try using just the frame # for the foundation. I think it should work. Mark 2007-05-30 # Horizontal spacer horizontalSpacer = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Minimum) # Widget containing all the buttons. self.topRowBtnsContainer = QWidget() # Create Hbox layout for main frame. topRowBtnsHLayout = QHBoxLayout(self.topRowBtnsContainer) topRowBtnsHLayout.setMargin(PM_TOPROWBUTTONS_MARGIN) topRowBtnsHLayout.setSpacing(PM_TOPROWBUTTONS_SPACING) # Set to True to center align the buttons in the PM if False: # Left aligns the buttons. topRowBtnsHLayout.addItem(horizontalSpacer) # Done (OK) button. self.done_btn = QToolButton(self.topRowBtnsContainer) self.done_btn.setIcon( geticon("ui/actions/Properties Manager/Done_16x16.png")) self.done_btn.setIconSize(topBtnSize) self.done_btn.setAutoRaise(True) self.connect(self.done_btn, SIGNAL("clicked()"), self.doneButtonClicked) self.done_btn.setToolTip("Done") topRowBtnsHLayout.addWidget(self.done_btn) # Cancel (Abort) button. self.cancel_btn = QToolButton(self.topRowBtnsContainer) self.cancel_btn.setIcon( geticon("ui/actions/Properties Manager/Abort_16x16.png")) self.cancel_btn.setIconSize(topBtnSize) self.cancel_btn.setAutoRaise(True) self.connect(self.cancel_btn, SIGNAL("clicked()"), self.cancelButtonClicked) self.cancel_btn.setToolTip("Cancel") topRowBtnsHLayout.addWidget(self.cancel_btn) #@ abort_btn deprecated. We still need it because modes use it. self.abort_btn = self.cancel_btn # Restore Defaults button. self.restore_defaults_btn = QToolButton(self.topRowBtnsContainer) self.restore_defaults_btn.setIcon( geticon("ui/actions/Properties Manager/Restore_16x16.png")) self.restore_defaults_btn.setIconSize(topBtnSize) self.restore_defaults_btn.setAutoRaise(True) self.connect(self.restore_defaults_btn, SIGNAL("clicked()"), self.restoreDefaultsButtonClicked) self.restore_defaults_btn.setToolTip("Restore Defaults") topRowBtnsHLayout.addWidget(self.restore_defaults_btn) # Preview (glasses) button. self.preview_btn = QToolButton(self.topRowBtnsContainer) self.preview_btn.setIcon( geticon("ui/actions/Properties Manager/Preview_16x16.png")) self.preview_btn.setIconSize(topBtnSize) self.preview_btn.setAutoRaise(True) self.connect(self.preview_btn, SIGNAL("clicked()"), self.previewButtonClicked) self.preview_btn.setToolTip("Preview") topRowBtnsHLayout.addWidget(self.preview_btn) # What's This (?) button. self.whatsthis_btn = QToolButton(self.topRowBtnsContainer) self.whatsthis_btn.setIcon( geticon("ui/actions/Properties Manager/WhatsThis_16x16.png")) self.whatsthis_btn.setIconSize(topBtnSize) self.whatsthis_btn.setAutoRaise(True) self.connect(self.whatsthis_btn, SIGNAL("clicked()"), self.whatsThisButtonClicked) self.whatsthis_btn.setToolTip("Enter \"What's This\" help mode") topRowBtnsHLayout.addWidget(self.whatsthis_btn) topRowBtnsHLayout.addItem(horizontalSpacer) # Create Button Row self.pmTopRowBtns.addWidget(self.topRowBtnsContainer) self.vBoxLayout.addLayout(self.pmTopRowBtns) # Add What's This for buttons. self.done_btn.setWhatsThis("""<b>Done</b> <p> <img source=\"ui/actions/Properties Manager/Done_16x16.png\"><br> Completes and/or exits the current command.</p>""") self.cancel_btn.setWhatsThis("""<b>Cancel</b> <p> <img source=\"ui/actions/Properties Manager/Abort_16x16.png\"><br> Cancels the current command.</p>""") self.restore_defaults_btn.setWhatsThis("""<b>Restore Defaults</b> <p><img source=\"ui/actions/Properties Manager/Restore_16x16.png\"><br> Restores the defaut values of the Property Manager.</p>""") self.preview_btn.setWhatsThis("""<b>Preview</b> <p> <img source=\"ui/actions/Properties Manager/Preview_16x16.png\"><br> Preview the structure based on current Property Manager settings. </p>""") self.whatsthis_btn.setWhatsThis("""<b>What's This</b> <p> <img source=\"ui/actions/Properties Manager/WhatsThis_16x16.png\"><br> This invokes \"What's This?\" help mode which is part of NanoEngineer-1's online help system, and provides users with information about the functionality and usage of a particular command button or widget. </p>""") return def hideTopRowButtons(self, pmButtonFlags=None): """ Hides one or more top row buttons using <pmButtonFlags>. Button flags not set will cause the button to be shown if currently hidden. @param pmButtonFlags: This enumerator describes the which buttons to hide, where: - PM_DONE_BUTTON = 1 - PM_CANCEL_BUTTON = 2 - PM_RESTORE_DEFAULTS_BUTTON = 4 - PM_PREVIEW_BUTTON = 8 - PM_WHATS_THIS_BUTTON = 16 - PM_ALL_BUTTONS = 31 @type pmButtonFlags: int """ if pmButtonFlags & PM_DONE_BUTTON: self.done_btn.hide() else: self.done_btn.show() if pmButtonFlags & PM_CANCEL_BUTTON: self.cancel_btn.hide() else: self.cancel_btn.show() if pmButtonFlags & PM_RESTORE_DEFAULTS_BUTTON: self.restore_defaults_btn.hide() else: self.restore_defaults_btn.show() if pmButtonFlags & PM_PREVIEW_BUTTON: self.preview_btn.hide() else: self.preview_btn.show() if pmButtonFlags & PM_WHATS_THIS_BUTTON: self.whatsthis_btn.hide() else: self.whatsthis_btn.show() def showTopRowButtons(self, pmButtonFlags=PM_ALL_BUTTONS): """ Shows one or more top row buttons using <pmButtonFlags>. Button flags not set will cause the button to be hidden if currently displayed. @param pmButtonFlags: this enumerator describes which buttons to display, where: - PM_DONE_BUTTON = 1 - PM_CANCEL_BUTTON = 2 - PM_RESTORE_DEFAULTS_BUTTON = 4 - PM_PREVIEW_BUTTON = 8 - PM_WHATS_THIS_BUTTON = 16 - PM_ALL_BUTTONS = 31 @type pmButtonFlags: int """ self.hideTopRowButtons(pmButtonFlags ^ PM_ALL_BUTTONS) def _getHeaderTitlePalette(self): """ Return a palette for header title (text) label. """ palette = QPalette() palette.setColor(QPalette.WindowText, pmHeaderTitleColor) return palette def doneButtonClicked(self): # note: never overridden, as of 080815 """ Slot for the Done button. """ self.ok_btn_clicked() def cancelButtonClicked(self): # note: never overridden, as of 080815 """ Slot for the Cancel button. """ self.cancel_btn_clicked() def restoreDefaultsButtonClicked(self): """ Slot for "Restore Defaults" button in the Property Manager. It is called each time the button is clicked. """ for widget in self._widgetList: if isinstance(widget, PM_GroupBox): widget.restoreDefault() def previewButtonClicked(self): """ Slot for the Preview button. """ self.preview_btn_clicked() def whatsThisButtonClicked(self): """ Slot for the What's This button. """ QWhatsThis.enterWhatsThisMode() # default implementations for subclasses # [bruce 080815 pulled these in from subclasses] def ok_btn_clicked(self): """ Implements Done button. Called by its slot method in PM_Dialog. [subclasses can override as needed] """ self.win.toolsDone() def cancel_btn_clicked(self): """ Implements Cancel button. Called by its slot method in PM_Dialog. [subclasses can override as needed] """ # Note: many subclasses override this to call self.w.toolsDone # (rather than toolsCancel). This should be cleaned up # so those overrides are not needed. (Maybe they are already # not needed.) [bruce 080815 comment] self.win.toolsCancel() pass
class PM_Dialog( QDialog, SponsorableMixin ): """ The PM_Dialog class is the base class for Property Manager dialogs. [To make a PM class from this superclass, subclass it to customize the widget set and add behavior. You must also provide certain methods that used to be provided by GeneratorBaseClass, including ok_btn_clicked and several others, including at least some defined by SponsorableMixin (open_sponsor_homepage, setSponsor). This set of requirements may be cleaned up.] """ headerTitleText = "" # The header title text. _widgetList = [] # A list of all group boxes in this PM dialog, # including the message group box # (but not header, sponsor button, etc.) _groupBoxCount = 0 # Number of PM_GroupBoxes in this PM dialog _lastGroupBox = None # The last PM_GroupBox in this PM dialog # (i.e. the most recent PM_GroupBox added) def __init__(self, name, iconPath = "", title = "" ): """ Property Manager constructor. @param name: the name to assign the property manager dialog object. @type name: str @param iconPath: the relative path for the icon (PNG image) that appears in the header. @type iconPath: str @param title: the title that appears in the header. @type title: str """ QDialog.__init__(self) self.setObjectName(name) self._widgetList = [] # Main pallete for PropMgr. self.setPalette(QPalette(pmColor)) # Main vertical layout for PropMgr. self.vBoxLayout = QVBoxLayout(self) self.vBoxLayout.setMargin(PM_MAINVBOXLAYOUT_MARGIN) self.vBoxLayout.setSpacing(PM_MAINVBOXLAYOUT_SPACING) # Add PropMgr's header, sponsor button, top row buttons and (hidden) # message group box. self._createHeader(iconPath, title) self._createSponsorButton() self._createTopRowBtns() # Create top buttons row self.MessageGroupBox = PM_MessageGroupBox(self) # Keep the line below around; it might be useful. # I may want to use it now that I understand it. # Mark 2007-05-17. #QMetaObject.connectSlotsByName(self) self._addGroupBoxes() try: self._addWhatsThisText() except: print_compact_traceback("Error loading whatsthis text for this " "property manager: ") try: self._addToolTipText() except: print_compact_traceback("Error loading tool tip text for this " "property manager: ") #The following attr is used for comparison in method #'_update_UI_wanted_as_something_changed' self._previous_all_change_indicators = None def update_UI(self): """ Update whatever is shown in this PM based on current state of the rest of the system, especially the state of self.command and of the model it shows. This is part of the PM API required by baseCommand, since it's called by baseCommand.command_update_UI. @note: Overridden in Command_PropertyManager, but should not be overridden in its subclasses. See that method's docstring for details. """ #bruce 081125 defined this here, to fix bugs in example commands return def keyPressEvent(self, event): """ Handles keyPress event. @note: Subclasses should carefully override this. Note that the default implementation doesn't permit ESC key as a way to close the PM_dialog. (This is typically desirable for Property Managers.) If any subclass needs to implement the key press, they should first call this method (i.e. superclass.keyPressEvent) and then implement specific code that closes the dialog when ESC key is pressed. """ key = event.key() # Don't use ESC key to close the PM dialog. Fixes bug 2596 if key == Qt.Key_Escape: pass else: QDialog.keyPressEvent(self, event) return def _addGroupBoxes(self): """ Add various group boxes to this PM. Subclasses should override this method. """ pass def _addWhatsThisText(self): """ Add what's this text. Subclasses should override this method. """ pass def _addToolTipText(self): """ Add Tool tip text. Subclasses should override this method. """ pass def show(self): """ Shows the Property Manager. """ self.setSponsor() # Show or hide the sponsor logo based on whether the user gave # permission to download sponsor logos. if env.prefs[sponsor_download_permission_prefs_key]: self.sponsorButtonContainer.show() else: self.sponsorButtonContainer.hide() if not self.pw or self: self.pw = self.win.activePartWindow() self.pw.updatePropertyManagerTab(self) self.pw.pwProjectTabWidget.setCurrentIndex( self.pw.pwProjectTabWidget.indexOf(self)) # Show the default message whenever we open the Property Manager. self.MessageGroupBox.MessageTextEdit.restoreDefault() def open(self, pm): """ Closes the current property manager (if any) and opens the property manager I{pm}. @param pm: The property manager to open. @type pm: L{PM_Dialog} or QDialog (of legacy PMs) @attention: This method is a temporary workaround for "Insert > Plane". The current command should always be responsible for (re)opening its own PM via self.show(). @see: L{show()} """ if 1: commandSequencer = self.win.commandSequencer #bruce 071008 commandName = commandSequencer.currentCommand.commandName # that's an internal name, but this is just for a debug print print "PM_Dialog.open(): Reopening the PM for command:", commandName # The following line of code is buggy when you, for instance, exit a PM # and reopen the previous one. It sends the disconnect signal twice to # the PM that is just closed.So disabling this line -- Ninad 2007-12-04 ##self.close() # Just in case there is another PM open. self.pw = self.win.activePartWindow() self.pw.updatePropertyManagerTab(pm) try: pm.setSponsor() except: print """PM_Dialog.open(): pm has no attribute 'setSponsor()' ignoring.""" self.pw.pwProjectTabWidget.setCurrentIndex( self.pw.pwProjectTabWidget.indexOf(pm)) def close(self): """ Closes the Property Manager. """ if not self.pw: self.pw = self.win.activePartWindow() self.pw.pwProjectTabWidget.setCurrentIndex(0) ## try: [bruce 071018 moved this lower, since errmsg only covers attr] pmWidget = self.pw.propertyManagerScrollArea.widget() if debug_flags.atom_debug: #bruce 071018 "atom_debug fyi: %r is closing %r (can they differ?)" % \ (self, pmWidget) try: pmWidget.update_props_if_needed_before_closing except AttributeError: if 1 or debug_flags.atom_debug: msg1 = "Last PropMgr %r doesn't have method" % pmWidget msg2 = " update_props_if_needed_before_closing. That's" msg3 = " OK (for now, only implemented for Plane PM). " msg4 = "Ignoring Exception: " print_compact_traceback(msg1 + msg2 + msg3 + msg4) #bruce 071018: I'll define that method in PM_Dialog # so this message should become rare or nonexistent, # so I'll make it happen whether or not atom_debug. else: pmWidget.update_props_if_needed_before_closing() self.pw.pwProjectTabWidget.removeTab( self.pw.pwProjectTabWidget.indexOf( self.pw.propertyManagerScrollArea)) if self.pw.propertyManagerTab: self.pw.propertyManagerTab = None def update_props_if_needed_before_closing(self): # bruce 071018 default implem """ Subclasses can override this to update some cosmetic properties of their associated model objects before closing self (the Property Manager). """ pass def updateMessage(self, msg = ''): """ Updates the message box with an informative message @param msg: Message to be displayed in the Message groupbox of the property manager @type msg: string """ self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault = False, minLines = 5) def _createHeader(self, iconPath, title): """ Creates the Property Manager header, which contains an icon (a QLabel with a pixmap) and white text (a QLabel with text). @param iconPath: The relative path for the icon (PNG image) that appears in the header. @type iconPath: str @param title: The title that appears in the header. @type title: str """ # Heading frame (dark gray), which contains # a pixmap and (white) heading text. self.headerFrame = QFrame(self) self.headerFrame.setFrameShape(QFrame.NoFrame) self.headerFrame.setFrameShadow(QFrame.Plain) self.headerFrame.setPalette(QPalette(pmHeaderFrameColor)) self.headerFrame.setAutoFillBackground(True) # HBox layout for heading frame, containing the pixmap # and label (title). HeaderFrameHLayout = QHBoxLayout(self.headerFrame) # 2 pixels around edges -- HeaderFrameHLayout.setMargin(PM_HEADER_FRAME_MARGIN) # 5 pixel between pixmap and label. -- HeaderFrameHLayout.setSpacing(PM_HEADER_FRAME_SPACING) # PropMgr icon. Set image by calling setHeaderIcon(). self.headerIcon = QLabel(self.headerFrame) self.headerIcon.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Fixed), QSizePolicy.Policy(QSizePolicy.Fixed))) self.headerIcon.setScaledContents(True) HeaderFrameHLayout.addWidget(self.headerIcon) # PropMgr header title text (a QLabel). self.headerTitle = QLabel(self.headerFrame) headerTitlePalette = self._getHeaderTitlePalette() self.headerTitle.setPalette(headerTitlePalette) self.headerTitle.setAlignment(PM_LABEL_LEFT_ALIGNMENT) # Assign header title font. self.headerTitle.setFont(self._getHeaderFont()) HeaderFrameHLayout.addWidget(self.headerTitle) self.vBoxLayout.addWidget(self.headerFrame) # Set header icon and title text. self.setHeaderIcon(iconPath) self.setHeaderTitle(title) def _getHeaderFont(self): """ Returns the font used for the header. @return: the header font @rtype: QFont """ font = QFont() font.setFamily(PM_HEADER_FONT) font.setPointSize(PM_HEADER_FONT_POINT_SIZE) font.setBold(PM_HEADER_FONT_BOLD) return font def setHeaderTitle(self, title): """ Set the Property Manager header title to string <title>. @param title: the title to insert in the header. @type title: str """ self.headerTitleText = title self.headerTitle.setText(title) def setHeaderIcon(self, iconPath): """ Set the Property Manager header icon. @param iconPath: the relative path to the PNG file containing the icon image. @type iconPath: str """ if not iconPath: return self.headerIcon.setPixmap(getpixmap(iconPath)) def _createSponsorButton(self): """ Creates the Property Manager sponsor button, which contains a QPushButton inside of a QGridLayout inside of a QFrame. The sponsor logo image is not loaded here. """ # Sponsor button (inside a frame) self.sponsorButtonContainer = QWidget(self) SponsorFrameGrid = QGridLayout(self.sponsorButtonContainer) SponsorFrameGrid.setMargin(PM_SPONSOR_FRAME_MARGIN) SponsorFrameGrid.setSpacing(PM_SPONSOR_FRAME_SPACING) # Has no effect. self.sponsor_btn = QToolButton(self.sponsorButtonContainer) self.sponsor_btn.setAutoRaise(True) self.connect(self.sponsor_btn, SIGNAL("clicked()"), self.open_sponsor_homepage) SponsorFrameGrid.addWidget(self.sponsor_btn, 0, 0, 1, 1) self.vBoxLayout.addWidget(self.sponsorButtonContainer) button_whatsthis_widget = self.sponsor_btn #bruce 070615 bugfix -- put tooltip & whatsthis on self.sponsor_btn, # not self. # [self.sponsorButtonContainer might be another possible place to put them.] button_whatsthis_widget.setWhatsThis("""<b>Sponsor Button</b> <p>When clicked, this sponsor logo will display a short description about a NanoEngineer-1 sponsor. This can be an official sponsor or credit given to a contributor that has helped code part or all of this command. A link is provided in the description to learn more about this sponsor.</p>""") button_whatsthis_widget.setToolTip("NanoEngineer-1 Sponsor Button") return def _createTopRowBtns(self): """ Creates the Done, Cancel, Preview, Restore Defaults and What's This buttons row at the top of the Property Manager. """ topBtnSize = QSize(22, 22) # button images should be 16 x 16, though. # Main "button group" widget (but it is not a QButtonGroup). self.pmTopRowBtns = QHBoxLayout() # This QHBoxLayout is (probably) not necessary. Try using just the frame # for the foundation. I think it should work. Mark 2007-05-30 # Horizontal spacer horizontalSpacer = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Minimum) # Widget containing all the buttons. self.topRowBtnsContainer = QWidget() # Create Hbox layout for main frame. topRowBtnsHLayout = QHBoxLayout(self.topRowBtnsContainer) topRowBtnsHLayout.setMargin(PM_TOPROWBUTTONS_MARGIN) topRowBtnsHLayout.setSpacing(PM_TOPROWBUTTONS_SPACING) # Set to True to center align the buttons in the PM if False: # Left aligns the buttons. topRowBtnsHLayout.addItem(horizontalSpacer) # Done (OK) button. self.done_btn = QToolButton(self.topRowBtnsContainer) self.done_btn.setIcon( geticon("ui/actions/Properties Manager/Done_16x16.png")) self.done_btn.setIconSize(topBtnSize) self.done_btn.setAutoRaise(True) self.connect(self.done_btn, SIGNAL("clicked()"), self.doneButtonClicked) self.done_btn.setToolTip("Done") topRowBtnsHLayout.addWidget(self.done_btn) # Cancel (Abort) button. self.cancel_btn = QToolButton(self.topRowBtnsContainer) self.cancel_btn.setIcon( geticon("ui/actions/Properties Manager/Abort_16x16.png")) self.cancel_btn.setIconSize(topBtnSize) self.cancel_btn.setAutoRaise(True) self.connect(self.cancel_btn, SIGNAL("clicked()"), self.cancelButtonClicked) self.cancel_btn.setToolTip("Cancel") topRowBtnsHLayout.addWidget(self.cancel_btn) #@ abort_btn deprecated. We still need it because modes use it. self.abort_btn = self.cancel_btn # Restore Defaults button. self.restore_defaults_btn = QToolButton(self.topRowBtnsContainer) self.restore_defaults_btn.setIcon( geticon("ui/actions/Properties Manager/Restore_16x16.png")) self.restore_defaults_btn.setIconSize(topBtnSize) self.restore_defaults_btn.setAutoRaise(True) self.connect(self.restore_defaults_btn, SIGNAL("clicked()"), self.restoreDefaultsButtonClicked) self.restore_defaults_btn.setToolTip("Restore Defaults") topRowBtnsHLayout.addWidget(self.restore_defaults_btn) # Preview (glasses) button. self.preview_btn = QToolButton(self.topRowBtnsContainer) self.preview_btn.setIcon( geticon("ui/actions/Properties Manager/Preview_16x16.png")) self.preview_btn.setIconSize(topBtnSize) self.preview_btn.setAutoRaise(True) self.connect(self.preview_btn, SIGNAL("clicked()"), self.previewButtonClicked) self.preview_btn.setToolTip("Preview") topRowBtnsHLayout.addWidget(self.preview_btn) # What's This (?) button. self.whatsthis_btn = QToolButton(self.topRowBtnsContainer) self.whatsthis_btn.setIcon( geticon("ui/actions/Properties Manager/WhatsThis_16x16.png")) self.whatsthis_btn.setIconSize(topBtnSize) self.whatsthis_btn.setAutoRaise(True) self.connect(self.whatsthis_btn, SIGNAL("clicked()"), self.whatsThisButtonClicked) self.whatsthis_btn.setToolTip("Enter \"What's This\" help mode") topRowBtnsHLayout.addWidget(self.whatsthis_btn) topRowBtnsHLayout.addItem(horizontalSpacer) # Create Button Row self.pmTopRowBtns.addWidget(self.topRowBtnsContainer) self.vBoxLayout.addLayout(self.pmTopRowBtns) # Add What's This for buttons. self.done_btn.setWhatsThis("""<b>Done</b> <p> <img source=\"ui/actions/Properties Manager/Done_16x16.png\"><br> Completes and/or exits the current command.</p>""") self.cancel_btn.setWhatsThis("""<b>Cancel</b> <p> <img source=\"ui/actions/Properties Manager/Abort_16x16.png\"><br> Cancels the current command.</p>""") self.restore_defaults_btn.setWhatsThis("""<b>Restore Defaults</b> <p><img source=\"ui/actions/Properties Manager/Restore_16x16.png\"><br> Restores the defaut values of the Property Manager.</p>""") self.preview_btn.setWhatsThis("""<b>Preview</b> <p> <img source=\"ui/actions/Properties Manager/Preview_16x16.png\"><br> Preview the structure based on current Property Manager settings. </p>""") self.whatsthis_btn.setWhatsThis("""<b>What's This</b> <p> <img source=\"ui/actions/Properties Manager/WhatsThis_16x16.png\"><br> This invokes \"What's This?\" help mode which is part of NanoEngineer-1's online help system, and provides users with information about the functionality and usage of a particular command button or widget. </p>""") return def hideTopRowButtons(self, pmButtonFlags = None): """ Hides one or more top row buttons using <pmButtonFlags>. Button flags not set will cause the button to be shown if currently hidden. @param pmButtonFlags: This enumerator describes the which buttons to hide, where: - PM_DONE_BUTTON = 1 - PM_CANCEL_BUTTON = 2 - PM_RESTORE_DEFAULTS_BUTTON = 4 - PM_PREVIEW_BUTTON = 8 - PM_WHATS_THIS_BUTTON = 16 - PM_ALL_BUTTONS = 31 @type pmButtonFlags: int """ if pmButtonFlags & PM_DONE_BUTTON: self.done_btn.hide() else: self.done_btn.show() if pmButtonFlags & PM_CANCEL_BUTTON: self.cancel_btn.hide() else: self.cancel_btn.show() if pmButtonFlags & PM_RESTORE_DEFAULTS_BUTTON: self.restore_defaults_btn.hide() else: self.restore_defaults_btn.show() if pmButtonFlags & PM_PREVIEW_BUTTON: self.preview_btn.hide() else: self.preview_btn.show() if pmButtonFlags & PM_WHATS_THIS_BUTTON: self.whatsthis_btn.hide() else: self.whatsthis_btn.show() def showTopRowButtons(self, pmButtonFlags = PM_ALL_BUTTONS): """ Shows one or more top row buttons using <pmButtonFlags>. Button flags not set will cause the button to be hidden if currently displayed. @param pmButtonFlags: this enumerator describes which buttons to display, where: - PM_DONE_BUTTON = 1 - PM_CANCEL_BUTTON = 2 - PM_RESTORE_DEFAULTS_BUTTON = 4 - PM_PREVIEW_BUTTON = 8 - PM_WHATS_THIS_BUTTON = 16 - PM_ALL_BUTTONS = 31 @type pmButtonFlags: int """ self.hideTopRowButtons(pmButtonFlags ^ PM_ALL_BUTTONS) def _getHeaderTitlePalette(self): """ Return a palette for header title (text) label. """ palette = QPalette() palette.setColor(QPalette.WindowText, pmHeaderTitleColor) return palette def doneButtonClicked(self): # note: never overridden, as of 080815 """ Slot for the Done button. """ self.ok_btn_clicked() def cancelButtonClicked(self): # note: never overridden, as of 080815 """ Slot for the Cancel button. """ self.cancel_btn_clicked() def restoreDefaultsButtonClicked(self): """ Slot for "Restore Defaults" button in the Property Manager. It is called each time the button is clicked. """ for widget in self._widgetList: if isinstance(widget, PM_GroupBox): widget.restoreDefault() def previewButtonClicked(self): """ Slot for the Preview button. """ self.preview_btn_clicked() def whatsThisButtonClicked(self): """ Slot for the What's This button. """ QWhatsThis.enterWhatsThisMode() # default implementations for subclasses # [bruce 080815 pulled these in from subclasses] def ok_btn_clicked(self): """ Implements Done button. Called by its slot method in PM_Dialog. [subclasses can override as needed] """ self.win.toolsDone() def cancel_btn_clicked(self): """ Implements Cancel button. Called by its slot method in PM_Dialog. [subclasses can override as needed] """ # Note: many subclasses override this to call self.w.toolsDone # (rather than toolsCancel). This should be cleaned up # so those overrides are not needed. (Maybe they are already # not needed.) [bruce 080815 comment] self.win.toolsCancel() pass