Пример #1
0
    def __init__(self,parent=None):
        super(DeliverySlipViewWidget,self).__init__(parent)

        self.delivery_slip_part_proto = []
        # self.delivery_slip_part_proto.append( OrderPartDisplayPrototype('order_part',_('Part'), editable=False))
        self.delivery_slip_part_proto.append( TextLinePrototype('part_label',_('Part'), editable=False))
        self.delivery_slip_part_proto.append( TextLinePrototype('description',_('Description'), editable=False))
        self.delivery_slip_part_proto.append( IntegerNumberPrototype('quantity_out',_('Q. out'), editable=False))


        # self.controller_operation = PrototypeController(self,
        #                                                 self.delivery_slip_part_proto,
        #                                                 ProxyTableView(None,self.delivery_slip_part_proto))
        # # self.controller_operation.view.verticalHeader().hide()
        # self.controller_operation.setModel(TrackingProxyModel(self,self.delivery_slip_part_proto))


        self.model = PrototypedModelView(self.delivery_slip_part_proto, self)
        self.view = PrototypedQuickView(self.delivery_slip_part_proto, self)
        self.view.setModel(self.model)
        self.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) # Description column wide enough
        self.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch)
        self.view.verticalHeader().hide()

        layout = QHBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        # layout.addWidget(self.controller_operation.view)
        layout.addWidget(self.view)
        self.setLayout(layout)
Пример #2
0
    def __init__(self, table_prototype, parent):
        super(QuickPrototypedFilter, self).__init__(parent)

        self.list_model = PrototypedModelView(table_prototype, self)

        self.list_model_filtered = FilteringModel(self)
        self.list_model_filtered.setSourceModel(self.list_model)

        self.line_in = FilterLineEdit()
        self.line_in.key_down.connect(self._focus_on_list)
        self.line_in.textChanged.connect(self._filter_changed)

        self.list_view = PrototypedQuickView(table_prototype, self)
        self.list_view.setTabKeyNavigation(False)
        self.list_view.horizontalHeader().hide()
        self.list_view.verticalHeader().hide()
        self.list_view.horizontalHeader().setStretchLastSection(True)

        self.list_view.setModel(self.list_model_filtered)

        vlayout = QVBoxLayout()
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.line_in)
        vlayout.addWidget(self.list_view)
        self.setLayout(vlayout)

        self.list_view.selectionModel().currentChanged.connect(
            self._selected_item_changed)  # FIXME Clear ownership issue

        self.last_selected_object = None
        self.line_in.setFocus()
        QWidget.setTabOrder(self.line_in, self.list_view)
Пример #3
0
    def __init__(self,parent=None):
        super(OperationsOverviewWidget,self).__init__(parent)

        self.operation_prototype = []
        self.operation_prototype.append( OperationDefinitionPrototype('operation_definition_id',_('Op.'),operation_definition_cache.all_on_order_part(), editable=False))
        self.operation_prototype.append( TextAreaPrototype('description',_('Description'),nullable=True,editable=False))
        self.operation_prototype.append( FloatNumberPrototype('value',_('Value'),nullable=True,editable=False))
        self.operation_prototype.append( DurationPrototype('planned_hours',_('Planned time'),nullable=True,editable=False))
        # operation_prototype.append( DurationPrototype('t_reel',_('Used time'),nullable=False,editable=False))
        self.operation_prototype.append( DurationPrototype('done_hours',_('Imputations'),editable=False))
        # operation_prototype.append( TextLinePrototype('note',_('Note'),editable=True,nullable=True,hidden=True))


        self.model = PrototypedModelView(self.operation_prototype,self)
        self.view = PrototypedQuickView(self.operation_prototype,self)
        self.view.setModel(self.model)
        self.view.verticalHeader().hide()
        self.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) # Description column wide enough
        self.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch)
        self.view.setWordWrap(True)
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.view.setSelectionMode(QAbstractItemView.ExtendedSelection)

        # self.controller_operation = PrototypeController(self,
        #                                                 self.operation_prototype,
        #                                                 ProxyTableView(None,self.operation_prototype))
        # self.controller_operation.view.verticalHeader().hide()
        # # self.controller_operation.setModel(TrackingProxyModel(None,operation_prototype))
        # self.controller_operation.setModel(TrackingProxyModel(self,self.operation_prototype))

        # self.controller_operation.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) # Description column wide enough
        # self.controller_operation.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch)

        # self.controller_operation.view.setSelectionBehavior(QAbstractItemView.SelectRows)

        layout = QHBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        layout.addWidget(self.view)
        self.setLayout(layout)

        self.copy_operations_action = QAction(_("Copy operations"),self.view)
        self.copy_operations_action.triggered.connect( self.copy_operations_slot)
        self.copy_operations_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C))
        self.copy_operations_action.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.view.addAction(self.copy_operations_action)
