def setTracks(self, tracks):

        if not self._first_display:
            self._removeOldWidgets()

        self._first_display = False

        self._widgets = {}
        parent = QWidget(self)
        l = QVBoxLayout(parent)
        for track in tracks:
            w = self._createTrackProgress(parent, track)
            l.addWidget(w)
            self._widgets[track['id']] = w

        l.addStretch(1)
        parent.setLayout(l)

        scroll = QScrollArea(self)
        scroll.setWidgetResizable(True)
        scroll.setWidget(parent)
        scroll.setAlignment(Qt.AlignCenter)
        scroll.setFrameShape(QFrame.NoFrame);

        l = QVBoxLayout(self)
        l.addWidget(scroll)
        l.addWidget(self.close_button)
        self.setLayout(l)

        self.close_button.setEnabled(False)
Example #2
0
    def create_scrolled_panel ( self, parent ):
        """ Returns a panel that can scroll its contents.
        """
        sa = QScrollArea( check_parent( parent ) )
        sa.setFrameShape( QFrame.NoFrame )
        sa.setWidgetResizable( True )

        return control_adapter_for( sa )
Example #3
0
    def create_doc_tab_populate_combobox(self):
        """
        Creates the supporting document component widget.
        """
        self.doc_tab_data()
        self.docs_tab = QTabWidget()
        self.docs_tab_index = OrderedDict()
        for i, (id, doc) in enumerate(self.doc_types.iteritems()):
            self.docs_tab_index[doc] = i
            # the tab widget containing the document widget layout
            # and the child of the tab.
            tab_widget = QWidget()
            tab_widget.setObjectName(doc)
            # The layout of the tab widget
            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName(u'widget_layout_{}'.format(doc))
            # the scroll area widget inside the tab widget.
            scroll_area = QScrollArea(tab_widget)
            scroll_area.setFrameShape(QFrame.NoFrame)
            scroll_area.setObjectName(u'tab_scroll_area_{}'.format(doc))

            layout_widget = QWidget()
            # the widget the is under the scroll area content and
            # the widget containing the document widget layout
            # This widget is hidden and shown based on the STR number
            layout_widget.setObjectName(u'widget_{}'.format(doc))

            doc_widget_layout = QVBoxLayout(layout_widget)
            doc_widget_layout.setObjectName(
                u'doc_widget_layout_{}'.format(doc))
            doc_widget = QWidget()
            doc_widget.setObjectName(u'doc_widget_{}_{}'.format(
                doc, self.str_number))

            doc_widget_layout.addWidget(doc_widget)

            # the layout containing document widget.
            ### This is the layout that is registered to add uploaded
            # supporting documents widgets into.
            tab_layout = QVBoxLayout(doc_widget)
            tab_layout.setObjectName(u'layout_{}_{}'.format(
                doc, self.str_number))

            scroll_area.setWidgetResizable(True)
            scroll_area.setWidget(layout_widget)

            cont_layout.addWidget(scroll_area)
            # Add the tab widget with the document
            # type name to create a tab.
            self.docs_tab.addTab(tab_widget, doc)

            self.container_box.addWidget(self.docs_tab, 1)
            if len(self.str_numbers) == 1:
                self.doc_type_cbo.addItem(doc, id)
 def _createCategoryView(self):
     self._clearCurrentView()
     
     self._currentToolBox = QToolBox(self)
     self._currentToolBox.setAutoFillBackground(False)
     for category in self._action.getPrivacyCategories():
         self._createAndInsertSingleView(category, -1)
     
     peerExceptions = SingleCategoryView(self._action, self._currentToolBox, self.logger, category=None, mode=PrivacySettings.POLICY_PEER_EXCEPTION)
     self._currentSingleViews[self._PEER_EXCEPTIONS_VIEW] = peerExceptions
     self._currentToolBox.addItem(peerExceptions, "Special Peers")
     
     w = QScrollArea(self)
     w.setAutoFillBackground(False)
     w.viewport().setAutoFillBackground(False)
     w.setWidgetResizable(True)
     w.setWidget(self._currentToolBox)
     w.setFrameShape(QFrame.NoFrame)
     self._settingsWidget.layout().addWidget(w)
Example #5
0
class _DocumentTypeContainer(QWidget):
    """
    Container for a single document type.
    """
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self._gl = QGridLayout(self)
        self._sa = QScrollArea(self)
        self._sa.setFrameShape(QFrame.NoFrame)
        self._sa.setWidgetResizable(True)
        self._sa_content_area = QWidget()
        self._vl_ca = QVBoxLayout(self._sa_content_area)
        self._vl_ca.setSpacing(0)
        self._vl_ca.setMargin(0)
        self._doc_container = QVBoxLayout()
        self._vl_ca.addLayout(self._doc_container)
        self._sa.setWidget(self._sa_content_area)
        self._gl.addWidget(self._sa, 0, 0, 1, 1)

    @property
    def container(self):
        return self._doc_container
Example #6
0
class _DocumentTypeContainer(QWidget):
    """
    Container for a single document type.
    """
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self._gl = QGridLayout(self)
        self._sa = QScrollArea(self)
        self._sa.setFrameShape(QFrame.NoFrame)
        self._sa.setWidgetResizable(True)
        self._sa_content_area = QWidget()
        self._vl_ca = QVBoxLayout(self._sa_content_area)
        self._vl_ca.setSpacing(0)
        self._vl_ca.setMargin(0)
        self._doc_container = QVBoxLayout()
        self._vl_ca.addLayout(self._doc_container)
        self._sa.setWidget(self._sa_content_area)
        self._gl.addWidget(self._sa, 0, 0, 1, 1)

    @property
    def container(self):
        return self._doc_container
Example #7
0
class PlotDialog(QDialog, PlotManager):
    """ Implements a dialog to which an arbitrary number of plots can be
    added.

    This class implements a `QDialog` with a number of plots on it. The
    axes of the plots can be arbitrarily synchronized and option checkboxes
    can be added which provide callbacks when the checkbox state changes.

    :param str wintitle: Title of the window.
    :param bool major_grid: Show major grid in plots.
    :param bool minor_grid: Show minor grid in plots.
    :param bool toolbar: Show toolbar.
    :param parent: Parent window.
    :param panels: A list of guiqwt panels to be added to the window.
    :param int min_plot_width: Default minimum width for plots.
    :param int min_plot_height: Default minimum height for plots.
    """

    def __init__(self, wintitle='Plot window', major_grid=True,
                 minor_grid=False, toolbar=True,  parent=None,
                 panels=None, min_plot_width=100, min_plot_height=75):
        QDialog.__init__(self, parent)
        self.setWindowFlags(Qt.Window)

        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(_fromUtf8(':/Application/Main')),
                       QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)

        self.major_grid = major_grid
        self.minor_grid = minor_grid
        self.min_plot_width = min_plot_width
        self.min_plot_height = min_plot_height

        # WidgetMixin copy
        PlotManager.__init__(self, main=self)
        
        self.main_layout = QVBoxLayout(self)
        self.color_layout = QHBoxLayout()
        self.plot_layout = QGridLayout()
        self.plot_layout.setMargin(0)
        self.plot_scroll_widget = QWidget()
        self.plot_scroll_area = QScrollArea()
        self.plot_scroll_area.setFrameShape(QFrame.NoFrame)
        self.plot_scroll_area.setWidgetResizable(True)
        self.option_layout = QHBoxLayout()

        self.plot_widget = None

        if panels is not None:
            for panel in panels:
                self.add_panel(panel)

        self.toolbar = QToolBar('Tools')
        if not toolbar:
            self.toolbar.hide()

        # Configuring widget layout
        self._setup_widget_properties(wintitle=wintitle, icon=icon)
        self._setup_widget_layout()
        
        # Options
        self.option_callbacks = {}
        self.legend = None
        self.axis_syncplots = {}
        
    def _setup_widget_properties(self, wintitle, icon):
        self.setWindowTitle(wintitle)
        if isinstance(icon, basestring):
            icon = get_icon(icon)
        self.setWindowIcon(icon)
        self.setMinimumSize(320, 240)
        self.resize(720, 540)
        
    def _setup_widget_layout(self):
        self.main_layout.addWidget(self.toolbar)
        self.main_layout.addLayout(self.color_layout)
        self.main_layout.addWidget(self.plot_scroll_area)
        self.plot_scroll_area.setWidget(self.plot_scroll_widget)
        self.plot_scroll_widget.setLayout(self.plot_layout)
        self.main_layout.addLayout(self.option_layout)
        self.setLayout(self.main_layout)
        
    def add_custom_curve_tools(self, antialiasing=True,
                               activate_zoom=True,
                               signal_stats=False):
        """ Adds typically needed curve tools to the window.

        :param bool antialiasing: Determines if the antialiasing tool is
            added.
        :param bool activate_zoom: Determines if the zoom tool is activated
            initially (otherwise, the selection tool will be activated).
        :param bool signal_stats: Determines if the signal stats tool is
            available.
        """
        self.add_toolbar(self.toolbar)

        t = self.add_tool(SelectTool)
        if not activate_zoom:
            self.set_default_tool(t)
        self.add_tool(BasePlotMenuTool, "item")
        self.add_tool(ExportItemDataTool)
        try:  # Old versions of guiqwt and spyderlib do not support this
            import spyderlib.widgets.objecteditor
            from guiqwt.tools import EditItemDataTool
            self.add_tool(EditItemDataTool)
        except ImportError:
            pass
        self.add_tool(ItemCenterTool)
        self.add_tool(DeleteItemTool)

        self.add_separator_tool()
        t = self.add_tool(RectZoomTool)
        if activate_zoom:
            self.set_default_tool(t)
        self.add_tool(guiqwt_tools.HomeTool)
        self.add_tool(guiqwt_tools.PanTool)

        self.add_separator_tool()
        self.add_tool(BasePlotMenuTool, "grid")
        self.add_tool(BasePlotMenuTool, "axes")
        self.add_tool(DisplayCoordsTool)
        if self.get_itemlist_panel():
            self.add_tool(ItemListPanelTool)

        if signal_stats:
            self.add_separator_tool()
            self.add_tool(SignalStatsTool)

        if antialiasing:
            self.add_tool(AntiAliasingTool)
        self.add_tool(AxisScaleTool)
        self.add_separator_tool()
        self.add_tool(SaveAsTool)
        self.add_tool(CopyToClipboardTool)
        self.add_tool(PrintTool)
        self.add_tool(guiqwt_tools.HelpTool)
        self.add_separator_tool()
        self.get_default_tool().activate()

    def add_custom_image_tools(self, activate_zoom=True):
        """ Adds typically needed image tools to the window.
        """
        self.add_toolbar(self.toolbar)

        t = self.add_tool(SelectTool)
        if not activate_zoom:
            self.set_default_tool(t)
        self.add_tool(BasePlotMenuTool, "item")
        self.add_tool(ExportItemDataTool)
        try:  # Old versions of guiqwt and spyderlib do not support this
            import spyderlib.widgets.objecteditor
            from guiqwt.tools import EditItemDataTool
            self.add_tool(EditItemDataTool)
        except ImportError:
            pass
        self.add_tool(ItemCenterTool)
        self.add_tool(DeleteItemTool)

        self.add_separator_tool()
        t = self.add_tool(RectZoomTool)
        if activate_zoom:
            self.set_default_tool(t)
        self.add_tool(guiqwt_tools.HomeTool)
        self.add_tool(guiqwt_tools.PanTool)

        self.add_separator_tool()
        self.add_tool(BasePlotMenuTool, "grid")
        self.add_tool(BasePlotMenuTool, "axes")
        self.add_tool(DisplayCoordsTool)
        if self.get_itemlist_panel():
            self.add_tool(ItemListPanelTool)

        self.add_separator_tool()
        self.add_tool(ColormapTool)
        self.add_tool(ReverseYAxisTool)
        self.add_tool(AspectRatioTool)
        if self.get_contrast_panel():
            self.add_tool(ContrastPanelTool)
        if self.get_xcs_panel() and self.get_ycs_panel():
            self.add_tool(XCSPanelTool)
            self.add_tool(YCSPanelTool)
            self.add_tool(CrossSectionTool)
            self.add_tool(AverageCrossSectionTool)

        self.add_separator_tool()
        self.add_tool(SaveAsTool)
        self.add_tool(CopyToClipboardTool)
        self.add_tool(PrintTool)
        self.add_tool(guiqwt_tools.HelpTool)
        self.add_separator_tool()
        self.get_default_tool().activate()
    
    def add_option(self, name, change_callback, active=False):
        """ Add an option (using a checkbox) to the window.

        :param str name: The name of the option.
        :param func change_callback: A function accepting the new state as
            a parameter. The function will be called whenever the state
            of the option changes.
        :param bool active: Determines if the option is activated initially.
        """
        checkBox = QtGui.QCheckBox(self)
        checkBox.setChecked(active)
        checkBox.setText(name)
        checkBox.stateChanged.connect(self._option_callback)
        self.option_callbacks[checkBox] = change_callback
        self.option_layout.addWidget(checkBox)

    def add_x_synchronization_option(self, active, ids=None):
        """ Offer an option for X axes synchronization. This method should
        be called after show(), so that a proper initial synchronization
        can be performed.

        :param bool active: Determines whether the axes are synchronized
            initially.
        :param sequence ids: List of plot ids to synchronize.
        """
        self.axis_syncplots[BasePlot.X_BOTTOM] = ids
        if active and ids:
            self.synchronize_axis(BasePlot.X_BOTTOM)
        self.add_option('Synchronize X Axes',
                        PlotDialog._synchronization_option_x, active)

    def add_y_synchronization_option(self, active, ids=None):
        """ Offer an option for Y axes synchronization. This method should
        be called after show(), so that a proper initial synchronization
        can be performed.

        :param bool active: Determines whether the axes are synchronized
            initially
        :param sequence ids: List of plot ids to synchronize.
        """
        self.axis_syncplots[BasePlot.Y_LEFT] = ids
        if active and ids:
            self.synchronize_axis(BasePlot.Y_LEFT)
        self.add_option('Synchronize Y Axes',
                        PlotDialog._synchronization_option_y, active)

    def _synchronization_option_x(self, state):
        """ Callback for x-axis synchronization
        """
        if state:
            self.synchronize_axis(BasePlot.X_BOTTOM)
        else:
            self.unsynchronize_axis(BasePlot.X_BOTTOM)

    def _synchronization_option_y(self, state):
        """ Callback for y-axis synchronization
        """
        if state:
            self.synchronize_axis(BasePlot.Y_LEFT)
        else:
            self.unsynchronize_axis(BasePlot.Y_LEFT)

    def replace_colors(self, replace_list):
        """ Replace colors of items in all plots.

            This can be useful when changing the background color to black
            and black items should be drawn in white:
            ``replace_colors([('#000000', '#ffffff']))``

        :param list replace_list: A list of tuples of Qt color names. The
            first color in each tuple is replaced by the second color.
        """
        for plot in self.plots.itervalues():
            for i in plot.get_items():
                if isinstance(i, CurveItem):
                    pen = i.pen()
                elif isinstance(i, Marker):
                    pen = i.linePen()
                else:
                    continue
                for color in replace_list:
                    c1 = QColor(color[0])
                    c2 = QColor(color[1])
                    if pen.color() != c1:
                        continue
                    pen.setColor(c2)
                    break
                if isinstance(i, CurveItem):
                    i.setPen(pen)
                elif isinstance(i, Marker):
                    i.setLinePen(pen)
            plot.replot()

    def set_background_color(self, color):
        """ Set the background color for all plots.

        :param str color: A Qt color name (e.g. '#ff0000')
        """
        for p in self.plots.itervalues():
            p.setCanvasBackground(QColor(color))
            p.replot()
        
    def add_unit_color(self, color, name='Unit color:'):
        """ Create a small legend on top of the window with only one entry.

        :param str color: A Qt color name (e.g. '#ff0000')
        :param str name: The name of the legend item. It will be displayed
            on the left of the color.
        """
        label = QtGui.QLabel(self)
        label.setText(name)
        label.setAlignment(Qt.AlignRight)
        self.color_layout.addWidget(label)
        label = QtGui.QLabel(self)
        label.setStyleSheet('background-color:' + str(color))
        label.setFrameShape(QFrame.StyledPanel)
        label.setMaximumWidth(80)
        self.color_layout.addWidget(label)

    def add_custom_label(self, legend_string):
        """ Add a label on the right of the plots

        :param str legend_string: An arbitrary string (which can contain
            newlines) to display on the right of the plots
        """
        label = QtGui.QLabel(self)
        label.setText(legend_string)
        self.plot_layout.addWidget(
            label, 0, self.plot_layout.columnCount(), -1, 1)

    def add_color_legend(self, legend, show_option=None):
        """ Create a legend on the right of the plots with colors and names.

        :param sequence legend: List of (color, text) tuples, where `color`
            is a Qt color name (e.g. '#ff0000') and `text` is the
            corresponding text to display in the legend.
        :param bool show_option: Determines whether a toggle for the legend
            will be shown (if the parameter is not ``None``) and if the legend
            is visible initially (``True``/``False``).
        """
        widget = QWidget(self)
        layout = QGridLayout(widget)
        widget.setLayout(layout)
        for l in legend:
            label = QtGui.QLabel(self)
            label.setStyleSheet('background-color:' + str(l[0]))
            label.setFrameShape(QFrame.StyledPanel)
            label.setMaximumWidth(80)
            label.setMaximumHeight(12)
            layout.addWidget(label, layout.rowCount(), 0, 1, 1)
            label = QtGui.QLabel(self)
            label.setText(l[1])
            layout.addWidget(label, layout.rowCount() - 1, 1, 1, 1)

        self.plot_layout.addWidget(
            widget, 0, self.plot_layout.columnCount(), -1, 1)
        if show_option is not None:
            widget.setVisible(show_option)
            self.add_option(
                'Show Legend Sidebar', lambda w, s: widget.setVisible(s),
                show_option)
    
    def add_legend_option(self, legends, active):
        """ Create a user option to show or hide a list of legend objects.

        :param sequence legends: The legend objects affected by the option.
        :param bool active: Determines whether the legends will be visible
            initially.
        """
        self.legends = legends
        self._set_legend_visibility(active)
        self.add_option('Show legend', self._legend_callback, active)
        if active:
            self._set_legend_visibility(True)
        
    def _option_callback(self, state):
        self.option_callbacks[self.sender()](self, state)

    #noinspection PyUnusedLocal
    def _legend_callback(self, win, state):
        self._set_legend_visibility(state > 0)
    
    def _set_legend_visibility(self, visible):
        for p in self.plots.itervalues():
            for l in self.legends:
                p.set_item_visible(l, visible)

    def add_plot_widget(self, plot_widget, plot_id, row=-1, column=0,
                        min_plot_width=None, min_plot_height=None):
        """ Adds a guiqwt plot to the window.

        :param plot_widget: The plot to add.
        :type plot_widget: guiqwt plot widget
        :param int plot_id: The id of the new plot.
        :param int row: The row of the new plot. If this is -1, the new plot
            will be added in a new row (if `column` is 0) or
            in the last row.
        :param int column: The column of the new plot.
        :param int min_plot_width: The minimum width of this plot. If
            ``None``, the default minimum width for this dialog
            is used.
        :param int max_plot_height: The minimum height of this plot. If
            ``None``, the default minimum height for this dialog
            is used.
        """
        if row == -1:
            if column == 0:
                row = self.plot_layout.rowCount()
            else:
                row = self.plot_layout.rowCount() - 1
        pw = min_plot_width
        if pw is None:
            pw = self.min_plot_width
        ph = min_plot_height
        if ph is None:
            ph = self.min_plot_height
        plot_widget.setMinimumSize(pw, ph)
        self.plot_layout.addWidget(plot_widget, row, column)
        new_plot = plot_widget.plot
        self.add_plot(new_plot, plot_id)
        
    def synchronize_axis(self, axis, plots=None):
        if plots is None:
            if axis in self.axis_syncplots:
                plots = self.axis_syncplots[axis]
            else:
                plots = self.plots.keys()
        if len(plots) < 1:
            return

        PlotManager.synchronize_axis(self, axis, plots)

        # Find interval that needs to be shown in order to include all
        # currently shown parts in the synchronized plots
        plot_objects = [self.plots[p] for p in plots]
        lb = min((p.axisScaleDiv(axis).lowerBound() for p in plot_objects))
        ub = max((p.axisScaleDiv(axis).upperBound() for p in plot_objects))
        for p in plot_objects:
            p.setAxisScale(axis, lb, ub)
            p.replot()

    def unsynchronize_axis(self, axis, plots=None):
        if plots is None:
            if axis in self.axis_syncplots:
                plots = self.axis_syncplots[axis]
            else:
                plots = self.plots.keys()
        for plot_id in plots:
            if not plot_id in self.synchronized_plots:
                continue
            synclist = self.synchronized_plots[plot_id]
            for plot2_id in plots:
                if plot_id == plot2_id:
                    continue
                item = (axis, plot2_id)
                if item in synclist:
                    synclist.remove(item)
        
    def plot_axis_changed(self, plot):
        ids = [k for k, p in self.plots.iteritems() if p == plot]
        if len(ids) < 1:
            return
        plot_id = ids[0]
        if plot_id not in self.synchronized_plots:
            return
        for (axis, other_plot_id) in self.synchronized_plots[plot_id]:
            scalediv = plot.axisScaleDiv(axis)
            other = self.get_plot(other_plot_id)
            lb = scalediv.lowerBound()
            ub = scalediv.upperBound()
            other.setAxisScale(axis, lb, ub)
            other.replot()

    def set_plot_title(self, plot, title):
        """ Set the title of a guiqwt plot and use the same font as for the
            rest of the window.

        :param plot: The plot for which the title is set.
        :param str title: The new title of the plot.
        """
        plot.setTitle(title)
        l = plot.titleLabel()
        l.setFont(self.font())
        plot.setTitle(l.text())

    def show(self):
        for p in self.plots.itervalues():
            if not self.minor_grid:
                p.grid.gridparam.min_xenabled = False
                p.grid.gridparam.min_yenabled = False
            if not self.major_grid:
                p.grid.gridparam.maj_xenabled = False
                p.grid.gridparam.maj_yenabled = False
            p.grid.update_params()
        super(PlotDialog, self).show()
