Example #1
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())
Example #2
0
    def __init__(self, parent, hide_on_close=False):
        QMainWindow.__init__(self, parent)
        self._hide_on_close = hide_on_close
        # replace the BusyIndicator class with a GUI-aware one
        Purr.BusyIndicator = BusyIndicator
        self._pounce = False
        # we keep a small stack of previously active purrers. This makes directory changes
        # faster (when going back and forth between dirs)
        # current purrer
        self.purrer = None
        self.purrer_stack = []
        # Purr pipes for receiving remote commands
        self.purrpipes = {}
        # init GUI
        self.setWindowTitle("PURR")
        self.setWindowIcon(pixmaps.purr_logo.icon())
        cw = QWidget(self)
        self.setCentralWidget(cw)
        cwlo = QVBoxLayout(cw)
        cwlo.setContentsMargins(0, 0, 0, 0)
        cwlo.setMargin(5)
        cwlo.setSpacing(0)
        toplo = QHBoxLayout();
        cwlo.addLayout(toplo)

        # About dialog
        self._about_dialog = QMessageBox(self)
        self._about_dialog.setWindowTitle("About PURR")
        self._about_dialog.setText(self.about_message + """
        <P>PURR is not watching any directories right now. You may need to restart it, and give it
  some directory names on the command line.</P>""")
        self._about_dialog.setIconPixmap(pixmaps.purr_logo.pm())
        # Log viewer dialog
        self.viewer_dialog = HTMLViewerDialog(self, config_name="log-viewer",
                                              buttons=[(pixmaps.blue_round_reload, "Regenerate",
                                                        """<P>Regenerates your log's HTML code from scratch. This can be useful if
                                                        your PURR version has changed, or if there was an error of some kind
                                                        the last time the files were generated.</P>
                                                        """)])
        self._viewer_timestamp = None
        self.connect(self.viewer_dialog, SIGNAL("Regenerate"), self._regenerateLog)
        self.connect(self.viewer_dialog, SIGNAL("viewPath"), self._viewPath)

        # Log title toolbar
        title_tb = QToolBar(cw)
        title_tb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        title_tb.setIconSize(QSize(16, 16))
        cwlo.addWidget(title_tb)
        title_label = QLabel("Purrlog title:", title_tb)
        title_tb.addWidget(title_label)
        self.title_editor = QLineEdit(title_tb)
        title_tb.addWidget(self.title_editor)
        self.connect(self.title_editor, SIGNAL("editingFinished()"), self._titleChanged)
        tip = """<P>This is your current log title. To rename the log, enter new name here and press Enter.</P>"""
        title_label.setToolTip(tip)
        self.title_editor.setToolTip(tip)
        self.wviewlog = title_tb.addAction(pixmaps.openbook.icon(), "View", self._showViewerDialog)
        self.wviewlog.setToolTip("Click to see an HTML rendering of your current log.")
        qa = title_tb.addAction(pixmaps.purr_logo.icon(), "About...", self._about_dialog.exec_)
        qa.setToolTip("<P>Click to see the About... dialog, which will tell you something about PURR.</P>")

        self.wdirframe = QFrame(cw)
        cwlo.addWidget(self.wdirframe)
        self.dirs_lo = QVBoxLayout(self.wdirframe)
        self.dirs_lo.setMargin(5)
        self.dirs_lo.setContentsMargins(5, 0, 5, 5)
        self.dirs_lo.setSpacing(0)
        self.wdirframe.setFrameStyle(QFrame.Box | QFrame.Raised)
        self.wdirframe.setLineWidth(1)

        ## Directories toolbar
        dirs_tb = QToolBar(self.wdirframe)
        dirs_tb.setToolButtonStyle(Qt.ToolButtonIconOnly)
        dirs_tb.setIconSize(QSize(16, 16))
        self.dirs_lo.addWidget(dirs_tb)
        label = QLabel("Monitoring directories:", dirs_tb)
        self._dirs_tip = """<P>PURR can monitor your working directories for new or updated files. If there's a checkmark
      next to the directory name in this list, PURR is monitoring it.</P>

      <P>If the checkmark is grey, PURR is monitoring things unobtrusively. When a new or updated file is detected in he monitored directory,
      it is quietly added to the list of files in the "New entry" window, even if this window is not currently visible.</P>

      <P>If the checkmark is black, PURR will be more obtrusive. Whenever a new or updated file is detected, the "New entry" window will
      pop up automatically. This is called "pouncing", and some people find it annoying.</P>
      """
        label.setToolTip(self._dirs_tip)
        label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        dirs_tb.addWidget(label)

        # add directory list widget
        self.wdirlist = DirectoryListWidget(self.wdirframe)
        self.wdirlist.setToolTip(self._dirs_tip)
        QObject.connect(self.wdirlist, SIGNAL("directoryStateChanged"), self._changeWatchedDirState)
        self.dirs_lo.addWidget(self.wdirlist)
        # self.wdirlist.setMaximumSize(1000000,64)

        # add directory button
        add = dirs_tb.addAction(pixmaps.list_add.icon(), "Add", self._showAddDirectoryDialog)
        add.setToolTip("<P>Click to add another directory to be monitored.</P>")

        # remove directory button
        delbtn = dirs_tb.addAction(pixmaps.list_remove.icon(), "Remove", self.wdirlist.removeCurrent)
        delbtn.setEnabled(False)
        delbtn.setToolTip("<P>Click to removed the currently selected directory from the list.</P>")
        QObject.connect(self.wdirlist, SIGNAL("hasSelection"), delbtn.setEnabled)

        #    # qa = dirs_tb.addAction(pixmaps.blue_round_reload.icon(),"Rescan",self._forceRescan)
        #    # qa.setToolTip("Click to rescan the directories for any new or updated files.")
        #    self.wshownew = QCheckBox("show new files",dirs_tb)
        #    dirs_tb.addWidget(self.wshownew)
        #    self.wshownew.setCheckState(Qt.Checked)
        #    self.wshownew.setToolTip("""<P>If this is checked, the "New entry" window will pop up automatically whenever
        #  new or updated files are detected. If this is unchecked, the files will be added to the window quietly
        #        and unobtrusively; you can show the window manually by clicking on the "New entry..." button below.</P>""")
        #    self._dir_entries = {}

        cwlo.addSpacing(5)

        wlogframe = QFrame(cw)
        cwlo.addWidget(wlogframe)
        log_lo = QVBoxLayout(wlogframe)
        log_lo.setMargin(5)
        log_lo.setContentsMargins(5, 5, 5, 5)
        log_lo.setSpacing(0)
        wlogframe.setFrameStyle(QFrame.Box | QFrame.Raised)
        wlogframe.setLineWidth(1)

        # listview of log entries
        self.etw = LogEntryTree(cw)
        log_lo.addWidget(self.etw, 1)
        self.etw.header().setDefaultSectionSize(128)
        self.etw.header().setMovable(False)
        self.etw.setHeaderLabels(["date", "entry title", "comment"])
        if hasattr(QHeaderView, 'ResizeToContents'):
            self.etw.header().setResizeMode(0, QHeaderView.ResizeToContents)
        else:
            self.etw.header().setResizeMode(0, QHeaderView.Custom)
            self.etw.header().resizeSection(0, 120)
        self.etw.header().setResizeMode(1, QHeaderView.Interactive)
        self.etw.header().setResizeMode(2, QHeaderView.Stretch)
        self.etw.header().show()
        try:
            self.etw.setAllColumnsShowFocus(True)
        except AttributeError:
            pass;  # Qt 4.2+
        # self.etw.setShowToolTips(True)
        self.etw.setSortingEnabled(False)
        # self.etw.setColumnAlignment(2,Qt.AlignLeft|Qt.AlignTop)
        self.etw.setSelectionMode(QTreeWidget.ExtendedSelection)
        self.etw.setRootIsDecorated(True)
        self.connect(self.etw, SIGNAL("itemSelectionChanged()"), self._entrySelectionChanged)
        self.connect(self.etw, SIGNAL("itemActivated(QTreeWidgetItem*,int)"), self._viewEntryItem)
        self.connect(self.etw, SIGNAL("itemContextMenuRequested"), self._showItemContextMenu)
        # create popup menu for data products
        self._archived_dp_menu = menu = QMenu(self)
        self._archived_dp_menu_title = QLabel()
        self._archived_dp_menu_title.setMargin(5)
        self._archived_dp_menu_title_wa = wa = QWidgetAction(self)
        wa.setDefaultWidget(self._archived_dp_menu_title)
        menu.addAction(wa)
        menu.addSeparator()
        menu.addAction(pixmaps.editcopy.icon(), "Restore file(s) from archived copy", self._restoreItemFromArchive)
        menu.addAction(pixmaps.editpaste.icon(), "Copy pathname of archived copy to clipboard",
                       self._copyItemToClipboard)
        self._current_item = None
        # create popup menu for entries
        self._entry_menu = menu = QMenu(self)
        self._entry_menu_title = QLabel()
        self._entry_menu_title.setMargin(5)
        self._entry_menu_title_wa = wa = QWidgetAction(self)
        wa.setDefaultWidget(self._entry_menu_title)
        menu.addAction(wa)
        menu.addSeparator()
        menu.addAction(pixmaps.filefind.icon(), "View this log entry", self._viewEntryItem)
        menu.addAction(pixmaps.editdelete.icon(), "Delete this log entry", self._deleteSelectedEntries)
        # buttons at bottom
        log_lo.addSpacing(5)
        btnlo = QHBoxLayout()
        log_lo.addLayout(btnlo)
        self.wnewbtn = QPushButton(pixmaps.filenew.icon(), "New entry...", cw)
        self.wnewbtn.setToolTip("Click to add a new log entry.")
        # self.wnewbtn.setFlat(True)
        self.wnewbtn.setEnabled(False)
        btnlo.addWidget(self.wnewbtn)
        btnlo.addSpacing(5)
        self.weditbtn = QPushButton(pixmaps.filefind.icon(), "View entry...", cw)
        self.weditbtn.setToolTip("Click to view or edit the selected log entry/")
        # self.weditbtn.setFlat(True)
        self.weditbtn.setEnabled(False)
        self.connect(self.weditbtn, SIGNAL("clicked()"), self._viewEntryItem)
        btnlo.addWidget(self.weditbtn)
        btnlo.addSpacing(5)
        self.wdelbtn = QPushButton(pixmaps.editdelete.icon(), "Delete", cw)
        self.wdelbtn.setToolTip("Click to delete the selected log entry or entries.")
        # self.wdelbtn.setFlat(True)
        self.wdelbtn.setEnabled(False)
        self.connect(self.wdelbtn, SIGNAL("clicked()"), self._deleteSelectedEntries)
        btnlo.addWidget(self.wdelbtn)
        # enable status line
        self.statusBar().show()
        Purr.progressMessage = self.message
        self._prev_msg = None
        # editor dialog for new entry
        self.new_entry_dialog = Purr.Editors.NewLogEntryDialog(self)
        self.connect(self.new_entry_dialog, SIGNAL("newLogEntry"), self._newLogEntry)
        self.connect(self.new_entry_dialog, SIGNAL("filesSelected"), self._addDPFiles)
        self.connect(self.wnewbtn, SIGNAL("clicked()"), self.new_entry_dialog.show)
        self.connect(self.new_entry_dialog, SIGNAL("shown"), self._checkPounceStatus)
        # entry viewer dialog
        self.view_entry_dialog = Purr.Editors.ExistingLogEntryDialog(self)
        self.connect(self.view_entry_dialog, SIGNAL("previous()"), self._viewPrevEntry)
        self.connect(self.view_entry_dialog, SIGNAL("next()"), self._viewNextEntry)
        self.connect(self.view_entry_dialog, SIGNAL("viewPath"), self._viewPath)
        self.connect(self.view_entry_dialog, SIGNAL("filesSelected"), self._addDPFilesToOldEntry)
        self.connect(self.view_entry_dialog, SIGNAL("entryChanged"), self._entryChanged)
        # saving a data product to an older entry will automatically drop it from the
        # new entry dialog
        self.connect(self.view_entry_dialog, SIGNAL("creatingDataProduct"),
                     self.new_entry_dialog.dropDataProducts)
        # resize selves
        width = Config.getint('main-window-width', 512)
        height = Config.getint('main-window-height', 512)
        self.resize(QSize(width, height))
        # create timer for pouncing
        self._timer = QTimer(self)
        self.connect(self._timer, SIGNAL("timeout()"), self._rescan)
        # create dict mapping index.html paths to entry numbers
        self._index_paths = {}