Пример #4
0
class DeliverySlipPanel(HorsePanel):

    delivery_slip_changed = Signal()

    def close_panel(self):
        self.filter_widget.remember_current_selection( configuration)

    def _selected_slip(self):
        cur_ndx = self.search_results_view.currentIndex()
        if cur_ndx.row() >= 0:
            return cur_ndx.model().object_at(cur_ndx.row())
        return None

    def reprint(self):
        cur_ndx = self.search_results_view.currentIndex()
        slip_id = cur_ndx.model().index(cur_ndx.row(),0).data()

        if dao.delivery_slip_part_dao.id_exists(slip_id):
            print_delivery_slip(dao,slip_id)
        else:
            makeErrorBox(_("The delivery slip {} doesn't exist").format(slip_id)).exec_()

    def desactivate(self):
        slip_id = self._selected_slip().delivery_slip_id
        mainlog.debug("Desactivate slip {}".format(slip_id))
        dao.delivery_slip_part_dao.deactivate(slip_id)
        self.refresh(slip_id)
        self.delivery_slip_changed.emit()

    def activate(self):
        slip_id = self._selected_slip().delivery_slip_id
        dao.delivery_slip_part_dao.activate(slip_id)
        self.refresh(slip_id)
        self.delivery_slip_changed.emit()

    def delete(self):
        slip_id = self._selected_slip().delivery_slip_id
        dao.delivery_slip_part_dao.delete_last(slip_id)
        self.refresh(slip_id)
        self.delivery_slip_changed.emit()

    @Slot()
    def show_actions(self):
        button = self.action_menu.parent()
        p = button.mapToGlobal(QPoint(0,button.height()))
        self.action_menu.exec_(p)

    @Slot()
    def _toggle_edit_filters(self):
        self.filter_widget.setVisible( not self.filter_widget.isVisible())

    def __init__(self,parent):
        super(DeliverySlipPanel,self).__init__(parent)

        title = _("Delivery slips")

        self.slip_data = None

        self.set_panel_title(_("Delivery slip overview"))
        self.reprint_delivery_slip = QAction(_("Reprint delivery slip"),self) # , parent
        self.reprint_delivery_slip.triggered.connect( self.reprint)
        # self.reprint_delivery_slip.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_V))
        self.reprint_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        # self.controller_operation.view.addAction(self.reprint_delivery_slip)


        self.desactivate_delivery_slip = QAction(_("Desactivate delivery slip"),self) # , parent
        self.desactivate_delivery_slip.triggered.connect( self.desactivate)
        self.desactivate_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)

        self.activate_delivery_slip = QAction(_("Activate delivery slip"),self) # , parent
        self.activate_delivery_slip.triggered.connect( self.activate)
        self.activate_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)

        self.delete_delivery_slip = QAction(_("Delete delivery slip"),self) # , parent
        self.delete_delivery_slip.triggered.connect( self.delete)
        self.delete_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)


        filter_family = FilterQuery.DELIVERY_SLIPS_FAMILY
        self.filter_widget = PersistentFilter( filter_family, suggestion_finder)
        self.filter_widget.apply_filter.connect(self.apply_filter_slot)
        self.filter_widget.hide()

        navigation = NavBar( self,
                             [ (self.filter_widget.get_filters_combo(), None),
                               (_("Edit filter"),self._toggle_edit_filters),
                               (_("Action"),self.show_actions) ] )

        self.title_widget = TitleWidget(title, self, navigation) # navigation)


        self.action_menu = QMenu(navigation.buttons[0])
        list_actions = [ (self.reprint_delivery_slip,None),
                         (self.activate_delivery_slip,None),
                         (self.desactivate_delivery_slip,None),
                         # (self.delete_delivery_slip,None)
        ]
        populate_menu(self.action_menu, self, list_actions, context=Qt.WidgetWithChildrenShortcut)

        # self.setWindowTitle(title)


        top_layout = QVBoxLayout(self)

        # self.filter_line_edit = QLineEdit()

        # self.filter_line_edit = QueryLineEdit(suggestion_finder)
        initialize_customer_cache() # FIXME Not the place to do that

        # filter_family = 'delivery_slips'
        # self.filter_name = FiltersCombo(self, filter_family)
        # filter_widget = PersistentFilter(filter_family, self.filter_name)
        # filter_widget.apply_filter.connect(self.apply_filter_slot)

        self.proto = []
        self.proto.append( IntegerNumberPrototype('delivery_slip_id',_("Slip Nr"), editable=False))
        self.proto.append( DatePrototype('creation',_('Date'), editable=False))
        self.proto.append( TextLinePrototype('fullname',_('Customer'), editable=False))
        self.proto.append( TextLinePrototype('user_label',_('Order'), editable=False))

        self.search_results_model = DeliverySlipPanelModel(self.proto, self)
        self.search_results_view = PrototypedQuickView(self.proto, self)
        self.search_results_view.setModel(self.search_results_model)


        self.search_results_view.verticalHeader().hide()
        self.search_results_view.horizontalHeader().setResizeMode(1, QHeaderView.ResizeToContents)
        self.search_results_view.horizontalHeader().setResizeMode(2, QHeaderView.Stretch)
        self.search_results_view.horizontalHeader().setSortIndicatorShown(True)
        self.search_results_view.horizontalHeader().setSortIndicator(0,Qt.AscendingOrder)
        self.search_results_view.horizontalHeader().sectionClicked.connect(self._section_clicked)
        self.search_results_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.search_results_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.search_results_view.selectionModel().currentRowChanged.connect(self.row_selected)

        self.slip_part_view = DeliverySlipViewWidget(self)

        hlayout_results = QHBoxLayout()
        # w = SubFrame(_("Delivery slips"),self.search_results_view,None)
        hlayout_results.addWidget(self.search_results_view)
        w = SubFrame(_("Detail"),self.slip_part_view,None)
        hlayout_results.addWidget(w)

        top_layout.addWidget(self.title_widget)
        top_layout.addWidget(self.filter_widget)
        top_layout.addLayout(hlayout_results)
        top_layout.setStretch(2,100)
        self.setLayout(top_layout)

        self.filter_widget.load_last_filter( configuration)



    def refresh(self,slip_id):
        filter_string = self.filter_line_edit.text()
        self.apply_filter_slot(filter_string)

        if slip_id:
            ndx = self.search_results_model.find_index( lambda s:s.delivery_slip_id == slip_id)

            r = 0
            m = self.search_results_model
            if ndx:
                r = ndx.row()

            s = QItemSelection(m.index(r,0), m.index(r, m.columnCount()-1))
            self.search_results_view.selectionModel().select(s, QItemSelectionModel.Select)

    @Slot(int)
    def _section_clicked(self,logical_ndx):
        self.refresh_action()

    @Slot(str)
    def apply_filter_slot(self, filter_string):
        if filter_string:
            try:
                self.slip_data = dao.delivery_slip_part_dao.load_slip_parts_on_filter(filter_string)
            except DataException as de:
                if de.code == DataException.CRITERIA_IS_EMPTY:
                    showErrorBox(_("Error in the filter !"),_("The filter can't be empty"),object_name="filter_is_empty")
                elif de.code == DataException.CRITERIA_IS_TOO_SHORT:
                    showErrorBox(_("Error in the filter !"),_("The filter is too short"),object_name="filter_is_too_short")
                elif de.code == DataException.CRITERIA_IS_TOO_LONG:
                    showErrorBox(_("Error in the filter !"),_("The filter is too long"),object_name="filter_is_too_long")
                return
        else:
            self.slip_data = dao.delivery_slip_part_dao.find_recent2(1000)
        self.refresh_action()


    def refresh_action(self):

        if self.slip_data == None:
            self.slip_data = dao.delivery_slip_part_dao.find_recent2(1000)

        data = self.slip_data
        headers = self.search_results_view.horizontalHeader()
        section_sorted = headers.sortIndicatorSection()
        sort_criteria = self._get_sort_criteria( section_sorted)
        sort_order = None

        if sort_criteria:
            sort_order = headers.sortIndicatorOrder()
            data = sorted(data, key=cmp_to_key(sort_criteria), reverse = sort_order == Qt.DescendingOrder)

        self.search_results_model.buildModelFromObjects(data)

        if sort_criteria:
            # Changing the model removes the sort order (which makes sense because
            # changing the model may alter the order of rows)
            headers.setSortIndicator(section_sorted,sort_order)

        self.search_results_view.setFocus(Qt.OtherFocusReason)


    @Slot(QModelIndex,QModelIndex)
    def row_selected(self,cur_ndx,prev_ndx):
        if cur_ndx.model():
            slip = cur_ndx.model().object_at( cur_ndx.row())
            if slip:
                slip_id = slip.delivery_slip_id
                self.slip_part_view.set_delivery_slip_parts(dao.delivery_slip_part_dao.load_slip_parts_frozen(slip_id))

                self.activate_delivery_slip.setEnabled( not slip.active)
                self.desactivate_delivery_slip.setEnabled( slip.active)
                self.delete_delivery_slip.setEnabled( cur_ndx.row() == 0)
                return

        self.slip_part_view.set_delivery_slip_parts(None)

    SECTION_SLIP_ID = 0
    SECTION_CREATION_DATE = 1
    SECTION_CUSTOMER = 2
    SECTION_ORDER_LABEL = 3


    def _get_sort_criteria(self,section_sorted):
        sort_criteria = None

        # True if the parts of the order stay grouped
        # together in the table
        order_stay_together = True

        if section_sorted == self.SECTION_CUSTOMER: # Customer
            sort_criteria = lambda a,b: cmp(a.fullname, b.fullname) or \
                            cmp(a.delivery_slip_id,b.delivery_slip_id)
        elif section_sorted == self.SECTION_CREATION_DATE:
            sort_criteria = lambda a,b: cmp(a.creation or date(2100,1,1), b.creation or date(2100,1,1))
        elif section_sorted == self.SECTION_SLIP_ID:
            sort_criteria = lambda a,b: cmp(a.delivery_slip_id, b.delivery_slip_id)
        elif section_sorted == self.SECTION_ORDER_LABEL:
            sort_criteria = lambda a,b: cmp(a.user_label, b.user_label)
        else:
            sort_criteria = None

        if sort_criteria is not None:
            self.current_sort_criteria = sort_criteria
        else:
            sort_criteria = self.current_sort_criteria

        return sort_criteria