Example #8
0
class DHakkinda(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.resize(500, 350)
        self.setMaximumSize(500, 350)
        self.gLayout =QGridLayout(self)
        self.logo = QLabel(self)
        self.logo.setPixmap(QPixmap(":/logo/data/logo.png"))
        self.gLayout.addWidget(self.logo, 0, 0, 2, 1)
        self.appName = QLabel(self)
        font = QFont()
        font.setPointSize(32)
        font.setWeight(50)
        self.appName.setFont(font)
        self.gLayout.addWidget(self.appName, 0, 1, 1, 2)
        self.appVersion = QLabel(self)
        font = QFont()
        font.setPointSize(9)
        font.setWeight(75)
        font.setBold(True)
        self.appVersion.setFont(font)
        self.appVersion.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
        self.gLayout.addWidget(self.appVersion, 1, 1, 1, 2)
        self.gBox = QGroupBox(self)
        font = QFont()
        font.setPointSize(12)
        font.setWeight(75)
        font.setBold(True)
        self.gBox.setFont(font)
        self.gLayout2 = QGridLayout(self.gBox)
        self.scrollArea = QScrollArea(self.gBox)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setWidgetResizable(True)

        self.scrollAreaWidgetContents = QWidget()
        self.scrollAreaWidgetContents.setGeometry(0, 0, 476, 199)

        self.gLayout3 = QGridLayout(self.scrollAreaWidgetContents)
        self.appHakkinda = QLabel(self.scrollAreaWidgetContents)
        font = QFont()
        font.setPointSize(9)
        font.setWeight(50)
        font.setBold(False)
        self.appHakkinda.setFont(font)
        self.appHakkinda.setWordWrap(True)
        self.gLayout3.addWidget(self.appHakkinda, 0, 0, 1, 1)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gLayout2.addWidget(self.scrollArea, 0, 0, 1, 1)
        self.gLayout.addWidget(self.gBox, 2, 0, 2, 4)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.gLayout.addItem(spacerItem, 0, 3, 1, 1)
        spacerItem1 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.gLayout.addItem(spacerItem1, 2, 1, 1, 2)

        self.setWindowTitle(u"Virux Hakkında")
        self.appName.setText(u"Virux")
        self.appVersion.setText(u"Sürüm %s"%QApplication.applicationVersion())
        self.gBox.setTitle(u"Hakkında")
        self.appHakkinda.setText(u"""
        <p>Virux, platform bağımsız bir antivirüs yazılımıdır :P</p>
        <p>Yazılımıın bir arayüzü yoktur. Sadece sistem çubuğunda bir tepsi oluşur. Bu tepsi animasyon şeklindedir.</p>
        <p>Rasgele zamanlarda mevcut olan dialoglardan bir tanesi ekranda gözükecektir. Sadece eğlence amacıyla yapılmıştır...</p>
        <p><b>Geliştirici:</b> Metehan Özbek - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Görsel Çalışma:</b> Yasin Özcan - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Katkı Yapanlar:</b> Yaşar Arabacı - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Lisans:</b> GPL v3</p>
        <p></p>""")
Example #9
0
class EntityEditorDialog(QDialog, MapperMixin):
    """
    Dialog for editing entity attributes.
    """
    addedModel = pyqtSignal(object)

    def __init__(
            self,
            entity,
            model=None,
            parent=None,
            manage_documents=True,
            collect_model=False,
            parent_entity=None,
            exclude_columns=[],
            plugin = None
    ):
        """
        Class constructor.
        :param entity: Entity object corresponding to a table object.
        :type entity: Entity
        :param model: Data object for loading data into the form widgets.
        If the model is set, then the editor dialog is assumed to be in edit
        mode.
        :type model: object
        :param parent: Parent widget that the form belongs to.
        :type parent: QWidget
        :param manage_documents: True if the dialog should provide controls
        for managing supporting documents. Only applicable if the entity
        allows for supporting documents to be attached.
        :type manage_documents: bool
        :param collect_model: If set to True only returns
        the filled form model without saving it to the database.
        :type collect_model: Boolean
        :param parent_entity: The parent entity of the editor
        :type parent_entity: Object
        :param exclude_columns: List of columns to be excluded if in a list.
        :type exclude_columns: List
        :return: If collect_model, returns SQLAlchemy Model
        """
        QDialog.__init__(self, parent)

        self.entity_table_model = {}

        self.collection_suffix = self.tr('Collection')

        #Set minimum width
        self.setMinimumWidth(450)

        self.plugin = plugin

        #Flag for mandatory columns
        self.has_mandatory = False
        self.reload_form = False
        self._entity = entity
        self.edit_model = model
        self.column_widgets = OrderedDict()
        self._parent = parent
        self.exclude_columns = exclude_columns
        self.entity_tab_widget = None
        self._disable_collections = False
        self.filter_val = None
        self.parent_entity = parent_entity
        self.child_models = OrderedDict()
        self.entity_scroll_area = None
        self.entity_editor_widgets = OrderedDict()
        # Set notification layout bar
        self.vlNotification = QVBoxLayout()
        self.vlNotification.setObjectName('vlNotification')
        self._notifBar = NotificationBar(self.vlNotification)
        self.do_not_check_dirty = False
        # Set manage documents only if the entity supports documents
        if self._entity.supports_documents:
            self._manage_documents = manage_documents
        else:
            self._manage_documents = False

        # Setup entity model
        self._ent_document_model = None
        if self._entity.supports_documents:
            self.ent_model, self._ent_document_model = entity_model(
                self._entity,
                with_supporting_document=True
            )
        else:
            self.ent_model = entity_model(self._entity)
        if not model is None:
            self.ent_model = model

        MapperMixin.__init__(self, self.ent_model, entity)

        self.collect_model = collect_model

        self.register_column_widgets()
        try:
            if isinstance(parent._parent, EntityEditorDialog):
                # hide collections form child editor
                self._disable_collections = True

        except AttributeError:
            self._parent._parent = None
        # Set title
        editor_trans = self.tr('Editor')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        self.title = u'{0} {1}'.format(title_str, editor_trans)

        self.setWindowTitle(self.title)

        self._init_gui()
        self.adjustSize()

        self._get_entity_editor_widgets()

        if isinstance(parent._parent, EntityEditorDialog):
            self.parent_entity = parent.parent_entity
            self.set_parent_values()
            # make the size smaller to differentiate from parent and as it
            # only has few tabs.
            self.adjustSize()

        self.attribute_mappers = self._attr_mapper_collection

        # Exception title for editor extension exceptions
        self._ext_exc_msg = self.tr(
            'An error has occured while executing Python code in the editor '
            'extension:'
        )

        # Register custom editor extension if specified
        self._editor_ext = entity_dlg_extension(self)
        if not self._editor_ext is None:
            self._editor_ext.post_init()

            # Initialize CascadingFieldContext objects
            self._editor_ext.connect_cf_contexts()

    def _init_gui(self):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(
            self.vlNotification, 0, 0, 1, 1
        )
        QApplication.processEvents()

        #set widgets values
        column_widget_area = self._setup_columns_content_area() 

        self.gridLayout.addWidget(
            column_widget_area, 1, 0, 1, 1
        )

        QApplication.processEvents()
        # Add notification for mandatory columns if applicable
        next_row = 2
        if self.has_mandatory:
            self.required_fields_lbl = QLabel(self)
            msg = self.tr(
                'Please fill out all required (*) fields.'
            )
            msg = self._highlight_asterisk(msg)
            self.required_fields_lbl.setText(msg)
            self.gridLayout.addWidget(
                self.required_fields_lbl, next_row, 0, 1, 2
            )
            # Bump up row reference
            next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(
            self.buttonBox, next_row, 0, 1, 1
        )

        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Cancel|QDialogButtonBox.Save
        )

        if self.edit_model is None:
            if not self.collect_model:
                self.save_new_button = QPushButton(
                    QApplication.translate(
                        'EntityEditorDialog', 'Save and New'
                    )
                )
                self.buttonBox.addButton(
                    self.save_new_button, QDialogButtonBox.ActionRole
                )

        # edit model, collect model
        # adding new record for child

        # Saving in parent editor
        if not isinstance(self._parent._parent, EntityEditorDialog):
            # adding a new record
            if self.edit_model is None:
                # saving when digitizing.
                if self.collect_model:
                    self.buttonBox.accepted.connect(self.on_model_added)
                # saving parent editor
                else:
                    self.buttonBox.accepted.connect(self.save_parent_editor)
                    self.save_new_button.clicked.connect(self.save_and_new)
            # updating existing record
            else:
                if not self.collect_model:
                    # updating existing record of the parent editor
                    self.buttonBox.accepted.connect(self.save_parent_editor)
                else:
                    self.buttonBox.accepted.connect(self.on_model_added)
        # Saving in child editor
        else:
            # save and new record
            if self.edit_model is None:
                self.buttonBox.accepted.connect(self.on_child_saved)
                self.save_new_button.clicked.connect(
                    lambda: self.on_child_saved(True)
                )

            else:
                # When updating an existing child editor save to the db
                self.buttonBox.accepted.connect(
                    self.on_child_saved
                )
                #self.buttonBox.accepted.connect(self.submit)

        self.buttonBox.rejected.connect(self.cancel)

    @property
    def notification_bar(self):
        """
        :return: Returns the dialog's notification bar.
        :rtype: NotificationBar
        """
        return self._notifBar

    def save_parent_editor(self):
        """
        Saves the parent editor and its children.
        """
        self.submit()
        self.save_children()

    def set_parent_values(self):
        """
        Sets the parent display column for the child.
        """
        if self.parent_entity is None:
            return
        for col in self._entity.columns.values():
            if col.TYPE_INFO == 'FOREIGN_KEY':
                parent_entity = col.parent
                if parent_entity == self.parent_entity:
                    self.parent_widgets_value_setter(self._parent._parent, col)

    def parent_widgets_value_setter(self, parent, col):
        """
        Finds and sets the value from parent widget and set it to the column
        widget of a child using the child column.
        :param parent: The parent widget
        :type parent: QWidget
        :param col: The child column object
        :type col: Object
        """
        for parent_col, parent_widget in parent.column_widgets.iteritems():
            if parent_col.name == col.name:
                self.single_parent_value_setter(col, parent_widget)
                break
            if parent_col.name in col.entity_relation.display_cols:
                self.single_parent_value_setter(col, parent_widget)
                break

    def single_parent_value_setter(self, col, parent_widget):
        """
        Gets value from parent widget and set it to the column widget of a
        child using the child column.
        :param parent: The parent widget
        :type parent: QWidget
        :param col: The child column object
        :type col: Object
        """
        local_widget = self.column_widgets[col]
        local_widget.show_clear_button()
        self.filter_val = parent_widget.text()
        local_widget.setText(self.filter_val)

    def save_and_new(self):
        """
        A slot raised when Save and New button is click. It saves the form
        without showing a success message. Then it sets reload_form property
        to True so that entity_browser can re-load the form.
        """
        from stdm.ui.entity_browser import (
            EntityBrowserWithEditor
        )
        self.submit(False, True)
        self.save_children()

        if self.is_valid:
            self.addedModel.emit(self.model())
            self.setModel(self.ent_model())
            self.clear()
            self.child_models.clear()
            for index in range(0, self.entity_tab_widget.count()-1):
                if isinstance(
                        self.entity_tab_widget.widget(index),
                        EntityBrowserWithEditor
                ):
                    child_browser = self.entity_tab_widget.widget(index)
                    child_browser.remove_rows()

    def on_model_added(self):
        """
        A slot raised when a form is submitted with collect model set to True.
        There will be no success message and the form does not close.
        """
        self.submit(True)
        self.addedModel.emit(self.model())

    def closeEvent(self, event):
        '''
        Raised when a request to close the window is received.
        Check the dirty state of input controls and prompt user to
        save if dirty.
        '''

        if self.do_not_check_dirty:
            event.accept()
            return
        isDirty, userResponse = self.checkDirty()

        if isDirty:
            if userResponse == QMessageBox.Yes:
                # We need to ignore the event so that validation and
                # saving operations can be executed
                event.ignore()
                self.submit()
            elif userResponse == QMessageBox.No:
                event.accept()
            elif userResponse == QMessageBox.Cancel:
                event.ignore()
        else:
            event.accept()

    def on_child_saved(self, save_and_new=False):
        """
        A slot raised when the save or save and new button is clicked. It sets
        the child_models dictionary of the parent when saved.
        :param save_and_new: A boolean indicating the save and new button is
        clicked to trigger the slot.
        :type save_and_new: Boolean
        """
        if self.parent_entity is None:
            return

        self.submit(True)

        insert_pos = self._parent.tbEntity.model().rowCount() + 1
        # Save to parent editor so that it is persistent.
        self._parent._parent.child_models[insert_pos, self._entity] = \
            self.model()
        self.addedModel.emit(self.model())
        if not save_and_new:
            self.accept()

        else:
            if self.is_valid:
                #self.addedModel.emit(self.model())
                self.setModel(self.ent_model())

                self.clear()
                self.set_parent_values()

    def save_children(self):
        """
        Saves children models into the database by assigning the the id of the
        parent for foreign key column.
        """
        if len(self.child_models) < 1:
            return
        children_obj = []
        for row_entity, model in self.child_models.iteritems():
            row_pos = row_entity[0]
            entity = row_entity[1]
            ent_model = entity_model(entity)
            entity_obj = ent_model()
            for col in entity.columns.values():
                if col.TYPE_INFO == 'FOREIGN_KEY':
                    if col.parent.name == self._entity.name:
                        setattr(model, col.name, self.model().id)
                        children_obj.append(model)
            entity_obj.saveMany(children_obj)

    def register_column_widgets(self):
        """
        Registers the column widgets.
        """
        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        self.scroll_widget_contents = QWidget()
        self.scroll_widget_contents.setObjectName(
            'scrollAreaWidgetContents'
        )
        for c in self._entity.columns.values():
            if c.name in self.exclude_columns:
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue
            # Get widget factory
            column_widget = ColumnWidgetRegistry.create(
                c,
                self.scroll_widget_contents,
                host=self
            )
            self.column_widgets[c] = column_widget

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')
    
        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for c, column_widget in self.column_widgets.iteritems():
            if c.name in self.exclude_columns:
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                # Format label text if it is a mandatory field
                if c.mandatory:
                    header = u'{0} *'.format(c.ui_display())
                    #Highlight asterisk
                    header = self._highlight_asterisk(header)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                self.column_widget = column_widget
                self.gl.addWidget(self.column_widget, row_id, 1, 1, 1)

                #Add user tip if specified for the column configuration
                if c.user_tip:
                    self.tip_lbl = UserTipLabel(user_tip=c.user_tip)
                    self.gl.addWidget(self.tip_lbl, row_id, 2, 1, 1)

                if c.mandatory and not self.has_mandatory:
                    self.has_mandatory = True

                col_name = c.name
                #Replace name accordingly based on column type
                if isinstance(c, MultipleSelectColumn):
                    col_name = c.model_attribute_name
    
                # Add widget to MapperMixin collection
                self.addMapping(
                    col_name,
                    self.column_widget,
                    c.mandatory,
                    pseudoname=c.ui_display()
                )

                # Bump up row_id
                row_id += 1
    
        self.entity_scroll_area.setWidget(self.scroll_widget_contents)

        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()

        if not self._disable_collections:
            ch_entities = self.children_entities()

            for col, ch in ch_entities.iteritems():
                if hasattr(col.entity_relation, 'show_in_parent'):
                    if col.entity_relation.show_in_parent != '0':
                        self._add_fk_browser(ch, col)
                else:
                    self._add_fk_browser(ch, col)

        #Add tab widget if entity supports documents
        if self._entity.supports_documents:
            self.doc_widget = SupportingDocumentsWidget(
                self._entity.supporting_doc,
                self._ent_document_model,
                self
            )

            # Map the source document manager object
            self.addMapping(
                'documents',
                self.doc_widget.source_document_manager
            )

            #
            # # Add attribute tab
            # self._add_primary_attr_widget()

            # Add supporting documents tab
            self.entity_tab_widget.addTab(
                self.doc_widget,
                self.tr('Supporting Documents')
            )

        # Return the correct widget
        if not self.entity_tab_widget is None:
            return self.entity_tab_widget
    
        return self.entity_scroll_area

    def _add_primary_attr_widget(self):
        # Check if the primary entity
        # exists and add if it does not
        pr_txt = self.tr('Primary')
        if not self.entity_tab_widget is None:
            tab_txt = self.entity_tab_widget.tabText(0)
            if not tab_txt == pr_txt:
                self.entity_tab_widget.addTab(
                self.entity_scroll_area,
                pr_txt
            )

    def _add_fk_browser(self, child_entity, column):
        # Create and add foreign key
        # browser to the collection
        from stdm.ui.entity_browser import (
            EntityBrowserWithEditor
        )

        attr = u'{0}_collection'.format(child_entity.name)

        # Return if the attribute does not exist
        if not hasattr(self._model, attr):
            return
        entity_browser = EntityBrowserWithEditor(
            child_entity,
            self,
            MANAGE,
            False,
            plugin=self.plugin
        )
        entity_browser.buttonBox.setVisible(False)
        entity_browser.record_filter = []

        if len(child_entity.label) > 2:
            column_label = child_entity.label
        else:
            # Split and join  to filter out entity name prefix
            # e.g. 'lo_parcel' to 'parcel'
            column_label = format_name(" ".join(child_entity.name.split("_", 1)[1:]))
        self.entity_tab_widget.addTab(
            entity_browser,
            u'{0}'.format(
                column_label
            )
        )
        self.set_filter(child_entity, entity_browser)

    def set_filter(self, entity, browser):
        col = self.filter_col(entity)
        child_model = entity_model(entity)
        child_model_obj = child_model()
        col_obj = getattr(child_model, col.name)
        if self.model() is not None:
            if self.model().id is None:
                browser.filtered_records = []
            else:
                browser.filtered_records = child_model_obj.queryObject().filter(
                    col_obj == self.model().id
                ).all()

        if self.edit_model is not None:
            browser.filtered_records = child_model_obj.queryObject().filter(
                col_obj == self.edit_model.id
            ).all()

        if self.edit_model is None and self.model() is None:
            browser.filtered_records = []

    def filter_col(self, child_entity):
        for col in child_entity.columns.values():
            if col.TYPE_INFO == 'FOREIGN_KEY':
                parent_entity = col.parent
                if parent_entity == self._entity:
                    return col


    def children_entities(self):
        """
        :return: Returns a list of children entities
        that refer to the main entity as the parent.
        :rtype: OrderedDict
        """
        child_columns = OrderedDict()
        for ch in self._entity.children():
            if ch.TYPE_INFO == Entity.TYPE_INFO:
                for col in ch.columns.values():
                    if hasattr(col, 'entity_relation'):

                        if col.parent.name == self._entity.name:
                            child_columns[col] = ch
        return child_columns

    def document_widget(self):
        """
        :return: Returns the widget for managing
        the supporting documents for an entity if enabled.
        :rtype: SupportingDocumentsWidget
        """
        return self.doc_widget

    def source_document_manager(self):
        """
        :return: Returns an instance of the
        SourceDocumentManager only if supporting
        documents are enabled for the given entity. Otherwise,
        None if supporting documents are not enabled.
        :rtype: SourceDocumentManager
        """
        if self.doc_widget is None:
            return None

        return self.doc_widget.source_document_manager

    def _highlight_asterisk(self, text):
        # Highlight asterisk in red
        c = '*'

        # Do not format if there is no asterisk
        if text.find(c) == -1:
            return text

        asterisk_highlight = '<span style=\" color:#ff0000;\">*</span>'
        text = text.replace(c, asterisk_highlight)

        return u'<html><head/><body><p>{0}</p></body></html>'.format(text)

    def _custom_validate(self):
        """
        Override of the MapperMixin which enables custom editor extensions to
        inject additional validation before saving form data.
        :return: Return True if the validation was successful,
        otherwise False.
        :rtype: bool
        """
        if not self._editor_ext is None:
            return self._editor_ext.validate()

        # Return True if there is no custom editor extension specified
        return True

    def _post_save(self, model):
        """
        Include additional post-save logic by custom extensions.
        :param model: SQLAlchemy model
        :type model: object
        """
        if not self._editor_ext is None:
            self._editor_ext.post_save(model)

    def _get_entity_editor_widgets(self):
        """
        Gets entity editor widgets and appends them to a dictionary
        """
        if self.entity_tab_widget:
            tab_count = self.entity_tab_widget.count()
            for i in range(tab_count):
                tab_object = self.entity_tab_widget.widget(i)
                tab_text = self.entity_tab_widget.tabText(i)
                self.entity_editor_widgets[tab_text] = tab_object
        else:
            self.entity_editor_widgets['no_tab'] = self.entity_scroll_area
