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 __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)
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)
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
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)
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_()
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()
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)
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()
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
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)
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)
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)