Пример #5
0
    def __init__(self,parent):
        super(DeliverySlipPanel,self).__init__(parent)

        title = _("Delivery slips")

        self.slip_data = None

        self.set_panel_title(_("Delivery slip overview"))
        self.reprint_delivery_slip = QAction(_("Reprint delivery slip"),self) # , parent
        self.reprint_delivery_slip.triggered.connect( self.reprint)
        # self.reprint_delivery_slip.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_V))
        self.reprint_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        # self.controller_operation.view.addAction(self.reprint_delivery_slip)


        self.desactivate_delivery_slip = QAction(_("Desactivate delivery slip"),self) # , parent
        self.desactivate_delivery_slip.triggered.connect( self.desactivate)
        self.desactivate_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)

        self.activate_delivery_slip = QAction(_("Activate delivery slip"),self) # , parent
        self.activate_delivery_slip.triggered.connect( self.activate)
        self.activate_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)

        self.delete_delivery_slip = QAction(_("Delete delivery slip"),self) # , parent
        self.delete_delivery_slip.triggered.connect( self.delete)
        self.delete_delivery_slip.setShortcutContext(Qt.WidgetWithChildrenShortcut)


        filter_family = FilterQuery.DELIVERY_SLIPS_FAMILY
        self.filter_widget = PersistentFilter( filter_family, suggestion_finder)
        self.filter_widget.apply_filter.connect(self.apply_filter_slot)
        self.filter_widget.hide()

        navigation = NavBar( self,
                             [ (self.filter_widget.get_filters_combo(), None),
                               (_("Edit filter"),self._toggle_edit_filters),
                               (_("Action"),self.show_actions) ] )

        self.title_widget = TitleWidget(title, self, navigation) # navigation)


        self.action_menu = QMenu(navigation.buttons[0])
        list_actions = [ (self.reprint_delivery_slip,None),
                         (self.activate_delivery_slip,None),
                         (self.desactivate_delivery_slip,None),
                         # (self.delete_delivery_slip,None)
        ]
        populate_menu(self.action_menu, self, list_actions, context=Qt.WidgetWithChildrenShortcut)

        # self.setWindowTitle(title)


        top_layout = QVBoxLayout(self)

        # self.filter_line_edit = QLineEdit()

        # self.filter_line_edit = QueryLineEdit(suggestion_finder)
        initialize_customer_cache() # FIXME Not the place to do that

        # filter_family = 'delivery_slips'
        # self.filter_name = FiltersCombo(self, filter_family)
        # filter_widget = PersistentFilter(filter_family, self.filter_name)
        # filter_widget.apply_filter.connect(self.apply_filter_slot)

        self.proto = []
        self.proto.append( IntegerNumberPrototype('delivery_slip_id',_("Slip Nr"), editable=False))
        self.proto.append( DatePrototype('creation',_('Date'), editable=False))
        self.proto.append( TextLinePrototype('fullname',_('Customer'), editable=False))
        self.proto.append( TextLinePrototype('user_label',_('Order'), editable=False))

        self.search_results_model = DeliverySlipPanelModel(self.proto, self)
        self.search_results_view = PrototypedQuickView(self.proto, self)
        self.search_results_view.setModel(self.search_results_model)


        self.search_results_view.verticalHeader().hide()
        self.search_results_view.horizontalHeader().setResizeMode(1, QHeaderView.ResizeToContents)
        self.search_results_view.horizontalHeader().setResizeMode(2, QHeaderView.Stretch)
        self.search_results_view.horizontalHeader().setSortIndicatorShown(True)
        self.search_results_view.horizontalHeader().setSortIndicator(0,Qt.AscendingOrder)
        self.search_results_view.horizontalHeader().sectionClicked.connect(self._section_clicked)
        self.search_results_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.search_results_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.search_results_view.selectionModel().currentRowChanged.connect(self.row_selected)

        self.slip_part_view = DeliverySlipViewWidget(self)

        hlayout_results = QHBoxLayout()
        # w = SubFrame(_("Delivery slips"),self.search_results_view,None)
        hlayout_results.addWidget(self.search_results_view)
        w = SubFrame(_("Detail"),self.slip_part_view,None)
        hlayout_results.addWidget(w)

        top_layout.addWidget(self.title_widget)
        top_layout.addWidget(self.filter_widget)
        top_layout.addLayout(hlayout_results)
        top_layout.setStretch(2,100)
        self.setLayout(top_layout)

        self.filter_widget.load_last_filter( configuration)
Пример #6
0
    TextLinePrototype('fullname', _('Fullname'), editable=False)
]

filter = PersistentFilter(QueryLineEdit(), 'supply_orders_overview')
service = None


def apply_filter(f):
    if f == '12134':
        objects = service.findByCode(f)
        model.loadObjects(objects)
    else:
        objects = service.findByQueryString(f)
        model.loadObjects(objects)


filter.activated.connect(apply_filter)

model = PrototypedModelView(prototype, None)
list_view = PrototypedQuickView(prototype, None)
detail_view = None

layout = hlayout(SubFrame("Stock items", vlayout(filter, list_view), None),
                 SubFrame("Details", detail_view, None))

w = QWidget()
w.setLayout(l)
w.show()

app.exec_()
Пример #7
0
    def __init__(self,parent,dialog_title,list_title,form_title,mapped_klass,table_prototype,form_prototype,sort_criterion,index_builder):

        """
        sort_criterion is a SQLAlchemy colum used when querying the list of edited objects to sort it.
        index_builder : a function that takes an object of the mapped class and returns a string
           suitable for index building.
        """
        super(MetaFormDialog,self).__init__(parent)

        self.index_builder = index_builder
        self.sort_criterion = sort_criterion
        self.form_prototype = form_prototype
        self.mapped_klass = mapped_klass
        # Locate the primary key
        # this will work only with a one-field PK
        pk_column = list(filter( lambda c:c.primary_key, self.mapped_klass.__table__.columns))[0]
        self.key_field = pk_column.name


        self.in_save = False

        # The current item is the one currently shown in the
        # form. If it's None, then the form contains data
        # for a soon-to-be created item. Else, it's a frozen
        # copy. Since we work on frozen stuff, we can carry
        # the object around safely

        self.current_item = None


        self.list_model = PrototypedModelView(table_prototype, self)

        self.list_model_filtered = FilteringModel(self)
        self.list_model_filtered.setSourceModel(self.list_model)

        self.line_in = FilterLineEdit()
        self.line_in.key_down.connect(self._focus_on_list)
        self.line_in.textChanged.connect(self._filter_changed)

        self.list_view = PrototypedQuickView(table_prototype, self)
        self.list_view.setTabKeyNavigation(False)


        self.setWindowTitle(dialog_title)
        self.title_widget = TitleWidget(dialog_title,self)

        self.list_view.setModel(self.list_model_filtered)
        self.list_view.horizontalHeader().hide()
        self.list_view.verticalHeader().hide()
        self.list_view.horizontalHeader().setStretchLastSection(True)


        blayout = QVBoxLayout()

        b = QPushButton(_("New"))
        b.setObjectName("newButton")

        b.clicked.connect(self.create_action)
        blayout.addWidget(b)

        b = QPushButton(_("Save"))
        b.setObjectName("saveButton")
        b.clicked.connect(self.save_action)
        blayout.addWidget(b)

        b = QPushButton(_("Delete"))
        b.setObjectName("deleteButton")
        b.clicked.connect(self.delete_action)
        blayout.addWidget(b)

        blayout.addStretch()

        self.buttons = QDialogButtonBox()
        self.buttons.addButton( QDialogButtonBox.Ok)

        # BUG According to QLayout, the layout takes ownership of the widget
        # therefore, we have to pay attention when deleting...

        form_layout = QFormLayout()
        for p in self.form_prototype:
            w = p.edit_widget(self)
            w.setEnabled(p.is_editable)
            w.setObjectName("form_" + p.field)
            form_layout.addRow( p.title, w)

        top_layout = QVBoxLayout()
        top_layout.addWidget(self.title_widget)

        hl = QHBoxLayout()


        vlayout = QVBoxLayout()

        vlayout.addWidget(self.line_in)
        vlayout.addWidget(self.list_view)
        # gbox = QGroupBox(list_title,self)
        # gbox.setLayout(vlayout)
        gbox = SubFrame(list_title,vlayout,self)
        hl.addWidget(gbox)

        # gbox = QGroupBox(form_title,self)
        # gbox.setLayout(form_layout)
        gbox = SubFrame(form_title,form_layout,self)
        hl.addWidget(gbox)
        hl.addLayout(blayout)

        # hl.setStretch(0,0.3)
        # hl.setStretch(1,0.7)
        # hl.setStretch(2,0)

        top_layout.addLayout(hl)
        top_layout.addWidget(self.buttons)

        self.setLayout(top_layout) # QWidget takes ownership of the layout
        self.buttons.accepted.connect(self.reject)

        QWidget.setTabOrder(self.line_in, self.list_view)

        nb_objs = self._refresh_list()
        self.line_in.setFocus()
        self.list_view.selectionModel().currentChanged.connect(self.selected_item_changed) # FIXME Clear ownership issue
        if nb_objs > 0:
            self.list_view.selectRow(0)
        else:
            # Special case to automaticaly enter creation mode when
            # the list is empty
            self.create_action()