Example #10
0
class EntityEditorDialog(QDialog, MapperMixin):
    """
    Dialog for editing entity attributes.
    """
    addedModel = pyqtSignal(object)

    def __init__(self,
                 entity,
                 model=None,
                 parent=None,
                 manage_documents=True,
                 collect_model=False,
                 parent_entity=None,
                 exclude_columns=[],
                 plugin=None):
        """
        Class constructor.
        :param entity: Entity object corresponding to a table object.
        :type entity: Entity
        :param model: Data object for loading data into the form widgets.
        If the model is set, then the editor dialog is assumed to be in edit
        mode.
        :type model: object
        :param parent: Parent widget that the form belongs to.
        :type parent: QWidget
        :param manage_documents: True if the dialog should provide controls
        for managing supporting documents. Only applicable if the entity
        allows for supporting documents to be attached.
        :type manage_documents: bool
        :param collect_model: If set to True only returns
        the filled form model without saving it to the database.
        :type collect_model: Boolean
        :param parent_entity: The parent entity of the editor
        :type parent_entity: Object
        :param exclude_columns: List of columns to be excluded if in a list.
        :type exclude_columns: List
        :return: If collect_model, returns SQLAlchemy Model
        """
        QDialog.__init__(self, parent)

        self.entity_table_model = {}

        self.collection_suffix = self.tr('Collection')

        #Set minimum width
        self.setMinimumWidth(450)

        self.plugin = plugin

        #Flag for mandatory columns
        self.has_mandatory = False
        self.reload_form = False
        self._entity = entity
        self.edit_model = model
        self.column_widgets = OrderedDict()
        self._parent = parent
        self.exclude_columns = exclude_columns
        self.entity_tab_widget = None
        self._disable_collections = False
        self.filter_val = None
        self.parent_entity = parent_entity
        self.child_models = OrderedDict()
        self.entity_scroll_area = None
        self.entity_editor_widgets = OrderedDict()
        # Set notification layout bar
        self.vlNotification = QVBoxLayout()
        self.vlNotification.setObjectName('vlNotification')
        self._notifBar = NotificationBar(self.vlNotification)
        self.do_not_check_dirty = False
        # Set manage documents only if the entity supports documents
        if self._entity.supports_documents:
            self._manage_documents = manage_documents
        else:
            self._manage_documents = False

        # Setup entity model
        self._ent_document_model = None
        if self._entity.supports_documents:
            self.ent_model, self._ent_document_model = entity_model(
                self._entity, with_supporting_document=True)
        else:
            self.ent_model = entity_model(self._entity)
        if not model is None:
            self.ent_model = model

        MapperMixin.__init__(self, self.ent_model, entity)

        self.collect_model = collect_model

        self.register_column_widgets()
        try:
            if isinstance(parent._parent, EntityEditorDialog):
                # hide collections form child editor
                self._disable_collections = True

        except AttributeError:
            self._parent._parent = None
        # Set title
        editor_trans = self.tr('Editor')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        self.title = u'{0} {1}'.format(title_str, editor_trans)

        self.setWindowTitle(self.title)

        self._init_gui()
        self.adjustSize()

        self._get_entity_editor_widgets()

        if isinstance(parent._parent, EntityEditorDialog):
            self.parent_entity = parent.parent_entity
            self.set_parent_values()
            # make the size smaller to differentiate from parent and as it
            # only has few tabs.
            self.adjustSize()

        self.attribute_mappers = self._attr_mapper_collection

        # Exception title for editor extension exceptions
        self._ext_exc_msg = self.tr(
            'An error has occured while executing Python code in the editor '
            'extension:')

        # Register custom editor extension if specified
        self._editor_ext = entity_dlg_extension(self)
        if not self._editor_ext is None:
            self._editor_ext.post_init()

            # Initialize CascadingFieldContext objects
            self._editor_ext.connect_cf_contexts()

    def _init_gui(self):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(self.vlNotification, 0, 0, 1, 1)
        QApplication.processEvents()

        #set widgets values
        column_widget_area = self._setup_columns_content_area()

        self.gridLayout.addWidget(column_widget_area, 1, 0, 1, 1)

        QApplication.processEvents()
        # Add notification for mandatory columns if applicable
        next_row = 2
        if self.has_mandatory:
            self.required_fields_lbl = QLabel(self)
            msg = self.tr('Please fill out all required (*) fields.')
            msg = self._highlight_asterisk(msg)
            self.required_fields_lbl.setText(msg)
            self.gridLayout.addWidget(self.required_fields_lbl, next_row, 0, 1,
                                      2)
            # Bump up row reference
            next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(self.buttonBox, next_row, 0, 1, 1)

        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Save)

        if self.edit_model is None:
            if not self.collect_model:
                self.save_new_button = QPushButton(
                    QApplication.translate('EntityEditorDialog',
                                           'Save and New'))
                self.buttonBox.addButton(self.save_new_button,
                                         QDialogButtonBox.ActionRole)

        # edit model, collect model
        # adding new record for child

        # Saving in parent editor
        if not isinstance(self._parent._parent, EntityEditorDialog):
            # adding a new record
            if self.edit_model is None:
                # saving when digitizing.
                if self.collect_model:
                    self.buttonBox.accepted.connect(self.on_model_added)
                # saving parent editor
                else:
                    self.buttonBox.accepted.connect(self.save_parent_editor)
                    self.save_new_button.clicked.connect(self.save_and_new)
            # updating existing record
            else:
                if not self.collect_model:
                    # updating existing record of the parent editor
                    self.buttonBox.accepted.connect(self.save_parent_editor)
                else:
                    self.buttonBox.accepted.connect(self.on_model_added)
        # Saving in child editor
        else:
            # save and new record
            if self.edit_model is None:
                self.buttonBox.accepted.connect(self.on_child_saved)
                self.save_new_button.clicked.connect(
                    lambda: self.on_child_saved(True))

            else:
                # When updating an existing child editor save to the db
                self.buttonBox.accepted.connect(self.on_child_saved)
                #self.buttonBox.accepted.connect(self.submit)

        self.buttonBox.rejected.connect(self.cancel)

    @property
    def notification_bar(self):
        """
        :return: Returns the dialog's notification bar.
        :rtype: NotificationBar
        """
        return self._notifBar

    def save_parent_editor(self):
        """
        Saves the parent editor and its children.
        """
        self.submit()
        self.save_children()

    def set_parent_values(self):
        """
        Sets the parent display column for the child.
        """
        if self.parent_entity is None:
            return
        for col in self._entity.columns.values():
            if col.TYPE_INFO == 'FOREIGN_KEY':
                parent_entity = col.parent
                if parent_entity == self.parent_entity:
                    self.parent_widgets_value_setter(self._parent._parent, col)

    def parent_widgets_value_setter(self, parent, col):
        """
        Finds and sets the value from parent widget and set it to the column
        widget of a child using the child column.
        :param parent: The parent widget
        :type parent: QWidget
        :param col: The child column object
        :type col: Object
        """
        for parent_col, parent_widget in parent.column_widgets.iteritems():
            if parent_col.name == col.name:
                self.single_parent_value_setter(col, parent_widget)
                break
            if parent_col.name in col.entity_relation.display_cols:
                self.single_parent_value_setter(col, parent_widget)
                break

    def single_parent_value_setter(self, col, parent_widget):
        """
        Gets value from parent widget and set it to the column widget of a
        child using the child column.
        :param parent: The parent widget
        :type parent: QWidget
        :param col: The child column object
        :type col: Object
        """
        local_widget = self.column_widgets[col]
        local_widget.show_clear_button()
        self.filter_val = parent_widget.text()
        local_widget.setText(self.filter_val)

    def save_and_new(self):
        """
        A slot raised when Save and New button is click. It saves the form
        without showing a success message. Then it sets reload_form property
        to True so that entity_browser can re-load the form.
        """
        from stdm.ui.entity_browser import (EntityBrowserWithEditor)
        self.submit(False, True)
        self.save_children()

        if self.is_valid:
            self.addedModel.emit(self.model())
            self.setModel(self.ent_model())
            self.clear()
            self.child_models.clear()
            for index in range(0, self.entity_tab_widget.count() - 1):
                if isinstance(self.entity_tab_widget.widget(index),
                              EntityBrowserWithEditor):
                    child_browser = self.entity_tab_widget.widget(index)
                    child_browser.remove_rows()

    def on_model_added(self):
        """
        A slot raised when a form is submitted with collect model set to True.
        There will be no success message and the form does not close.
        """
        self.submit(True)
        self.addedModel.emit(self.model())

    def closeEvent(self, event):
        '''
        Raised when a request to close the window is received.
        Check the dirty state of input controls and prompt user to
        save if dirty.
        '''

        if self.do_not_check_dirty:
            event.accept()
            return
        isDirty, userResponse = self.checkDirty()

        if isDirty:
            if userResponse == QMessageBox.Yes:
                # We need to ignore the event so that validation and
                # saving operations can be executed
                event.ignore()
                self.submit()
            elif userResponse == QMessageBox.No:
                event.accept()
            elif userResponse == QMessageBox.Cancel:
                event.ignore()
        else:
            event.accept()

    def on_child_saved(self, save_and_new=False):
        """
        A slot raised when the save or save and new button is clicked. It sets
        the child_models dictionary of the parent when saved.
        :param save_and_new: A boolean indicating the save and new button is
        clicked to trigger the slot.
        :type save_and_new: Boolean
        """
        if self.parent_entity is None:
            return

        self.submit(True)

        insert_pos = self._parent.tbEntity.model().rowCount() + 1
        # Save to parent editor so that it is persistent.
        self._parent._parent.child_models[insert_pos, self._entity] = \
            self.model()
        self.addedModel.emit(self.model())
        if not save_and_new:
            self.accept()

        else:
            if self.is_valid:
                #self.addedModel.emit(self.model())
                self.setModel(self.ent_model())

                self.clear()
                self.set_parent_values()

    def save_children(self):
        """
        Saves children models into the database by assigning the the id of the
        parent for foreign key column.
        """
        if len(self.child_models) < 1:
            return
        children_obj = []
        for row_entity, model in self.child_models.iteritems():
            row_pos = row_entity[0]
            entity = row_entity[1]
            ent_model = entity_model(entity)
            entity_obj = ent_model()
            for col in entity.columns.values():
                if col.TYPE_INFO == 'FOREIGN_KEY':
                    if col.parent.name == self._entity.name:
                        setattr(model, col.name, self.model().id)
                        children_obj.append(model)
            entity_obj.saveMany(children_obj)

    def register_column_widgets(self):
        """
        Registers the column widgets.
        """
        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        self.scroll_widget_contents = QWidget()
        self.scroll_widget_contents.setObjectName('scrollAreaWidgetContents')
        for c in self._entity.columns.values():
            if c.name in self.exclude_columns:
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue
            # Get widget factory
            column_widget = ColumnWidgetRegistry.create(
                c, self.scroll_widget_contents, host=self)
            self.column_widgets[c] = column_widget

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')

        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for c, column_widget in self.column_widgets.iteritems():
            if c.name in self.exclude_columns:
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                # Format label text if it is a mandatory field
                if c.mandatory:
                    header = u'{0} *'.format(c.ui_display())
                    #Highlight asterisk
                    header = self._highlight_asterisk(header)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                self.column_widget = column_widget
                self.gl.addWidget(self.column_widget, row_id, 1, 1, 1)

                #Add user tip if specified for the column configuration
                if c.user_tip:
                    self.tip_lbl = UserTipLabel(user_tip=c.user_tip)
                    self.gl.addWidget(self.tip_lbl, row_id, 2, 1, 1)

                if c.mandatory and not self.has_mandatory:
                    self.has_mandatory = True

                col_name = c.name
                #Replace name accordingly based on column type
                if isinstance(c, MultipleSelectColumn):
                    col_name = c.model_attribute_name

                # Add widget to MapperMixin collection
                self.addMapping(col_name,
                                self.column_widget,
                                c.mandatory,
                                pseudoname=c.ui_display())

                # Bump up row_id
                row_id += 1

        self.entity_scroll_area.setWidget(self.scroll_widget_contents)

        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()

        if not self._disable_collections:
            ch_entities = self.children_entities()

            for col, ch in ch_entities.iteritems():
                if hasattr(col.entity_relation, 'show_in_parent'):
                    if col.entity_relation.show_in_parent != '0':
                        self._add_fk_browser(ch, col)
                else:
                    self._add_fk_browser(ch, col)

        #Add tab widget if entity supports documents
        if self._entity.supports_documents:
            self.doc_widget = SupportingDocumentsWidget(
                self._entity.supporting_doc, self._ent_document_model, self)

            # Map the source document manager object
            self.addMapping('documents',
                            self.doc_widget.source_document_manager)

            #
            # # Add attribute tab
            # self._add_primary_attr_widget()

            # Add supporting documents tab
            self.entity_tab_widget.addTab(self.doc_widget,
                                          self.tr('Supporting Documents'))

        # Return the correct widget
        if not self.entity_tab_widget is None:
            return self.entity_tab_widget

        return self.entity_scroll_area

    def _add_primary_attr_widget(self):
        # Check if the primary entity
        # exists and add if it does not
        pr_txt = self.tr('Primary')
        if not self.entity_tab_widget is None:
            tab_txt = self.entity_tab_widget.tabText(0)
            if not tab_txt == pr_txt:
                self.entity_tab_widget.addTab(self.entity_scroll_area, pr_txt)

    def _add_fk_browser(self, child_entity, column):
        # Create and add foreign key
        # browser to the collection
        from stdm.ui.entity_browser import (EntityBrowserWithEditor)

        attr = u'{0}_collection'.format(child_entity.name)

        # Return if the attribute does not exist
        if not hasattr(self._model, attr):
            return
        entity_browser = EntityBrowserWithEditor(child_entity,
                                                 self,
                                                 MANAGE,
                                                 False,
                                                 plugin=self.plugin)
        entity_browser.buttonBox.setVisible(False)
        entity_browser.record_filter = []

        if len(child_entity.label) > 2:
            column_label = child_entity.label
        else:
            # Split and join  to filter out entity name prefix
            # e.g. 'lo_parcel' to 'parcel'
            column_label = format_name(" ".join(
                child_entity.name.split("_", 1)[1:]))
        self.entity_tab_widget.addTab(entity_browser,
                                      u'{0}'.format(column_label))
        self.set_filter(child_entity, entity_browser)

    def set_filter(self, entity, browser):
        col = self.filter_col(entity)
        child_model = entity_model(entity)
        child_model_obj = child_model()
        col_obj = getattr(child_model, col.name)
        if self.model() is not None:
            if self.model().id is None:
                browser.filtered_records = []
            else:
                browser.filtered_records = child_model_obj.queryObject(
                ).filter(col_obj == self.model().id).all()

        if self.edit_model is not None:
            browser.filtered_records = child_model_obj.queryObject().filter(
                col_obj == self.edit_model.id).all()

        if self.edit_model is None and self.model() is None:
            browser.filtered_records = []

    def filter_col(self, child_entity):
        for col in child_entity.columns.values():
            if col.TYPE_INFO == 'FOREIGN_KEY':
                parent_entity = col.parent
                if parent_entity == self._entity:
                    return col

    def children_entities(self):
        """
        :return: Returns a list of children entities
        that refer to the main entity as the parent.
        :rtype: OrderedDict
        """
        child_columns = OrderedDict()
        for ch in self._entity.children():
            if ch.TYPE_INFO == Entity.TYPE_INFO:
                for col in ch.columns.values():
                    if hasattr(col, 'entity_relation'):

                        if col.parent.name == self._entity.name:
                            child_columns[col] = ch
        return child_columns

    def document_widget(self):
        """
        :return: Returns the widget for managing
        the supporting documents for an entity if enabled.
        :rtype: SupportingDocumentsWidget
        """
        return self.doc_widget

    def source_document_manager(self):
        """
        :return: Returns an instance of the
        SourceDocumentManager only if supporting
        documents are enabled for the given entity. Otherwise,
        None if supporting documents are not enabled.
        :rtype: SourceDocumentManager
        """
        if self.doc_widget is None:
            return None

        return self.doc_widget.source_document_manager

    def _highlight_asterisk(self, text):
        # Highlight asterisk in red
        c = '*'

        # Do not format if there is no asterisk
        if text.find(c) == -1:
            return text

        asterisk_highlight = '<span style=\" color:#ff0000;\">*</span>'
        text = text.replace(c, asterisk_highlight)

        return u'<html><head/><body><p>{0}</p></body></html>'.format(text)

    def _custom_validate(self):
        """
        Override of the MapperMixin which enables custom editor extensions to
        inject additional validation before saving form data.
        :return: Return True if the validation was successful,
        otherwise False.
        :rtype: bool
        """
        if not self._editor_ext is None:
            return self._editor_ext.validate()

        # Return True if there is no custom editor extension specified
        return True

    def _post_save(self, model):
        """
        Include additional post-save logic by custom extensions.
        :param model: SQLAlchemy model
        :type model: object
        """
        if not self._editor_ext is None:
            self._editor_ext.post_save(model)

    def _get_entity_editor_widgets(self):
        """
        Gets entity editor widgets and appends them to a dictionary
        """
        if self.entity_tab_widget:
            tab_count = self.entity_tab_widget.count()
            for i in range(tab_count):
                tab_object = self.entity_tab_widget.widget(i)
                tab_text = self.entity_tab_widget.tabText(i)
                self.entity_editor_widgets[tab_text] = tab_object
        else:
            self.entity_editor_widgets['no_tab'] = self.entity_scroll_area
