Beispiel #1
0
    def _createToolButtonWidget(self, parent):
        """
        @see: self.createWidget()
        """
        btn = QToolButton(parent)
        btn.setAutoFillBackground(True)
        btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        btn.setMinimumWidth(75)
        btn.setMaximumWidth(75)
        btn.setMinimumHeight(62)
        btn.setAutoRaise(True)

        btn.setCheckable(True)
        btn.setDefaultAction(self)

        text = truncateText(self.text())
        btn.setText(text)

        if self._toolButtonPalette:
            btn.setPalette(self._toolButtonPalette)

        #@@@ ninad070125 The following function
        #adds a newline character after each word in toolbutton text.
        #but the changes are reflected only on 'mode' toolbuttons
        #on the flyout toolbar (i.e.only Checkable buttons..don't know
        #why. Disabling its use for now.
        debug_wrapText = False

        if debug_wrapText:
            text = wrapToolButtonText(action.text())
            if text:
                action.setText(text)

        return btn
Beispiel #2
0
    class SliderControl(QObject):
        """This class implements a slider control for a colormap"""

        def __init__(self, name, value, minval, maxval, step, format="%s: %.1f"):
            QObject.__init__(self)
            self.name, self.value, self.minval, self.maxval, self.step, self.format = \
                name, value, minval, maxval, step, format
            self._default = value
            self._wlabel = None

        def makeControlWidgets(self, parent, gridlayout, row, column):
            toprow = QWidget(parent)
            gridlayout.addWidget(toprow, row * 2, column)
            top_lo = QHBoxLayout(toprow)
            top_lo.setContentsMargins(0, 0, 0, 0)
            self._wlabel = QLabel(self.format % (self.name, self.value), toprow)
            top_lo.addWidget(self._wlabel)
            self._wreset = QToolButton(toprow)
            self._wreset.setText("reset")
            self._wreset.setToolButtonStyle(Qt.ToolButtonTextOnly)
            self._wreset.setAutoRaise(True)
            self._wreset.setEnabled(self.value != self._default)
            QObject.connect(self._wreset, SIGNAL("clicked()"), self._resetValue)
            top_lo.addWidget(self._wreset)
            self._wslider = QwtSlider(parent)
            # This works around a stupid bug in QwtSliders -- see comments on histogram zoom wheel above
            self._wslider_timer = QTimer(parent)
            self._wslider_timer.setSingleShot(True)
            self._wslider_timer.setInterval(500)
            QObject.connect(self._wslider_timer, SIGNAL("timeout()"), self.setValue)
            gridlayout.addWidget(self._wslider, row * 2 + 1, column)
            self._wslider.setRange(self.minval, self.maxval)
            self._wslider.setStep(self.step)
            self._wslider.setValue(self.value)
            self._wslider.setTracking(False)
            QObject.connect(self._wslider, SIGNAL("valueChanged(double)"), self.setValue)
            QObject.connect(self._wslider, SIGNAL("sliderMoved(double)"), self._previewValue)

        def _resetValue(self):
            self._wslider.setValue(self._default)
            self.setValue(self._default)

        def setValue(self, value=None, notify=True):
            # only update widgets if already created
            self.value = value
            if self._wlabel is not None:
                if value is None:
                    self.value = value = self._wslider.value()
                self._wreset.setEnabled(value != self._default)
                self._wlabel.setText(self.format % (self.name, self.value))
                # stop timer if being called to finalize the change in value
                if notify:
                    self._wslider_timer.stop()
                    self.emit(SIGNAL("valueChanged"), self.value)

        def _previewValue(self, value):
            self.setValue(notify=False)
            self._wslider_timer.start(500)
            self.emit(SIGNAL("valueMoved"), self.value)