Пример #8
0
class MetaFormDialog(QDialog):

    def preselect_item(self,item):
        # Pay attention ! The selection implictely uses "__eq__" to find out
        # an object in the list of objects. So be careful with objects
        # that are outside sessions and which have no __eq__ operation : these
        # will be compared on basis of the python's Id which might defer for
        # 2 objects denoting the same thing. See Customer for an example.

        # mainlog.debug("Preselect item {}".format(item))
        # mainlog.debug("Preselect item in this list")
        # mainlog.debug(" -- ".join(sorted(map(lambda c:c.fullname,self.list_model.objects))))
        t = self.list_model.objects.index(item)
        # mainlog.debug("Preselect item index {}".format(t))
        self.list_view.setCurrentIndex(self.list_model.index(t,0))


    def __init__(self,parent,dialog_title,list_title,form_title,mapped_klass,table_prototype,form_prototype,sort_criterion,index_builder):

        """
        sort_criterion is a SQLAlchemy colum used when querying the list of edited objects to sort it.
        index_builder : a function that takes an object of the mapped class and returns a string
           suitable for index building.
        """
        super(MetaFormDialog,self).__init__(parent)

        self.index_builder = index_builder
        self.sort_criterion = sort_criterion
        self.form_prototype = form_prototype
        self.mapped_klass = mapped_klass
        # Locate the primary key
        # this will work only with a one-field PK
        pk_column = list(filter( lambda c:c.primary_key, self.mapped_klass.__table__.columns))[0]
        self.key_field = pk_column.name


        self.in_save = False

        # The current item is the one currently shown in the
        # form. If it's None, then the form contains data
        # for a soon-to-be created item. Else, it's a frozen
        # copy. Since we work on frozen stuff, we can carry
        # the object around safely

        self.current_item = None


        self.list_model = PrototypedModelView(table_prototype, self)

        self.list_model_filtered = FilteringModel(self)
        self.list_model_filtered.setSourceModel(self.list_model)

        self.line_in = FilterLineEdit()
        self.line_in.key_down.connect(self._focus_on_list)
        self.line_in.textChanged.connect(self._filter_changed)

        self.list_view = PrototypedQuickView(table_prototype, self)
        self.list_view.setTabKeyNavigation(False)


        self.setWindowTitle(dialog_title)
        self.title_widget = TitleWidget(dialog_title,self)

        self.list_view.setModel(self.list_model_filtered)
        self.list_view.horizontalHeader().hide()
        self.list_view.verticalHeader().hide()
        self.list_view.horizontalHeader().setStretchLastSection(True)


        blayout = QVBoxLayout()

        b = QPushButton(_("New"))
        b.setObjectName("newButton")

        b.clicked.connect(self.create_action)
        blayout.addWidget(b)

        b = QPushButton(_("Save"))
        b.setObjectName("saveButton")
        b.clicked.connect(self.save_action)
        blayout.addWidget(b)

        b = QPushButton(_("Delete"))
        b.setObjectName("deleteButton")
        b.clicked.connect(self.delete_action)
        blayout.addWidget(b)

        blayout.addStretch()

        self.buttons = QDialogButtonBox()
        self.buttons.addButton( QDialogButtonBox.Ok)

        # BUG According to QLayout, the layout takes ownership of the widget
        # therefore, we have to pay attention when deleting...

        form_layout = QFormLayout()
        for p in self.form_prototype:
            w = p.edit_widget(self)
            w.setEnabled(p.is_editable)
            w.setObjectName("form_" + p.field)
            form_layout.addRow( p.title, w)

        top_layout = QVBoxLayout()
        top_layout.addWidget(self.title_widget)

        hl = QHBoxLayout()


        vlayout = QVBoxLayout()

        vlayout.addWidget(self.line_in)
        vlayout.addWidget(self.list_view)
        # gbox = QGroupBox(list_title,self)
        # gbox.setLayout(vlayout)
        gbox = SubFrame(list_title,vlayout,self)
        hl.addWidget(gbox)

        # gbox = QGroupBox(form_title,self)
        # gbox.setLayout(form_layout)
        gbox = SubFrame(form_title,form_layout,self)
        hl.addWidget(gbox)
        hl.addLayout(blayout)

        # hl.setStretch(0,0.3)
        # hl.setStretch(1,0.7)
        # hl.setStretch(2,0)

        top_layout.addLayout(hl)
        top_layout.addWidget(self.buttons)

        self.setLayout(top_layout) # QWidget takes ownership of the layout
        self.buttons.accepted.connect(self.reject)

        QWidget.setTabOrder(self.line_in, self.list_view)

        nb_objs = self._refresh_list()
        self.line_in.setFocus()
        self.list_view.selectionModel().currentChanged.connect(self.selected_item_changed) # FIXME Clear ownership issue
        if nb_objs > 0:
            self.list_view.selectRow(0)
        else:
            # Special case to automaticaly enter creation mode when
            # the list is empty
            self.create_action()

    @Slot()
    def _focus_on_list(self):
        """ When the user hits the down key on the filter, we transfer
        the focus to the filtered list
        """
        self.list_view.setFocus()
        self.list_view.selectionModel().setCurrentIndex(self.list_view.model().index(0,0), QItemSelectionModel.ClearAndSelect)


    def _filter_changed(self,s):
        self.list_model_filtered.setFilterFixedString(s)
        # self.list_view.selectRow(0)

        self.list_view.selectionModel().setCurrentIndex(self.list_view.model().index(0,0), QItemSelectionModel.ClearAndSelect)

    def _refresh_list(self):
        mainlog.debug("_refresh_list")
        self.current_item = None
        objs = self.objects_list()

        self.list_model.buildModelFromObjects(objs)
        self.list_model_filtered.setIndexData([self.index_builder(o) for o in objs])

        return len(objs)

    def _select_on_object_id(self,o_id,update_view_selection=True):
        objects = self.list_model.objects

        ndx = -1
        for i in range(self.list_model.rowCount()):
            obj = self.list_model.object_at(i)
            if getattr(obj,self.key_field) == o_id:
                ndx = i
                break

        mainlog.debug("_select_on_object_id: ndx={}".format(ndx))

        if update_view_selection:
            self.list_view.clearSelection()

            # Look where the selected object is in the *filtered* view

            filtered_ndx = self.list_model_filtered.mapFromSource( self.list_model.index(ndx,0))

            mainlog.debug("Filtered ndx isValid = {}".format(filtered_ndx.isValid()))

            if not filtered_ndx.isValid():
                # The object is not visible in the filtered list.
                # So we clear the filter to show everything

                # self.line_in.setText("") # This triggers a refresh of the list
                filtered_ndx = self.list_model_filtered.mapFromSource( self.list_model.index(ndx,0))

            mainlog.debug("Filtered ndx isValid = {}".format(filtered_ndx.isValid()))

            self.list_view.setCurrentIndex(filtered_ndx)
            self.list_view.selectionModel().setCurrentIndex(filtered_ndx, QItemSelectionModel.NoUpdate)
            self.list_view.setFocus()



    def _populate_form(self,obj):
        mainlog.debug("_populate_form with {}".format(obj))
        self.current_item = obj

        if obj:
            for p in self.form_prototype:
                mainlog.debug("   {} -> {}".format(p.field, getattr(obj,p.field)))
                p.set_edit_widget_data(getattr(obj,p.field))
        else:
            # Clear the form
            for p in self.form_prototype:
                p.set_edit_widget_data( p.default_value())


    def _load_forms_data(self):
        d = dict()

        for p in self.form_prototype:
            # mainlog.debug("_load_forms_data : {} = {}".format(p.field, p.edit_widget_data()))
            d[p.field] = p.edit_widget_data()

        if self.current_item:
            d[self.key_field] = getattr(self.current_item, self.key_field)
        else:
            mainlog.debug("_load_forms_data : no current item")
        return d



    def _data_changed(self, form_data, obj):
        """ True if the data in the hash form_data are different
        than what is in sqla_obj
        """

        def cmp_instrumented_list(a,b):
            # mainlog.debug("cmp_instrumented_list")

            if len(a) != len(b):
                return False

            # mainlog.debug("cmp_instrumented_list lengths are equal")

            for i in range(len(a)):
                if a[i] != b[i]:
                    # mainlog.debug(u"cmp_instrumented_list {} != {}".format(a[i],b[i]))
                    return False

            return True

        def pixmap_hash(pixmap):
            """ Compute a hash of the *content* of a picture.
            I've tried to use QPixmap.cacheKey() but somehow
            it's not dependent on content only (thus two
            Pixmap containing the same picture have different
            cacheKey() (and the documentation is not quite
            clear.
            """

            import hashlib
            # mainlog.debug("Hashing pixmap {} {}".format(id(pixmap),type(pixmap)))
            m = hashlib.md5()
            m.update(pixmap.toImage().bits())
            return m.digest()

        if obj:
            # Form data compared to actual object content
            for p in self.form_prototype:
                if not p.is_editable:
                    continue

                attr = getattr(obj,p.field)
                new_attr = form_data[p.field]

                # Be cool with spaces. Note that we can have surplus
                # spaces from database as well as from the form...

                if type(attr) == str:
                    attr = attr.strip() or None
                if type(new_attr) == str:
                    new_attr = new_attr.strip() or None

                # mainlog.debug(u"MetaFormDialog : field:{} : obj:{} - form:{}".format(p.field, attr, new_attr))

                if ( (type(attr) == QPixmap and pixmap_hash(attr) != pixmap_hash(new_attr)) or\
                     (type(attr) == InstrumentedList and not cmp_instrumented_list(attr,new_attr)) or\
                     (type(attr) != QPixmap and type(attr) != InstrumentedList and attr != new_attr)):
                    mainlog.debug(u"_data_changed2 : data are different on {} : '{}' != '{}'".format(p.field, attr, new_attr))
                    return True
            return False
        else:
            # Form data compared to empty object
            for p in self.form_prototype:
                new_attr = form_data[p.field]
                mainlog.debug("MetaFormDialog : empty ! {} : new_attr {}, default = {}".format(p.field, new_attr, p.default_value()))
                # We compare to a non filled object
                if new_attr and str(new_attr) != u"":
                    # That seems like a change, but we'll consider it
                    # only if we differ from the default value
                    # This helps in case we compare to fields which can
                    # not be None when empty (for example a combo box with
                    # a few values)
                    if new_attr != p.default_value():
                        return True
            return False


    def _validate_and_save(self, form_data):
        """ Returns saved object's id or False is save could not be
        completed (either because there are errors in the validation
        or because there are other technical errors).
        """

        errors = dict()
        for p in self.form_prototype:
            data = form_data[p.field]

            if p.is_editable:
                v = p.validate(data)
                if v != True:
                    errors[p.title] = v

        if len(errors) > 0:
            info_text = ""
            for field,error in errors.items():
                info_text += u"<li>{}</li>".format(error)

            showErrorBox(_("Some of the data you encoded is not right"),u"<ul>{}</ul>".format(info_text))
            return False


        # check = self.check_before_save(self.current_item)

        check = True

        if check == True:
            try:
                return self.save_object(form_data)
            except Exception as e:
                showErrorBox(_("There was an error while saving"),str(e),e)
                return False
        else:
            showErrorBox(_("There was an error while saving"),check)
            return False

    SAVE_DECLINED_BY_USER = -1
    SAVE_FAILED_BECAUSE_OF_ERRORS = -2

    def _save_if_necessary(self):
        """ Returns :
        - primary key if something was saved
        - True if the user choose to not save (for wahtever reason) or a save
          was not needed (no data changed)
        - False if there were errors during the save
        """

        form_data = self._load_forms_data()
        mainlog.debug("_save_if_necessary: form_data = {}".format(form_data))
        if self._data_changed(form_data, self.current_item):
            ynb = yesNoBox(_("Data were changed"),
                           _("You have changed some of the data in this. Do you want to save before proceeding ?"))

            if ynb == QMessageBox.Yes:
                saved_obj_id = self._validate_and_save(form_data)
                mainlog.debug("_save_if_necessary: saved object id  {}".format(saved_obj_id))
                if saved_obj_id != False:
                    mainlog.debug("_save_if_necessary: returning  {}".format(saved_obj_id))
                    return saved_obj_id
                else:
                    # There were errors while trying to save
                    return False

        return True


    @Slot(QModelIndex,QModelIndex)
    def selected_item_changed(self, current, previous):

        mainlog.debug("selected_item_changed old: {} new: {} in_save:{}".format(previous.row(),current.row(), self.in_save))

        # The test below avoids some recursion. It's a bit more clever
        # than it looks. What happens is this. The user modifies
        # some data then ask to change the edited object (in the left list)
        # Doing so it triggers this method. The program then save (if
        # necessary). But that save may trigger a reorganisation of the
        # list. So what the user has selected may be at a different
        # index than the "current" one we received as a parameter
        # of this method. To account for that we actually reselect
        # the item in the table. And this triggers the recursion we avoid
        # here. FIXME there is a recursion but the way we avoid
        # it is not 100% satisfactory, we should use a "semaphore"
        # for that.


        if current.isValid() and current.row() >= 0 and \
           current.row() != previous.row():

            ndx = self.list_model_filtered.mapToSource(self.list_model_filtered.index(current.row(),0))
            target = self.list_model.object_at(ndx.row())

            mainlog.debug("selected_item_changed: trying to save something")
            if (not self.in_save) and type(self._save_if_necessary()) is int:
                # Something was actually saved
                mainlog.debug("selected_item_changed : something was saved starting list refresh")
                self.in_save = True
                self._refresh_list()
                self._select_on_object_id(getattr(target,self.key_field))
                self.in_save = False
                # mainlog.debug("selected_item_changed : done list refresh")
            self._populate_form(target)



    # FIXME Qt Bug ??? Mismatch between the parameters of the signal in the doc
    # and what I really get (no param...)
    @Slot(bool)
    def create_action(self):
        self.list_view.clearSelection()
        self._populate_form(None)
        self.form_prototype[0].edit_widget(None).setFocus(Qt.OtherFocusReason)

    @Slot(bool)
    def save_action(self):
        # mainlog.debug("Current row is {}".format(self.list_view.currentIndex().row()))

        self.in_save = True
        form_data = self._load_forms_data()
        obj_id = self._validate_and_save(form_data)

        # Following a save, the position of the object in the
        # list might have changed. So we need to reload the
        # list to account for that and we also need to reselect
        # the object

        if obj_id != False:
            # mainlog.debug("Save action : refreshing")
            self._refresh_list()
            # mainlog.debug("Save action : selecting")
            self._select_on_object_id(obj_id)

        self.in_save = False


    @Slot(bool)
    def delete_action(self):
        if self.current_item:
            o_id = getattr( self.current_item, self.key_field)

            if o_id >= 0: # For some reason I have o_id = 0 somewhere...

                # mainlog.debug("About to delete {}".format(o_id))

                try:
                    if self.delete_object(o_id):
                        self.current_item = None # Do this only if delete was successful !
                        self.in_save = True
                        self._refresh_list()

                        # The current filter might lead to a 0-length list
                        # or we might delete the only item of the list
                        # In that case, we clear the form.

                        if self.list_view.model().rowCount() > 0:
                            self.list_view.selectRow(0)
                        else:
                            self._populate_form(None)

                        self.in_save = False

                except Exception as e:
                    showErrorBox(_("There was an error while deleting"),str(e),e)
                    return

            else:
                mainlog.error("The current object has no id => I can't delete it")
        else:
            showWarningBox(_("You have selected nothing for delete."),None)
            return


    def done(self,x):
        current_ndx = self.list_view.currentIndex().row()
        if self._save_if_necessary():
            super(MetaFormDialog,self).done(x)
        # here be dragons

    def check_before_save(self,obj):
        return True


    def save_object(self,form_data):
        """ Save object hook
        """

        c = recursive_defrost_into(form_data, self.mapped_klass)
        session().commit()
        return getattr(c, self.key_field)

    def delete_object(self,o_id):
        generic_delete(self.mapped_klass, o_id)
        return True

    def objects_list(self):
        """ Reload the objects from the database.
        :return: a list of DTO.
        """
        return generic_load_all_frozen(self.mapped_klass, self.sort_criterion)