Example #11
0
class DHakkinda(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.resize(500, 350)
        self.setMaximumSize(500, 350)
        self.gLayout = QGridLayout(self)
        self.logo = QLabel(self)
        self.logo.setPixmap(QPixmap(":/logo/data/logo.png"))
        self.gLayout.addWidget(self.logo, 0, 0, 2, 1)
        self.appName = QLabel(self)
        font = QFont()
        font.setPointSize(32)
        font.setWeight(50)
        self.appName.setFont(font)
        self.gLayout.addWidget(self.appName, 0, 1, 1, 2)
        self.appVersion = QLabel(self)
        font = QFont()
        font.setPointSize(9)
        font.setWeight(75)
        font.setBold(True)
        self.appVersion.setFont(font)
        self.appVersion.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
        self.gLayout.addWidget(self.appVersion, 1, 1, 1, 2)
        self.gBox = QGroupBox(self)
        font = QFont()
        font.setPointSize(12)
        font.setWeight(75)
        font.setBold(True)
        self.gBox.setFont(font)
        self.gLayout2 = QGridLayout(self.gBox)
        self.scrollArea = QScrollArea(self.gBox)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setWidgetResizable(True)

        self.scrollAreaWidgetContents = QWidget()
        self.scrollAreaWidgetContents.setGeometry(0, 0, 476, 199)

        self.gLayout3 = QGridLayout(self.scrollAreaWidgetContents)
        self.appHakkinda = QLabel(self.scrollAreaWidgetContents)
        font = QFont()
        font.setPointSize(9)
        font.setWeight(50)
        font.setBold(False)
        self.appHakkinda.setFont(font)
        self.appHakkinda.setWordWrap(True)
        self.gLayout3.addWidget(self.appHakkinda, 0, 0, 1, 1)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gLayout2.addWidget(self.scrollArea, 0, 0, 1, 1)
        self.gLayout.addWidget(self.gBox, 2, 0, 2, 4)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        self.gLayout.addItem(spacerItem, 0, 3, 1, 1)
        spacerItem1 = QSpacerItem(20, 40, QSizePolicy.Minimum,
                                  QSizePolicy.Expanding)
        self.gLayout.addItem(spacerItem1, 2, 1, 1, 2)

        self.setWindowTitle(u"Virux Hakkında")
        self.appName.setText(u"Virux")
        self.appVersion.setText(u"Sürüm %s" %
                                QApplication.applicationVersion())
        self.gBox.setTitle(u"Hakkında")
        self.appHakkinda.setText(u"""
        <p>Virux, platform bağımsız bir antivirüs yazılımıdır :P</p>
        <p>Yazılımıın bir arayüzü yoktur. Sadece sistem çubuğunda bir tepsi oluşur. Bu tepsi animasyon şeklindedir.</p>
        <p>Rasgele zamanlarda mevcut olan dialoglardan bir tanesi ekranda gözükecektir. Sadece eğlence amacıyla yapılmıştır...</p>
        <p><b>Geliştirici:</b> Metehan Özbek - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Görsel Çalışma:</b> Yasin Özcan - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Katkı Yapanlar:</b> Yaşar Arabacı - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Lisans:</b> GPL v3</p>
        <p></p>""")
Example #12
0
class XPopupWidget(QWidget):
    """ """
    Direction = enum('North', 'South', 'East', 'West')
    Mode      = enum('Popup', 'Dialog', 'ToolTip')
    Anchor    = enum('TopLeft',
                     'TopCenter',
                     'TopRight',
                     'LeftTop',
                     'LeftCenter',
                     'LeftBottom',
                     'RightTop',
                     'RightCenter',
                     'RightBottom',
                     'BottomLeft',
                     'BottomCenter',
                     'BottomRight')
    
    aboutToShow     = qt.Signal()
    accepted        = qt.Signal()
    closed          = qt.Signal()
    rejected        = qt.Signal()
    resetRequested  = qt.Signal()
    shown           = qt.Signal()
    buttonClicked   = qt.Signal(QAbstractButton)
    
    def __init__(self, parent=None, buttons=None):
        super(XPopupWidget, self).__init__(parent)
        
        # define custom properties
        self._anchor                = XPopupWidget.Anchor.TopCenter
        self._autoCalculateAnchor   = False
        self._autoCloseOnAccept     = True
        self._autoCloseOnReject     = True
        self._autoCloseOnFocusOut   = False
        self._autoDefault           = True
        self._first                 = True
        self._animated              = False
        self._currentMode           = None
        self._positionLinkedTo      = []
        
        # define controls
        self._resizable     = True
        self._popupPadding  = 10
        self._titleBarVisible = True
        self._buttonBoxVisible = True
        self._dialogButton  = QToolButton(self)
        self._closeButton   = QToolButton(self)
        self._scrollArea    = QScrollArea(self)
        self._sizeGrip      = QSizeGrip(self)
        self._sizeGrip.setFixedWidth(12)
        self._sizeGrip.setFixedHeight(12)
        
        self._leftSizeGrip  = QSizeGrip(self)
        self._leftSizeGrip.setFixedWidth(12)
        self._leftSizeGrip.setFixedHeight(12)
        
        if buttons is None:
            buttons = QDialogButtonBox.NoButton
        
        self._buttonBox     = QDialogButtonBox(buttons, Qt.Horizontal, self)
        self._buttonBox.setContentsMargins(3, 0, 3, 9)
        
        self._scrollArea.setWidgetResizable(True)
        self._scrollArea.setFrameShape(QScrollArea.NoFrame)
        self._scrollArea.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        
        palette = self.palette()
        self._scrollArea.setPalette(palette)
        
        self._dialogButton.setToolTip('Popout to Dialog')
        self._closeButton.setToolTip('Close Popup')
        
        for btn in (self._dialogButton, self._closeButton):
            btn.setAutoRaise(True)
            btn.setIconSize(QSize(14, 14))
            btn.setMaximumSize(16, 16)
        
        # setup the icons
        icon = QIcon(projexui.resources.find('img/dialog.png'))
        self._dialogButton.setIcon(icon)
        
        icon = QIcon(projexui.resources.find('img/close.png'))
        self._closeButton.setIcon(icon)
        
        # define the ui
        hlayout = QHBoxLayout()
        hlayout.setSpacing(0)
        hlayout.addStretch(1)
        hlayout.addWidget(self._dialogButton)
        hlayout.addWidget(self._closeButton)
        hlayout.setContentsMargins(0, 0, 0, 0)
        
        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(self._buttonBox)
        hlayout2.setContentsMargins(0, 0, 3, 0)
        
        vlayout = QVBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self._scrollArea)
        vlayout.addLayout(hlayout2)
        vlayout.setContentsMargins(3, 2, 3, 2)
        vlayout.setSpacing(0)
        
        self.setLayout(vlayout)
        self.setPositionLinkedTo(parent)
        
        # set default properties
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.Button)
        self.setWindowTitle('Popup')
        self.setFocusPolicy(Qt.StrongFocus)
        self.setCurrentMode(XPopupWidget.Mode.Popup)
        
        # create connections
        self._dialogButton.clicked.connect(self.setDialogMode)
        self._closeButton.clicked.connect(self.reject)
        self._buttonBox.accepted.connect(self.accept)
        self._buttonBox.rejected.connect(self.reject)
        self._buttonBox.clicked.connect(self.handleButtonClick)
    
    def addButton(self, button, role=QDialogButtonBox.ActionRole):
        """
        Adds a custom button to the button box for this popup widget.
        
        :param      button | <QAbstractButton> || <str>
        
        :return     <button> || None (based on if a button or string was given)
        """
        return self._buttonBox.addButton(button, role)
    
    def adjustContentsMargins( self ):
        """
        Adjusts the contents for this widget based on the anchor and \
        mode.
        """
        anchor = self.anchor()
        mode   = self.currentMode()
        
        # margins for a dialog
        if ( mode == XPopupWidget.Mode.Dialog ):
            self.setContentsMargins(0, 0, 0, 0)
        
        # margins for a top anchor point
        elif ( anchor & (XPopupWidget.Anchor.TopLeft |
                         XPopupWidget.Anchor.TopCenter |
                         XPopupWidget.Anchor.TopRight) ):
            self.setContentsMargins(0, self.popupPadding() + 5, 0, 0)
        
        # margins for a bottom anchor point
        elif ( anchor & (XPopupWidget.Anchor.BottomLeft |
                         XPopupWidget.Anchor.BottomCenter |
                         XPopupWidget.Anchor.BottomRight) ):
            self.setContentsMargins(0, 0, 0, self.popupPadding())
        
        # margins for a left anchor point
        elif ( anchor & (XPopupWidget.Anchor.LeftTop |
                         XPopupWidget.Anchor.LeftCenter |
                         XPopupWidget.Anchor.LeftBottom) ):
            self.setContentsMargins(self.popupPadding(), 0, 0, 0)
        
        # margins for a right anchor point
        else:
            self.setContentsMargins(0, 0, self.popupPadding(), 0)
        
        self.adjustMask()
    
    def adjustMask(self):
        """
        Updates the alpha mask for this popup widget.
        """
        if self.currentMode() == XPopupWidget.Mode.Dialog:
            self.clearMask()
            return
        
        path = self.borderPath()
        bitmap = QBitmap(self.width(), self.height())
        bitmap.fill(QColor('white'))
        
        painter = QPainter()
        painter.begin(bitmap)
        painter.setRenderHint(QPainter.Antialiasing)
        pen = QPen(QColor('black'))
        pen.setWidthF(0.75)
        painter.setPen(pen)
        painter.setBrush(QColor('black'))
        painter.drawPath(path)
        painter.end()
        
        self.setMask(bitmap)
    
    def adjustSize(self):
        """
        Adjusts the size of this popup to best fit the new widget size.
        """
        widget = self.centralWidget()
        if widget is None:
            super(XPopupWidget, self).adjustSize()
            return
        
        widget.adjustSize()
        hint = widget.minimumSizeHint()
        size = widget.minimumSize()
        
        width  = max(size.width(),  hint.width())
        height = max(size.height(), hint.height())
        
        width += 20
        height += 20
        
        if self._buttonBoxVisible:
            height += self.buttonBox().height() + 10
            
        if self._titleBarVisible:
            height += max(self._dialogButton.height(),
                          self._closeButton.height()) + 10
        
        curr_w = self.width()
        curr_h = self.height()
        
        # determine if we need to move based on our anchor
        anchor = self.anchor()
        if anchor & (self.Anchor.LeftBottom | self.Anchor.RightBottom | \
                       self.Anchor.BottomLeft | self.Anchor.BottomCenter | \
                       self.Anchor.BottomRight):
            delta_y = height - curr_h
        
        elif anchor & (self.Anchor.LeftCenter | self.Anchor.RightCenter):
            delta_y = (height - curr_h) / 2
        
        else:
            delta_y = 0
        
        if anchor & (self.Anchor.RightTop | self.Anchor.RightCenter | \
                       self.Anchor.RightTop | self.Anchor.TopRight):
            delta_x = width - curr_w
        
        elif anchor & (self.Anchor.TopCenter | self.Anchor.BottomCenter):
            delta_x = (width - curr_w) / 2
        
        else:
            delta_x = 0
        
        self.setMinimumSize(width, height)
        self.resize(width, height)
        
        pos = self.pos()
        pos.setX(pos.x() - delta_x)
        pos.setY(pos.y() - delta_y)
        
        self.move(pos)
    
    @qt.Slot()
    def accept(self):
        """
        Emits the accepted signal and closes the popup.
        """
        if not self.signalsBlocked():
            self.accepted.emit()
        
        if self.autoCloseOnAccept():
            self.close()
    
    def anchor( self ):
        """
        Returns the anchor point for this popup widget.
        
        :return     <XPopupWidget.Anchor>
        """
        return self._anchor
    
    def autoCalculateAnchor( self ):
        """
        Returns whether or not this popup should calculate the anchor point
        on popup based on the parent widget and the popup point.
        
        :return     <bool>
        """
        return self._autoCalculateAnchor
    
    def autoCloseOnAccept( self ):
        """
        Returns whether or not this popup widget manages its own close on accept
        behavior.
        
        :return     <bool>
        """
        return self._autoCloseOnAccept
    
    def autoCloseOnReject( self ):
        """
        Returns whether or not this popup widget manages its own close on reject
        behavior.
        
        :return     <bool>
        """
        return self._autoCloseOnReject
    
    def autoCloseOnFocusOut(self):
        """
        Returns whether or not this popup widget should auto-close when the user
        clicks off the view.
        
        :return     <bool>
        """
        return self._autoCloseOnFocusOut
    
    def autoDefault(self):
        """
        Returns whether or not clicking enter should default to the accept key.
        
        :return     <bool>
        """
        return self._autoDefault
    
    def borderPath(self):
        """
        Returns the border path that will be drawn for this widget.
        
        :return     <QPainterPath>
        """
        
        path = QPainterPath()
        
        x = 1
        y = 1
        w = self.width() - 2
        h = self.height() - 2
        pad = self.popupPadding()
        anchor = self.anchor()
        
        # create a path for a top-center based popup
        if anchor == XPopupWidget.Anchor.TopCenter:
            path.moveTo(x, y + pad)
            path.lineTo(x + ((w/2) - pad), y + pad)
            path.lineTo(x + (w/2), y)
            path.lineTo(x + ((w/2) + pad), y + pad)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y + pad)
        
        # create a path for a top-left based popup
        elif anchor == XPopupWidget.Anchor.TopLeft:
            path.moveTo(x, y + pad)
            path.lineTo(x + pad, y)
            path.lineTo(x + 2 * pad, y + pad)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y + pad)
        
        # create a path for a top-right based popup
        elif anchor == XPopupWidget.Anchor.TopRight:
            path.moveTo(x, y + pad)
            path.lineTo(x + w - 2 * pad, y + pad)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y + pad)
        
        # create a path for a bottom-left based popup
        elif anchor == XPopupWidget.Anchor.BottomLeft:
            path.moveTo(x, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + 2 * pad, y + h - pad)
            path.lineTo(x + pad, y + h)
            path.lineTo(x, y + h - pad)
            path.lineTo(x, y)
        
        # create a path for a south based popup
        elif anchor == XPopupWidget.Anchor.BottomCenter:
            path.moveTo(x, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + ((w/2) + pad), y + h - pad)
            path.lineTo(x + (w/2), y + h)
            path.lineTo(x + ((w/2) - pad), y + h - pad)
            path.lineTo(x, y + h - pad)
            path.lineTo(x, y)
        
        # create a path for a bottom-right based popup
        elif anchor == XPopupWidget.Anchor.BottomRight:
            path.moveTo(x, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x + w - 2 * pad, y + h - pad)
            path.lineTo(x, y + h - pad)
            path.lineTo(x, y)
        
        # create a path for a right-top based popup
        elif anchor == XPopupWidget.Anchor.RightTop:
            path.moveTo(x, y)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w - pad, y + 2 * pad)
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y)
        
        # create a path for a right-center based popup
        elif anchor == XPopupWidget.Anchor.RightCenter:
            path.moveTo(x, y)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w - pad, y + ((h/2) - pad))
            path.lineTo(x + w, y + (h/2))
            path.lineTo(x + w - pad, y + ((h/2) + pad))
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y)
        
        # create a path for a right-bottom based popup
        elif anchor == XPopupWidget.Anchor.RightBottom:
            path.moveTo(x, y)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w - pad, y + h - 2 * pad)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y)
        
        # create a path for a left-top based popup
        elif anchor == XPopupWidget.Anchor.LeftTop:
            path.moveTo(x + pad, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h)
            path.lineTo(x + pad, y + h)
            path.lineTo(x + pad, y + 2 * pad)
            path.lineTo(x, y + pad)
            path.lineTo(x + pad, y)
        
        # create a path for an left-center based popup
        elif anchor == XPopupWidget.Anchor.LeftCenter:
            path.moveTo(x + pad, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h)
            path.lineTo(x + pad, y + h)
            path.lineTo(x + pad, y + ((h/2) + pad))
            path.lineTo(x, y + (h/2))
            path.lineTo(x + pad, y + ((h/2) - pad))
            path.lineTo(x + pad, y)
        
        # create a path for a left-bottom based popup
        elif anchor == XPopupWidget.Anchor.LeftBottom:
            path.moveTo(x + pad, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h)
            path.lineTo(x + pad, y + h)
            path.lineTo(x, y + h - pad)
            path.lineTo(x + pad, y + h - 2 * pad)
            path.lineTo(x + pad, y)
        
        return path
    
    def buttonBox( self ):
        """
        Returns the button box that is used to control this popup widget.
        
        :return     <QDialogButtonBox>
        """
        return self._buttonBox
    
    def centralWidget( self ):
        """
        Returns the central widget that is being used by this popup.
        
        :return     <QWidget>
        """
        return self._scrollArea.widget()
    
    def close(self):
        """
        Closes the popup widget and central widget.
        """
        widget = self.centralWidget()
        if widget and not widget.close():
            return
            
        super(XPopupWidget, self).close()
    
    def closeEvent(self, event):
        widget = self.centralWidget()
        if widget and not widget.close() and \
           self.currentMode() != XPopupWidget.Mode.ToolTip:
            event.ignore()
        else:
            super(XPopupWidget, self).closeEvent(event)
        
        self.closed.emit()
    
    def currentMode( self ):
        """
        Returns the current mode for this widget.
        
        :return     <XPopupWidget.Mode>
        """
        return self._currentMode
    
    @deprecatedmethod('XPopupWidget', 
                      'Direction is no longer used, use anchor instead')
    def direction( self ):
        """
        Returns the current direction parameter for this widget.
        
        :return     <XPopupWidget.Direction>
        """
        anchor = self.anchor()
        if ( anchor & (XPopupWidget.Anchor.TopLeft |
                       XPopupWidget.Anchor.TopCenter |
                       XPopupWidget.Anchor.TopRight) ):
            return XPopupWidget.Direction.North
        
        elif ( anchor & (XPopupWidget.Anchor.BottomLeft |
                         XPopupWidget.Anchor.BottomCenter |
                         XPopupWidget.Anchor.BottomRight) ):
            return XPopupWidget.Direction.South
        
        elif ( anchor & (XPopupWidget.Anchor.LeftTop |
                         XPopupWidget.Anchor.LeftCenter |
                         XPopupWidget.Anchor.LeftBottom) ):
            return XPopupWidget.Direction.East
        
        else:
            return XPopupWidget.Direction.West
    
    def eventFilter(self, object, event):
        """
        Processes when the window is moving to update the position for the
        popup if in popup mode.
        
        :param      object | <QObject>
                    event  | <QEvent>
        """
        links = self.positionLinkedTo()
        is_dialog = self.currentMode() == self.Mode.Dialog
        if object not in links:
            return False
        
        if event.type() == event.Close:
            self.close()
            return False
        
        if event.type() == event.Hide and not is_dialog:
            self.hide()
            return False
        
        if event.type() == event.Move and not is_dialog:
            deltaPos = event.pos() - event.oldPos()
            self.move(self.pos() + deltaPos)
            return False
        
        if self.currentMode() != self.Mode.ToolTip:
            return False
        
        if event.type() == event.Leave:
            pos = object.mapFromGlobal(QCursor.pos())
            if (not object.rect().contains(pos)):
                self.close()
                event.accept()
                return True
        
        if event.type() in (event.MouseButtonPress, event.MouseButtonDblClick):
            self.close()
            event.accept()
            return True
        
        return False
    
    @qt.Slot(QAbstractButton)
    def handleButtonClick(self, button):
        """
        Handles the button click for this widget.  If the Reset button was
        clicked, then the resetRequested signal will be emitted.  All buttons
        will emit the buttonClicked signal.
        
        :param      button | <QAbstractButton>
        """
        if ( self.signalsBlocked() ):
            return
        
        if ( button == self._buttonBox.button(QDialogButtonBox.Reset) ):
            self.resetRequested.emit()
        
        self.buttonClicked.emit(button)
    
    def isAnimated(self):
        """
        Returns whether or not the popup widget should animate its opacity
        when it is shown.
        
        :return     <bool>
        """
        return self._animated
    
    def isResizable(self):
        """
        Returns if this popup is resizable or not.
        
        :return     <bool>
        """
        return self._resizable
    
    def keyPressEvent( self, event ):
        """
        Looks for the Esc key to close the popup.
        
        :param      event | <QKeyEvent>
        """
        if ( event.key() == Qt.Key_Escape ):
            self.reject()
            event.accept()
            return
        
        elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ):
            if self._autoDefault:
                self.accept()
                event.accept()
            return
        
        super(XPopupWidget, self).keyPressEvent(event)
    
    def mapAnchorFrom( self, widget, globalPos ):
        """
        Returns the anchor point that best fits within the given widget from
        the inputed global position.
        
        :param      widget      | <QWidget>
                    globalPos   | <QPoint>
        
        :return     <XPopupWidget.Anchor>
        """
        localPos = widget.mapFromGlobal(globalPos)
        
        x = localPos.x()
        y = localPos.y()
        w = widget.width()
        h = widget.height()
        
        cw = self.width() / 2
        ch = self.height() / 2
        
        # by default, try to do a center point, so make sure the center point
        # is at least 1/2 the width longer from each edge
        if x < cw and h - y < ch:
            return XPopupWidget.Anchor.BottomLeft
        elif x < cw:
            return XPopupWidget.Anchor.TopLeft
        elif w - x < cw and h - y < ch:
            return XPopupWidget.Anchor.BottomRight
        elif w - x < cw:
            return XPopupWidget.Anchor.TopRight
        elif h - y < ch:
            return XPopupWidget.Anchor.BottomCenter
        else:
            return XPopupWidget.Anchor.TopCenter
    
    def popup(self, pos=None):
        """
        Pops up this widget at the inputed position.  The inputed point should \
        be in global space.
        
        :param      pos | <QPoint>
        
        :return     <bool> success
        """
        if self._first and self.centralWidget() is not None:
            self.adjustSize()
            self._first = False
        
        if not self.signalsBlocked():
            self.aboutToShow.emit()
        
        if not pos:
            pos = QCursor.pos()
        
        if self.currentMode() == XPopupWidget.Mode.Dialog and \
             self.isVisible():
            return False
        
        elif self.currentMode() == XPopupWidget.Mode.Dialog:
            self.setPopupMode()
        
        # auto-calculate the point
        if self.autoCalculateAnchor():
            self.setAnchor(self.mapAnchorFrom( self.parent(), pos ))
        
        pad = self.popupPadding()
        
        # determine where to move based on the anchor
        anchor = self.anchor()
        
        # MODIFY X POSITION
        # align x-left
        if ( anchor & (XPopupWidget.Anchor.TopLeft |
                       XPopupWidget.Anchor.BottomLeft) ):
            pos.setX(pos.x() - pad)
        
        # align x-center
        elif ( anchor & (XPopupWidget.Anchor.TopCenter |
                         XPopupWidget.Anchor.BottomCenter) ):
            pos.setX(pos.x() - self.width() / 2)
        
        # align x-right
        elif ( anchor & (XPopupWidget.Anchor.TopRight |
                         XPopupWidget.Anchor.BottomRight) ):
            pos.setX(pos.x() - self.width() + pad)
        
        # align x-padded
        elif ( anchor & (XPopupWidget.Anchor.RightTop |
                         XPopupWidget.Anchor.RightCenter |
                         XPopupWidget.Anchor.RightBottom) ):
            pos.setX(pos.x() - self.width())
        
        # MODIFY Y POSITION
        # align y-top
        if ( anchor & (XPopupWidget.Anchor.LeftTop |
                       XPopupWidget.Anchor.RightTop) ):
            pos.setY(pos.y() - pad)
        
        # align y-center
        elif ( anchor & (XPopupWidget.Anchor.LeftCenter |
                         XPopupWidget.Anchor.RightCenter) ):
            pos.setY(pos.y() - self.height() / 2)
        
        # align y-bottom
        elif ( anchor & (XPopupWidget.Anchor.LeftBottom |
                         XPopupWidget.Anchor.RightBottom) ):
            pos.setY(pos.y() - self.height() + pad)
        
        # align y-padded
        elif ( anchor & (XPopupWidget.Anchor.BottomLeft |
                         XPopupWidget.Anchor.BottomCenter |
                         XPopupWidget.Anchor.BottomRight) ):
            pos.setY(pos.y() - self.height())
        
        self.adjustMask()
        self.move(pos)
        self.update()
        self.setUpdatesEnabled(True)
        
        if self.isAnimated():
            anim = QPropertyAnimation(self, 'windowOpacity')
            anim.setParent(self)
            anim.setStartValue(0.0)
            anim.setEndValue(self.windowOpacity())
            anim.setDuration(500)
            anim.finished.connect(anim.deleteLater)
            self.setWindowOpacity(0.0)
        else:
            anim = None
            
        
        self.show()
        
        if self.currentMode() != XPopupWidget.Mode.ToolTip:
            self.activateWindow()
            
            widget = self.centralWidget()
            if widget:
                self.centralWidget().setFocus()
            
        
        if anim:
            anim.start()
        
        if not self.signalsBlocked():
            self.shown.emit()
        
        return True
        
    def paintEvent(self, event):
        """
        Overloads the paint event to handle painting pointers for the popup \
        mode.
        
        :param      event | <QPaintEvent>
        """
        # use the base technique for the dialog mode
        if self.currentMode() == XPopupWidget.Mode.Dialog:
            super(XPopupWidget, self).paintEvent(event)
            return
        
        # setup the coloring options
        palette = self.palette()
        
        painter = QPainter()
        painter.begin(self)
        
        pen = QPen(palette.color(palette.Window).darker(130))
        pen.setWidthF(1.75)
        painter.setPen(pen)
        painter.setRenderHint(painter.Antialiasing)
        painter.setBrush(palette.color(palette.Window))
        painter.drawPath(self.borderPath())
        painter.end()
    
    def popupPadding(self):
        """
        Returns the amount of pixels to pad the popup arrow for this widget.
        
        :return     <int>
        """
        return self._popupPadding
    
    def positionLinkedTo(self):
        """
        Returns the widget that this popup is linked to for positional changes.
        
        :return     [<QWidget>, ..]
        """
        return self._positionLinkedTo
    
    @qt.Slot()
    def reject(self):
        """
        Emits the accepted signal and closes the popup.
        """
        if not self.signalsBlocked():
            self.rejected.emit()
        
        if self.autoCloseOnReject():
            self.close()
        
    def resizeEvent(self, event):
        """
        Resizes this widget and updates the mask.
        
        :param      event | <QResizeEvent>
        """
        self.setUpdatesEnabled(False)
        super(XPopupWidget, self).resizeEvent(event)
        
        self.adjustMask()
        self.setUpdatesEnabled(True)
        
        x = self.width() - self._sizeGrip.width()
        y = self.height() - self._sizeGrip.height()
        
        self._leftSizeGrip.move(0, y)
        self._sizeGrip.move(x, y)
    
    def scrollArea(self):
        """
        Returns the scroll area widget for this popup.
        
        :return     <QScrollArea>
        """
        return self._scrollArea
    
    def setAnimated(self, state):
        """
        Sets whether or not the popup widget should animate its opacity
        when it is shown.
        
        :param      state | <bool>
        """
        self._animated = state
        self.setAttribute(Qt.WA_TranslucentBackground, state)
    
    def setAutoCloseOnAccept( self, state ):
        """
        Sets whether or not the popup handles closing for accepting states.
        
        :param      state | <bool>
        """
        self._autoCloseOnAccept = state
    
    def setAutoCloseOnReject( self, state ):
        """
        Sets whether or not the popup handles closing for rejecting states.
        
        :param      state | <bool>
        """
        self._autoCloseOnReject = state
    
    def setAutoDefault(self, state):
        """
        Sets whether or not the buttons should respond to defaulting options
        when the user is interacting with it.
        
        :param      state | <bool>
        """
        self._autoDefault = state
        for button in self.buttonBox().buttons():
            button.setAutoDefault(state)
            button.setDefault(state)
    
    def setAnchor( self, anchor ):
        """
        Sets the anchor position for this popup widget to the inputed point.
        
        :param      anchor | <XPopupWidget.Anchor>
        """
        self._anchor = anchor
        self.adjustContentsMargins()
    
    def setAutoCalculateAnchor( self, state ):
        """
        Sets whether or not this widget should auto-calculate the anchor point
        based on the parent position when the popup is triggered.
        
        :param      state | <bool>
        """
        self._autoCalculateAnchor = state
    
    def setAutoCloseOnFocusOut(self, state):
        """
        Sets whether or not this popup widget should auto-close when the user
        clicks off the view.
        
        :param     state | <bool>
        """
        self._autoCloseOnFocusOut = state
        self.updateModeSettings()
    
    def setCentralWidget( self, widget ):
        """
        Sets the central widget that will be used by this popup.
        
        :param      widget | <QWidget> || None
        """
        self._scrollArea.takeWidget()
        self._scrollArea.setWidget(widget)
        
        self.adjustSize()
    
    def setCurrentMode( self, mode ):
        """
        Sets the current mode for this dialog to the inputed mode.
        
        :param      mode | <XPopupWidget.Mode>
        """
        if ( self._currentMode == mode ):
            return
        
        self._currentMode = mode
        self.updateModeSettings()
    
    @qt.Slot()
    def setDialogMode(self):
        """
        Sets the current mode value to Dialog.
        """
        self.setCurrentMode(XPopupWidget.Mode.Dialog)
    
    @deprecatedmethod('XPopupWidget',
                      'Direction is no longer used, use setAnchor instead')
    def setDirection( self, direction ):
        """
        Sets the direction for this widget to the inputed direction.
        
        :param      direction | <XPopupWidget.Direction>
        """
        if ( direction == XPopupWidget.Direction.North ):
            self.setAnchor(XPopupWidget.Anchor.TopCenter)
        
        elif ( direction == XPopupWidget.Direction.South ):
            self.setAnchor(XPopupWidget.Anchor.BottomCenter)
        
        elif ( direction == XPopupWidget.Direction.East ):
            self.setAnchor(XPopupWidget.Anchor.LeftCenter)
        
        else:
            self.setAnchor(XPopupWidget.Anchor.RightCenter)
    
    def setPalette(self, palette):
        """
        Sets the palette for this widget and the scroll area.
        
        :param      palette | <QPalette>
        """
        super(XPopupWidget, self).setPalette(palette)
        self._scrollArea.setPalette(palette)
    
    def setPopupMode( self ):
        """
        Sets the current mode value to Popup.
        """
        self.setCurrentMode(XPopupWidget.Mode.Popup)
    
    def setPopupPadding( self, padding ):
        """
        Sets the amount to pad the popup area when displaying this widget.
        
        :param      padding | <int>
        """
        self._popupPadding = padding
        self.adjustContentsMargins()
    
    def setPositionLinkedTo(self, widgets):
        """
        Sets the widget that this popup will be linked to for positional
        changes.
        
        :param      widgets | <QWidget> || [<QWidget>, ..]
        """
        if type(widgets) in (list, set, tuple):
            new_widgets = list(widgets)
        else:
            new_widgets = []
            widget = widgets
            while widget:
                widget.installEventFilter(self)
                new_widgets.append(widget)
                widget = widget.parent()
        
        self._positionLinkedTo = new_widgets
    
    def setResizable( self, state ):
        self._resizable = state
        self._sizeGrip.setVisible(state)
        self._leftSizeGrip.setVisible(state)
    
    def setShowButtonBox(self, state):
        self._buttonBoxVisible = state
        self.buttonBox().setVisible(state)
    
    def setShowTitleBar(self, state):
        self._titleBarVisible = state
        self._dialogButton.setVisible(state)
        self._closeButton.setVisible(state)
    
    def setToolTipMode(self):
        """
        Sets the mode for this popup widget to ToolTip
        """
        self.setCurrentMode(XPopupWidget.Mode.ToolTip)
    
    def setVisible(self, state):
        super(XPopupWidget, self).setVisible(state)
        widget = self.centralWidget()
        if widget:
            widget.setVisible(state)
    
    def timerEvent( self, event ):
        """
        When the timer finishes, hide the tooltip popup widget.
        
        :param      event | <QEvent>
        """
        if self.currentMode() == XPopupWidget.Mode.ToolTip:
            self.killTimer(event.timerId())
            event.accept()
            self.close()
        else:
            super(XPopupWidget, self).timerEvent(event)
        
    def updateModeSettings(self):
        mode = self.currentMode()
        is_visible = self.isVisible()
        
        # display as a floating dialog
        if mode == XPopupWidget.Mode.Dialog:
            self.setWindowFlags(Qt.Dialog | Qt.Tool)
            self.setAttribute(Qt.WA_TransparentForMouseEvents, False)
            self._closeButton.setVisible(False)
            self._dialogButton.setVisible(False)
        
        # display as a user tooltip
        elif mode == XPopupWidget.Mode.ToolTip:
            flags = Qt.Popup | Qt.FramelessWindowHint
            
            self.setWindowFlags(flags)
            self.setBackgroundRole(QPalette.Window)
            self.setAttribute(Qt.WA_TransparentForMouseEvents)
            self.setShowTitleBar(False)
            self.setShowButtonBox(False)
            self.setFocusPolicy(Qt.NoFocus)
            
            # hide the scrollbars
            policy = Qt.ScrollBarAlwaysOff
            self._scrollArea.setVerticalScrollBarPolicy(policy)
            self._scrollArea.setHorizontalScrollBarPolicy(policy)
        
        # display as a popup widget
        else:
            flags = Qt.Popup | Qt.FramelessWindowHint
            
            if not self.autoCloseOnFocusOut():
                flags |= Qt.Tool
            
            self.setWindowFlags(flags)
            self._closeButton.setVisible(self._titleBarVisible)
            self._dialogButton.setVisible(self._titleBarVisible)
            self.setBackgroundRole(QPalette.Button)
        
        self.adjustContentsMargins()
        
        if ( is_visible ):
            self.show()
    
    @staticmethod
    @deprecatedmethod('XPopupWidget',
                      'This method no longer has an effect as we are not '\
                      'storing references to the tooltip.')
    def hideToolTip(key = None):
        """
        Hides any existing tooltip popup widgets.
        
        :warning    This method is deprecated!
        """
        pass
    
    @staticmethod
    def showToolTip( text,
                     point      = None,
                     anchor     = None,
                     parent     = None,
                     background = None,
                     foreground = None,
                     key        = None,
                     seconds    = 5 ):
        """
        Displays a popup widget as a tooltip bubble.
        
        :param      text        | <str>
                    point       | <QPoint> || None
                    anchor      | <XPopupWidget.Mode.Anchor> || None
                    parent      | <QWidget> || None
                    background  | <QColor> || None
                    foreground  | <QColor> || None
                    key         | <str> || None
                    seconds     | <int>
        """
        if point is None:
            point = QCursor.pos()
            
        if parent is None:
            parent = QApplication.activeWindow()
        
        if anchor is None and parent is None:
            anchor = XPopupWidget.Anchor.TopCenter
        
        # create a new tooltip widget
        widget = XPopupWidget(parent)
        widget.setToolTipMode()
        widget.setResizable(False)
        
        # create the tooltip label
        label = QLabel(text, widget)
        label.setOpenExternalLinks(True)
        label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        label.setMargin(3)
        label.setIndent(3)
        label.adjustSize()
    
        widget.setCentralWidget(label)
        
        # update the tip
        label.adjustSize()
        widget.adjustSize()
        
        palette = widget.palette()
        if not background:
            background = palette.color(palette.ToolTipBase)
        if not foreground:
            foreground = palette.color(palette.ToolTipText)
        
        palette.setColor(palette.Window,     QColor(background))
        palette.setColor(palette.WindowText, QColor(foreground))
        widget.setPalette(palette)
        widget.centralWidget().setPalette(palette)
        
        if anchor is None:
            widget.setAutoCalculateAnchor(True)
        else:
            widget.setAnchor(anchor)
        
        widget.setAutoCloseOnFocusOut(True)
        widget.setAttribute(Qt.WA_DeleteOnClose)
        widget.popup(point)
        widget.startTimer(1000 * seconds)
        
        return widget