Example #3
0
 def __init__(self, parent, modal=True, flags=Qt.WindowFlags()):
     QDialog.__init__(self, parent, flags)
     self.setModal(modal)
     self.setWindowTitle("Restore model into image")
     lo = QVBoxLayout(self)
     lo.setMargin(10)
     lo.setSpacing(5)
     # file selector
     self.wfile_in = FileSelector(self, label="Input FITS file:", dialog_label="Input FITS file",
                                  default_suffix="fits", file_types="FITS files (*.fits *.FITS)",
                                  file_mode=QFileDialog.ExistingFile)
     lo.addWidget(self.wfile_in)
     self.wfile_out = FileSelector(self, label="Output FITS file:", dialog_label="Output FITS file",
                                   default_suffix="fits", file_types="FITS files (*.fits *.FITS)",
                                   file_mode=QFileDialog.AnyFile)
     lo.addWidget(self.wfile_out)
     # beam size
     lo1 = QHBoxLayout()
     lo.addLayout(lo1)
     lo1.setContentsMargins(0, 0, 0, 0)
     lo1.addWidget(QLabel("Restoring beam FWHM, major axis:", self))
     self.wbmaj = QLineEdit(self)
     lo1.addWidget(self.wbmaj)
     lo1.addWidget(QLabel("\"     minor axis:", self))
     self.wbmin = QLineEdit(self)
     lo1.addWidget(self.wbmin)
     lo1.addWidget(QLabel("\"     P.A.:", self))
     self.wbpa = QLineEdit(self)
     lo1.addWidget(self.wbpa)
     lo1.addWidget(QLabel("\u00B0", self))
     for w in self.wbmaj, self.wbmin, self.wbpa:
         w.setValidator(QDoubleValidator(self))
     lo1 = QHBoxLayout()
     lo.addLayout(lo1)
     lo1.setContentsMargins(0, 0, 0, 0)
     self.wfile_psf = FileSelector(self, label="Set restoring beam by fitting PSF image:",
                                   dialog_label="PSF FITS file", default_suffix="fits",
                                   file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile)
     lo1.addSpacing(32)
     lo1.addWidget(self.wfile_psf)
     # selection only
     self.wselonly = QCheckBox("restore selected model sources only", self)
     lo.addWidget(self.wselonly)
     # OK/cancel buttons
     lo.addSpacing(10)
     lo2 = QHBoxLayout()
     lo.addLayout(lo2)
     lo2.setContentsMargins(0, 0, 0, 0)
     lo2.setMargin(5)
     self.wokbtn = QPushButton("OK", self)
     self.wokbtn.setMinimumWidth(128)
     QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept)
     self.wokbtn.setEnabled(False)
     cancelbtn = QPushButton("Cancel", self)
     cancelbtn.setMinimumWidth(128)
     QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject)
     lo2.addWidget(self.wokbtn)
     lo2.addStretch(1)
     lo2.addWidget(cancelbtn)
     self.setMinimumWidth(384)
     # signals
     QObject.connect(self.wfile_in, SIGNAL("filenameSelected"), self._fileSelected)
     QObject.connect(self.wfile_in, SIGNAL("filenameSelected"), self._inputFileSelected)
     QObject.connect(self.wfile_out, SIGNAL("filenameSelected"), self._fileSelected)
     QObject.connect(self.wfile_psf, SIGNAL("filenameSelected"), self._psfFileSelected)
     # internal state
     self.qerrmsg = QErrorMessage(self)