Пример #9
0
class DeliverySlipViewWidget(QWidget):
    def __init__(self,parent=None):
        super(DeliverySlipViewWidget,self).__init__(parent)

        self.delivery_slip_part_proto = []
        # self.delivery_slip_part_proto.append( OrderPartDisplayPrototype('order_part',_('Part'), editable=False))
        self.delivery_slip_part_proto.append( TextLinePrototype('part_label',_('Part'), editable=False))
        self.delivery_slip_part_proto.append( TextLinePrototype('description',_('Description'), editable=False))
        self.delivery_slip_part_proto.append( IntegerNumberPrototype('quantity_out',_('Q. out'), editable=False))


        # self.controller_operation = PrototypeController(self,
        #                                                 self.delivery_slip_part_proto,
        #                                                 ProxyTableView(None,self.delivery_slip_part_proto))
        # # self.controller_operation.view.verticalHeader().hide()
        # self.controller_operation.setModel(TrackingProxyModel(self,self.delivery_slip_part_proto))


        self.model = PrototypedModelView(self.delivery_slip_part_proto, self)
        self.view = PrototypedQuickView(self.delivery_slip_part_proto, self)
        self.view.setModel(self.model)
        self.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) # Description column wide enough
        self.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch)
        self.view.verticalHeader().hide()

        layout = QHBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        # layout.addWidget(self.controller_operation.view)
        layout.addWidget(self.view)
        self.setLayout(layout)



    def set_delivery_slip_parts(self, parts):
        """ Fill with order part's data.
        Doesn't keep any reference to the order part.
        """

        if parts:
            self.model.buildModelFromObjects(parts)
        else:
            self.model.clear()

        ndx = self.model.index(0,0)
        self.view.setCurrentIndex(ndx)
        self.view.resizeRowsToContents()