Example #13
0
class XNavigationEdit(XLineEdit):
    """ """
    navigationChanged = Signal()
    
    __designer_icon__ = projexui.resources.find('img/ui/navigate.png')
    
    def __init__( self, parent = None ):
        super(XNavigationEdit, self).__init__( parent )
        
        # define custom properties
        self._separator             = '/'
        self._partsEditingEnabled   = True
        self._originalText          = ''
        self._scrollWidget          = QScrollArea(self)
        self._partsWidget           = QWidget(self._scrollWidget)
        self._buttonGroup           = QButtonGroup(self)
        self._scrollAmount          = 0
        self._navigationModel       = None
        
        # create the completer tree
        palette = self.palette()
        palette.setColor(palette.Base, palette.color(palette.Window))
        palette.setColor(palette.Text, palette.color(palette.WindowText))
        
        bg      = palette.color(palette.Highlight)
        abg     = bg.darker(115)
        fg      = palette.color(palette.HighlightedText)
        sbg     = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue())
        sabg    = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue())
        sfg     = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue())
        style   = 'QTreeView::item:hover { '\
                  '     color: %s;'\
                  '     background: qlineargradient(x1:0,'\
                  '                                 y1:0,'\
                  '                                 x2:0,'\
                  '                                 y2:1,'\
                  '                                 stop: 0 %s,'\
                  '                                 stop: 1 %s);'\
                  '}' % (sfg, sbg, sabg)
        
        self._completerTree = QTreeView(self)
        self._completerTree.setStyleSheet(style)
        self._completerTree.header().hide()
        self._completerTree.setFrameShape(QTreeView.Box)
        self._completerTree.setFrameShadow(QTreeView.Plain)
        self._completerTree.setPalette(palette)
        self._completerTree.setEditTriggers(QTreeView.NoEditTriggers)
        self._completerTree.setWindowFlags(Qt.Popup)
        self._completerTree.installEventFilter(self)
        self._completerTree.setRootIsDecorated(False)
        self._completerTree.setItemsExpandable(False)
        
        # create the editing widget
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addStretch()
        
        self._scrollWidget.setFrameShape( QScrollArea.NoFrame )
        self._scrollWidget.setFocusPolicy(Qt.NoFocus)
        self._scrollWidget.setWidget(self._partsWidget)
        self._scrollWidget.setWidgetResizable(True)
        self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight)
        self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scrollWidget.setContentsMargins(0, 0, 0, 0)
        self._scrollWidget.setViewportMargins(0, 0, 0, 0)
        self._scrollWidget.move(2, 2)
        
        self._partsWidget.setLayout(layout)
        self._partsWidget.setCursor(Qt.ArrowCursor)
        self._partsWidget.setAutoFillBackground(True)
        self._partsWidget.setFixedHeight(self.height() - 12)
        
        palette = self._partsWidget.palette()
        palette.setColor(palette.Background, palette.color(palette.Base))
        self._partsWidget.setPalette(palette)
        
        # create connections
        self._completerTree.clicked.connect( self.navigateToIndex )
        self._buttonGroup.buttonClicked.connect( self.handleButtonClick )
        self._scrollWidget.horizontalScrollBar().valueChanged.connect( 
                                                        self.scrollParts )
    
    def acceptEdit( self ):
        """
        Accepts the current text and rebuilds the parts widget.
        """
        
        if ( self._partsWidget.isVisible() ):
            return False
        
        use_completion = self.completer().popup().isVisible()
        completion     = self.completer().currentCompletion()
        
        self._completerTree.hide()
        self.completer().popup().hide()
        
        if ( use_completion ):
            self.setText(completion)
        else:
            self.rebuild()
            
        return True
    
    def cancelEdit( self ):
        """
        Rejects the current edit and shows the parts widget.
        """
        
        if ( self._partsWidget.isVisible() ):
            return False
            
        self._completerTree.hide()
        self.completer().popup().hide()
        
        self.setText(self._originalText)
        return True
    
    def currentItem( self ):
        """
        Returns the current navigation item from the current path.
        
        :return     <XNavigationItem> || None
        """
        model = self.navigationModel()
        if ( not model ):
            return None
        
        return model.itemByPath(self.text())
    
    def eventFilter( self, object, event ):
        """
        Filters the events for the inputed object through this edit.
        
        :param      object | <QObject>
                    event  | <QEvent>
        
        :return     <bool> | consumed
        """
        if ( event.type() == event.KeyPress ):
            if ( event.key() == Qt.Key_Escape ):
                self._completerTree.hide()
                self.completer().popup().hide()
                
                self.cancelEdit()
                
            elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ):
                self.acceptEdit()
                return True
                
            elif ( event.key() == Qt.Key_Tab ):
                if ( self.completer().popup().isVisible() ):
                    text   = str(self.completer().currentCompletion())
                    super(XNavigationEdit, self).setText(text)
                    return True
                else:
                    self.acceptEdit()
                    return False
            
        elif ( event.type() == event.MouseButtonPress ):
            if ( not self._completerTree.rect().contains(event.pos()) ):
                self._completerTree.hide()
                self.completer().popup().hide()
                
                self.cancelEdit()
        
        return False
    
    def focusOutEvent( self, event ):
        """
        Overloads the focus out event to cancel editing when the widget loses
        focus.
        
        :param      event | <QFocusEvent>
        """
        super(XNavigationEdit, self).focusOutEvent(event)
        
        self.cancelEdit()
    
    def handleButtonClick( self, button ):
        """
        Handle the event when a user clicks on one of the part buttons.
        
        :param      button | <QToolButton>
        """
        path            = button.property('path')
        is_completer    = button.property('is_completer')
        
        # popup a completion menu
        if ( unwrapVariant(is_completer) ):
            model = self.navigationModel()
            if ( not model ):
                return
            
            sep  = self.separator()
            path = str(unwrapVariant(path))
            item = model.itemByPath(path, includeRoot = True)
            if ( not item ):
                return
            
            curr_path = str(self.text()).strip(self.separator())
            curr_path = curr_path.replace(path, '').strip(self.separator())
            
            child_name = ''
            if ( curr_path ):
                child_name = curr_path.split(self.separator())[0]
            
            index = model.indexFromItem(item)
            
            self._completerTree.move(QCursor.pos())
            self._completerTree.setRootIndex(index)
            self._completerTree.verticalScrollBar().setValue(0)
            
            if ( child_name ):
                child_item = None
                for i in range(item.rowCount()):
                    child = item.child(i)
                    if ( child.text() == child_name ):
                        child_item = child
                        break
                
                if ( child_item ):
                    child_index = model.indexFromItem(child_item)
                    self._completerTree.setCurrentIndex(child_index)
                    self._completerTree.scrollTo(child_index)
            
            self._completerTree.show()
            self._completerTree.setUpdatesEnabled(True)
        else:
            self.setText(unwrapVariant(path))
    
    def keyPressEvent( self, event ):
        """
        Overloads the key press event to listen for escape calls to cancel the
        parts editing.
        
        :param      event | <QKeyPressEvent>
        """
        if ( self.scrollWidget().isHidden() ):
            if ( event.key() == Qt.Key_Escape ):
                self.cancelEdit()
                return
                
            elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ):
                self.acceptEdit()
                return
            
        elif ( event.key() == Qt.Key_A and 
               event.modifiers() == Qt.ControlModifier ):
            self.startEdit()
        
        super(XNavigationEdit, self).keyPressEvent(event)
    
    def mouseDoubleClickEvent( self, event ):
        """
        Overloads the system to enable editing when a user double clicks.
        
        :param      event | <QMouseEvent>
        """
        super(XNavigationEdit, self).mouseDoubleClickEvent(event)
        
        self.startEdit()
    
    def navigationModel( self ):
        """
        Returns the navigation model linked with this edit.
        
        :return     <XNavigationModel> || None
        """
        return self._navigationModel
    
    def navigateToIndex( self, index ):
        """
        Navigates to the inputed action's path.
        
        :param      action | <QAction>
        """
        self._completerTree.hide()
        item = self._navigationModel.itemFromIndex(index)
        self.setText(self._navigationModel.itemPath(item))
    
    def parts( self ):
        """
        Returns the parts that are used for this system.
        
        :return     [<str>, ..]
        """
        path = str(self.text()).strip(self.separator())
        if ( not path ):
            return []
        return path.split(self.separator())
    
    def partsWidget( self ):
        """
        Returns the widget that contains the parts system.
        
        :return     <QScrollArea>
        """
        return self._partsWidget
    
    def startEdit( self ):
        """
        Rebuilds the pathing based on the parts.
        """
        self._originalText = self.text()
        self.scrollWidget().hide()
        self.setFocus()
        self.selectAll()
    
    def rebuild( self ):
        """
        Rebuilds the parts widget with the latest text.
        """
        navitem = self.currentItem()
        if ( navitem ):
            navitem.initialize()
            
        self.setUpdatesEnabled(False)
        self.scrollWidget().show()
        self._originalText = ''
        
        partsw = self.partsWidget()
        for button in self._buttonGroup.buttons():
            self._buttonGroup.removeButton(button)
            button.close()
            button.setParent(None)
            button.deleteLater()
        
        # create the root button
        layout = partsw.layout()
        parts  = self.parts()
        
        button = QToolButton(partsw)
        button.setAutoRaise(True)
        button.setMaximumWidth(12)
        button.setArrowType(Qt.RightArrow)
        
        button.setProperty('path',          wrapVariant(''))
        button.setProperty('is_completer',  wrapVariant(True))
        last_button = button
            
        self._buttonGroup.addButton(button)
        layout.insertWidget(0, button)
        
        # check to see if we have a navigation model setup
        if ( self._navigationModel ):
            last_item = self._navigationModel.itemByPath(self.text())
            show_last =  last_item and last_item.rowCount() > 0
        else:
            show_last = False
        
        # load the navigation system
        count = len(parts)
        for i, part in enumerate(parts):
            path = self.separator().join(parts[:i+1])
            
            button = QToolButton(partsw)
            button.setAutoRaise(True)
            button.setText(part)
            
            if ( self._navigationModel ):
                item = self._navigationModel.itemByPath(path)
                if ( item ):
                    button.setIcon(item.icon())
                    button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            
            button.setProperty('path',         wrapVariant(path))
            button.setProperty('is_completer', wrapVariant(False))
            
            self._buttonGroup.addButton(button)
            layout.insertWidget((i * 2) + 1, button)
            
            # determine if we should show the final button
            if ( show_last or i < (count - 1) ):
                button = QToolButton(partsw)
                button.setAutoRaise(True)
                button.setMaximumWidth(12)
                button.setArrowType(Qt.RightArrow)
                
                button.setProperty('path',          wrapVariant(path))
                button.setProperty('is_completer',  wrapVariant(True))
            
                self._buttonGroup.addButton(button)
                layout.insertWidget((i * 2) + 2, button)
                
                last_button = button
        
        if ( self.scrollWidget().width() < partsw.width() ):
            self.scrollParts(partsw.width() - self.scrollWidget().width())
            
        self.setUpdatesEnabled(True)
        self.navigationChanged.emit()
    
    def resizeEvent( self, event ):
        """
        Resizes the current widget and its parts widget.
        
        :param      event | <QResizeEvent>
        """
        super(XNavigationEdit, self).resizeEvent(event)
        
        w = self.width()
        h = self.height()
        
        self._scrollWidget.resize(w - 4, h - 4)
        
        if ( self._scrollWidget.width() < self._partsWidget.width() ):
           self.scrollParts( self._partsWidget.width() - self._scrollWidget.width() )
    
    def scrollParts( self, amount ):
        """
        Scrolls the parts to offset the scrolling amount.
        
        :param      amount | <int>
        """
        change = self._scrollAmount - amount
        self._partsWidget.scroll(change, 0)
        self._scrollAmount = amount
    
    def scrollWidget( self ):
        """
        Returns the scrolling widget.
        
        :return     <QScrollArea>
        """
        return self._scrollWidget
    
    def separator( self ):
        """
        Returns the separation character that is used for this edit.
        
        :return     <str>
        """
        return self._separator
    
    def setTopLevelItems( self, items ):
        """
        Initializes the navigation system to start with the inputed root \
        item.
        
        :param      item | <XNavigationItem>
        """
        if ( not self._navigationModel ):
            self.setNavigationModel(XNavigationModel(self))
        
        self._navigationModel.setTopLevelItems(items)
    
    def setNavigationModel( self, model ):
        """
        Sets the navigation model for this edit.
        
        :param      model | <XNavigationModel>
        """
        self._navigationModel = model
        self._completerTree.setModel(model)
        
        if ( model ):
            model.setSeparator(self.separator())
            completer = XNavigationCompleter(model, self)
            self.setCompleter(completer)
            completer.popup().installEventFilter(self)
        else:
            self.setCompleter(None)
        
        self.rebuild()
    
    def setParts( self, parts ):
        """
        Sets the path for this edit widget by providing the parts to the path.
        
        :param      parts | [<str>, ..]
        """
        self.setText(self.separator().join(map(str, parts)))
    
    def setSeparator( self, separator ):
        """
        Sets the separator to the inputed character.
        
        :param      separator | <str>
        """
        self._separator = separator
        if ( self._navigationModel ):
            self._navigationModel.setSeparator(separator)
        self.rebuild()
    
    def setText( self, text ):
        """
        Sets the text for this edit to the inputed text.
        
        :param      text | <str>
        """
        super(XNavigationEdit, self).setText(text)
        
        self.scrollWidget().show()
        if ( text == '' or self._originalText != text ):
            self.rebuild()