Example #4
0
    def __init__(self, parent, hide_on_close=False):
        QMainWindow.__init__(self, parent)
        self._hide_on_close = hide_on_close
        # replace the BusyIndicator class with a GUI-aware one
        Purr.BusyIndicator = BusyIndicator
        self._pounce = False
        # we keep a small stack of previously active purrers. This makes directory changes
        # faster (when going back and forth between dirs)
        # current purrer
        self.purrer = None
        self.purrer_stack = []
        # Purr pipes for receiving remote commands
        self.purrpipes = {}
        # init GUI
        self.setWindowTitle("PURR")
        self.setWindowIcon(pixmaps.purr_logo.icon())
        cw = QWidget(self)
        self.setCentralWidget(cw)
        cwlo = QVBoxLayout(cw)
        cwlo.setContentsMargins(0, 0, 0, 0)
        cwlo.setMargin(5)
        cwlo.setSpacing(0)
        toplo = QHBoxLayout()
        cwlo.addLayout(toplo)

        # About dialog
        self._about_dialog = QMessageBox(self)
        self._about_dialog.setWindowTitle("About PURR")
        self._about_dialog.setText(self.about_message + """
        <P>PURR is not watching any directories right now. You may need to restart it, and give it
  some directory names on the command line.</P>""")
        self._about_dialog.setIconPixmap(pixmaps.purr_logo.pm())
        # Log viewer dialog
        self.viewer_dialog = HTMLViewerDialog(
            self,
            config_name="log-viewer",
            buttons=
            [(pixmaps.blue_round_reload, "Regenerate",
              """<P>Regenerates your log's HTML code from scratch. This can be useful if
                                                        your PURR version has changed, or if there was an error of some kind
                                                        the last time the files were generated.</P>
                                                        """)])
        self._viewer_timestamp = None
        self.connect(self.viewer_dialog, SIGNAL("Regenerate"),
                     self._regenerateLog)
        self.connect(self.viewer_dialog, SIGNAL("viewPath"), self._viewPath)

        # Log title toolbar
        title_tb = QToolBar(cw)
        title_tb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        title_tb.setIconSize(QSize(16, 16))
        cwlo.addWidget(title_tb)
        title_label = QLabel("Purrlog title:", title_tb)
        title_tb.addWidget(title_label)
        self.title_editor = QLineEdit(title_tb)
        title_tb.addWidget(self.title_editor)
        self.connect(self.title_editor, SIGNAL("editingFinished()"),
                     self._titleChanged)
        tip = """<P>This is your current log title. To rename the log, enter new name here and press Enter.</P>"""
        title_label.setToolTip(tip)
        self.title_editor.setToolTip(tip)
        self.wviewlog = title_tb.addAction(pixmaps.openbook.icon(), "View",
                                           self._showViewerDialog)
        self.wviewlog.setToolTip(
            "Click to see an HTML rendering of your current log.")
        qa = title_tb.addAction(pixmaps.purr_logo.icon(), "About...",
                                self._about_dialog.exec_)
        qa.setToolTip(
            "<P>Click to see the About... dialog, which will tell you something about PURR.</P>"
        )

        self.wdirframe = QFrame(cw)
        cwlo.addWidget(self.wdirframe)
        self.dirs_lo = QVBoxLayout(self.wdirframe)
        self.dirs_lo.setMargin(5)
        self.dirs_lo.setContentsMargins(5, 0, 5, 5)
        self.dirs_lo.setSpacing(0)
        self.wdirframe.setFrameStyle(QFrame.Box | QFrame.Raised)
        self.wdirframe.setLineWidth(1)

        ## Directories toolbar
        dirs_tb = QToolBar(self.wdirframe)
        dirs_tb.setToolButtonStyle(Qt.ToolButtonIconOnly)
        dirs_tb.setIconSize(QSize(16, 16))
        self.dirs_lo.addWidget(dirs_tb)
        label = QLabel("Monitoring directories:", dirs_tb)
        self._dirs_tip = """<P>PURR can monitor your working directories for new or updated files. If there's a checkmark
      next to the directory name in this list, PURR is monitoring it.</P>

      <P>If the checkmark is grey, PURR is monitoring things unobtrusively. When a new or updated file is detected in he monitored directory,
      it is quietly added to the list of files in the "New entry" window, even if this window is not currently visible.</P>

      <P>If the checkmark is black, PURR will be more obtrusive. Whenever a new or updated file is detected, the "New entry" window will
      pop up automatically. This is called "pouncing", and some people find it annoying.</P>
      """
        label.setToolTip(self._dirs_tip)
        label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        dirs_tb.addWidget(label)

        # add directory list widget
        self.wdirlist = DirectoryListWidget(self.wdirframe)
        self.wdirlist.setToolTip(self._dirs_tip)
        QObject.connect(self.wdirlist, SIGNAL("directoryStateChanged"),
                        self._changeWatchedDirState)
        self.dirs_lo.addWidget(self.wdirlist)
        # self.wdirlist.setMaximumSize(1000000,64)

        # add directory button
        add = dirs_tb.addAction(pixmaps.list_add.icon(), "Add",
                                self._showAddDirectoryDialog)
        add.setToolTip(
            "<P>Click to add another directory to be monitored.</P>")

        # remove directory button
        delbtn = dirs_tb.addAction(pixmaps.list_remove.icon(), "Remove",
                                   self.wdirlist.removeCurrent)
        delbtn.setEnabled(False)
        delbtn.setToolTip(
            "<P>Click to removed the currently selected directory from the list.</P>"
        )
        QObject.connect(self.wdirlist, SIGNAL("hasSelection"),
                        delbtn.setEnabled)

        #    # qa = dirs_tb.addAction(pixmaps.blue_round_reload.icon(),"Rescan",self._forceRescan)
        #    # qa.setToolTip("Click to rescan the directories for any new or updated files.")
        #    self.wshownew = QCheckBox("show new files",dirs_tb)
        #    dirs_tb.addWidget(self.wshownew)
        #    self.wshownew.setCheckState(Qt.Checked)
        #    self.wshownew.setToolTip("""<P>If this is checked, the "New entry" window will pop up automatically whenever
        #  new or updated files are detected. If this is unchecked, the files will be added to the window quietly
        #        and unobtrusively; you can show the window manually by clicking on the "New entry..." button below.</P>""")
        #    self._dir_entries = {}

        cwlo.addSpacing(5)

        wlogframe = QFrame(cw)
        cwlo.addWidget(wlogframe)
        log_lo = QVBoxLayout(wlogframe)
        log_lo.setMargin(5)
        log_lo.setContentsMargins(5, 5, 5, 5)
        log_lo.setSpacing(0)
        wlogframe.setFrameStyle(QFrame.Box | QFrame.Raised)
        wlogframe.setLineWidth(1)

        # listview of log entries
        self.etw = LogEntryTree(cw)
        log_lo.addWidget(self.etw, 1)
        self.etw.header().setDefaultSectionSize(128)
        self.etw.header().setMovable(False)
        self.etw.setHeaderLabels(["date", "entry title", "comment"])
        if hasattr(QHeaderView, 'ResizeToContents'):
            self.etw.header().setResizeMode(0, QHeaderView.ResizeToContents)
        else:
            self.etw.header().setResizeMode(0, QHeaderView.Custom)
            self.etw.header().resizeSection(0, 120)
        self.etw.header().setResizeMode(1, QHeaderView.Interactive)
        self.etw.header().setResizeMode(2, QHeaderView.Stretch)
        self.etw.header().show()
        try:
            self.etw.setAllColumnsShowFocus(True)
        except AttributeError:
            pass
            # Qt 4.2+
        # self.etw.setShowToolTips(True)
        self.etw.setSortingEnabled(False)
        # self.etw.setColumnAlignment(2,Qt.AlignLeft|Qt.AlignTop)
        self.etw.setSelectionMode(QTreeWidget.ExtendedSelection)
        self.etw.setRootIsDecorated(True)
        self.connect(self.etw, SIGNAL("itemSelectionChanged()"),
                     self._entrySelectionChanged)
        self.connect(self.etw, SIGNAL("itemActivated(QTreeWidgetItem*,int)"),
                     self._viewEntryItem)
        self.connect(self.etw, SIGNAL("itemContextMenuRequested"),
                     self._showItemContextMenu)
        # create popup menu for data products
        self._archived_dp_menu = menu = QMenu(self)
        self._archived_dp_menu_title = QLabel()
        self._archived_dp_menu_title.setMargin(5)
        self._archived_dp_menu_title_wa = wa = QWidgetAction(self)
        wa.setDefaultWidget(self._archived_dp_menu_title)
        menu.addAction(wa)
        menu.addSeparator()
        menu.addAction(pixmaps.editcopy.icon(),
                       "Restore file(s) from archived copy",
                       self._restoreItemFromArchive)
        menu.addAction(pixmaps.editpaste.icon(),
                       "Copy pathname of archived copy to clipboard",
                       self._copyItemToClipboard)
        self._current_item = None
        # create popup menu for entries
        self._entry_menu = menu = QMenu(self)
        self._entry_menu_title = QLabel()
        self._entry_menu_title.setMargin(5)
        self._entry_menu_title_wa = wa = QWidgetAction(self)
        wa.setDefaultWidget(self._entry_menu_title)
        menu.addAction(wa)
        menu.addSeparator()
        menu.addAction(pixmaps.filefind.icon(), "View this log entry",
                       self._viewEntryItem)
        menu.addAction(pixmaps.editdelete.icon(), "Delete this log entry",
                       self._deleteSelectedEntries)
        # buttons at bottom
        log_lo.addSpacing(5)
        btnlo = QHBoxLayout()
        log_lo.addLayout(btnlo)
        self.wnewbtn = QPushButton(pixmaps.filenew.icon(), "New entry...", cw)
        self.wnewbtn.setToolTip("Click to add a new log entry.")
        # self.wnewbtn.setFlat(True)
        self.wnewbtn.setEnabled(False)
        btnlo.addWidget(self.wnewbtn)
        btnlo.addSpacing(5)
        self.weditbtn = QPushButton(pixmaps.filefind.icon(), "View entry...",
                                    cw)
        self.weditbtn.setToolTip(
            "Click to view or edit the selected log entry/")
        # self.weditbtn.setFlat(True)
        self.weditbtn.setEnabled(False)
        self.connect(self.weditbtn, SIGNAL("clicked()"), self._viewEntryItem)
        btnlo.addWidget(self.weditbtn)
        btnlo.addSpacing(5)
        self.wdelbtn = QPushButton(pixmaps.editdelete.icon(), "Delete", cw)
        self.wdelbtn.setToolTip(
            "Click to delete the selected log entry or entries.")
        # self.wdelbtn.setFlat(True)
        self.wdelbtn.setEnabled(False)
        self.connect(self.wdelbtn, SIGNAL("clicked()"),
                     self._deleteSelectedEntries)
        btnlo.addWidget(self.wdelbtn)
        # enable status line
        self.statusBar().show()
        Purr.progressMessage = self.message
        self._prev_msg = None
        # editor dialog for new entry
        self.new_entry_dialog = Purr.Editors.NewLogEntryDialog(self)
        self.connect(self.new_entry_dialog, SIGNAL("newLogEntry"),
                     self._newLogEntry)
        self.connect(self.new_entry_dialog, SIGNAL("filesSelected"),
                     self._addDPFiles)
        self.connect(self.wnewbtn, SIGNAL("clicked()"),
                     self.new_entry_dialog.show)
        self.connect(self.new_entry_dialog, SIGNAL("shown"),
                     self._checkPounceStatus)
        # entry viewer dialog
        self.view_entry_dialog = Purr.Editors.ExistingLogEntryDialog(self)
        self.connect(self.view_entry_dialog, SIGNAL("previous()"),
                     self._viewPrevEntry)
        self.connect(self.view_entry_dialog, SIGNAL("next()"),
                     self._viewNextEntry)
        self.connect(self.view_entry_dialog, SIGNAL("viewPath"),
                     self._viewPath)
        self.connect(self.view_entry_dialog, SIGNAL("filesSelected"),
                     self._addDPFilesToOldEntry)
        self.connect(self.view_entry_dialog, SIGNAL("entryChanged"),
                     self._entryChanged)
        # saving a data product to an older entry will automatically drop it from the
        # new entry dialog
        self.connect(self.view_entry_dialog, SIGNAL("creatingDataProduct"),
                     self.new_entry_dialog.dropDataProducts)
        # resize selves
        width = Config.getint('main-window-width', 512)
        height = Config.getint('main-window-height', 512)
        self.resize(QSize(width, height))
        # create timer for pouncing
        self._timer = QTimer(self)
        self.connect(self._timer, SIGNAL("timeout()"), self._rescan)
        # create dict mapping index.html paths to entry numbers
        self._index_paths = {}