Пример #10
0
class OperationsOverviewWidget(QWidget):
    def __init__(self,parent=None):
        super(OperationsOverviewWidget,self).__init__(parent)

        self.operation_prototype = []
        self.operation_prototype.append( OperationDefinitionPrototype('operation_definition_id',_('Op.'),operation_definition_cache.all_on_order_part(), editable=False))
        self.operation_prototype.append( TextAreaPrototype('description',_('Description'),nullable=True,editable=False))
        self.operation_prototype.append( FloatNumberPrototype('value',_('Value'),nullable=True,editable=False))
        self.operation_prototype.append( DurationPrototype('planned_hours',_('Planned time'),nullable=True,editable=False))
        # operation_prototype.append( DurationPrototype('t_reel',_('Used time'),nullable=False,editable=False))
        self.operation_prototype.append( DurationPrototype('done_hours',_('Imputations'),editable=False))
        # operation_prototype.append( TextLinePrototype('note',_('Note'),editable=True,nullable=True,hidden=True))


        self.model = PrototypedModelView(self.operation_prototype,self)
        self.view = PrototypedQuickView(self.operation_prototype,self)
        self.view.setModel(self.model)
        self.view.verticalHeader().hide()
        self.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) # Description column wide enough
        self.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch)
        self.view.setWordWrap(True)
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.view.setSelectionMode(QAbstractItemView.ExtendedSelection)

        # self.controller_operation = PrototypeController(self,
        #                                                 self.operation_prototype,
        #                                                 ProxyTableView(None,self.operation_prototype))
        # self.controller_operation.view.verticalHeader().hide()
        # # self.controller_operation.setModel(TrackingProxyModel(None,operation_prototype))
        # self.controller_operation.setModel(TrackingProxyModel(self,self.operation_prototype))

        # self.controller_operation.view.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) # Description column wide enough
        # self.controller_operation.view.horizontalHeader().setResizeMode(1,QHeaderView.Stretch)

        # self.controller_operation.view.setSelectionBehavior(QAbstractItemView.SelectRows)

        layout = QHBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        layout.addWidget(self.view)
        self.setLayout(layout)

        self.copy_operations_action = QAction(_("Copy operations"),self.view)
        self.copy_operations_action.triggered.connect( self.copy_operations_slot)
        self.copy_operations_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_C))
        self.copy_operations_action.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.view.addAction(self.copy_operations_action)


    def fill_order_part(self, order_part_id):
        """ Fill with order part's data.
        Doesn't keep any reference to the order part.
        """

        if order_part_id:
            mainlog.debug("fill_order_part : triggered !")
            operations = dao.operation_dao.find_by_order_part_id_frozen(order_part_id)
            mainlog.debug("fill_order_part : build model !")
            self.model.buildModelFromObjects(operations)
            mainlog.debug("fill_order_part : build model complete !")
            self.view.verticalHeader().setResizeMode(QHeaderView.ResizeToContents)
        else:
            self.model.clear()

        ndx = self.model.index(0,0)
        mainlog.debug("fill_order_part : setCurrentIndex !")
        self.view.setCurrentIndex(ndx)
        mainlog.debug("fill_order_part : setCurrentIndex done!")

    @Slot()
    def copy_operations_slot(self):
        mainlog.debug("copy_operations_slot")

        view = self.view
        model = self.model

        # Collect the rows indices

        rows = set()
        for ndx in view.selectedIndexes():
            if ndx.row() >= 0:
                rows.add(ndx.row())

        # There are no guarantee on the selectedIndexes order
        rows = sorted(list(rows))


        # Copy for elsewhere in Horse

        if len(rows):
            operations = []
            for row_ndx in rows:
                operation = model.object_at(row_ndx)
                mainlog.debug(operation)
                operations.append(operation)
            copy_paste_manager.copy_operations(operations)
        else:
            # If nothing to copy then we leave the copy/paste clipboard
            # as it is. So one could paste again what he copied before.
            pass