Example #14
0
class XPopupWidget(QWidget):
    """ """
    Direction = enum('North', 'South', 'East', 'West')
    Mode = enum('Popup', 'Dialog', 'ToolTip')
    Anchor = enum('TopLeft', 'TopCenter', 'TopRight', 'LeftTop', 'LeftCenter',
                  'LeftBottom', 'RightTop', 'RightCenter', 'RightBottom',
                  'BottomLeft', 'BottomCenter', 'BottomRight')

    aboutToShow = qt.Signal()
    accepted = qt.Signal()
    closed = qt.Signal()
    rejected = qt.Signal()
    resetRequested = qt.Signal()
    shown = qt.Signal()
    buttonClicked = qt.Signal(QAbstractButton)

    def __init__(self, parent=None, buttons=None):
        super(XPopupWidget, self).__init__(parent)

        # define custom properties
        self._anchor = XPopupWidget.Anchor.TopCenter
        self._autoCalculateAnchor = False
        self._autoCloseOnAccept = True
        self._autoCloseOnReject = True
        self._autoCloseOnFocusOut = False
        self._autoDefault = True
        self._first = True
        self._animated = False
        self._currentMode = None
        self._positionLinkedTo = []

        # define controls
        self._resizable = True
        self._popupPadding = 10
        self._titleBarVisible = True
        self._buttonBoxVisible = True
        self._dialogButton = QToolButton(self)
        self._closeButton = QToolButton(self)
        self._scrollArea = QScrollArea(self)
        self._sizeGrip = QSizeGrip(self)
        self._sizeGrip.setFixedWidth(12)
        self._sizeGrip.setFixedHeight(12)

        self._leftSizeGrip = QSizeGrip(self)
        self._leftSizeGrip.setFixedWidth(12)
        self._leftSizeGrip.setFixedHeight(12)

        if buttons is None:
            buttons = QDialogButtonBox.NoButton

        self._buttonBox = QDialogButtonBox(buttons, Qt.Horizontal, self)
        self._buttonBox.setContentsMargins(3, 0, 3, 9)

        self._scrollArea.setWidgetResizable(True)
        self._scrollArea.setFrameShape(QScrollArea.NoFrame)
        self._scrollArea.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)

        palette = self.palette()
        self._scrollArea.setPalette(palette)

        self._dialogButton.setToolTip('Popout to Dialog')
        self._closeButton.setToolTip('Close Popup')

        for btn in (self._dialogButton, self._closeButton):
            btn.setAutoRaise(True)
            btn.setIconSize(QSize(14, 14))
            btn.setMaximumSize(16, 16)

        # setup the icons
        icon = QIcon(projexui.resources.find('img/dialog.png'))
        self._dialogButton.setIcon(icon)

        icon = QIcon(projexui.resources.find('img/close.png'))
        self._closeButton.setIcon(icon)

        # define the ui
        hlayout = QHBoxLayout()
        hlayout.setSpacing(0)
        hlayout.addStretch(1)
        hlayout.addWidget(self._dialogButton)
        hlayout.addWidget(self._closeButton)
        hlayout.setContentsMargins(0, 0, 0, 0)

        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(self._buttonBox)
        hlayout2.setContentsMargins(0, 0, 3, 0)

        vlayout = QVBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self._scrollArea)
        vlayout.addLayout(hlayout2)
        vlayout.setContentsMargins(3, 2, 3, 2)
        vlayout.setSpacing(0)

        self.setLayout(vlayout)
        self.setPositionLinkedTo(parent)

        # set default properties
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.Button)
        self.setWindowTitle('Popup')
        self.setFocusPolicy(Qt.StrongFocus)
        self.setCurrentMode(XPopupWidget.Mode.Popup)

        # create connections
        self._dialogButton.clicked.connect(self.setDialogMode)
        self._closeButton.clicked.connect(self.reject)
        self._buttonBox.accepted.connect(self.accept)
        self._buttonBox.rejected.connect(self.reject)
        self._buttonBox.clicked.connect(self.handleButtonClick)

    def addButton(self, button, role=QDialogButtonBox.ActionRole):
        """
        Adds a custom button to the button box for this popup widget.
        
        :param      button | <QAbstractButton> || <str>
        
        :return     <button> || None (based on if a button or string was given)
        """
        return self._buttonBox.addButton(button, role)

    def adjustContentsMargins(self):
        """
        Adjusts the contents for this widget based on the anchor and \
        mode.
        """
        anchor = self.anchor()
        mode = self.currentMode()

        # margins for a dialog
        if (mode == XPopupWidget.Mode.Dialog):
            self.setContentsMargins(0, 0, 0, 0)

        # margins for a top anchor point
        elif (anchor &
              (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.TopCenter
               | XPopupWidget.Anchor.TopRight)):
            self.setContentsMargins(0, self.popupPadding() + 5, 0, 0)

        # margins for a bottom anchor point
        elif (
                anchor &
            (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter
             | XPopupWidget.Anchor.BottomRight)):
            self.setContentsMargins(0, 0, 0, self.popupPadding())

        # margins for a left anchor point
        elif (anchor &
              (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.LeftCenter
               | XPopupWidget.Anchor.LeftBottom)):
            self.setContentsMargins(self.popupPadding(), 0, 0, 0)

        # margins for a right anchor point
        else:
            self.setContentsMargins(0, 0, self.popupPadding(), 0)

        self.adjustMask()

    def adjustMask(self):
        """
        Updates the alpha mask for this popup widget.
        """
        if self.currentMode() == XPopupWidget.Mode.Dialog:
            self.clearMask()
            return

        path = self.borderPath()
        bitmap = QBitmap(self.width(), self.height())
        bitmap.fill(QColor('white'))

        painter = QPainter()
        painter.begin(bitmap)
        painter.setRenderHint(QPainter.Antialiasing)
        pen = QPen(QColor('black'))
        pen.setWidthF(0.75)
        painter.setPen(pen)
        painter.setBrush(QColor('black'))
        painter.drawPath(path)
        painter.end()

        self.setMask(bitmap)

    def adjustSize(self):
        """
        Adjusts the size of this popup to best fit the new widget size.
        """
        widget = self.centralWidget()
        if widget is None:
            super(XPopupWidget, self).adjustSize()
            return

        widget.adjustSize()
        hint = widget.minimumSizeHint()
        size = widget.minimumSize()

        width = max(size.width(), hint.width())
        height = max(size.height(), hint.height())

        width += 20
        height += 20

        if self._buttonBoxVisible:
            height += self.buttonBox().height() + 10

        if self._titleBarVisible:
            height += max(self._dialogButton.height(),
                          self._closeButton.height()) + 10

        curr_w = self.width()
        curr_h = self.height()

        # determine if we need to move based on our anchor
        anchor = self.anchor()
        if anchor & (self.Anchor.LeftBottom | self.Anchor.RightBottom | \
                       self.Anchor.BottomLeft | self.Anchor.BottomCenter | \
                       self.Anchor.BottomRight):
            delta_y = height - curr_h

        elif anchor & (self.Anchor.LeftCenter | self.Anchor.RightCenter):
            delta_y = (height - curr_h) / 2

        else:
            delta_y = 0

        if anchor & (self.Anchor.RightTop | self.Anchor.RightCenter | \
                       self.Anchor.RightTop | self.Anchor.TopRight):
            delta_x = width - curr_w

        elif anchor & (self.Anchor.TopCenter | self.Anchor.BottomCenter):
            delta_x = (width - curr_w) / 2

        else:
            delta_x = 0

        self.setMinimumSize(width, height)
        self.resize(width, height)

        pos = self.pos()
        pos.setX(pos.x() - delta_x)
        pos.setY(pos.y() - delta_y)

        self.move(pos)

    @qt.Slot()
    def accept(self):
        """
        Emits the accepted signal and closes the popup.
        """
        if not self.signalsBlocked():
            self.accepted.emit()

        if self.autoCloseOnAccept():
            self.close()

    def anchor(self):
        """
        Returns the anchor point for this popup widget.
        
        :return     <XPopupWidget.Anchor>
        """
        return self._anchor

    def autoCalculateAnchor(self):
        """
        Returns whether or not this popup should calculate the anchor point
        on popup based on the parent widget and the popup point.
        
        :return     <bool>
        """
        return self._autoCalculateAnchor

    def autoCloseOnAccept(self):
        """
        Returns whether or not this popup widget manages its own close on accept
        behavior.
        
        :return     <bool>
        """
        return self._autoCloseOnAccept

    def autoCloseOnReject(self):
        """
        Returns whether or not this popup widget manages its own close on reject
        behavior.
        
        :return     <bool>
        """
        return self._autoCloseOnReject

    def autoCloseOnFocusOut(self):
        """
        Returns whether or not this popup widget should auto-close when the user
        clicks off the view.
        
        :return     <bool>
        """
        return self._autoCloseOnFocusOut

    def autoDefault(self):
        """
        Returns whether or not clicking enter should default to the accept key.
        
        :return     <bool>
        """
        return self._autoDefault

    def borderPath(self):
        """
        Returns the border path that will be drawn for this widget.
        
        :return     <QPainterPath>
        """

        path = QPainterPath()

        x = 1
        y = 1
        w = self.width() - 2
        h = self.height() - 2
        pad = self.popupPadding()
        anchor = self.anchor()

        # create a path for a top-center based popup
        if anchor == XPopupWidget.Anchor.TopCenter:
            path.moveTo(x, y + pad)
            path.lineTo(x + ((w / 2) - pad), y + pad)
            path.lineTo(x + (w / 2), y)
            path.lineTo(x + ((w / 2) + pad), y + pad)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y + pad)

        # create a path for a top-left based popup
        elif anchor == XPopupWidget.Anchor.TopLeft:
            path.moveTo(x, y + pad)
            path.lineTo(x + pad, y)
            path.lineTo(x + 2 * pad, y + pad)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y + pad)

        # create a path for a top-right based popup
        elif anchor == XPopupWidget.Anchor.TopRight:
            path.moveTo(x, y + pad)
            path.lineTo(x + w - 2 * pad, y + pad)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y + pad)

        # create a path for a bottom-left based popup
        elif anchor == XPopupWidget.Anchor.BottomLeft:
            path.moveTo(x, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + 2 * pad, y + h - pad)
            path.lineTo(x + pad, y + h)
            path.lineTo(x, y + h - pad)
            path.lineTo(x, y)

        # create a path for a south based popup
        elif anchor == XPopupWidget.Anchor.BottomCenter:
            path.moveTo(x, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + ((w / 2) + pad), y + h - pad)
            path.lineTo(x + (w / 2), y + h)
            path.lineTo(x + ((w / 2) - pad), y + h - pad)
            path.lineTo(x, y + h - pad)
            path.lineTo(x, y)

        # create a path for a bottom-right based popup
        elif anchor == XPopupWidget.Anchor.BottomRight:
            path.moveTo(x, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x + w - 2 * pad, y + h - pad)
            path.lineTo(x, y + h - pad)
            path.lineTo(x, y)

        # create a path for a right-top based popup
        elif anchor == XPopupWidget.Anchor.RightTop:
            path.moveTo(x, y)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w, y + pad)
            path.lineTo(x + w - pad, y + 2 * pad)
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y)

        # create a path for a right-center based popup
        elif anchor == XPopupWidget.Anchor.RightCenter:
            path.moveTo(x, y)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w - pad, y + ((h / 2) - pad))
            path.lineTo(x + w, y + (h / 2))
            path.lineTo(x + w - pad, y + ((h / 2) + pad))
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y)

        # create a path for a right-bottom based popup
        elif anchor == XPopupWidget.Anchor.RightBottom:
            path.moveTo(x, y)
            path.lineTo(x + w - pad, y)
            path.lineTo(x + w - pad, y + h - 2 * pad)
            path.lineTo(x + w, y + h - pad)
            path.lineTo(x + w - pad, y + h)
            path.lineTo(x, y + h)
            path.lineTo(x, y)

        # create a path for a left-top based popup
        elif anchor == XPopupWidget.Anchor.LeftTop:
            path.moveTo(x + pad, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h)
            path.lineTo(x + pad, y + h)
            path.lineTo(x + pad, y + 2 * pad)
            path.lineTo(x, y + pad)
            path.lineTo(x + pad, y)

        # create a path for an left-center based popup
        elif anchor == XPopupWidget.Anchor.LeftCenter:
            path.moveTo(x + pad, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h)
            path.lineTo(x + pad, y + h)
            path.lineTo(x + pad, y + ((h / 2) + pad))
            path.lineTo(x, y + (h / 2))
            path.lineTo(x + pad, y + ((h / 2) - pad))
            path.lineTo(x + pad, y)

        # create a path for a left-bottom based popup
        elif anchor == XPopupWidget.Anchor.LeftBottom:
            path.moveTo(x + pad, y)
            path.lineTo(x + w, y)
            path.lineTo(x + w, y + h)
            path.lineTo(x + pad, y + h)
            path.lineTo(x, y + h - pad)
            path.lineTo(x + pad, y + h - 2 * pad)
            path.lineTo(x + pad, y)

        return path

    def buttonBox(self):
        """
        Returns the button box that is used to control this popup widget.
        
        :return     <QDialogButtonBox>
        """
        return self._buttonBox

    def centralWidget(self):
        """
        Returns the central widget that is being used by this popup.
        
        :return     <QWidget>
        """
        return self._scrollArea.widget()

    def close(self):
        """
        Closes the popup widget and central widget.
        """
        widget = self.centralWidget()
        if widget and not widget.close():
            return

        super(XPopupWidget, self).close()

    def closeEvent(self, event):
        widget = self.centralWidget()
        if widget and not widget.close() and \
           self.currentMode() != XPopupWidget.Mode.ToolTip:
            event.ignore()
        else:
            super(XPopupWidget, self).closeEvent(event)

        self.closed.emit()

    def currentMode(self):
        """
        Returns the current mode for this widget.
        
        :return     <XPopupWidget.Mode>
        """
        return self._currentMode

    @deprecatedmethod('XPopupWidget',
                      'Direction is no longer used, use anchor instead')
    def direction(self):
        """
        Returns the current direction parameter for this widget.
        
        :return     <XPopupWidget.Direction>
        """
        anchor = self.anchor()
        if (anchor &
            (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.TopCenter
             | XPopupWidget.Anchor.TopRight)):
            return XPopupWidget.Direction.North

        elif (
                anchor &
            (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter
             | XPopupWidget.Anchor.BottomRight)):
            return XPopupWidget.Direction.South

        elif (anchor &
              (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.LeftCenter
               | XPopupWidget.Anchor.LeftBottom)):
            return XPopupWidget.Direction.East

        else:
            return XPopupWidget.Direction.West

    def eventFilter(self, object, event):
        """
        Processes when the window is moving to update the position for the
        popup if in popup mode.
        
        :param      object | <QObject>
                    event  | <QEvent>
        """
        links = self.positionLinkedTo()
        is_dialog = self.currentMode() == self.Mode.Dialog
        if object not in links:
            return False

        if event.type() == event.Close:
            self.close()
            return False

        if event.type() == event.Hide and not is_dialog:
            self.hide()
            return False

        if event.type() == event.Move and not is_dialog:
            deltaPos = event.pos() - event.oldPos()
            self.move(self.pos() + deltaPos)
            return False

        if self.currentMode() != self.Mode.ToolTip:
            return False

        if event.type() == event.Leave:
            pos = object.mapFromGlobal(QCursor.pos())
            if (not object.rect().contains(pos)):
                self.close()
                event.accept()
                return True

        if event.type() in (event.MouseButtonPress, event.MouseButtonDblClick):
            self.close()
            event.accept()
            return True

        return False

    @qt.Slot(QAbstractButton)
    def handleButtonClick(self, button):
        """
        Handles the button click for this widget.  If the Reset button was
        clicked, then the resetRequested signal will be emitted.  All buttons
        will emit the buttonClicked signal.
        
        :param      button | <QAbstractButton>
        """
        if (self.signalsBlocked()):
            return

        if (button == self._buttonBox.button(QDialogButtonBox.Reset)):
            self.resetRequested.emit()

        self.buttonClicked.emit(button)

    def isAnimated(self):
        """
        Returns whether or not the popup widget should animate its opacity
        when it is shown.
        
        :return     <bool>
        """
        return self._animated

    def isResizable(self):
        """
        Returns if this popup is resizable or not.
        
        :return     <bool>
        """
        return self._resizable

    def keyPressEvent(self, event):
        """
        Looks for the Esc key to close the popup.
        
        :param      event | <QKeyEvent>
        """
        if (event.key() == Qt.Key_Escape):
            self.reject()
            event.accept()
            return

        elif (event.key() in (Qt.Key_Return, Qt.Key_Enter)):
            if self._autoDefault:
                self.accept()
                event.accept()
            return

        super(XPopupWidget, self).keyPressEvent(event)

    def mapAnchorFrom(self, widget, globalPos):
        """
        Returns the anchor point that best fits within the given widget from
        the inputed global position.
        
        :param      widget      | <QWidget>
                    globalPos   | <QPoint>
        
        :return     <XPopupWidget.Anchor>
        """
        localPos = widget.mapFromGlobal(globalPos)

        x = localPos.x()
        y = localPos.y()
        w = widget.width()
        h = widget.height()

        cw = self.width() / 2
        ch = self.height() / 2

        # by default, try to do a center point, so make sure the center point
        # is at least 1/2 the width longer from each edge
        if x < cw and h - y < ch:
            return XPopupWidget.Anchor.BottomLeft
        elif x < cw:
            return XPopupWidget.Anchor.TopLeft
        elif w - x < cw and h - y < ch:
            return XPopupWidget.Anchor.BottomRight
        elif w - x < cw:
            return XPopupWidget.Anchor.TopRight
        elif h - y < ch:
            return XPopupWidget.Anchor.BottomCenter
        else:
            return XPopupWidget.Anchor.TopCenter

    def popup(self, pos=None):
        """
        Pops up this widget at the inputed position.  The inputed point should \
        be in global space.
        
        :param      pos | <QPoint>
        
        :return     <bool> success
        """
        if self._first and self.centralWidget() is not None:
            self.adjustSize()
            self._first = False

        if not self.signalsBlocked():
            self.aboutToShow.emit()

        if not pos:
            pos = QCursor.pos()

        if self.currentMode() == XPopupWidget.Mode.Dialog and \
             self.isVisible():
            return False

        elif self.currentMode() == XPopupWidget.Mode.Dialog:
            self.setPopupMode()

        # auto-calculate the point
        if self.autoCalculateAnchor():
            self.setAnchor(self.mapAnchorFrom(self.parent(), pos))

        pad = self.popupPadding()

        # determine where to move based on the anchor
        anchor = self.anchor()

        # MODIFY X POSITION
        # align x-left
        if (anchor &
            (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.BottomLeft)):
            pos.setX(pos.x() - pad)

        # align x-center
        elif (anchor & (XPopupWidget.Anchor.TopCenter
                        | XPopupWidget.Anchor.BottomCenter)):
            pos.setX(pos.x() - self.width() / 2)

        # align x-right
        elif (
                anchor &
            (XPopupWidget.Anchor.TopRight | XPopupWidget.Anchor.BottomRight)):
            pos.setX(pos.x() - self.width() + pad)

        # align x-padded
        elif (anchor &
              (XPopupWidget.Anchor.RightTop | XPopupWidget.Anchor.RightCenter
               | XPopupWidget.Anchor.RightBottom)):
            pos.setX(pos.x() - self.width())

        # MODIFY Y POSITION
        # align y-top
        if (anchor &
            (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.RightTop)):
            pos.setY(pos.y() - pad)

        # align y-center
        elif (anchor & (XPopupWidget.Anchor.LeftCenter
                        | XPopupWidget.Anchor.RightCenter)):
            pos.setY(pos.y() - self.height() / 2)

        # align y-bottom
        elif (anchor & (XPopupWidget.Anchor.LeftBottom
                        | XPopupWidget.Anchor.RightBottom)):
            pos.setY(pos.y() - self.height() + pad)

        # align y-padded
        elif (
                anchor &
            (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter
             | XPopupWidget.Anchor.BottomRight)):
            pos.setY(pos.y() - self.height())

        self.adjustMask()
        self.move(pos)
        self.update()
        self.setUpdatesEnabled(True)

        if self.isAnimated():
            anim = QPropertyAnimation(self, 'windowOpacity')
            anim.setParent(self)
            anim.setStartValue(0.0)
            anim.setEndValue(self.windowOpacity())
            anim.setDuration(500)
            anim.finished.connect(anim.deleteLater)
            self.setWindowOpacity(0.0)
        else:
            anim = None

        self.show()

        if self.currentMode() != XPopupWidget.Mode.ToolTip:
            self.activateWindow()

            widget = self.centralWidget()
            if widget:
                self.centralWidget().setFocus()

        if anim:
            anim.start()

        if not self.signalsBlocked():
            self.shown.emit()

        return True

    def paintEvent(self, event):
        """
        Overloads the paint event to handle painting pointers for the popup \
        mode.
        
        :param      event | <QPaintEvent>
        """
        # use the base technique for the dialog mode
        if self.currentMode() == XPopupWidget.Mode.Dialog:
            super(XPopupWidget, self).paintEvent(event)
            return

        # setup the coloring options
        palette = self.palette()

        painter = QPainter()
        painter.begin(self)

        pen = QPen(palette.color(palette.Window).darker(130))
        pen.setWidthF(1.75)
        painter.setPen(pen)
        painter.setRenderHint(painter.Antialiasing)
        painter.setBrush(palette.color(palette.Window))
        painter.drawPath(self.borderPath())
        painter.end()

    def popupPadding(self):
        """
        Returns the amount of pixels to pad the popup arrow for this widget.
        
        :return     <int>
        """
        return self._popupPadding

    def positionLinkedTo(self):
        """
        Returns the widget that this popup is linked to for positional changes.
        
        :return     [<QWidget>, ..]
        """
        return self._positionLinkedTo

    @qt.Slot()
    def reject(self):
        """
        Emits the accepted signal and closes the popup.
        """
        if not self.signalsBlocked():
            self.rejected.emit()

        if self.autoCloseOnReject():
            self.close()

    def resizeEvent(self, event):
        """
        Resizes this widget and updates the mask.
        
        :param      event | <QResizeEvent>
        """
        self.setUpdatesEnabled(False)
        super(XPopupWidget, self).resizeEvent(event)

        self.adjustMask()
        self.setUpdatesEnabled(True)

        x = self.width() - self._sizeGrip.width()
        y = self.height() - self._sizeGrip.height()

        self._leftSizeGrip.move(0, y)
        self._sizeGrip.move(x, y)

    def scrollArea(self):
        """
        Returns the scroll area widget for this popup.
        
        :return     <QScrollArea>
        """
        return self._scrollArea

    def setAnimated(self, state):
        """
        Sets whether or not the popup widget should animate its opacity
        when it is shown.
        
        :param      state | <bool>
        """
        self._animated = state
        self.setAttribute(Qt.WA_TranslucentBackground, state)

    def setAutoCloseOnAccept(self, state):
        """
        Sets whether or not the popup handles closing for accepting states.
        
        :param      state | <bool>
        """
        self._autoCloseOnAccept = state

    def setAutoCloseOnReject(self, state):
        """
        Sets whether or not the popup handles closing for rejecting states.
        
        :param      state | <bool>
        """
        self._autoCloseOnReject = state

    def setAutoDefault(self, state):
        """
        Sets whether or not the buttons should respond to defaulting options
        when the user is interacting with it.
        
        :param      state | <bool>
        """
        self._autoDefault = state
        for button in self.buttonBox().buttons():
            button.setAutoDefault(state)
            button.setDefault(state)

    def setAnchor(self, anchor):
        """
        Sets the anchor position for this popup widget to the inputed point.
        
        :param      anchor | <XPopupWidget.Anchor>
        """
        self._anchor = anchor
        self.adjustContentsMargins()

    def setAutoCalculateAnchor(self, state):
        """
        Sets whether or not this widget should auto-calculate the anchor point
        based on the parent position when the popup is triggered.
        
        :param      state | <bool>
        """
        self._autoCalculateAnchor = state

    def setAutoCloseOnFocusOut(self, state):
        """
        Sets whether or not this popup widget should auto-close when the user
        clicks off the view.
        
        :param     state | <bool>
        """
        self._autoCloseOnFocusOut = state
        self.updateModeSettings()

    def setCentralWidget(self, widget):
        """
        Sets the central widget that will be used by this popup.
        
        :param      widget | <QWidget> || None
        """
        self._scrollArea.takeWidget()
        self._scrollArea.setWidget(widget)

        self.adjustSize()

    def setCurrentMode(self, mode):
        """
        Sets the current mode for this dialog to the inputed mode.
        
        :param      mode | <XPopupWidget.Mode>
        """
        if (self._currentMode == mode):
            return

        self._currentMode = mode
        self.updateModeSettings()

    @qt.Slot()
    def setDialogMode(self):
        """
        Sets the current mode value to Dialog.
        """
        self.setCurrentMode(XPopupWidget.Mode.Dialog)

    @deprecatedmethod('XPopupWidget',
                      'Direction is no longer used, use setAnchor instead')
    def setDirection(self, direction):
        """
        Sets the direction for this widget to the inputed direction.
        
        :param      direction | <XPopupWidget.Direction>
        """
        if (direction == XPopupWidget.Direction.North):
            self.setAnchor(XPopupWidget.Anchor.TopCenter)

        elif (direction == XPopupWidget.Direction.South):
            self.setAnchor(XPopupWidget.Anchor.BottomCenter)

        elif (direction == XPopupWidget.Direction.East):
            self.setAnchor(XPopupWidget.Anchor.LeftCenter)

        else:
            self.setAnchor(XPopupWidget.Anchor.RightCenter)

    def setPalette(self, palette):
        """
        Sets the palette for this widget and the scroll area.
        
        :param      palette | <QPalette>
        """
        super(XPopupWidget, self).setPalette(palette)
        self._scrollArea.setPalette(palette)

    def setPopupMode(self):
        """
        Sets the current mode value to Popup.
        """
        self.setCurrentMode(XPopupWidget.Mode.Popup)

    def setPopupPadding(self, padding):
        """
        Sets the amount to pad the popup area when displaying this widget.
        
        :param      padding | <int>
        """
        self._popupPadding = padding
        self.adjustContentsMargins()

    def setPositionLinkedTo(self, widgets):
        """
        Sets the widget that this popup will be linked to for positional
        changes.
        
        :param      widgets | <QWidget> || [<QWidget>, ..]
        """
        if type(widgets) in (list, set, tuple):
            new_widgets = list(widgets)
        else:
            new_widgets = []
            widget = widgets
            while widget:
                widget.installEventFilter(self)
                new_widgets.append(widget)
                widget = widget.parent()

        self._positionLinkedTo = new_widgets

    def setResizable(self, state):
        self._resizable = state
        self._sizeGrip.setVisible(state)
        self._leftSizeGrip.setVisible(state)

    def setShowButtonBox(self, state):
        self._buttonBoxVisible = state
        self.buttonBox().setVisible(state)

    def setShowTitleBar(self, state):
        self._titleBarVisible = state
        self._dialogButton.setVisible(state)
        self._closeButton.setVisible(state)

    def setToolTipMode(self):
        """
        Sets the mode for this popup widget to ToolTip
        """
        self.setCurrentMode(XPopupWidget.Mode.ToolTip)

    def setVisible(self, state):
        super(XPopupWidget, self).setVisible(state)
        widget = self.centralWidget()
        if widget:
            widget.setVisible(state)

    def timerEvent(self, event):
        """
        When the timer finishes, hide the tooltip popup widget.
        
        :param      event | <QEvent>
        """
        if self.currentMode() == XPopupWidget.Mode.ToolTip:
            self.killTimer(event.timerId())
            event.accept()
            self.close()
        else:
            super(XPopupWidget, self).timerEvent(event)

    def updateModeSettings(self):
        mode = self.currentMode()
        is_visible = self.isVisible()

        # display as a floating dialog
        if mode == XPopupWidget.Mode.Dialog:
            self.setWindowFlags(Qt.Dialog | Qt.Tool)
            self.setAttribute(Qt.WA_TransparentForMouseEvents, False)
            self._closeButton.setVisible(False)
            self._dialogButton.setVisible(False)

        # display as a user tooltip
        elif mode == XPopupWidget.Mode.ToolTip:
            flags = Qt.Popup | Qt.FramelessWindowHint

            self.setWindowFlags(flags)
            self.setBackgroundRole(QPalette.Window)
            self.setAttribute(Qt.WA_TransparentForMouseEvents)
            self.setShowTitleBar(False)
            self.setShowButtonBox(False)
            self.setFocusPolicy(Qt.NoFocus)

            # hide the scrollbars
            policy = Qt.ScrollBarAlwaysOff
            self._scrollArea.setVerticalScrollBarPolicy(policy)
            self._scrollArea.setHorizontalScrollBarPolicy(policy)

        # display as a popup widget
        else:
            flags = Qt.Popup | Qt.FramelessWindowHint

            if not self.autoCloseOnFocusOut():
                flags |= Qt.Tool

            self.setWindowFlags(flags)
            self._closeButton.setVisible(self._titleBarVisible)
            self._dialogButton.setVisible(self._titleBarVisible)
            self.setBackgroundRole(QPalette.Button)

        self.adjustContentsMargins()

        if (is_visible):
            self.show()

    @staticmethod
    @deprecatedmethod('XPopupWidget',
                      'This method no longer has an effect as we are not '\
                      'storing references to the tooltip.')
    def hideToolTip(key=None):
        """
        Hides any existing tooltip popup widgets.
        
        :warning    This method is deprecated!
        """
        pass

    @staticmethod
    def showToolTip(text,
                    point=None,
                    anchor=None,
                    parent=None,
                    background=None,
                    foreground=None,
                    key=None,
                    seconds=5):
        """
        Displays a popup widget as a tooltip bubble.
        
        :param      text        | <str>
                    point       | <QPoint> || None
                    anchor      | <XPopupWidget.Mode.Anchor> || None
                    parent      | <QWidget> || None
                    background  | <QColor> || None
                    foreground  | <QColor> || None
                    key         | <str> || None
                    seconds     | <int>
        """
        if point is None:
            point = QCursor.pos()

        if parent is None:
            parent = QApplication.activeWindow()

        if anchor is None and parent is None:
            anchor = XPopupWidget.Anchor.TopCenter

        # create a new tooltip widget
        widget = XPopupWidget(parent)
        widget.setToolTipMode()
        widget.setResizable(False)

        # create the tooltip label
        label = QLabel(text, widget)
        label.setOpenExternalLinks(True)
        label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        label.setMargin(3)
        label.setIndent(3)
        label.adjustSize()

        widget.setCentralWidget(label)

        # update the tip
        label.adjustSize()
        widget.adjustSize()

        palette = widget.palette()
        if not background:
            background = palette.color(palette.ToolTipBase)
        if not foreground:
            foreground = palette.color(palette.ToolTipText)

        palette.setColor(palette.Window, QColor(background))
        palette.setColor(palette.WindowText, QColor(foreground))
        widget.setPalette(palette)
        widget.centralWidget().setPalette(palette)

        if anchor is None:
            widget.setAutoCalculateAnchor(True)
        else:
            widget.setAnchor(anchor)

        widget.setAutoCloseOnFocusOut(True)
        widget.setAttribute(Qt.WA_DeleteOnClose)
        widget.popup(point)
        widget.startTimer(1000 * seconds)

        return widget
Example #15
0
class EntityEditorDialog(QDialog, MapperMixin):
    """
    Dialog for editing entity attributes.
    """
    addedModel = pyqtSignal(object)

    def __init__(
            self,
            entity,
            model=None,
            parent=None,
            manage_documents=True,
            collect_model=False
    ):
        """
        Class constructor.
        :param entity: Entity object corresponding to a table object.
        :type entity: Entity
        :param model: Data object for loading data into the form widgets.
        If the model is set, then the editor dialog is assumed to be in edit
        mode.
        :type model: object
        :param parent: Parent widget that the form belongs to.
        :type parent: QWidget
        :param manage_documents: True if the dialog should provide controls
        for managing supporting documents. Only applicable if the entity
        allows for supporting documents to be attached.
        :type manage_documents: bool
        :param collect_model: If set to True only returns
        the filled form model without saving it to the database.
        :type collect_model: Boolean
        :return: If collect_model, returns SQLAlchemy Model
        """
        QDialog.__init__(self, parent)

        self.collection_suffix = self.tr('Collection')

        #Set minimum width
        self.setMinimumWidth(350)

        #Flag for mandatory columns
        self.has_mandatory = False

        self._entity = entity
        self._fk_browsers = OrderedDict()

        #Set notification layout bar
        self.vlNotification = QVBoxLayout()
        self.vlNotification.setObjectName('vlNotification')
        self._notifBar = NotificationBar(self.vlNotification)

        # Set manage documents only if the entity supports documents
        if self._entity.supports_documents:
            self._manage_documents = manage_documents

        else:
            self._manage_documents = False

        #Setup entity model
        self._ent_document_model = None
        if self._entity.supports_documents:
                ent_model, self._ent_document_model = entity_model(
                    self._entity,
                    with_supporting_document=True
                )
        else:
            ent_model = entity_model(self._entity)

        if not model is None:
            ent_model = model

        MapperMixin.__init__(self, ent_model)

        self.collect_model = collect_model
        #Initialize UI setup
        self._init_gui()

        #Set title
        editor_trans = self.tr('Editor')
        title = u'{0} {1}'.format(
            format_name(self._entity.short_name),
            editor_trans
        )
        self.setWindowTitle(title)


    def _init_gui(self):
        #Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(
            self.vlNotification, 0, 0, 1, 1
        )

        #Set column widget area
        column_widget_area = self._setup_columns_content_area()
        self.gridLayout.addWidget(
            column_widget_area, 1, 0, 1, 1
        )

        #Add notification for mandatory columns if applicable
        next_row = 2
        if self.has_mandatory:
            self.required_fields_lbl = QLabel(self)
            msg = self.tr(
                'Please fill out all required (*) fields.'
            )
            msg = self._highlight_asterisk(msg)
            self.required_fields_lbl.setText(msg)
            self.gridLayout.addWidget(
                self.required_fields_lbl, next_row, 0, 1, 2
            )

            #Bump up row reference
            next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Cancel|QDialogButtonBox.Save
        )
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(
            self.buttonBox, next_row, 0, 1, 1
        )

        if self.collect_model:
            self.buttonBox.accepted.connect(
                self.on_model_added
            )
            self.buttonBox.rejected.connect(
                self.cancel
            )
        else:
            #Connect to MapperMixin slots
            self.buttonBox.accepted.connect(
                self.submit
            )
            self.buttonBox.rejected.connect(
                self.cancel
            )

    def on_model_added(self):
        model = self.submit(True)
        self.addedModel.emit(model)

    def _setup_columns_content_area(self):
        #Only use this if entity supports documents
        self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')
        self.scroll_widget_contents = QWidget()
        self.scroll_widget_contents.setObjectName(
            'scrollAreaWidgetContents'
        )
    
        #Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')
    
        #Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        #Iterate entity column and assert if they exist
        row_id = 0
        for c in self._entity.columns.values():
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue
    
            #Get widget factory
            column_widget = ColumnWidgetRegistry.create(
                c,
                self.scroll_widget_contents
            )
            if not column_widget is None:
                header = c.header()
                self.c_label = QLabel(self.scroll_widget_contents)

                #Format label text if it is a mandatory field
                if c.mandatory:
                    header = '{0} *'.format(c.header())
                    #Highlight asterisk
                    header = self._highlight_asterisk(header)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                self.column_widget = column_widget
                self.gl.addWidget(self.column_widget, row_id, 1, 1, 1)

                #Add user tip if specified for the column configuration
                if c.user_tip:
                    self.tip_lbl = UserTipLabel(user_tip=c.user_tip)
                    self.gl.addWidget(self.tip_lbl, row_id, 2, 1, 1)

                if c.mandatory and not self.has_mandatory:
                    self.has_mandatory = True

                col_name = c.name
                #Replace name accordingly based on column type
                if isinstance(c, MultipleSelectColumn):
                    col_name = c.model_attribute_name
    
                #Add widget to MapperMixin collection
                self.addMapping(
                    col_name,
                    self.column_widget,
                    c.mandatory,
                    pseudoname=c.header()
                )

                #Bump up row_id
                row_id += 1
    
        self.entity_scroll_area.setWidget(
            self.scroll_widget_contents
        )

        #Check if there are children and add foreign key browsers
        ch_entities = self.children_entities()
        if len(ch_entities) > 0:
            if self.entity_tab_widget is None:
                self.entity_tab_widget = QTabWidget(self)

            #Add primary tab if necessary
            self._add_primary_attr_widget()

            for ch in ch_entities:
                self._add_fk_browser(ch)

        #Add tab widget if entity supports documents
        if self._entity.supports_documents:
            self.doc_widget = SupportingDocumentsWidget(
                self._entity.supporting_doc,
                self._ent_document_model,
                self
            )

            #Map the source document manager object
            self.addMapping(
                'documents',
                self.doc_widget.source_document_manager
            )

            if self.entity_tab_widget is None:
                self.entity_tab_widget = QTabWidget(self)

            #Add attribute tab
            self._add_primary_attr_widget()

            #Add supporting documents tab
            self.entity_tab_widget.addTab(
                self.doc_widget,
                self.tr('Supporting Documents')
            )

        #Return the correct widget
        if not self.entity_tab_widget is None:
            return self.entity_tab_widget
    
        return self.entity_scroll_area

    def _add_primary_attr_widget(self):
        # Check if the primary entity
        # exists and add if it does not
        pr_txt = self.tr('Primary')
        if not self.entity_tab_widget is None:
            tab_txt = self.entity_tab_widget.tabText(0)
            if not tab_txt == pr_txt:
                self.entity_tab_widget.addTab(
                self.entity_scroll_area,
                pr_txt
            )

    def _add_fk_browser(self, child_entity):
        # Create and add foreign key
        # browser to the collection
        attr = u'{0}_collection'.format(
            child_entity.name
        )

        #Return if the attribute does not exist
        if not hasattr(self._model, attr):
            return

        fkb = ForeignKeyMapper(
            child_entity,
            self,
            notification_bar=self._notifBar,
            can_filter=True
        )

        #Add to mapped collection
        self.addMapping(
            attr,
            fkb
        )

        self.entity_tab_widget.addTab(
            fkb,
            u'{0} {1}'.format(
                child_entity.short_name,
                self.collection_suffix
            )
        )

        #Add to the collection
        self._fk_browsers[child_entity.name] = fkb

    def children_entities(self):
        """
        :return: Returns a list of children entities
        that refer to the main entity as the parent.
        :rtype: list
        """
        return [ch for ch in self._entity.children()
                if ch.TYPE_INFO == Entity.TYPE_INFO]

    def document_widget(self):
        """
        :return: Returns the widget for managing
        the supporting documents for an entity if enabled.
        :rtype: SupportingDocumentsWidget
        """
        return self.doc_widget

    def source_document_manager(self):
        """
        :return: Returns an instance of the
        SourceDocumentManager only if supporting
        documents are enabled for the given entity. Otherwise,
        None if supporting documents are not enabled.
        :rtype: SourceDocumentManager
        """
        if self.doc_widget is None:
            return None

        return self.doc_widget.source_document_manager

    def _highlight_asterisk(self, text):
        #Highlight asterisk in red
        c = '*'

        #Do not format if there is no asterisk
        if text.find(c) == -1:
            return text

        asterisk_highlight = '<span style=\" color:#ff0000;\">*</span>'
        text = text.replace(c, asterisk_highlight)

        return u'<html><head/><body><p>{0}</p></body></html>'.format(text)