Beispiel #3
0
    def __init__(self, parent, rc, imgman):
        """An ImageControlDialog is initialized with a parent widget, a RenderControl object,
        and an ImageManager object"""
        QDialog.__init__(self, parent)
        image = rc.image
        self.setWindowTitle("%s: Colour Controls" % image.name)
        self.setWindowIcon(pixmaps.colours.icon())
        self.setModal(False)
        self.image = image
        self._rc = rc
        self._imgman = imgman
        self._currier = PersistentCurrier()

        # init internal state
        self._prev_range = self._display_range = None, None
        self._hist = None
        self._geometry = None

        # create layouts
        lo0 = QVBoxLayout(self)
        #    lo0.setContentsMargins(0,0,0,0)

        # histogram plot
        whide = self.makeButton("Hide", self.hide, width=128)
        whide.setShortcut(Qt.Key_F9)
        lo0.addWidget(Separator(self, "Histogram and ITF", extra_widgets=[whide]))
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        self._histplot = QwtPlot(self)
        self._histplot.setAutoDelete(False)
        lo1.addWidget(self._histplot, 1)
        lo2 = QHBoxLayout()
        lo2.setContentsMargins(0, 0, 0, 0)
        lo2.setSpacing(2)
        lo0.addLayout(lo2)
        lo0.addLayout(lo1)
        self._wautozoom = QCheckBox("autozoom", self)
        self._wautozoom.setChecked(True)
        self._wautozoom.setToolTip("""<P>If checked, then the histrogram plot will zoom in automatically when
      you narrow the current intensity range.</P>""")
        self._wlogy = QCheckBox("log Y", self)
        self._wlogy.setChecked(True)
        self._ylogscale = True
        self._wlogy.setToolTip(
            """<P>If checked, a log-scale Y axis is used for the histogram plot instead of a linear one.""")
        QObject.connect(self._wlogy, SIGNAL("toggled(bool)"), self._setHistLogScale)
        self._whistunzoom = self.makeButton("", self._unzoomHistogram, icon=pixmaps.full_range.icon())
        self._whistzoomout = self.makeButton("-", self._currier.curry(self._zoomHistogramByFactor, math.sqrt(.1)))
        self._whistzoomin = self.makeButton("+", self._currier.curry(self._zoomHistogramByFactor, math.sqrt(10)))
        self._whistzoomin.setToolTip("""<P>Click to zoom into the histogram plot by one step. This does not
      change the current intensity range.</P>""")
        self._whistzoomout.setToolTip("""<P>Click to zoom out of the histogram plot by one step. This does not
      change the current intensity range.</P>""")
        self._whistunzoom.setToolTip("""<P>Click to reset the histogram plot back to its full extent.
      This does not change the current intensity range.</P>""")
        self._whistzoom = QwtWheel(self)
        self._whistzoom.setOrientation(Qt.Horizontal)
        self._whistzoom.setMaximumWidth(80)
        self._whistzoom.setRange(10, 0)
        self._whistzoom.setStep(0.1)
        self._whistzoom.setTickCnt(30)
        self._whistzoom.setTracking(False)
        QObject.connect(self._whistzoom, SIGNAL("valueChanged(double)"), self._zoomHistogramFinalize)
        QObject.connect(self._whistzoom, SIGNAL("sliderMoved(double)"), self._zoomHistogramPreview)
        self._whistzoom.setToolTip("""<P>Use this wheel control to zoom in/out of the histogram plot.
      This does not change the current intensity range.
      Note that the zoom wheel should also respond to your mouse wheel, if you have one.</P>""")
        # This works around a stupid bug in QwtSliders -- when using the mousewheel, only sliderMoved() signals are emitted,
        # with no final  valueChanged(). If we want to do a fast preview of something on sliderMoved(), and a "slow" final
        # step on valueChanged(), we're in trouble. So we start a timer on sliderMoved(), and if the timer expires without
        # anything else happening, do a valueChanged().
        # Here we use a timer to call zoomHistogramFinalize() w/o an argument.
        self._whistzoom_timer = QTimer(self)
        self._whistzoom_timer.setSingleShot(True)
        self._whistzoom_timer.setInterval(500)
        QObject.connect(self._whistzoom_timer, SIGNAL("timeout()"), self._zoomHistogramFinalize)
        # set same size for all buttons and controls
        width = 24
        for w in self._whistunzoom, self._whistzoomin, self._whistzoomout:
            w.setMinimumSize(width, width)
            w.setMaximumSize(width, width)
        self._whistzoom.setMinimumSize(80, width)
        self._wlab_histpos_text = "(hover here for help)"
        self._wlab_histpos = QLabel(self._wlab_histpos_text, self)
        self._wlab_histpos.setToolTip("""
      <P>The plot shows a histogram of either the full image or its selected subset
      (as per the "Data subset" section below).</P>
      <P>The current intensity range is indicated by the grey box
      in the plot.</P>
      <P>Use the left mouse button to change the low intensity limit, and the right
      button (on Macs, use Ctrl-click) to change the high limit.</P>
      <P>Use Shift with the left mouse button to zoom into an area of the histogram,
      or else use the "zoom wheel" control or the plus/minus toolbuttons above the histogram to zoom in or out.
      To zoom back out to the full extent of the histogram, click on the rightmost button above the histogram.</P>
      """)
        lo2.addWidget(self._wlab_histpos, 1)
        lo2.addWidget(self._wautozoom)
        lo2.addWidget(self._wlogy, 0)
        lo2.addWidget(self._whistzoomin, 0)
        lo2.addWidget(self._whistzoom, 0)
        lo2.addWidget(self._whistzoomout, 0)
        lo2.addWidget(self._whistunzoom, 0)
        self._zooming_histogram = False

        sliced_axes = rc.slicedAxes()
        dprint(1, "sliced axes are", sliced_axes)
        self._stokes_axis = None

        # subset indication
        lo0.addWidget(Separator(self, "Data subset"))
        # sliced axis selectors
        self._wslicers = []
        if sliced_axes:
            lo1 = QHBoxLayout()
            lo1.setContentsMargins(0, 0, 0, 0)
            lo1.setSpacing(2)
            lo0.addLayout(lo1)
            lo1.addWidget(QLabel("Current slice:  ", self))
            for i, (iextra, name, labels) in enumerate(sliced_axes):
                lo1.addWidget(QLabel("%s:" % name, self))
                if name == "STOKES":
                    self._stokes_axis = iextra
                # add controls
                wslicer = QComboBox(self)
                self._wslicers.append(wslicer)
                wslicer.addItems(labels)
                wslicer.setToolTip("""<P>Selects current slice along the %s axis.</P>""" % name)
                wslicer.setCurrentIndex(self._rc.currentSlice()[iextra])
                QObject.connect(wslicer, SIGNAL("activated(int)"), self._currier.curry(self._rc.changeSlice, iextra))
                lo2 = QVBoxLayout()
                lo1.addLayout(lo2)
                lo2.setContentsMargins(0, 0, 0, 0)
                lo2.setSpacing(0)
                wminus = QToolButton(self)
                wminus.setArrowType(Qt.UpArrow)
                QObject.connect(wminus, SIGNAL("clicked()"), self._currier.curry(self._rc.incrementSlice, iextra, 1))
                if i == 0:
                    wminus.setShortcut(Qt.SHIFT + Qt.Key_F7)
                elif i == 1:
                    wminus.setShortcut(Qt.SHIFT + Qt.Key_F8)
                wplus = QToolButton(self)
                wplus.setArrowType(Qt.DownArrow)
                QObject.connect(wplus, SIGNAL("clicked()"), self._currier.curry(self._rc.incrementSlice, iextra, -1))
                if i == 0:
                    wplus.setShortcut(Qt.Key_F7)
                elif i == 1:
                    wplus.setShortcut(Qt.Key_F8)
                wminus.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                wplus.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                sz = QSize(12, 8)
                wminus.setMinimumSize(sz)
                wplus.setMinimumSize(sz)
                wminus.resize(sz)
                wplus.resize(sz)
                lo2.addWidget(wminus)
                lo2.addWidget(wplus)
                lo1.addWidget(wslicer)
                lo1.addSpacing(5)
            lo1.addStretch(1)
        # subset indicator
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.setSpacing(2)
        lo0.addLayout(lo1)
        self._wlab_subset = QLabel("Subset: xxx", self)
        self._wlab_subset.setToolTip("""<P>This indicates the current data subset to which the histogram
      and the stats given here apply. Use the "Reset to" control on the right to change the
      current subset and recompute the histogram and stats.</P>""")
        lo1.addWidget(self._wlab_subset, 1)

        self._wreset_full = self.makeButton("\u2192 full", self._rc.setFullSubset)
        lo1.addWidget(self._wreset_full)
        if sliced_axes:
            #      if self._stokes_axis is not None and len(sliced_axes)>1:
            #        self._wreset_stokes = self.makeButton(u"\u21920Stokes",self._rc.setFullSubset)
            self._wreset_slice = self.makeButton("\u2192 slice", self._rc.setSliceSubset)
            lo1.addWidget(self._wreset_slice)
        else:
            self._wreset_slice = None

        # min/max controls
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        self._wlab_stats = QLabel(self)
        lo1.addWidget(self._wlab_stats, 0)
        self._wmore_stats = self.makeButton("more...", self._showMeanStd)
        self._wlab_stats.setMinimumHeight(self._wmore_stats.height())
        lo1.addWidget(self._wmore_stats, 0)
        lo1.addStretch(1)

        # intensity controls
        lo0.addWidget(Separator(self, "Intensity mapping"))
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.setSpacing(2)
        lo0.addLayout(lo1, 0)
        self._range_validator = FloatValidator(self)
        self._wrange = QLineEdit(self), QLineEdit(self)
        self._wrange[0].setToolTip("""<P>This is the low end of the intensity range.</P>""")
        self._wrange[1].setToolTip("""<P>This is the high end of the intensity range.</P>""")
        for w in self._wrange:
            w.setValidator(self._range_validator)
            QObject.connect(w, SIGNAL("editingFinished()"), self._changeDisplayRange)
        lo1.addWidget(QLabel("low:", self), 0)
        lo1.addWidget(self._wrange[0], 1)
        self._wrangeleft0 = self.makeButton("\u21920", self._setZeroLeftLimit, width=32)
        self._wrangeleft0.setToolTip("""<P>Click this to set the low end of the intensity range to 0.</P>""")
        lo1.addWidget(self._wrangeleft0, 0)
        lo1.addSpacing(8)
        lo1.addWidget(QLabel("high:", self), 0)
        lo1.addWidget(self._wrange[1], 1)
        lo1.addSpacing(8)
        self._wrange_full = self.makeButton(None, self._setHistDisplayRange, icon=pixmaps.intensity_graph.icon())
        lo1.addWidget(self._wrange_full)
        self._wrange_full.setToolTip(
            """<P>Click this to reset the intensity range to the current extent of the histogram plot.</P>""")
        # add menu for display range
        range_menu = QMenu(self)
        wrange_menu = QToolButton(self)
        wrange_menu.setText("Reset to")
        wrange_menu.setToolTip("""<P>Use this to reset the intensity range to various pre-defined settings.</P>""")
        lo1.addWidget(wrange_menu)
        self._qa_range_full = range_menu.addAction(pixmaps.full_range.icon(), "Full subset",
                                                   self._rc.resetSubsetDisplayRange)
        self._qa_range_hist = range_menu.addAction(pixmaps.intensity_graph.icon(), "Current histogram limits",
                                                   self._setHistDisplayRange)
        for percent in (99.99, 99.9, 99.5, 99, 98, 95):
            range_menu.addAction("%g%%" % percent, self._currier.curry(self._changeDisplayRangeToPercent, percent))
        wrange_menu.setMenu(range_menu)
        wrange_menu.setPopupMode(QToolButton.InstantPopup)

        lo1 = QGridLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        self._wimap = QComboBox(self)
        lo1.addWidget(QLabel("Intensity policy:", self), 0, 0)
        lo1.addWidget(self._wimap, 1, 0)
        self._wimap.addItems(rc.getIntensityMapNames())
        QObject.connect(self._wimap, SIGNAL("currentIndexChanged(int)"), self._rc.setIntensityMapNumber)
        self._wimap.setToolTip("""<P>Use this to change the type of the intensity transfer function (ITF).</P>""")

        # log cycles control
        lo1.setColumnStretch(1, 1)
        self._wlogcycles_label = QLabel("Log cycles: ", self)
        lo1.addWidget(self._wlogcycles_label, 0, 1)
        #    self._wlogcycles = QwtWheel(self)
        #    self._wlogcycles.setTotalAngle(360)
        self._wlogcycles = QwtSlider(self)
        self._wlogcycles.setToolTip(
            """<P>Use this to change the log-base for the logarithmic intensity transfer function (ITF).</P>""")
        # This works around a stupid bug in QwtSliders -- see comments on histogram zoom wheel above
        self._wlogcycles_timer = QTimer(self)
        self._wlogcycles_timer.setSingleShot(True)
        self._wlogcycles_timer.setInterval(500)
        QObject.connect(self._wlogcycles_timer, SIGNAL("timeout()"), self._setIntensityLogCycles)
        lo1.addWidget(self._wlogcycles, 1, 1)
        self._wlogcycles.setRange(1., 10)
        self._wlogcycles.setStep(0.1)
        self._wlogcycles.setTracking(False)
        QObject.connect(self._wlogcycles, SIGNAL("valueChanged(double)"), self._setIntensityLogCycles)
        QObject.connect(self._wlogcycles, SIGNAL("sliderMoved(double)"), self._previewIntensityLogCycles)
        self._updating_imap = False

        # lock intensity map
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        #    lo1.addWidget(QLabel("Lock range accross",self))
        wlock = QCheckBox("Lock display range", self)
        wlock.setToolTip("""<P>If checked, then the intensity range will be locked. The ranges of all locked images
      change simultaneously.</P>""")
        lo1.addWidget(wlock)
        wlockall = QToolButton(self)
        wlockall.setIcon(pixmaps.locked.icon())
        wlockall.setText("Lock all to this")
        wlockall.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        wlockall.setAutoRaise(True)
        wlockall.setToolTip("""<P>Click this to lock together the intensity ranges of all images.</P>""")
        lo1.addWidget(wlockall)
        wunlockall = QToolButton(self)
        wunlockall.setIcon(pixmaps.unlocked.icon())
        wunlockall.setText("Unlock all")
        wunlockall.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        wunlockall.setAutoRaise(True)
        wunlockall.setToolTip("""<P>Click this to unlock the intensity ranges of all images.</P>""")
        lo1.addWidget(wunlockall)
        wlock.setChecked(self._rc.isDisplayRangeLocked())
        QObject.connect(wlock, SIGNAL("clicked(bool)"), self._rc.lockDisplayRange)
        QObject.connect(wlockall, SIGNAL("clicked()"),
                        self._currier.curry(self._imgman.lockAllDisplayRanges, self._rc))
        QObject.connect(wunlockall, SIGNAL("clicked()"), self._imgman.unlockAllDisplayRanges)
        QObject.connect(self._rc, SIGNAL("displayRangeLocked"), wlock.setChecked)

        #    self._wlock_imap_axis = [ QCheckBox(name,self) for iaxis,name,labels in sliced_axes ]
        #    for iw,w in enumerate(self._wlock_imap_axis):
        #      QObject.connect(w,SIGNAL("toggled(bool)"),self._currier.curry(self._rc.lockDisplayRangeForAxis,iw))
        #      lo1.addWidget(w,0)
        lo1.addStretch(1)

        # lo0.addWidget(Separator(self,"Colourmap"))
        # color bar
        self._colorbar = QwtPlot(self)
        lo0.addWidget(self._colorbar)
        self._colorbar.setAutoDelete(False)
        self._colorbar.setMinimumHeight(32)
        self._colorbar.enableAxis(QwtPlot.yLeft, False)
        self._colorbar.enableAxis(QwtPlot.xBottom, False)
        # color plot
        self._colorplot = QwtPlot(self)
        lo0.addWidget(self._colorplot)
        self._colorplot.setAutoDelete(False)
        self._colorplot.setMinimumHeight(64)
        self._colorplot.enableAxis(QwtPlot.yLeft, False)
        self._colorplot.enableAxis(QwtPlot.xBottom, False)
        # self._colorplot.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Preferred)
        self._colorbar.hide()
        self._colorplot.hide()
        # color controls
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 1)
        lo1.addWidget(QLabel("Colourmap:", self))
        # colormap list
        ### NB: use setIconSize() and icons in QComboBox!!!
        self._wcolmaps = QComboBox(self)
        self._wcolmaps.setIconSize(QSize(128, 16))
        self._wcolmaps.setToolTip("""<P>Use this to select a different colourmap.</P>""")
        for cmap in self._rc.getColormapList():
            self._wcolmaps.addItem(QIcon(cmap.makeQPixmap(128, 16)), cmap.name)
        lo1.addWidget(self._wcolmaps)
        QObject.connect(self._wcolmaps, SIGNAL("activated(int)"), self._rc.setColorMapNumber)
        # add widgetstack for colormap controls
        self._wcolmap_control_stack = QStackedWidget(self)
        self._wcolmap_control_blank = QWidget(self._wcolmap_control_stack)
        self._wcolmap_control_stack.addWidget(self._wcolmap_control_blank)
        lo0.addWidget(self._wcolmap_control_stack)
        self._colmap_controls = []
        # add controls to stack
        for index, cmap in enumerate(self._rc.getColormapList()):
            if isinstance(cmap, Colormaps.ColormapWithControls):
                controls = cmap.makeControlWidgets(self._wcolmap_control_stack)
                self._wcolmap_control_stack.addWidget(controls)
                QObject.connect(cmap, SIGNAL("colormapChanged"),
                                self._currier.curry(self._previewColormapParameters, index, cmap))
                QObject.connect(cmap, SIGNAL("colormapPreviewed"),
                                self._currier.curry(self._previewColormapParameters, index, cmap))
                self._colmap_controls.append(controls)
            else:
                self._colmap_controls.append(self._wcolmap_control_blank)

        # connect updates from renderControl and image
        self.image.connect(SIGNAL("slice"), self._updateImageSlice)
        QObject.connect(self._rc, SIGNAL("intensityMapChanged"), self._updateIntensityMap)
        QObject.connect(self._rc, SIGNAL("colorMapChanged"), self._updateColorMap)
        QObject.connect(self._rc, SIGNAL("dataSubsetChanged"), self._updateDataSubset)
        QObject.connect(self._rc, SIGNAL("displayRangeChanged"), self._updateDisplayRange)

        # update widgets
        self._setupHistogramPlot()
        self._updateDataSubset(*self._rc.currentSubset())
        self._updateColorMap(image.colorMap())
        self._updateIntensityMap(rc.currentIntensityMap(), rc.currentIntensityMapNumber())
        self._updateDisplayRange(*self._rc.displayRange())