Пример #11
0
class SupplyOrderOverview(HorsePanel):
    def close_panel(self):
        self.filter_widget.remember_current_selection(configuration)

    def _selected_supply_order_part(self):
        cur_ndx = self.search_results_view.currentIndex()
        if cur_ndx.row() >= 0:
            return cur_ndx.model().object_at(cur_ndx.row())
        return None

    @Slot()
    def show_actions(self):
        button = self.action_menu.parent()
        p = button.mapToGlobal(QPoint(0, button.height()))
        self.action_menu.exec_(p)

    SECTION_REFERENCE = 0
    SECTION_SUPPLIER = 1
    SECTION_DEADLINE = 2
    SECTION_DESCRIPTION = 3
    SECTION_CREATION_DATE = 6

    def _make_sort_criteria(self, section_sorted, sort_order):

        sort_criteria = None

        if section_sorted == self.SECTION_REFERENCE:
            if sort_order == Qt.AscendingOrder:

                def ordering_key(p):
                    return p.accounting_label * 1000 + p.position

                sort_criteria = lambda a, b: cmp(ordering_key(a),
                                                 ordering_key(b))
            else:

                def ordering_key(p):
                    return p.accounting_label * 1000 + 999 - p.position

                sort_criteria = lambda a, b: cmp(ordering_key(a),
                                                 ordering_key(b))
        elif section_sorted == self.SECTION_SUPPLIER:
            order_correction = +1
            if sort_order == Qt.DescendingOrder:
                order_correction = -1

            sort_criteria = lambda a,b: cmp(a.supplier_fullname, b.supplier_fullname) or \
                            cmp(a.accounting_label,b.accounting_label) or order_correction * cmp(a.position,b.position)
        elif section_sorted == self.SECTION_DEADLINE:  # Deadline
            sort_criteria = lambda a, b: cmp(
                a.expected_delivery_date or date(2100, 1, 1), b.
                expected_delivery_date or date(2100, 1, 1))

        return sort_criteria

    def _fill_model(self, parts_data=None):
        """ Refills the list of parts data with given parts
        data. If no parts data are given, then the last
        data set is reused.

        This thake into account the currently selected sort
        indicator and, if any, the currently selected filter.
        """

        if parts_data is not None:
            self.parts_data = parts_data
        else:
            parts_data = self.parts_data

        view = self.search_results_view
        model = self.search_results_model
        headers = view.horizontalHeader()
        sort_order = headers.sortIndicatorOrder()
        section_sorted = headers.sortIndicatorSection()

        sort_criteria = self._make_sort_criteria(section_sorted, sort_order)
        if sort_criteria:
            sort_order = headers.sortIndicatorOrder()

            if sys.version[0] == '3':
                parts_data = sorted(parts_data,
                                    key=cmp_to_key(sort_criteria),
                                    reverse=sort_order == Qt.DescendingOrder)
            else:
                parts_data = sorted(parts_data,
                                    sort_criteria,
                                    reverse=sort_order == Qt.DescendingOrder)

        model.buildModelFromObjects(parts_data)

        if sort_criteria:
            # Changing the model removes the sort order (which makes sense because
            # changing the model may alter the order of rows)
            # => I have to reset it
            headers.setSortIndicator(section_sorted, sort_order)

        if len(parts_data) == 0:
            # Must do this because when the model is empty
            # the slection is not updated, so no signal
            # is sent
            self._fill_order_part_detail(None)
        else:
            view.selectionModel().reset()
            view.selectionModel().select(
                view.model().index(0, 0),
                QItemSelectionModel.Select | QItemSelectionModel.Rows)

        view.horizontalHeader().setResizeMode(self.SECTION_DESCRIPTION,
                                              QHeaderView.Stretch)

    @Slot()
    def refresh_action(self):
        mainlog.debug("refresh_action")
        self._apply_filter(self.filter_widget.current_filter())

    @Slot(int)
    def section_clicked(self, logical_ndx):
        self._fill_model()

    @Slot(str)
    def _apply_filter(self, filter_text):
        mainlog.debug(u"_apply_filter : {}".format(filter_text))

        parts = []
        len_check = False

        if " " in filter_text.strip():
            # More than one word in the filter => I assume it's the full
            # fledged filtering

            check = check_parse(filter_text)
            if check == True:
                parts = supply_order_service.find_parts_expression_filter(
                    filter_text)
                len_check = True
            else:
                showErrorBox(_("Error in the filter !"),
                             check,
                             object_name="filter_is_wrong")

        elif filter_text:
            parts = supply_order_service.find_parts_filtered(filter_text)
            len_check = True
        else:
            parts = supply_order_service.find_recent_parts()
            len_check = False

        if len_check and len(parts) >= supply_order_service.MAX_RESULTS:
            showWarningBox(
                _("Too many results"),
                _("The query you've given brought back too many results. Only a part of them is displayed. Consider refining your query"
                  ))
        self._fill_model(parts)
        self.search_results_view.setFocus(Qt.OtherFocusReason)

    def _make_supply_order_detail_view(self):

        # There's a self.proto somewhere, don't mess with it :-)

        # proto = []
        # proto.append( TextLinePrototype('description',_('Description'), editable=True,nullable=False))
        # proto.append( FloatNumberPrototype('quantity',_('Quantity'), editable=True,nullable=False))
        # proto.append( FloatNumberPrototype('unit_price',_('Unit price'), editable=True,nullable=False))

        # self.detail_model = PrototypedModelView(proto, self)
        # self.detail_view = PrototypedQuickView(proto, self)
        # self.detail_view.setModel(self.detail_model)
        # self.detail_view.verticalHeader().hide()

        self.detail_description = QTextEdit()
        self.detail_description.setTextInteractionFlags(
            Qt.TextBrowserInteraction)

        self.delivery_date_widget = QLabel()
        self.creation_date_widget = QLabel()
        self.supplier_reference_widget = QLabel()

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel(_("Delivery date")))
        hlayout.addWidget(self.delivery_date_widget)
        hlayout.addStretch()

        hlayout3 = QHBoxLayout()
        hlayout3.addWidget(QLabel(_("Creation date")))
        hlayout3.addWidget(self.creation_date_widget)
        hlayout3.addStretch()

        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(QLabel(_("Supplier's reference")))
        hlayout2.addWidget(self.supplier_reference_widget)
        hlayout2.addStretch()

        layout = QVBoxLayout()
        layout.addLayout(hlayout)
        layout.addLayout(hlayout3)
        layout.addLayout(hlayout2)
        layout.addWidget(self.detail_description)
        layout.addStretch()

        # layout.addWidget(self.detail_view)
        # layout.setStretch(0,1)
        # layout.setStretch(1,3)

        return layout

    @Slot()
    def _toggle_edit_filters(self):
        self.filter_widget.setVisible(not self.filter_widget.isVisible())

    def __init__(self, parent):
        super(SupplyOrderOverview, self).__init__(parent)

        initialize_supplier_cache()

        self.set_panel_title(_("Supply Orders"))

        # navigation = NavBar( self,
        #                      [ (_("Action"),self.show_actions) ] )
        # self.action_menu = QMenu(navigation.buttons[0])

        title = _("Supply orders")
        # self.setWindowTitle(title)

        filter_family = FilterQuery.SUPPLIER_ORDER_SLIPS_FAMILY
        self.filter_widget = PersistentFilter(filter_family)
        self.filter_widget.apply_filter.connect(self._apply_filter)
        self.filter_widget.hide()

        self.proto = []
        self.proto.append(
            TextLinePrototype('human_identifier', _("Part Nr"),
                              editable=False))
        self.proto.append(
            TextLinePrototype('supplier_fullname',
                              _('Supplier'),
                              editable=False))
        self.proto.append(
            DatePrototype('expected_delivery_date',
                          _('Deadline'),
                          editable=True,
                          nullable=False))
        self.proto.append(
            TextLinePrototype('description',
                              _('Description'),
                              editable=True,
                              nullable=False))
        self.proto.append(
            FloatNumberPrototype('quantity',
                                 _('Quantity'),
                                 editable=True,
                                 nullable=False))
        self.proto.append(
            FloatNumberPrototype('unit_price',
                                 _('Unit price'),
                                 editable=True,
                                 nullable=False))
        self.proto.append(
            DatePrototype('creation_date',
                          _('Creation date'),
                          editable=False,
                          nullable=False))

        # self.proto.append( DatePrototype('creation_date',_('Creation'), editable=False))
        # self.proto.append( DatePrototype('expected_delivery_date',_('Expected\ndelivery'), editable=False))
        # self.proto.append( TextLinePrototype('supplier_fullname',_('Supplier'), editable=False))

        self.search_results_model = PrototypedModelView(self.proto, self)
        self.search_results_view = PrototypedQuickView(self.proto, self)
        self.search_results_view.setModel(self.search_results_model)
        self.search_results_view.horizontalHeader().setSortIndicatorShown(True)

        self.search_results_view.verticalHeader().hide()
        self.search_results_view.verticalHeader().setResizeMode(
            QHeaderView.ResizeToContents)
        self.search_results_view.doubleClicked.connect(
            self._supply_order_double_clicked)
        self.search_results_view.activated.connect(
            self._supply_order_double_clicked)
        self.search_results_view.horizontalHeader().sectionClicked.connect(
            self.section_clicked)

        top_layout = QVBoxLayout(self)

        navigation = NavBar(self,
                            [(self.filter_widget.get_filters_combo(), None),
                             (_("Edit filter"), self._toggle_edit_filters)])

        self.title_widget = TitleWidget(title, self, navigation)  # navigation)

        hlayout_results = QHBoxLayout()
        # w = SubFrame(_("Supply order parts"),self.search_results_view,None)
        hlayout_results.addWidget(self.search_results_view)

        w = SubFrame(_("Supply order detail"),
                     self._make_supply_order_detail_view(), None)
        hlayout_results.addWidget(w)

        hlayout_results.setStretch(0, 2)
        hlayout_results.setStretch(0, 1)

        top_layout.addWidget(self.title_widget)
        top_layout.addWidget(self.filter_widget)
        top_layout.addLayout(hlayout_results)
        top_layout.setStretch(3, 100)
        self.setLayout(top_layout)

        self.search_results_view.setSelectionBehavior(
            QAbstractItemView.SelectRows)
        self.search_results_view.setSelectionMode(
            QAbstractItemView.SingleSelection)

        # self.search_results_view.activated.connect(self.row_activated)
        self.search_results_view.selectionModel().currentRowChanged.connect(
            self.row_selected)
        # self.detail_view.doubleClicked.connect(self._supply_order_double_clicked)

        # pub.subscribe(self.refresh_panel, 'supply_order.changed')
        # pub.subscribe(self.refresh_panel, 'supply_order.deleted')
        # self.filter_widget.select_default_filter()
        self.filter_widget.load_last_filter(configuration)

    supply_order_selected = Signal(object)

    @Slot(QModelIndex)
    def _supply_order_double_clicked(self, cur_ndx):
        mainlog.debug("_supply_order_double_clicked")
        supply_order = cur_ndx.model().object_at(cur_ndx.row())
        if supply_order:
            self.supply_order_selected.emit(supply_order)

    def _fill_order_part_detail(self, supply_order_part):
        if supply_order_part:
            supply_order_part_id = supply_order_part.supply_order_id
            supply_order, parts = supply_order_service.find_by_id(
                supply_order_part.supply_order_id)
            self.detail_description.setText(supply_order.description)
            self.delivery_date_widget.setText(
                date_to_s(supply_order.expected_delivery_date) or "-")
            self.supplier_reference_widget.setText(
                supply_order.supplier_reference or "-")
            self.creation_date_widget.setText(
                date_to_s(supply_order.creation_date) or "-")
        else:
            self.detail_description.setText("-")
            self.delivery_date_widget.setText("-")
            self.supplier_reference_widget.setText("-")
            self.creation_date_widget.setText("-")

    @Slot(QModelIndex, QModelIndex)
    def row_selected(self, cur_ndx, prev_ndx):
        if cur_ndx.model():
            supply_order_part = cur_ndx.model().object_at(cur_ndx.row())
            self._fill_order_part_detail(supply_order_part)