Example #16
0
class AdvancedSearch(EntityEditorDialog):
    def __init__(self, entity, parent):

        EntityEditorDialog.__init__(self, entity, parent=parent)
        self.parent = parent

    def _init_gui(self):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(
            self.vlNotification, 0, 0, 1, 1
        )
        QApplication.processEvents()

        column_widget_area = self._setup_columns_content_area()
        self.gridLayout.addWidget(
            column_widget_area, 1, 0, 1, 1
        )
        QApplication.processEvents()
        # Add notification for mandatory columns if applicable
        next_row = 2
        # Set title
        search_trans = self.tr('Advanced Search')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        title = u'{0} {1}'.format(title_str, search_trans)
        self.do_not_check_dirty = True
        self.setWindowTitle(title)
        # if self.has_mandatory:
        #     self.required_fields_lbl = QLabel(self)
        #     msg = self.tr(
        #         'Please fill out all required (*) fields.'
        #     )
        #     msg = self._highlight_asterisk(msg)
        #     self.required_fields_lbl.setText(msg)
        #     self.gridLayout.addWidget(
        #         self.required_fields_lbl, next_row, 0, 1, 2
        #     )
        #     # Bump up row reference
        #     next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(
            self.buttonBox, next_row, 0, 1, 1
        )

        self.buttonBox.setOrientation(Qt.Horizontal)

        self.search = QPushButton(
            QApplication.translate(
                'EntityEditorDialog', 'Search'
            )
        )
        self.buttonBox.addButton(
            self.search, QDialogButtonBox.ActionRole
        )
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Cancel
        )
        self.search.clicked.connect(self.on_search)
        #
        #
        # # edit model, collect model
        # # adding new record for child
        #
        # # Saving in parent editor
        # if not isinstance(self._parent._parent, EntityEditorDialog):
        #     # adding a new record
        #     if self.edit_model is None:
        #         # saving when digitizing.
        #         if self.collect_model:
        #             self.buttonBox.accepted.connect(self.on_model_added)
        #         # saving parent editor
        #         else:
        #             self.buttonBox.accepted.connect(self.save_parent_editor)
        #             self.save_new_button.clicked.connect(self.save_and_new)
        #     # updating existing record
        #     else:
        #         if not self.collect_model:
        #             # updating existing record of the parent editor
        #             self.buttonBox.accepted.connect(self.save_parent_editor)
        #         else:
        #             self.buttonBox.accepted.connect(self.on_model_added)
        # # Saving in child editor
        # else:
        #     # save and new record
        #     if self.edit_model is None:
        #         self.buttonBox.accepted.connect(self.on_child_saved)
        #         self.save_new_button.clicked.connect(
        #             lambda: self.on_child_saved(True)
        #         )
        #
        #     else:
        #         # When updating an existing child editor save to the db
        #         self.buttonBox.accepted.connect(
        #             self.on_child_saved
        #         )
        #         #self.buttonBox.accepted.connect(self.submit)
        #
        self.buttonBox.rejected.connect(self.cancel)

    def on_search(self):
        search_data = {}
        for column in self._entity.columns.values():
            if column.name in entity_display_columns(self._entity):
                if column.name == 'id':
                    continue
                handler = self.attribute_mappers[
                    column.name].valueHandler()
                value = handler.value()
                if value != handler.default() and bool(value):
                    search_data[column.name] = value
        # self.search_db(search_data)
        result = self.search_db_raw(search_data)
        self.parent._tableModel.removeRows(0, self.parent._tableModel.rowCount())
        if result is not None:
            found = QApplication.translate('AdvancedSearch', 'records found')
            new_title = '{} - {} {}'.format(self.title, result.rowcount, found)
            if result.rowcount > 3000:
                title = QApplication.translate(
                        'AdvancedSearch',
                        'Advanced Search'
                )
                message = QApplication.translate(
                    'AdvancedSearch',
                    'The search result returned {0} records, which is above the '
                    'search result limit. <br>Would you like to see the first 3000 '
                    'records?'.format("{:,}".format(result.rowcount ))
                )

                res, chk_result = simple_dialog(self, title, message)
                if res:
                    self.setWindowTitle(new_title)
                    self.parent._initializeData(result)
                else:
                    return

            else:
                self.setWindowTitle(new_title)
                self.parent._initializeData(result)

    def search_db(self, search_data):
        ent_model_obj = self.ent_model()
        # query = ent_model_obj.queryObject()
        for attr, value in search_data.iteritems():
            ent_model_obj.queryObject().filter(
                getattr(self.ent_model(), attr) == value)

        # now we can run the query
        # print ent_model_obj.queryObject(), vars(ent_model_obj.queryObject())
        # print str(ent_model_obj.queryObject())
        results = ent_model_obj.queryObject().all()

    def search_db_raw(self, search_data):
        sql = u"SELECT * FROM {} WHERE ".format(self._entity.name)
        # query = ent_model_obj.queryObject()
        param = []
        if len(search_data) == 0:
            return None
        for attr, value in search_data.iteritems():
            if isinstance(value, (int, float)):
                param.append(u'{} = {}'.format(unicode(attr), unicode(value)))
            if isinstance(value, (unicode, str)):
                param.append(u"{} = '{}'".format(unicode(attr), unicode(value)))
        final_sql = u'{} {}'.format(sql, ' AND '.join(param))
        # sql_text = text(final_sql)
        results = fetch_with_filter(final_sql)
        # now we can run the query

        return results

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')

        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for c, column_widget in self.column_widgets.iteritems():
            if c.name in self.exclude_columns:
                continue
            if isinstance(c, MultipleSelectColumn):
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)


                if c.TYPE_INFO == 'AUTO_GENERATED':
                    column_widget.setReadOnly(False)
                    column_widget.btn_load.hide()
                self.gl.addWidget(column_widget, row_id, 1, 1, 1)

                col_name = c.name

                # Add widget to MapperMixin collection
                self.addMapping(
                    col_name,
                    column_widget,
                    c.mandatory,
                    pseudoname=c.ui_display()
                )

                # Bump up row_id
                row_id += 1

        self.entity_scroll_area.setWidget(self.scroll_widget_contents)
        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()
        # self.entity_tab_widget.setTabEnabled(0, False)  # enable/disable the tab
        # set the style sheet
        self.setStyleSheet(
            "QTabBar::tab::selected {width: 0; height: 0; margin: 0; "
            "padding: 0; border: none;} ")
        # Return the correct widget
        if self.entity_tab_widget is not None:
            return self.entity_tab_widget

        return self.entity_scroll_area

    def closeEvent(self, event):
        '''
        Raised when a request to close the window is received.
        Check the dirty state of input controls and prompt user to
        save if dirty.
        '''
        event.accept()

    def cancel(self):
        '''
        Slot for closing the dialog.
        Checks the dirty state first before closing.
        '''
        self.reject()
Example #17
0
class SimpleEditor(Editor):
    """ Simple style of editor for lists, which displays a scrolling list box
        with only one item visible at a time. A icon next to the list box
        displays a menu of operations on the list.
    """

    # -- Class Constants --------------------------------------------------------

    # Whether the list is displayed in a single row:
    single_row = True

    # Menu for modifying the list
    list_menu = """
       Add &Before     [_menu_before]: self.add_before()
       Add &After      [_menu_after]:  self.add_after()
       ---
       &Delete         [_menu_delete]: self.delete_item()
       ---
       Move &Up        [_menu_up]:     self.move_up()
       Move &Down      [_menu_down]:   self.move_down()
       Move to &Top    [_menu_top]:    self.move_top()
       Move to &Bottom [_menu_bottom]: self.move_bottom()
    """

    empty_list_menu = """
       Add: self.add_empty()
    """

    # -- Facet Definitions ------------------------------------------------------

    # The kind of editor to create for each list item:
    kind = Str

    # Is the list of items being edited mutable?
    mutable = Bool(True)

    # -- Public Methods ---------------------------------------------------------

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        # Initialize the facet handler to use:
        facet_handler = self.factory.facet_handler
        if facet_handler is None:
            facet_handler = self.object.base_facet(self.name).handler
        self._facet_handler = facet_handler

        # Create a scrolled window to hold all of the list item controls:
        self.control = QScrollArea(parent)
        self.control.setFrameShape(QFrame.NoFrame)

        # Create a widget with a grid layout as the container.
        self._list_pane = QWidget()
        layout = QGridLayout(self._list_pane)
        layout.setMargin(0)

        # Remember the editor to use for each individual list item:
        editor = self.factory.editor
        if editor is None:
            editor = facet_handler.item_facet.get_editor()

        self._editor = getattr(editor, self.kind)

        # Set up the additional 'list items changed' event handler needed for
        # a list based facet:
        self.context_object.on_facet_set(self.update_editor_item, self.extended_name + "_items?", dispatch="ui")
        self.set_tooltip()

    def dispose(self):
        """ Disposes of the contents of an editor.
        """
        self.context_object.on_facet_set(self.update_editor_item, self.extended_name + "_items?", remove=True)

        super(SimpleEditor, self).dispose()

    def update_editor(self):
        """ Updates the editor when the object facet changes externally to the
            editor.
        """
        # Disconnect the editor from any control about to be destroyed:
        self._dispose_items()

        list_pane = self._list_pane
        layout = list_pane.layout()

        # Create all of the list item facet editors:
        facet_handler = self._facet_handler
        resizable = (facet_handler.minlen != facet_handler.maxlen) and self.mutable
        item_facet = facet_handler.item_facet
        values = self.value
        index = 0

        is_fake = resizable and (len(values) == 0)
        if is_fake:
            values = [item_facet.default_value()[1]]

        editor = self._editor
        # FIXME: Add support for more than one column.
        for value in values:
            if resizable:
                control = IconButton("@facets:list_editor", self.popup_menu)
                layout.addWidget(control, index, 0)

            try:
                proxy = ListItemProxy(self.object, self.name, index, item_facet, value)
                if resizable:
                    control.proxy = proxy

                peditor = editor(self.ui, proxy, "value", self.description).set(object_name="")
                peditor.prepare(list_pane)
                pcontrol = peditor.control
                pcontrol.proxy = proxy
            except:
                if not is_fake:
                    raise

                pcontrol = QPushButton("sample", list_pane)

            if isinstance(pcontrol, QWidget):
                layout.addWidget(pcontrol, index, 1)
            else:
                layout.addLayout(pcontrol, index, 1)

            index += 1

        if is_fake:
            self._cur_control = control
            self.empty_list()
            control.setParent(None)

        if self.single_row:
            rows = 1
        else:
            rows = self.factory.rows

        # list_pane.SetSize( wx.Size(
        #     width + ((facet_handler.maxlen > rows) * scrollbar_dx),
        #     height * rows ) )

        # QScrollArea can have problems if the widget being scrolled is set too
        # early (ie. before it contains something).
        if self.control.widget() is None:
            self.control.setWidget(list_pane)

    def update_editor_item(self, object, name, old, event):
        """ Updates the editor when an item in the object facet changes
            externally to the editor.
        """
        # If this is not a simple, single item update, rebuild entire editor:
        if (len(event.removed) != 1) or (len(event.added) != 1):
            self.update_editor()

            return

        # Otherwise, find the proxy for this index and update it with the
        # changed value:
        for control in self.control.widget().children():
            if isinstance(control, QLayout):
                continue

            proxy = control.proxy
            if proxy.index == event.index:
                proxy.value = event.added[0]
                break

    def empty_list(self):
        """ Creates an empty list entry (so the user can add a new item).
        """
        control = IconButton("@facets:list_editor", self.popup_menu)
        control.is_empty = True
        proxy = ListItemProxy(self.object, self.name, -1, None, None)
        pcontrol = QLabel("   (Empty List)")
        pcontrol.proxy = control.proxy = proxy
        self.reload_sizer([(control, pcontrol)])

    def reload_sizer(self, controls, extra=0):
        """ Reloads the layout from the specified list of ( button, proxy )
            pairs.
        """
        layout = self._list_pane.layout()

        child = layout.takeAt(0)
        while child is not None:
            child = layout.takeAt(0)

        del child

        index = 0
        for control, pcontrol in controls:
            layout.addWidget(control)
            layout.addWidget(pcontrol)

            control.proxy.index = index
            index += 1

    def get_info(self):
        """ Returns the associated object list and current item index.
        """
        proxy = self._cur_control.proxy
        return (proxy.list, proxy.index)

    def popup_empty_menu(self, control):
        """ Displays the empty list editor popup menu.
        """
        self._cur_control = control
        control.PopupMenuXY(MakeMenu(self.empty_list_menu, self, True, control).menu, 0, 0)

    def popup_menu(self):
        """ Displays the list editor popup menu.
        """
        self._cur_control = control = self.control.sender()
        proxy = control.proxy
        index = proxy.index
        menu = MakeMenu(self.list_menu, self, True, control).menu
        len_list = len(proxy.list)
        not_full = len_list < self._facet_handler.maxlen
        self._menu_before.enabled(not_full)
        self._menu_after.enabled(not_full)
        self._menu_delete.enabled(len_list > self._facet_handler.minlen)
        self._menu_up.enabled(index > 0)
        self._menu_top.enabled(index > 0)
        self._menu_down.enabled(index < (len_list - 1))
        self._menu_bottom.enabled(index < (len_list - 1))
        menu.exec_(control.mapToGlobal(QPoint(0, 0)))

    def add_item(self, offset):
        """ Adds a new value at the specified list index.
        """
        list, index = self.get_info()
        index += offset
        item_facet = self._facet_handler.item_facet
        dv = item_facet.default_value()
        if dv[0] == 7:
            func, args, kw = dv[1]
            if kw is None:
                kw = {}
            value = func(*args, **kw)
        else:
            value = dv[1]
        self.value = list[:index] + [value] + list[index:]
        self.update_editor()

    def add_before(self):
        """ Inserts a new item before the current item.
        """
        self.add_item(0)

    def add_after(self):
        """ Inserts a new item after the current item.
        """
        self.add_item(1)

    def add_empty(self):
        """ Adds a new item when the list is empty.
        """
        list, index = self.get_info()
        self.add_item(0)

    def delete_item(self):
        """ Delete the current item.
        """
        list, index = self.get_info()
        self.value = list[:index] + list[index + 1 :]
        self.update_editor()

    def move_up(self):
        """ Move the current item up one in the list.
        """
        list, index = self.get_info()
        self.value = list[: index - 1] + [list[index], list[index - 1]] + list[index + 1 :]

    def move_down(self):
        """ Moves the current item down one in the list.
        """
        list, index = self.get_info()
        self.value = list[:index] + [list[index + 1], list[index]] + list[index + 2 :]

    def move_top(self):
        """ Moves the current item to the top of the list.
        """
        list, index = self.get_info()
        self.value = [list[index]] + list[:index] + list[index + 1 :]

    def move_bottom(self):
        """ Moves the current item to the bottom of the list.
        """
        list, index = self.get_info()
        self.value = list[:index] + list[index + 1 :] + [list[index]]

    # -- Private Methods --------------------------------------------------------

    def _dispose_items(self):
        """ Disposes of each current list item.
        """
        list_pane = self._list_pane
        layout = list_pane.layout()

        for control in list_pane.children():
            editor = getattr(control, "_editor", None)
            if editor is not None:
                editor.dispose()
                editor.control = None
            elif control is not layout:
                control.setParent(None)

        del control
Example #18
0
    def create_doc_tab_populate_combobox(self):
        """
        Creates the supporting document component widget.
        """
        self.doc_tab_data()
        self.docs_tab = QTabWidget()
        self.docs_tab_index = OrderedDict()
        for i, (id, doc) in enumerate(self.doc_types.iteritems()):
            self.docs_tab_index[doc] = i
            # the tab widget containing the document widget layout
            # and the child of the tab.
            tab_widget = QWidget()
            tab_widget.setObjectName(doc)
            # The layout of the tab widget
            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName(
                u'widget_layout_{}'.format(doc)
            )
            # the scroll area widget inside the tab widget.
            scroll_area = QScrollArea(tab_widget)
            scroll_area.setFrameShape(QFrame.NoFrame)
            scroll_area.setObjectName(
                u'tab_scroll_area_{}'.format(doc)
            )

            layout_widget = QWidget()
            # the widget the is under the scroll area content and
            # the widget containing the document widget layout
            # This widget is hidden and shown based on the STR number
            layout_widget.setObjectName(
                u'widget_{}'.format(doc)
            )

            doc_widget_layout = QVBoxLayout(layout_widget)
            doc_widget_layout.setObjectName(
                u'doc_widget_layout_{}'.format(
                    doc
                )
            )
            doc_widget = QWidget()
            doc_widget.setObjectName(
                u'doc_widget_{}_{}'.format(doc, self.str_number)
            )

            doc_widget_layout.addWidget(doc_widget)

            # the layout containing document widget.
            ### This is the layout that is registered to add uploaded
            # supporting documents widgets into.
            tab_layout = QVBoxLayout(doc_widget)
            tab_layout.setObjectName(
                u'layout_{}_{}'.format(doc, self.str_number)
            )

            scroll_area.setWidgetResizable(True)
            scroll_area.setWidget(layout_widget)

            cont_layout.addWidget(scroll_area)
            # Add the tab widget with the document
            # type name to create a tab.
            self.docs_tab.addTab(tab_widget, doc)

            self.container_box.addWidget(self.docs_tab, 1)
            if len(self.str_numbers) == 1:
                self.doc_type_cbo.addItem(doc, id)
# coding: utf-8
from PyQt4.QtCore import QRect
from PyQt4.QtGui import (QDialog, QFrame, QLineEdit, QScrollArea,
                         QSizePolicy, QVBoxLayout, QWidget)
from qgis.gui import QgsCollapsibleGroupBox

new_dialog = QDialog()
new_dialog.resize(200, 100)

scroll_area = QScrollArea(new_dialog)
scroll_area.setFrameShape(QFrame.NoFrame)
scroll_area.setFrameShadow(QFrame.Plain)
scroll_area.setWidgetResizable(True)
scroll_area.setGeometry(QRect(10, 20, 170, 70))

scrollAreaWidgetContents = QWidget()
scrollAreaWidgetContents.setGeometry(QRect(0, 0, 170, 70))
vertical_layout = QVBoxLayout(scrollAreaWidgetContents)

collapsible_group_box = QgsCollapsibleGroupBox(scrollAreaWidgetContents)
collapsible_group_box.setTitle('Collapsible')
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(
    collapsible_group_box.sizePolicy().hasHeightForWidth()
)
collapsible_group_box.setSizePolicy(sizePolicy)
collapsible_group_box.setChecked(False)

vbox_layout = QVBoxLayout(collapsible_group_box)
# coding: utf-8
from PyQt4.QtCore import QRect
from PyQt4.QtGui import (QDialog, QFrame, QLineEdit, QScrollArea, QSizePolicy,
                         QVBoxLayout, QWidget)
from qgis.gui import QgsCollapsibleGroupBoxBasic

new_dialog = QDialog()
new_dialog.resize(200, 100)

scroll_area = QScrollArea(new_dialog)
scroll_area.setFrameShape(QFrame.NoFrame)
scroll_area.setFrameShadow(QFrame.Plain)
scroll_area.setWidgetResizable(True)
scroll_area.setGeometry(QRect(10, 20, 170, 70))

scrollAreaWidgetContents = QWidget()
scrollAreaWidgetContents.setGeometry(QRect(0, 0, 170, 70))
vertical_layout = QVBoxLayout(scrollAreaWidgetContents)

collapsible_group_box_basic = QgsCollapsibleGroupBoxBasic(
    scrollAreaWidgetContents)
collapsible_group_box_basic.setTitle('Collapsible')
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(
    collapsible_group_box_basic.sizePolicy().hasHeightForWidth())
collapsible_group_box_basic.setSizePolicy(sizePolicy)
collapsible_group_box_basic.setChecked(False)