Beispiel #4
0
 def setupUi(self):
     """
     Setup the UI for the command toolbar.
     """
     #ninad 070123 : It's important to set the Vertical size policy of the 
     # cmd toolbar widget. otherwise the flyout QToolbar messes up the 
     #layout (makes the command toolbar twice as big) 
     #I have set the vertical policy as fixed. Works fine. There are some 
     # MainWindow resizing problems for but those are not due to this 
     #size policy AFAIK        
     self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
             
     layout_cmdtoolbar = QHBoxLayout(self)
     layout_cmdtoolbar.setMargin(2)
     layout_cmdtoolbar.setSpacing(2)
     
     #See comment at the top for details about this flag
     if DEFINE_CONTROL_AREA_AS_A_QWIDGET:
         self.cmdToolbarControlArea = QWidget(self)    
     else:
         self.cmdToolbarControlArea = QToolBar_WikiHelp(self)
         
     self.cmdToolbarControlArea.setAutoFillBackground(True)
             
     self.ctrlAreaPalette = self.getCmdMgrCtrlAreaPalette()  
     self.cmdToolbarControlArea.setPalette(self.ctrlAreaPalette)
             
     self.cmdToolbarControlArea.setMinimumHeight(62)
     self.cmdToolbarControlArea.setMinimumWidth(380)
     self.cmdToolbarControlArea.setSizePolicy(QSizePolicy.Fixed, 
                                              QSizePolicy.Fixed)  
     
     #See comment at the top for details about this flag
     if DEFINE_CONTROL_AREA_AS_A_QWIDGET:
         layout_controlArea = QHBoxLayout(self.cmdToolbarControlArea)
         layout_controlArea.setMargin(0)
         layout_controlArea.setSpacing(0)
     
     self.cmdButtonGroup = QButtonGroup()    
     btn_index = 0
     
     for name in ('Build', 'Insert', 'Tools', 'Move', 'Simulation'):
         btn = QToolButton(self.cmdToolbarControlArea)           
         btn.setObjectName(name)
         btn.setMinimumWidth(75)
         btn.setMaximumWidth(75)
         btn.setMinimumHeight(62)
         btn.setAutoRaise(True)
         btn.setCheckable(True)
         btn.setAutoExclusive(True)
         iconpath = "ui/actions/Command Toolbar/ControlArea/" + name + ".png"
         btn.setIcon(geticon(iconpath))
         btn.setIconSize(QSize(22, 22))
         btn.setText(name)
         btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
         btn.setPalette(self.ctrlAreaPalette)
         self.cmdButtonGroup.addButton(btn, btn_index)
         btn_index += 1        
         #See comment at the top for details about this flag
         if DEFINE_CONTROL_AREA_AS_A_QWIDGET:
             layout_controlArea.addWidget(btn)
         else:
             self.cmdToolbarControlArea.layout().addWidget(btn)                
             #following has issues. so not adding widget directly to the 
             #toolbar. (instead adding it in its layout)-- ninad 070124
             ##self.cmdToolbarControlArea.addWidget(btn)      
     
     layout_cmdtoolbar.addWidget(self.cmdToolbarControlArea) 
     
     #Flyout Toolbar in the command toolbar  
     self.flyoutToolBar = FlyoutToolBar(self)
     
     layout_cmdtoolbar.addWidget(self.flyoutToolBar)   
     
     #ninad 070116: Define a spacer item. It will have the exact geometry 
     # as that of the flyout toolbar. it is added to the command toolbar 
     # layout only when the Flyout Toolbar is hidden. It is required
     # to keep the 'Control Area' widget fixed in its place (otherwise, 
     #after hiding the flyout toolbar, the layout adjusts the position of 
     #remaining widget items) 
     
     self.spacerItem = QSpacerItem(0, 
                                   0, 
                                   QtGui.QSizePolicy.Expanding, 
                                   QtGui.QSizePolicy.Minimum)
     self.spacerItem.setGeometry = self.flyoutToolBar.geometry()
     
     for btn in self.cmdButtonGroup.buttons():
         if str(btn.objectName()) == 'Build':
             btn.setMenu(self.win.buildStructuresMenu)
             btn.setPopupMode(QToolButton.MenuButtonPopup)
             btn.setToolTip("Build Commands")
             whatsThisTextForCommandToolbarBuildButton(btn)
         if str(btn.objectName()) == 'Insert':
             btn.setMenu(self.win.insertMenu)
             btn.setPopupMode(QToolButton.MenuButtonPopup)
             btn.setToolTip("Insert Commands")
             whatsThisTextForCommandToolbarInsertButton(btn)
         if str(btn.objectName()) == 'Tools':
             #fyi: cmd stands for 'command toolbar' - ninad070406
             self.win.cmdToolsMenu = QtGui.QMenu(self.win)
             self.win.cmdToolsMenu.addAction(self.win.toolsExtrudeAction) 
             self.win.cmdToolsMenu.addAction(self.win.toolsFuseChunksAction)
             self.win.cmdToolsMenu.addSeparator()
             self.win.cmdToolsMenu.addAction(self.win.modifyMergeAction)
             self.win.cmdToolsMenu.addAction(self.win.modifyMirrorAction)
             self.win.cmdToolsMenu.addAction(self.win.modifyInvertAction)
             self.win.cmdToolsMenu.addAction(self.win.modifyStretchAction)
             btn.setMenu(self.win.cmdToolsMenu)
             btn.setPopupMode(QToolButton.MenuButtonPopup)
             btn.setToolTip("Tools")
             whatsThisTextForCommandToolbarToolsButton(btn)
         if str(btn.objectName()) == 'Move':
             self.win.moveMenu = QtGui.QMenu(self.win)
             self.win.moveMenu.addAction(self.win.toolsMoveMoleculeAction)
             self.win.moveMenu.addAction(self.win.rotateComponentsAction)
             self.win.moveMenu.addSeparator()
             self.win.moveMenu.addAction(
                 self.win.modifyAlignCommonAxisAction)
             ##self.win.moveMenu.addAction(\
             ##    self.win.modifyCenterCommonAxisAction)
             btn.setMenu(self.win.moveMenu)
             btn.setPopupMode(QToolButton.MenuButtonPopup)
             btn.setToolTip("Move Commands")
             whatsThisTextForCommandToolbarMoveButton(btn)
         if str(btn.objectName()) == 'Dimension':
             btn.setMenu(self.win.dimensionsMenu)
             btn.setPopupMode(QToolButton.MenuButtonPopup)
             btn.setToolTip("Dimensioning Commands")
         if str(btn.objectName()) == 'Simulation':
             btn.setMenu(self.win.simulationMenu)
             btn.setPopupMode(QToolButton.MenuButtonPopup)
             btn.setToolTip("Simulation Commands")
             whatsThisTextForCommandToolbarSimulationButton(btn)
         
         # Convert all "img" tags in the button's "What's This" text 
         # into abs paths (from their original rel paths).
         # Partially fixes bug 2943. --mark 2008-12-07
         # [bruce 081209 revised this -- removed mac = False]
         fix_QAction_whatsthis(btn)
     return