Пример #12
0
    def __init__(self, parent):
        super(SupplyOrderOverview, self).__init__(parent)

        initialize_supplier_cache()

        self.set_panel_title(_("Supply Orders"))

        # navigation = NavBar( self,
        #                      [ (_("Action"),self.show_actions) ] )
        # self.action_menu = QMenu(navigation.buttons[0])

        title = _("Supply orders")
        # self.setWindowTitle(title)

        filter_family = FilterQuery.SUPPLIER_ORDER_SLIPS_FAMILY
        self.filter_widget = PersistentFilter(filter_family)
        self.filter_widget.apply_filter.connect(self._apply_filter)
        self.filter_widget.hide()

        self.proto = []
        self.proto.append(
            TextLinePrototype('human_identifier', _("Part Nr"),
                              editable=False))
        self.proto.append(
            TextLinePrototype('supplier_fullname',
                              _('Supplier'),
                              editable=False))
        self.proto.append(
            DatePrototype('expected_delivery_date',
                          _('Deadline'),
                          editable=True,
                          nullable=False))
        self.proto.append(
            TextLinePrototype('description',
                              _('Description'),
                              editable=True,
                              nullable=False))
        self.proto.append(
            FloatNumberPrototype('quantity',
                                 _('Quantity'),
                                 editable=True,
                                 nullable=False))
        self.proto.append(
            FloatNumberPrototype('unit_price',
                                 _('Unit price'),
                                 editable=True,
                                 nullable=False))
        self.proto.append(
            DatePrototype('creation_date',
                          _('Creation date'),
                          editable=False,
                          nullable=False))

        # self.proto.append( DatePrototype('creation_date',_('Creation'), editable=False))
        # self.proto.append( DatePrototype('expected_delivery_date',_('Expected\ndelivery'), editable=False))
        # self.proto.append( TextLinePrototype('supplier_fullname',_('Supplier'), editable=False))

        self.search_results_model = PrototypedModelView(self.proto, self)
        self.search_results_view = PrototypedQuickView(self.proto, self)
        self.search_results_view.setModel(self.search_results_model)
        self.search_results_view.horizontalHeader().setSortIndicatorShown(True)

        self.search_results_view.verticalHeader().hide()
        self.search_results_view.verticalHeader().setResizeMode(
            QHeaderView.ResizeToContents)
        self.search_results_view.doubleClicked.connect(
            self._supply_order_double_clicked)
        self.search_results_view.activated.connect(
            self._supply_order_double_clicked)
        self.search_results_view.horizontalHeader().sectionClicked.connect(
            self.section_clicked)

        top_layout = QVBoxLayout(self)

        navigation = NavBar(self,
                            [(self.filter_widget.get_filters_combo(), None),
                             (_("Edit filter"), self._toggle_edit_filters)])

        self.title_widget = TitleWidget(title, self, navigation)  # navigation)

        hlayout_results = QHBoxLayout()
        # w = SubFrame(_("Supply order parts"),self.search_results_view,None)
        hlayout_results.addWidget(self.search_results_view)

        w = SubFrame(_("Supply order detail"),
                     self._make_supply_order_detail_view(), None)
        hlayout_results.addWidget(w)

        hlayout_results.setStretch(0, 2)
        hlayout_results.setStretch(0, 1)

        top_layout.addWidget(self.title_widget)
        top_layout.addWidget(self.filter_widget)
        top_layout.addLayout(hlayout_results)
        top_layout.setStretch(3, 100)
        self.setLayout(top_layout)

        self.search_results_view.setSelectionBehavior(
            QAbstractItemView.SelectRows)
        self.search_results_view.setSelectionMode(
            QAbstractItemView.SingleSelection)

        # self.search_results_view.activated.connect(self.row_activated)
        self.search_results_view.selectionModel().currentRowChanged.connect(
            self.row_selected)
        # self.detail_view.doubleClicked.connect(self._supply_order_double_clicked)

        # pub.subscribe(self.refresh_panel, 'supply_order.changed')
        # pub.subscribe(self.refresh_panel, 'supply_order.deleted')
        # self.filter_widget.select_default_filter()
        self.filter_widget.load_last_filter(configuration)
Пример #13
0
class QuickPrototypedFilter(QWidget):

    selected_object_changed = Signal(object)  # passes an object

    @Slot()
    def _focus_on_list(self):
        """ When the user hits the down key on the filter, we transfer
        the focus to the filtered list
        """
        self.list_view.setFocus()
        self.list_view.selectionModel().setCurrentIndex(
            self.list_view.model().index(0, 0),
            QItemSelectionModel.ClearAndSelect)

    @Slot(str)
    def _filter_changed(self, s):
        self.list_model_filtered.setFilterFixedString(s)
        self.list_view.selectionModel().setCurrentIndex(
            self.list_view.model().index(0, 0),
            QItemSelectionModel.ClearAndSelect)

    @Slot(QModelIndex, QModelIndex)
    def _selected_item_changed(self, current, previous):

        # The test below avoids some recursion. It's a bit more clever
        # than it looks. What happens is this. The user modify
        # some data then ask to change the edited object (in the left list)
        # Doing so it triggers this method. The program then save (if
        # necessary). But that save may trigger a reorganisation of the
        # list. So what the user has selected may be at a different
        # index than the "current" one we received as a parameter
        # of this method. To account for that we actually reselect
        # the item in the table. And this trigger a recursion we avoid
        # here. FIXME there is a recursion but the way we avoid
        # it is not 100% satisfactory, we should use a "semaphore"
        # for that.


        if current.isValid() and current.row() >= 0 and \
           current.row() != previous.row():

            ndx = self.list_model_filtered.mapToSource(
                self.list_model_filtered.index(current.row(), 0))
            self.last_selected_object = self.list_model.object_at(ndx.row())

            self.selected_object_changed.emit(self.last_selected_object)

    def set_data(self, objs, index_builder):
        self.list_model.buildModelFromObjects(objs)
        self.list_model_filtered.setIndexData([index_builder(x) for x in objs])
        self.list_view.setCurrentIndex(self.list_view.model().index(0, 0))

    def __init__(self, table_prototype, parent):
        super(QuickPrototypedFilter, self).__init__(parent)

        self.list_model = PrototypedModelView(table_prototype, self)

        self.list_model_filtered = FilteringModel(self)
        self.list_model_filtered.setSourceModel(self.list_model)

        self.line_in = FilterLineEdit()
        self.line_in.key_down.connect(self._focus_on_list)
        self.line_in.textChanged.connect(self._filter_changed)

        self.list_view = PrototypedQuickView(table_prototype, self)
        self.list_view.setTabKeyNavigation(False)
        self.list_view.horizontalHeader().hide()
        self.list_view.verticalHeader().hide()
        self.list_view.horizontalHeader().setStretchLastSection(True)

        self.list_view.setModel(self.list_model_filtered)

        vlayout = QVBoxLayout()
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.line_in)
        vlayout.addWidget(self.list_view)
        self.setLayout(vlayout)

        self.list_view.selectionModel().currentChanged.connect(
            self._selected_item_changed)  # FIXME Clear ownership issue

        self.last_selected_object = None
        self.line_in.setFocus()
        QWidget.setTabOrder(self.line_in, self.list_view)