vbox_layout = QVBoxLayout(collapsible_group_box_basic)
Example #21
0
class AdvancedSearch(EntityEditorDialog):
    def __init__(self, entity, parent):

        EntityEditorDialog.__init__(self, entity, parent=parent)
        self.parent = parent

    def _init_gui(self):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(self.vlNotification, 0, 0, 1, 1)
        QApplication.processEvents()

        column_widget_area = self._setup_columns_content_area()
        self.gridLayout.addWidget(column_widget_area, 1, 0, 1, 1)
        QApplication.processEvents()
        # Add notification for mandatory columns if applicable
        next_row = 2
        # Set title
        search_trans = self.tr('Advanced Search')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        title = u'{0} {1}'.format(title_str, search_trans)
        self.do_not_check_dirty = True
        self.setWindowTitle(title)
        # if self.has_mandatory:
        #     self.required_fields_lbl = QLabel(self)
        #     msg = self.tr(
        #         'Please fill out all required (*) fields.'
        #     )
        #     msg = self._highlight_asterisk(msg)
        #     self.required_fields_lbl.setText(msg)
        #     self.gridLayout.addWidget(
        #         self.required_fields_lbl, next_row, 0, 1, 2
        #     )
        #     # Bump up row reference
        #     next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(self.buttonBox, next_row, 0, 1, 1)

        self.buttonBox.setOrientation(Qt.Horizontal)

        self.search = QPushButton(
            QApplication.translate('EntityEditorDialog', 'Search'))
        self.buttonBox.addButton(self.search, QDialogButtonBox.ActionRole)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
        self.search.clicked.connect(self.on_search)
        #
        #
        # # edit model, collect model
        # # adding new record for child
        #
        # # Saving in parent editor
        # if not isinstance(self._parent._parent, EntityEditorDialog):
        #     # adding a new record
        #     if self.edit_model is None:
        #         # saving when digitizing.
        #         if self.collect_model:
        #             self.buttonBox.accepted.connect(self.on_model_added)
        #         # saving parent editor
        #         else:
        #             self.buttonBox.accepted.connect(self.save_parent_editor)
        #             self.save_new_button.clicked.connect(self.save_and_new)
        #     # updating existing record
        #     else:
        #         if not self.collect_model:
        #             # updating existing record of the parent editor
        #             self.buttonBox.accepted.connect(self.save_parent_editor)
        #         else:
        #             self.buttonBox.accepted.connect(self.on_model_added)
        # # Saving in child editor
        # else:
        #     # save and new record
        #     if self.edit_model is None:
        #         self.buttonBox.accepted.connect(self.on_child_saved)
        #         self.save_new_button.clicked.connect(
        #             lambda: self.on_child_saved(True)
        #         )
        #
        #     else:
        #         # When updating an existing child editor save to the db
        #         self.buttonBox.accepted.connect(
        #             self.on_child_saved
        #         )
        #         #self.buttonBox.accepted.connect(self.submit)
        #
        self.buttonBox.rejected.connect(self.cancel)

    def on_search(self):
        search_data = {}
        for column in self._entity.columns.values():
            if column.name in entity_display_columns(self._entity):
                if column.name == 'id':
                    continue
                handler = self.attribute_mappers[column.name].valueHandler()
                value = handler.value()
                if value != handler.default() and bool(value):
                    search_data[column.name] = value
        # self.search_db(search_data)
        result = self.search_db_raw(search_data)
        self.parent._tableModel.removeRows(0,
                                           self.parent._tableModel.rowCount())
        if result is not None:
            found = QApplication.translate('AdvancedSearch', 'records found')
            new_title = '{} - {} {}'.format(self.title, result.rowcount, found)
            if result.rowcount > 3000:
                title = QApplication.translate('AdvancedSearch',
                                               'Advanced Search')
                message = QApplication.translate(
                    'AdvancedSearch',
                    'The search result returned {0} records, which is above the '
                    'search result limit. <br>Would you like to see the first 3000 '
                    'records?'.format("{:,}".format(result.rowcount)))

                res, chk_result = simple_dialog(self, title, message)
                if res:
                    self.setWindowTitle(new_title)
                    self.parent._initializeData(result)
                else:
                    return

            else:
                self.setWindowTitle(new_title)
                self.parent._initializeData(result)

    def search_db(self, search_data):
        ent_model_obj = self.ent_model()
        # query = ent_model_obj.queryObject()
        for attr, value in search_data.iteritems():
            ent_model_obj.queryObject().filter(
                getattr(self.ent_model(), attr) == value)

        # now we can run the query
        # print ent_model_obj.queryObject(), vars(ent_model_obj.queryObject())
        # print str(ent_model_obj.queryObject())
        results = ent_model_obj.queryObject().all()

    def search_db_raw(self, search_data):
        sql = u"SELECT * FROM {} WHERE ".format(self._entity.name)
        # query = ent_model_obj.queryObject()
        param = []
        if len(search_data) == 0:
            return None
        for attr, value in search_data.iteritems():
            if isinstance(value, (int, float)):
                param.append(u'{} = {}'.format(unicode(attr), unicode(value)))
            if isinstance(value, (unicode, str)):
                param.append(u"{} = '{}'".format(unicode(attr),
                                                 unicode(value)))
        final_sql = u'{} {}'.format(sql, ' AND '.join(param))
        # sql_text = text(final_sql)
        results = fetch_with_filter(final_sql)
        # now we can run the query

        return results

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')

        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for c, column_widget in self.column_widgets.iteritems():
            if c.name in self.exclude_columns:
                continue
            if isinstance(c, MultipleSelectColumn):
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                if c.TYPE_INFO == 'AUTO_GENERATED':
                    column_widget.setReadOnly(False)
                    column_widget.btn_load.hide()
                self.gl.addWidget(column_widget, row_id, 1, 1, 1)

                col_name = c.name

                # Add widget to MapperMixin collection
                self.addMapping(col_name,
                                column_widget,
                                c.mandatory,
                                pseudoname=c.ui_display())

                # Bump up row_id
                row_id += 1

        self.entity_scroll_area.setWidget(self.scroll_widget_contents)
        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()
        # self.entity_tab_widget.setTabEnabled(0, False)  # enable/disable the tab
        # set the style sheet
        self.setStyleSheet(
            "QTabBar::tab::selected {width: 0; height: 0; margin: 0; "
            "padding: 0; border: none;} ")
        # Return the correct widget
        if self.entity_tab_widget is not None:
            return self.entity_tab_widget

        return self.entity_scroll_area

    def closeEvent(self, event):
        '''
        Raised when a request to close the window is received.
        Check the dirty state of input controls and prompt user to
        save if dirty.
        '''
        event.accept()

    def cancel(self):
        '''
        Slot for closing the dialog.
        Checks the dirty state first before closing.
        '''
        self.reject()
Example #22
0
class PlotDialog(QDialog, PlotManager):
    """ Implements a dialog to which an arbitrary number of plots can be
    added.

    This class implements a `QDialog` with a number of plots on it. The
    axes of the plots can be arbitrarily synchronized and option checkboxes
    can be added which provide callbacks when the checkbox state changes.

    :param str wintitle: Title of the window.
    :param bool major_grid: Show major grid in plots.
    :param bool minor_grid: Show minor grid in plots.
    :param bool toolbar: Show toolbar.
    :param parent: Parent window.
    :param panels: A list of guiqwt panels to be added to the window.
    :param int min_plot_width: Default minimum width for plots.
    :param int min_plot_height: Default minimum height for plots.
    """
    def __init__(self,
                 wintitle='Plot window',
                 major_grid=True,
                 minor_grid=False,
                 toolbar=True,
                 parent=None,
                 panels=None,
                 min_plot_width=100,
                 min_plot_height=75):
        QDialog.__init__(self, parent)
        self.setWindowFlags(Qt.Window)

        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(_fromUtf8(':/Application/Main')),
                       QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)

        self.major_grid = major_grid
        self.minor_grid = minor_grid
        self.min_plot_width = min_plot_width
        self.min_plot_height = min_plot_height

        # WidgetMixin copy
        PlotManager.__init__(self, main=self)

        self.main_layout = QVBoxLayout(self)
        self.color_layout = QHBoxLayout()
        self.plot_layout = QGridLayout()
        self.plot_layout.setMargin(0)
        self.plot_scroll_widget = QWidget()
        self.plot_scroll_area = QScrollArea()
        self.plot_scroll_area.setFrameShape(QFrame.NoFrame)
        self.plot_scroll_area.setWidgetResizable(True)
        self.option_layout = QHBoxLayout()

        self.plot_widget = None

        if panels is not None:
            for panel in panels:
                self.add_panel(panel)

        self.toolbar = QToolBar('Tools')
        if not toolbar:
            self.toolbar.hide()

        # Configuring widget layout
        self._setup_widget_properties(wintitle=wintitle, icon=icon)
        self._setup_widget_layout()

        # Options
        self.option_callbacks = {}
        self.legend = None
        self.axis_syncplots = {}

    def _setup_widget_properties(self, wintitle, icon):
        self.setWindowTitle(wintitle)
        if isinstance(icon, basestring):
            icon = get_icon(icon)
        self.setWindowIcon(icon)
        self.setMinimumSize(320, 240)
        self.resize(720, 540)

    def _setup_widget_layout(self):
        self.main_layout.addWidget(self.toolbar)
        self.main_layout.addLayout(self.color_layout)
        self.main_layout.addWidget(self.plot_scroll_area)
        self.plot_scroll_area.setWidget(self.plot_scroll_widget)
        self.plot_scroll_widget.setLayout(self.plot_layout)
        self.main_layout.addLayout(self.option_layout)
        self.setLayout(self.main_layout)

    def add_custom_curve_tools(self,
                               antialiasing=True,
                               activate_zoom=True,
                               signal_stats=False):
        """ Adds typically needed curve tools to the window.

        :param bool antialiasing: Determines if the antialiasing tool is
            added.
        :param bool activate_zoom: Determines if the zoom tool is activated
            initially (otherwise, the selection tool will be activated).
        :param bool signal_stats: Determines if the signal stats tool is
            available.
        """
        self.add_toolbar(self.toolbar)

        t = self.add_tool(SelectTool)
        if not activate_zoom:
            self.set_default_tool(t)
        self.add_tool(BasePlotMenuTool, "item")
        self.add_tool(ExportItemDataTool)
        try:  # Old versions of guiqwt and spyderlib do not support this
            import spyderlib.widgets.objecteditor
            from guiqwt.tools import EditItemDataTool
            self.add_tool(EditItemDataTool)
        except ImportError:
            pass
        self.add_tool(ItemCenterTool)
        self.add_tool(DeleteItemTool)

        self.add_separator_tool()
        t = self.add_tool(RectZoomTool)
        if activate_zoom:
            self.set_default_tool(t)
        self.add_tool(guiqwt_tools.HomeTool)
        self.add_tool(guiqwt_tools.PanTool)

        self.add_separator_tool()
        self.add_tool(BasePlotMenuTool, "grid")
        self.add_tool(BasePlotMenuTool, "axes")
        self.add_tool(DisplayCoordsTool)
        if self.get_itemlist_panel():
            self.add_tool(ItemListPanelTool)

        if signal_stats:
            self.add_separator_tool()
            self.add_tool(SignalStatsTool)

        if antialiasing:
            self.add_tool(AntiAliasingTool)
        self.add_tool(AxisScaleTool)
        self.add_separator_tool()
        self.add_tool(SaveAsTool)
        self.add_tool(CopyToClipboardTool)
        self.add_tool(PrintTool)
        self.add_tool(guiqwt_tools.HelpTool)
        self.add_separator_tool()
        self.get_default_tool().activate()

    def add_custom_image_tools(self, activate_zoom=True):
        """ Adds typically needed image tools to the window.
        """
        self.add_toolbar(self.toolbar)

        t = self.add_tool(SelectTool)
        if not activate_zoom:
            self.set_default_tool(t)
        self.add_tool(BasePlotMenuTool, "item")
        self.add_tool(ExportItemDataTool)
        try:  # Old versions of guiqwt and spyderlib do not support this
            import spyderlib.widgets.objecteditor
            from guiqwt.tools import EditItemDataTool
            self.add_tool(EditItemDataTool)
        except ImportError:
            pass
        self.add_tool(ItemCenterTool)
        self.add_tool(DeleteItemTool)

        self.add_separator_tool()
        t = self.add_tool(RectZoomTool)
        if activate_zoom:
            self.set_default_tool(t)
        self.add_tool(guiqwt_tools.HomeTool)
        self.add_tool(guiqwt_tools.PanTool)

        self.add_separator_tool()
        self.add_tool(BasePlotMenuTool, "grid")
        self.add_tool(BasePlotMenuTool, "axes")
        self.add_tool(DisplayCoordsTool)
        if self.get_itemlist_panel():
            self.add_tool(ItemListPanelTool)

        self.add_separator_tool()
        self.add_tool(ColormapTool)
        self.add_tool(ReverseYAxisTool)
        self.add_tool(AspectRatioTool)
        if self.get_contrast_panel():
            self.add_tool(ContrastPanelTool)
        if self.get_xcs_panel() and self.get_ycs_panel():
            self.add_tool(XCSPanelTool)
            self.add_tool(YCSPanelTool)
            self.add_tool(CrossSectionTool)
            self.add_tool(AverageCrossSectionTool)

        self.add_separator_tool()
        self.add_tool(SaveAsTool)
        self.add_tool(CopyToClipboardTool)
        self.add_tool(PrintTool)
        self.add_tool(guiqwt_tools.HelpTool)
        self.add_separator_tool()
        self.get_default_tool().activate()

    def add_option(self, name, change_callback, active=False):
        """ Add an option (using a checkbox) to the window.

        :param str name: The name of the option.
        :param func change_callback: A function accepting the new state as
            a parameter. The function will be called whenever the state
            of the option changes.
        :param bool active: Determines if the option is activated initially.
        """
        checkBox = QtGui.QCheckBox(self)
        checkBox.setChecked(active)
        checkBox.setText(name)
        checkBox.stateChanged.connect(self._option_callback)
        self.option_callbacks[checkBox] = change_callback
        self.option_layout.addWidget(checkBox)

    def add_x_synchronization_option(self, active, ids=None):
        """ Offer an option for X axes synchronization. This method should
        be called after show(), so that a proper initial synchronization
        can be performed.

        :param bool active: Determines whether the axes are synchronized
            initially.
        :param sequence ids: List of plot ids to synchronize.
        """
        self.axis_syncplots[BasePlot.X_BOTTOM] = ids
        if active and ids:
            self.synchronize_axis(BasePlot.X_BOTTOM)
        self.add_option('Synchronize X Axes',
                        PlotDialog._synchronization_option_x, active)

    def add_y_synchronization_option(self, active, ids=None):
        """ Offer an option for Y axes synchronization. This method should
        be called after show(), so that a proper initial synchronization
        can be performed.

        :param bool active: Determines whether the axes are synchronized
            initially
        :param sequence ids: List of plot ids to synchronize.
        """
        self.axis_syncplots[BasePlot.Y_LEFT] = ids
        if active and ids:
            self.synchronize_axis(BasePlot.Y_LEFT)
        self.add_option('Synchronize Y Axes',
                        PlotDialog._synchronization_option_y, active)

    def _synchronization_option_x(self, state):
        """ Callback for x-axis synchronization
        """
        if state:
            self.synchronize_axis(BasePlot.X_BOTTOM)
        else:
            self.unsynchronize_axis(BasePlot.X_BOTTOM)

    def _synchronization_option_y(self, state):
        """ Callback for y-axis synchronization
        """
        if state:
            self.synchronize_axis(BasePlot.Y_LEFT)
        else:
            self.unsynchronize_axis(BasePlot.Y_LEFT)

    def replace_colors(self, replace_list):
        """ Replace colors of items in all plots.

            This can be useful when changing the background color to black
            and black items should be drawn in white:
            ``replace_colors([('#000000', '#ffffff']))``

        :param list replace_list: A list of tuples of Qt color names. The
            first color in each tuple is replaced by the second color.
        """
        for plot in self.plots.itervalues():
            for i in plot.get_items():
                if isinstance(i, CurveItem):
                    pen = i.pen()
                elif isinstance(i, Marker):
                    pen = i.linePen()
                else:
                    continue
                for color in replace_list:
                    c1 = QColor(color[0])
                    c2 = QColor(color[1])
                    if pen.color() != c1:
                        continue
                    pen.setColor(c2)
                    break
                if isinstance(i, CurveItem):
                    i.setPen(pen)
                elif isinstance(i, Marker):
                    i.setLinePen(pen)
            plot.replot()

    def set_background_color(self, color):
        """ Set the background color for all plots.

        :param str color: A Qt color name (e.g. '#ff0000')
        """
        for p in self.plots.itervalues():
            p.setCanvasBackground(QColor(color))
            p.replot()

    def add_unit_color(self, color, name='Unit color:'):
        """ Create a small legend on top of the window with only one entry.

        :param str color: A Qt color name (e.g. '#ff0000')
        :param str name: The name of the legend item. It will be displayed
            on the left of the color.
        """
        label = QtGui.QLabel(self)
        label.setText(name)
        label.setAlignment(Qt.AlignRight)
        self.color_layout.addWidget(label)
        label = QtGui.QLabel(self)
        label.setStyleSheet('background-color:' + str(color))
        label.setFrameShape(QFrame.StyledPanel)
        label.setMaximumWidth(80)
        self.color_layout.addWidget(label)

    def add_custom_label(self, legend_string):
        """ Add a label on the right of the plots

        :param str legend_string: An arbitrary string (which can contain
            newlines) to display on the right of the plots
        """
        label = QtGui.QLabel(self)
        label.setText(legend_string)
        self.plot_layout.addWidget(label, 0, self.plot_layout.columnCount(),
                                   -1, 1)

    def add_color_legend(self, legend, show_option=None):
        """ Create a legend on the right of the plots with colors and names.

        :param sequence legend: List of (color, text) tuples, where `color`
            is a Qt color name (e.g. '#ff0000') and `text` is the
            corresponding text to display in the legend.
        :param bool show_option: Determines whether a toggle for the legend
            will be shown (if the parameter is not ``None``) and if the legend
            is visible initially (``True``/``False``).
        """
        widget = QWidget(self)
        layout = QGridLayout(widget)
        widget.setLayout(layout)
        for l in legend:
            label = QtGui.QLabel(self)
            label.setStyleSheet('background-color:' + str(l[0]))
            label.setFrameShape(QFrame.StyledPanel)
            label.setMaximumWidth(80)
            label.setMaximumHeight(12)
            layout.addWidget(label, layout.rowCount(), 0, 1, 1)
            label = QtGui.QLabel(self)
            label.setText(l[1])
            layout.addWidget(label, layout.rowCount() - 1, 1, 1, 1)

        self.plot_layout.addWidget(widget, 0, self.plot_layout.columnCount(),
                                   -1, 1)
        if show_option is not None:
            widget.setVisible(show_option)
            self.add_option('Show Legend Sidebar',
                            lambda w, s: widget.setVisible(s), show_option)

    def add_legend_option(self, legends, active):
        """ Create a user option to show or hide a list of legend objects.

        :param sequence legends: The legend objects affected by the option.
        :param bool active: Determines whether the legends will be visible
            initially.
        """
        self.legends = legends
        self._set_legend_visibility(active)
        self.add_option('Show legend', self._legend_callback, active)
        if active:
            self._set_legend_visibility(True)

    def _option_callback(self, state):
        self.option_callbacks[self.sender()](self, state)

    #noinspection PyUnusedLocal
    def _legend_callback(self, win, state):
        self._set_legend_visibility(state > 0)

    def _set_legend_visibility(self, visible):
        for p in self.plots.itervalues():
            for l in self.legends:
                p.set_item_visible(l, visible)

    def add_plot_widget(self,
                        plot_widget,
                        plot_id,
                        row=-1,
                        column=0,
                        min_plot_width=None,
                        min_plot_height=None):
        """ Adds a guiqwt plot to the window.

        :param plot_widget: The plot to add.
        :type plot_widget: guiqwt plot widget
        :param int plot_id: The id of the new plot.
        :param int row: The row of the new plot. If this is -1, the new plot
            will be added in a new row (if `column` is 0) or
            in the last row.
        :param int column: The column of the new plot.
        :param int min_plot_width: The minimum width of this plot. If
            ``None``, the default minimum width for this dialog
            is used.
        :param int max_plot_height: The minimum height of this plot. If
            ``None``, the default minimum height for this dialog
            is used.
        """
        if row == -1:
            if column == 0:
                row = self.plot_layout.rowCount()
            else:
                row = self.plot_layout.rowCount() - 1
        pw = min_plot_width
        if pw is None:
            pw = self.min_plot_width
        ph = min_plot_height
        if ph is None:
            ph = self.min_plot_height
        plot_widget.setMinimumSize(pw, ph)
        self.plot_layout.addWidget(plot_widget, row, column)
        new_plot = plot_widget.plot
        self.add_plot(new_plot, plot_id)

    def synchronize_axis(self, axis, plots=None):
        if plots is None:
            if axis in self.axis_syncplots:
                plots = self.axis_syncplots[axis]
            else:
                plots = self.plots.keys()
        if len(plots) < 1:
            return

        PlotManager.synchronize_axis(self, axis, plots)

        # Find interval that needs to be shown in order to include all
        # currently shown parts in the synchronized plots
        plot_objects = [self.plots[p] for p in plots]
        lb = min((p.axisScaleDiv(axis).lowerBound() for p in plot_objects))
        ub = max((p.axisScaleDiv(axis).upperBound() for p in plot_objects))
        for p in plot_objects:
            p.setAxisScale(axis, lb, ub)
            p.replot()

    def unsynchronize_axis(self, axis, plots=None):
        if plots is None:
            if axis in self.axis_syncplots:
                plots = self.axis_syncplots[axis]
            else:
                plots = self.plots.keys()
        for plot_id in plots:
            if not plot_id in self.synchronized_plots:
                continue
            synclist = self.synchronized_plots[plot_id]
            for plot2_id in plots:
                if plot_id == plot2_id:
                    continue
                item = (axis, plot2_id)
                if item in synclist:
                    synclist.remove(item)

    def plot_axis_changed(self, plot):
        ids = [k for k, p in self.plots.iteritems() if p == plot]
        if len(ids) < 1:
            return
        plot_id = ids[0]
        if plot_id not in self.synchronized_plots:
            return
        for (axis, other_plot_id) in self.synchronized_plots[plot_id]:
            scalediv = plot.axisScaleDiv(axis)
            other = self.get_plot(other_plot_id)
            lb = scalediv.lowerBound()
            ub = scalediv.upperBound()
            other.setAxisScale(axis, lb, ub)
            other.replot()

    def set_plot_title(self, plot, title):
        """ Set the title of a guiqwt plot and use the same font as for the
            rest of the window.

        :param plot: The plot for which the title is set.
        :param str title: The new title of the plot.
        """
        plot.setTitle(title)
        l = plot.titleLabel()
        l.setFont(self.font())
        plot.setTitle(l.text())

    def show(self):
        for p in self.plots.itervalues():
            if not self.minor_grid:
                p.grid.gridparam.min_xenabled = False
                p.grid.gridparam.min_yenabled = False
            if not self.major_grid:
                p.grid.gridparam.maj_xenabled = False
                p.grid.gridparam.maj_yenabled = False
            p.grid.update_params()
        super(PlotDialog, self).show()
Example #23
0
class Hakkinda(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.resize(500, 350)
        self.setMaximumSize(500, 350)
        self.gLayout = QGridLayout(self)
        self.logo = QLabel(self)
        self.logo.setPixmap(QPixmap(":/resim/parcala.png"))
        self.gLayout.addWidget(self.logo, 0, 0, 2, 1)
        self.appName = QLabel(self)
        font = QFont()
        font.setPointSize(32)
        font.setWeight(50)
        self.appName.setFont(font)
        self.gLayout.addWidget(self.appName, 0, 1, 1, 2)
        self.appVersion = QLabel(self)
        font = QFont()
        font.setPointSize(9)
        font.setWeight(75)
        font.setBold(True)
        self.appVersion.setFont(font)
        self.appVersion.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
        self.gLayout.addWidget(self.appVersion, 1, 1, 1, 2)
        self.gBox = QGroupBox(self)
        font = QFont()
        font.setPointSize(12)
        font.setWeight(75)
        font.setBold(True)
        self.gBox.setFont(font)
        self.gLayout2 = QGridLayout(self.gBox)
        self.scrollArea = QScrollArea(self.gBox)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setWidgetResizable(True)

        self.scrollAreaWidgetContents = QWidget()
        self.scrollAreaWidgetContents.setGeometry(0, 0, 476, 199)

        self.gLayout3 = QGridLayout(self.scrollAreaWidgetContents)
        self.appHakkinda = QLabel(self.scrollAreaWidgetContents)
        font = QFont()
        font.setPointSize(9)
        font.setWeight(50)
        font.setBold(False)
        self.appHakkinda.setFont(font)
        self.appHakkinda.setWordWrap(True)
        self.gLayout3.addWidget(self.appHakkinda, 0, 0, 1, 1)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gLayout2.addWidget(self.scrollArea, 0, 0, 1, 1)
        self.gLayout.addWidget(self.gBox, 2, 0, 2, 4)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.gLayout.addItem(spacerItem, 0, 3, 1, 1)
        spacerItem1 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.gLayout.addItem(spacerItem1, 2, 1, 1, 2)

        self.setWindowTitle(self.trUtf8(u"%s Hakkında"%QApplication.applicationName()))
        self.appName.setText(self.trUtf8(u"%s"%QApplication.applicationName()))
        self.appVersion.setText(self.trUtf8(u"Sürüm %s"%QApplication.applicationVersion()))
        self.gBox.setTitle(self.trUtf8("Hakkında"))
        self.appHakkinda.setText(self.trUtf8("""
        <p>Parçala, Hj-Split ile aynı işi yapan dosya parçalama ve birleştirme yazılımıdır.</p>
        <p>Parçala, büyük boyutlu dosyaları parçalara böldüğü gibi parçalanmış dosyaları birleştirme ve dosyaların doğrulamasını yapabilmektedir.</p>

        <p><b>Geliştirici:</b> Metehan Özbek - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Görsel Çalışma:</b> Yasin Özcan - <a href='mailto:[email protected]'>[email protected]</a></p>
        <p><b>Lisans:</b> GPL v3</p>
        <p></p>"""))