Beispiel #5
0
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
Beispiel #6
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>""")
Beispiel #7
0
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
Beispiel #8
0
    class SliderControl(QObject):
        """This class implements a slider control for a colormap"""
        def __init__(self,
                     name,
                     value,
                     minval,
                     maxval,
                     step,
                     format="%s: %.1f"):
            QObject.__init__(self)
            self.name, self.value, self.minval, self.maxval, self.step, self.format = \
                name, value, minval, maxval, step, format
            self._default = value
            self._wlabel = None

        def makeControlWidgets(self, parent, gridlayout, row, column):
            toprow = QWidget(parent)
            gridlayout.addWidget(toprow, row * 2, column)
            top_lo = QHBoxLayout(toprow)
            top_lo.setContentsMargins(0, 0, 0, 0)
            self._wlabel = QLabel(self.format % (self.name, self.value),
                                  toprow)
            top_lo.addWidget(self._wlabel)
            self._wreset = QToolButton(toprow)
            self._wreset.setText("reset")
            self._wreset.setToolButtonStyle(Qt.ToolButtonTextOnly)
            self._wreset.setAutoRaise(True)
            self._wreset.setEnabled(self.value != self._default)
            QObject.connect(self._wreset, SIGNAL("clicked()"),
                            self._resetValue)
            top_lo.addWidget(self._wreset)
            self._wslider = QwtSlider(parent)
            # This works around a stupid bug in QwtSliders -- see comments on histogram zoom wheel above
            self._wslider_timer = QTimer(parent)
            self._wslider_timer.setSingleShot(True)
            self._wslider_timer.setInterval(500)
            QObject.connect(self._wslider_timer, SIGNAL("timeout()"),
                            self.setValue)
            gridlayout.addWidget(self._wslider, row * 2 + 1, column)
            self._wslider.setRange(self.minval, self.maxval)
            self._wslider.setStep(self.step)
            self._wslider.setValue(self.value)
            self._wslider.setTracking(False)
            QObject.connect(self._wslider, SIGNAL("valueChanged(double)"),
                            self.setValue)
            QObject.connect(self._wslider, SIGNAL("sliderMoved(double)"),
                            self._previewValue)

        def _resetValue(self):
            self._wslider.setValue(self._default)
            self.setValue(self._default)

        def setValue(self, value=None, notify=True):
            # only update widgets if already created
            self.value = value
            if self._wlabel is not None:
                if value is None:
                    self.value = value = self._wslider.value()
                self._wreset.setEnabled(value != self._default)
                self._wlabel.setText(self.format % (self.name, self.value))
                # stop timer if being called to finalize the change in value
                if notify:
                    self._wslider_timer.stop()
                    self.emit(SIGNAL("valueChanged"), self.value)

        def _previewValue(self, value):
            self.setValue(notify=False)
            self._wslider_timer.start(500)
            self.emit(SIGNAL("valueMoved"), self.value)