def dump_state(self): """Dump the state of the application to the output, this method is triggered by pressing :kbd:`Ctrl-Alt-D` in the GUI""" from camelot.view.model_thread import post from camelot.view.register import dump_register from camelot.view.proxy.collection_proxy import CollectionProxy import gc gc.collect() dump_register() def dump_session_state(): import collections from camelot.model.authentication import Person print '======= begin session ==============' type_counter = collections.defaultdict(int) for o in Person.query.session: type_counter[type(o).__name__] += 1 for k,v in type_counter.items(): print k,v print '====== end session ==============' post( dump_session_state ) for o in gc.get_objects(): if isinstance(o, CollectionProxy): print o for r in gc.get_referrers(o): print ' ', type(r).__name__ for rr in gc.get_referrers(r): print ' ', type(rr).__name__
def expand_search_options(self): if self._expanded_search.isHidden(): if not self._expanded_filters_created: post(self._admin.get_columns, self._fill_expanded_search_options) self._expanded_search.show() else: self._expanded_search.hide()
def validateClose(self): logger.debug( 'validate before close : %s' % self.validate_before_close ) form = self.form_view.findChild(QtGui.QWidget, 'form' ) if self.validate_before_close: form.submit() logger.debug( 'unflushed rows : %s' % str(model.hasUnflushedRows()) ) if model.hasUnflushedRows(): def validate_and_flush(): valid = validator.isValid(0) if valid: admin.flush( model.get_new_object() ) return valid post(validate_and_flush, self.showMessage) return False else: return True return True
def createNew(self): @model_function def get_has_subclasses(): return len(self.admin.get_subclass_tree()) post(get_has_subclasses, self.show_new_view)
def _get_row_data(self, row, cache): """Get the data which is to be visualized at a certain row of the table, if needed, post a refill request the cache to get the object and its neighbours in the cache, meanwhile, return an empty object :param row: the row of the table for which to get the data :param cache: the cache out of which to get row data :return: row_data """ assert row >= 0 try: data = cache.get_data_at_row(row) # # check if data is None, then the cache was a copy of previous # cache, and the data should be refetched # if data is None: raise KeyError return data except KeyError: locker = QtCore.QMutexLocker(self._mutex) if row not in self.rows_under_request: self.rows_under_request.add(row) # # unlock before posting to model thread, since in the # single threaded mode, the model thread function needs to # acquire the lock # locker.unlock() post(self._extend_cache) return empty_row_data
def handle_entity_update(self, sender, entity): """Handles the entity signal, indicating that the model is out of date""" assert object_thread(self) self.logger.debug( '%s %s received entity update signal' % \ ( self.__class__.__name__, self.admin.get_verbose_name() ) ) if sender != self: try: row = self.display_cache.get_row_by_entity(entity) except KeyError: self.logger.debug('entity not in cache') return # # Because the entity is updated, it might no longer be in our # collection, therefore, make sure we don't access the collection # to strip data of the entity # def create_entity_update(row, entity): def entity_update(): columns = self._columns self._add_data(columns, row, entity) # the validity of an object might have changed when it was # modified by an action self.validator.isValid(row) return entity_update post(create_entity_update(row, entity)) else: self.logger.debug('duplicate update')
def handle_entity_update( self, sender, entity ): """Handles the entity signal, indicating that the model is out of date""" self.logger.debug( '%s %s received entity update signal' % \ ( self.__class__.__name__, self.admin.get_verbose_name() ) ) if sender != self: try: row = self.display_cache.get_row_by_entity(entity) except KeyError: self.logger.debug( 'entity not in cache' ) return # # Because the entity is updated, it might no longer be in our # collection, therefore, make sure we don't access the collection # to strip data of the entity # def create_entity_update(row, entity): def entity_update(): columns = self.getColumns() self._add_data(columns, row, entity) return row return entity_update post(create_entity_update(row, entity), self._emit_changes) else: self.logger.debug( 'duplicate update' )
def set_admin( self, admin ): """Switch to a different subclass, where admin is the admin object of the subclass""" assert object_thread( self ) logger.debug('set_admin called') self.admin = admin if self.table: self.table.model().layoutChanged.disconnect( self.tableLayoutChanged ) self.table_layout.removeWidget(self.table) self.table.deleteLater() self.table.model().deleteLater() splitter = self.findChild( QtGui.QWidget, 'splitter' ) self.table = self.AdminTableWidget( self.admin, splitter ) self.table.setObjectName('AdminTableWidget') new_model = self.create_table_model( admin ) self.table.setModel( new_model ) self.table.verticalHeader().sectionClicked.connect( self.sectionClicked ) self.table.keyboard_selection_signal.connect(self.on_keyboard_selection_signal) self.table.model().layoutChanged.connect( self.tableLayoutChanged ) self.tableLayoutChanged() self.table_layout.insertWidget( 1, self.table ) self.gui_context = self.application_gui_context.copy( ListActionGuiContext ) self.gui_context.view = self self.gui_context.admin = self.admin self.gui_context.item_view = self.table def get_filters_and_actions(): return ( admin.get_filters(), admin.get_list_actions() ) post( get_filters_and_actions, self.set_filters_and_actions )
def preview( self, view, parent ): logger.debug( 'print preview dialog' ) def generate_html(): client_address = '<br/>'.join( ['2 Azalea St.', 'Fredericksburg', '22406 VA'] ) import datetime ts = datetime.datetime.today() datestring = 'Date: %s/%s/%s' % ( ts.month, ts.day, ts.year ) view_content = view.to_html() context = { 'logo' : icon, 'company_name' : 'Conceptive Engineering', 'company_address_1' : 'L. Van Bauwelstraat 16', 'company_address_2' : '2220 Heist-op-den-Berg', 'city' : 'Belgium', 'date' : datestring, 'client_address' : client_address, 'client_name' : 'Client', 'content' : view_content, 'signature' : 'M. Anager' } from jinja2 import Environment from camelot.view.templates import loader e = Environment( loader = loader ) t = e.get_template( 'base.html' ) html = t.render( context ) return html from camelot.view.export.printer import open_html_in_print_preview_from_gui_thread post( generate_html, open_html_in_print_preview_from_gui_thread )
def selectEntity(self, entity_instance_getter): @model_function def insert(): o = entity_instance_getter() self.model.append_object(o) post(insert, self.emit_editing_finished)
def run( self, collection_getter, selection_getter ): self.options = super(ListActionFromModelFunction, self).run( collection_getter, selection_getter ) from camelot.admin.form_action import FormActionProgressDialog progress = FormActionProgressDialog( unicode(self._name) ) if not self.options and self.Options: return self.options def create_request( collection_getter, selection_getter, options ): def request(): from sqlalchemy.orm.session import Session from camelot.view.remote_signals import get_signal_handler sh = get_signal_handler() c = list(collection_getter()) s = list(selection_getter()) self._model_function( c, s, options ) to_flush = [] if self._selection_flush: to_flush = s if self._collection_flush: to_flush = c for o in to_flush: Session.object_session( o ).flush( [o] ) sh.sendEntityUpdate( self, o ) return request post( create_request( collection_getter, selection_getter, self.options ), progress.finished, exception = progress.finished ) progress.exec_()
def run(self, collection_getter, selection_getter): self.options = super(ListActionFromModelFunction, self).run(collection_getter, selection_getter) from camelot.admin.form_action import FormActionProgressDialog progress = FormActionProgressDialog(unicode(self._name)) if not self.options and self.Options: return self.options def create_request(collection_getter, selection_getter, options): def request(): from sqlalchemy.orm.session import Session from camelot.view.remote_signals import get_signal_handler sh = get_signal_handler() c = list(collection_getter()) s = list(selection_getter()) self._model_function(c, s, options) to_flush = [] if self._selection_flush: to_flush = s if self._collection_flush: to_flush = c for o in to_flush: Session.object_session(o).flush([o]) sh.sendEntityUpdate(self, o) return request post(create_request(collection_getter, selection_getter, self.options), progress.finished, exception=progress.finished) progress.exec_()
def __init__(self, parent=None, model=None, collection_getter=None): from camelot.view.controls.editors import NoteEditor super(DataPreviewPage, self).__init__(parent) assert model assert collection_getter self.setTitle(_('Data Preview')) self.setSubTitle(_('Please review the data below.')) self._complete = False self.model = model validator = self.model.get_validator() validator.validity_changed_signal.connect(self.update_complete) model.layoutChanged.connect(self.validate_all_rows) post(validator.validate_all_rows) self.collection_getter = collection_getter icon = 'tango/32x32/mimetypes/x-office-spreadsheet.png' self.setPixmap(QtGui.QWizard.LogoPixmap, Pixmap(icon).getQPixmap()) self.previewtable = One2ManyEditor( admin = model.get_admin(), parent = self, create_inline = True, vertical_header_clickable = False, ) self._note = NoteEditor() self._note.set_value(None) ly = QtGui.QVBoxLayout() ly.addWidget(self.previewtable) ly.addWidget(self._note) self.setLayout(ly) self.setCommitPage(True) self.setButtonText(QtGui.QWizard.CommitButton, _('Import')) self.update_complete()
def handle_entity_update(self, sender, entity): """Handles the entity signal, indicating that the model is out of date""" self.logger.debug( '%s %s received entity update signal' % \ ( self.__class__.__name__, self.admin.get_verbose_name() ) ) if sender != self: try: row = self.display_cache.get_row_by_entity(entity) except KeyError: self.logger.debug('entity not in cache') return # # Because the entity is updated, it might no longer be in our # collection, therefore, make sure we don't access the collection # to strip data of the entity # def create_entity_update(row, entity): def entity_update(): columns = self.getColumns() self._add_data(columns, row, entity) return row return entity_update post(create_entity_update(row, entity), self._emit_changes) else: self.logger.debug('duplicate update')
def preview(self, view, parent): logger.debug('print preview dialog') def generate_html(): client_address = '<br/>'.join( ['2 Azalea St.', 'Fredericksburg', '22406 VA']) import datetime ts = datetime.datetime.today() datestring = 'Date: %s/%s/%s' % (ts.month, ts.day, ts.year) view_content = view.to_html() context = { 'logo': icon, 'company_name': 'Conceptive Engineering', 'company_address_1': 'L. Van Bauwelstraat 16', 'company_address_2': '2220 Heist-op-den-Berg', 'city': 'Belgium', 'date': datestring, 'client_address': client_address, 'client_name': 'Client', 'content': view_content, 'signature': 'M. Anager' } from jinja2 import Environment from camelot.view.templates import loader e = Environment(loader=loader) t = e.get_template('base.html') html = t.render(context) return html from camelot.view.export.printer import open_html_in_print_preview_from_gui_thread post(generate_html, open_html_in_print_preview_from_gui_thread)
def update_title(self): def get_title(): obj = self.getEntity() return u'%s %s' % (self.title_prefix, self.admin.get_verbose_identifier(obj)) post(get_title, self.change_title)
def set_admin(self, admin): """Switch to a different subclass, where admin is the admin object of the subclass""" assert object_thread(self) logger.debug('set_admin called') self.admin = admin if self.table: self.table.model().layoutChanged.disconnect( self.tableLayoutChanged) self.table_layout.removeWidget(self.table) self.table.deleteLater() self.table.model().deleteLater() splitter = self.findChild(QtGui.QWidget, 'splitter') self.table = self.AdminTableWidget(self.admin, splitter) self.table.setObjectName('AdminTableWidget') new_model = self.create_table_model(admin) self.table.setModel(new_model) self.table.verticalHeader().sectionClicked.connect(self.sectionClicked) self.table.keyboard_selection_signal.connect( self.on_keyboard_selection_signal) self.table.model().layoutChanged.connect(self.tableLayoutChanged) self.tableLayoutChanged() self.table_layout.insertWidget(1, self.table) self.gui_context = self.application_gui_context.copy( ListActionGuiContext) self.gui_context.view = self self.gui_context.admin = self.admin self.gui_context.item_view = self.table def get_filters_and_actions(): return (admin.get_filters(), admin.get_list_actions()) post(get_filters_and_actions, self.set_filters_and_actions)
def run(self, entity_getter): """When the run method is called, a progress dialog will apear while the model function is executed. :param entity_getter: a function that when called returns the object currently in the form.""" progress = FormActionProgressDialog(self._name) def create_request(entity_getter): def request(): from sqlalchemy.orm.session import Session from camelot.view.remote_signals import get_signal_handler o = entity_getter() self._model_function(o) if self._flush: sh = get_signal_handler() Session.object_session(o).flush([o]) sh.sendEntityUpdate(self, o) return True return request post(create_request(entity_getter), progress.finished, exception=progress.exception) progress.exec_()
def set_sections(self, sections): logger.debug('setting navpane sections') animation = QtCore.QPropertyAnimation(self._dock_widget, 'minimumWidth', self) animation.setDuration( 500 ) animation.setStartValue( 10 ) animation.setEndValue( 220 ) animation.start() self._sections = sections self._buttons = [( index, section.get_verbose_name(), section.get_icon().getQPixmap(), ) for index, section in enumerate(sections)] for index, name, pixmap in self._buttons: tree_widget = self.get_tree_widget() if index!=0: tree_widget.setMaximumHeight(0) pane_button = PaneButton(name, pixmap) pane_button.pressed.connect(self.change_current) self._dock_widget.layout().addWidget(pane_button) self._dock_widget.layout().addWidget(tree_widget) last_tree_index = self._dock_widget.layout().count() - 1 def get_models_for_tree(): return (last_tree_index, self._sections[index].get_items()) post(get_models_for_tree, self.set_models_for_tree) self.change_current(0)
def setData(self, index, value, role=Qt.EditRole): """Value should be a function taking no arguments that returns the data to be set This function will then be called in the model_thread """ assert object_thread(self) # # prevent data of being set in rows not actually in this model # if (not index.isValid()) or (index.model() != self): return False if role == Qt.EditRole: # if the field is not editable, don't waste any time and get out of here # editable should be explicitely True, since the _get_field_attribute_value # might return intermediary values such as ValueLoading ?? if self._get_field_attribute_value(index, "editable") != True: return locker = QtCore.QMutexLocker(self._mutex) flushed = index.row() not in self.unflushed_rows self.unflushed_rows.add(index.row()) self._update_requests.append((flushed, index.row(), index.column(), value)) locker.unlock() post(self._handle_update_requests) return True
def __init__(self, parent=None, model=None, collection_getter=None): from camelot.view.controls.editors import NoteEditor super(DataPreviewPage, self).__init__(parent) assert model assert collection_getter self.setTitle(_('Data Preview')) self.setSubTitle(_('Please review the data below.')) self._complete = False self.model = model validator = self.model.get_validator() validator.validity_changed_signal.connect(self.update_complete) model.layoutChanged.connect(self.validate_all_rows) post(validator.validate_all_rows) self.collection_getter = collection_getter icon = 'tango/32x32/mimetypes/x-office-spreadsheet.png' self.setPixmap(QtGui.QWizard.LogoPixmap, Pixmap(icon).getQPixmap()) self.previewtable = One2ManyEditor( admin=model.get_admin(), parent=self, create_inline=True, vertical_header_clickable=False, ) self._note = NoteEditor() self._note.set_value(None) ly = QtGui.QVBoxLayout() ly.addWidget(self.previewtable) ly.addWidget(self._note) self.setLayout(ly) self.setCommitPage(True) self.setButtonText(QtGui.QWizard.CommitButton, _('Import')) self.update_complete()
def setData(self, index, value, role=Qt.EditRole): """Value should be a function taking no arguments that returns the data to be set This function will then be called in the model_thread """ assert object_thread(self) # # prevent data of being set in rows not actually in this model # if (not index.isValid()) or (index.model() != self): return False if role == Qt.EditRole: # if the field is not editable, don't waste any time and get out of here # editable should be explicitely True, since the _get_field_attribute_value # might return intermediary values such as ValueLoading ?? if self._get_field_attribute_value(index, 'editable') != True: return locker = QtCore.QMutexLocker(self._mutex) flushed = (index.row() not in self.unflushed_rows) self.unflushed_rows.add(index.row()) self._update_requests.append( (flushed, index.row(), index.column(), value)) locker.unlock() post(self._handle_update_requests) return True
def runAction(self, name, callable): progress = QtGui.QProgressDialog(_('Please wait'), QtCore.QString(), 0, 0) progress.setWindowTitle(name) progress.show() post(callable, progress.close, exception=self.display_exception_message_box)
def show(self): """This method wait until the main window is completely set up, and only then shows it. This is a workaround for a bug in Qt on OS X https://bugreports.qt.nokia.com/browse/QTBUG-18567 """ post(lambda: None, self._delayed_show)
def update_action_status(self): toolbar = self.findChild(QtGui.QToolBar) if toolbar: model_context = self.gui_context.create_model_context() for qaction in toolbar.actions(): post(qaction.action.get_state, qaction.set_state, args=(model_context, ))
def set_value( self, model ): model = CustomEditor.set_value( self, model ) table = self.findChild(QtGui.QWidget, 'table') if table and model and model != self.model: self.model = model table.setModel( model ) register.register( self.model, table ) post( model._extend_cache, self.update_delegates )
def __init__( self, action, gui_context, parent ): super( ActionAction, self ).__init__( parent ) self.action = action if action.shortcut != None: self.setShortcut( action.shortcut ) post( action.get_state, self.set_state, args = (gui_context.create_model_context(),) )
def update_action_status( self ): toolbar = self.findChild( QtGui.QToolBar ) if toolbar: model_context = self.gui_context.create_model_context() for qaction in toolbar.actions(): post( qaction.action.get_state, qaction.set_state, args = ( model_context, ) )
def show( self ): """This method wait until the main window is completely set up, and only then shows it. This is a workaround for a bug in Qt on OS X https://bugreports.qt.nokia.com/browse/QTBUG-18567 """ post( lambda:None, self._delayed_show )
def __init__( self, obj, admin, title=_("Please complete"), subtitle=_("Complete the form and press the OK button"), icon=Icon("tango/22x22/categories/preferences-system.png"), parent=None, flags=QtCore.Qt.Dialog, ): from camelot.view.controls.formview import FormWidget from camelot.view.proxy.collection_proxy import CollectionProxy super(ChangeObjectDialog, self).__init__("", parent, flags) self.setWindowTitle(admin.get_verbose_name()) self.set_banner_logo_pixmap(icon.getQPixmap()) self.set_banner_title(unicode(title)) self.set_banner_subtitle(unicode(subtitle)) self.banner_widget().setStyleSheet("background-color: white;") model = CollectionProxy(admin, lambda: [obj], admin.get_fields) validator = model.get_validator() layout = QtGui.QHBoxLayout() layout.setObjectName("form_and_actions_layout") form_widget = FormWidget(parent=self, admin=admin) layout.addWidget(form_widget) validator.validity_changed_signal.connect(self._validity_changed) form_widget.set_model(model) form_widget.setObjectName("form") self.main_widget().setLayout(layout) self.gui_context = FormActionGuiContext() self.gui_context.workspace = self self.gui_context.admin = admin self.gui_context.view = self self.gui_context.widget_mapper = self.findChild(QtGui.QDataWidgetMapper, "widget_mapper") cancel_button = QtGui.QPushButton(ugettext("Cancel")) cancel_button.setObjectName("cancel") ok_button = QtGui.QPushButton(ugettext("OK")) ok_button.setObjectName("ok") ok_button.setEnabled(False) layout = QtGui.QHBoxLayout() layout.setDirection(QtGui.QBoxLayout.RightToLeft) layout.addWidget(ok_button) layout.addWidget(cancel_button) layout.addStretch() self.buttons_widget().setLayout(layout) cancel_button.pressed.connect(self.reject) ok_button.pressed.connect(self.accept) # do inital validation, so the validity changed signal is valid self._validity_changed(0) # set the actions in the actions panel get_actions = admin.get_form_actions post(functools.update_wrapper(functools.partial(get_actions, None), get_actions), self.set_actions)
def __init__(self, admin, parent): header_labels = ['Types'] ModelTree.__init__(self, header_labels, parent) self.admin = admin self.subclasses = [] post(self.admin.get_subclass_tree, self.setSubclasses) self.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.clicked.connect(self.emit_subclass_clicked)
def sort(self, column, order): def create_set_sort_decorator(column, order): def set_sort_decorator(): from sqlalchemy import orm from sqlalchemy.exceptions import InvalidRequestError field_name = self._columns[column][0] class_attribute = getattr(self.admin.entity, field_name) mapper = orm.class_mapper(self.admin.entity) try: property = mapper.get_property(field_name, resolve_synonyms=True) except InvalidRequestError: # # If the field name is not a property of the mapper, we cannot # sort it using sql # return self._rows # If the field is a relation: # If it specifies an order_by option we have to join the related table, # else we use the foreing key as sort field, without joining join = None if isinstance(property, orm.properties.PropertyLoader): target = property._get_target() if target: if target.order_by: join = field_name class_attribute = target.order_by[0] else: # # _foreign_keys is for sqla pre 0.6.4 # if hasattr(property, '_foreign_keys'): class_attribute = list( property._foreign_keys)[0] else: class_attribute = list( property._calculated_foreign_keys)[0] def create_sort_decorator(class_attribute, order, join): def sort_decorator(query): if join: query = query.join(join) if order: return query.order_by(class_attribute.desc()) else: return query.order_by(class_attribute) return sort_decorator self._sort_decorator = create_sort_decorator( class_attribute, order, join) return self._rows return set_sort_decorator post(create_set_sort_decorator(column, order), self._refresh_content)
def expand_search_options(self): assert object_thread( self ) if self._expanded_search.isHidden(): if not self._expanded_filters_created: post( self._admin.get_expanded_search_fields, self._fill_expanded_search_options ) self._expanded_search.show() else: self._expanded_search.hide()
def __init__( self, gui_context, admin, search_text = None, parent = None ): super(TableView, self).__init__( parent ) assert object_thread( self ) self.admin = admin self.application_gui_context = gui_context self.gui_context = gui_context post( self.get_title, self.change_title ) widget_layout = QtGui.QVBoxLayout() if self.header_widget: self.header = self.header_widget( self, admin ) widget_layout.addWidget( self.header ) self.header.search.search_signal.connect( self.startSearch ) self.header.search.cancel_signal.connect( self.cancelSearch ) self.header.search.on_arrow_down_signal.connect(self.focusTable) if search_text: self.header.search.search( search_text ) else: self.header = None widget_layout.setSpacing( 0 ) widget_layout.setContentsMargins(0, 0, 0, 0) splitter = QtGui.QSplitter( self ) splitter.setObjectName('splitter') widget_layout.addWidget( splitter ) table_widget = QtGui.QWidget( self ) filters_widget = QtGui.QWidget( self ) self.table_layout = QtGui.QVBoxLayout() self.table_layout.setSpacing( 0 ) self.table_layout.setContentsMargins(0, 0, 0, 0) self.table = None self.filters_layout = QtGui.QVBoxLayout() self.filters_layout.setSpacing( 0 ) self.filters_layout.setContentsMargins(0, 0, 0, 0) self.actions = None table_widget.setLayout( self.table_layout ) filters_widget.setLayout( self.filters_layout ) #filters_widget.hide() self.set_admin( admin ) splitter.addWidget( table_widget ) splitter.addWidget( filters_widget ) self.setLayout( widget_layout ) self.search_filter = lambda q: q shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Find), self) shortcut.activated.connect( self.activate_search ) if self.header_widget: self.header.filters_changed_signal.connect( self.rebuild_query ) # give the table widget focus to prevent the header and its search control to # receive default focus, as this would prevent the displaying of 'Search...' in the # search control, but this conflicts with the MDI, resulting in the window not # being active and the menus not to work properly #table_widget.setFocus( QtCore.Qt.OtherFocusReason ) #self.setFocusProxy(table_widget) #self.setFocus( QtCore.Qt.OtherFocusReason ) post( self.admin.get_subclass_tree, self.setSubclassTree )
def onActionButtonEntered(self): actionButton = self.sender() actionButtonInfoWidget = self.findChild(QtGui.QWidget, 'actionButtonInfoWidget') if actionButtonInfoWidget is not None: # @todo : get state should be called with a model context as first # argument post( actionButton.action.get_state, actionButtonInfoWidget.setInfoFromState, args = (None,) )
def createFormView(self): if self.entity_instance_getter: def get_admin_and_title(): obj = self.entity_instance_getter() admin = self.admin.get_related_entity_admin(obj.__class__) return admin, '' post(get_admin_and_title, self.show_form_view)
def runAction(self, name, callable): progress = QtGui.QProgressDialog(_('Please wait'), QtCore.QString(), 0, 0) progress.setWindowTitle(name) progress.show() post( callable, progress.close, exception=self.display_exception_message_box )
def data_changed(self, from_index, thru_index): def create_validity_updater(from_row, thru_row): def validity_updater(): for i in range(from_row, thru_row + 1): self.isValid(i) return validity_updater post(create_validity_updater(from_index.row(), thru_index.row()))
def test_schema_display(self): def schema_display_task(): import os from camelot.bin.camelot_manage import schema_display schema_display(os.path.join(self.images_path, 'schema.png')) from camelot.view.model_thread import get_model_thread, post post(schema_display_task) get_model_thread().wait_on_work()
def expand_search_options(self): assert object_thread(self) if self._expanded_search.isHidden(): if not self._expanded_filters_created: post(self.gui_context.admin.get_expanded_search_filters, self._fill_expanded_search_options) self._expanded_search.show() else: self._expanded_search.hide()
def _validity_changed(self, row): form = self.findChild(QtGui.QWidget, "form") if not form: return model = form.get_model() def is_valid(): return model.get_validator().isValid(0) post(is_valid, self._change_complete)
def _validity_changed(self, row): form = self.findChild( QtGui.QWidget, 'form' ) if not form: return model = form.get_model() def is_valid(): return model.get_validator().isValid(0) post(is_valid, self._change_complete)
def update_title(self): def get_title(): obj = self.getEntity() return u'%s %s' % ( self.title_prefix, self.admin.get_verbose_identifier(obj) ) post(get_title, self.change_title)
def test_schema_display(self): def schema_display_task(): import os from camelot.bin.camelot_manage import schema_display schema_display(os.path.join(self.images_path, 'schema.png')) from camelot.view.model_thread import get_model_thread, post post( schema_display_task ) get_model_thread().wait_on_work()
def textEdited(self, text): self._last_highlighted_entity_getter = None text = six.text_type(self.search_input.text()) def create_search_completion(text): return lambda: self.search_completions(text) post(create_search_completion(six.text_type(text)), self.display_search_completions) self.completer.complete()
def onActionButtonEntered(self): actionButton = self.sender() actionButtonInfoWidget = self.findChild(QtGui.QWidget, 'actionButtonInfoWidget') if actionButtonInfoWidget is not None: # @todo : get state should be called with a model context as first # argument post(actionButton.action.get_state, actionButtonInfoWidget.setInfoFromState, args=(None, ))
def open_stored_file(parent, stored_file): """Open the stored file with the default system editor for this file type""" progress = OpenFileProgressDialog() def get_path(): return stored_file.storage.checkout(stored_file) post(get_path, progress.open_path, model_thread_exception_message_box) progress.exec_()
def data_changed(self, from_index, thru_index): def create_validity_updater(from_row, thru_row): def validity_updater(): for i in range(from_row, thru_row+1): self.isValid(i) return validity_updater post(create_validity_updater(from_index.row(), thru_index.row()))
def __init__(self, admin, parent): header_labels = ['Types'] ModelTree.__init__(self, header_labels, parent) self.admin = admin self.subclasses = [] post(self.admin.get_subclass_tree, self.setSubclasses) self.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding ) self.clicked.connect( self.emit_subclass_clicked )
def sort(self, column, order): def create_set_sort_decorator(column, order): def set_sort_decorator(): from sqlalchemy import orm from sqlalchemy.exceptions import InvalidRequestError field_name = self._columns[column][0] class_attribute = getattr(self.admin.entity, field_name) mapper = orm.class_mapper(self.admin.entity) try: property = mapper.get_property(field_name, resolve_synonyms=True) except InvalidRequestError: # # If the field name is not a property of the mapper, we cannot # sort it using sql # return self._rows # If the field is a relation: # If it specifies an order_by option we have to join the related table, # else we use the foreing key as sort field, without joining join = None if isinstance(property, orm.properties.PropertyLoader): target = property._get_target() if target: if target.order_by: join = field_name class_attribute = target.order_by[0] else: # # _foreign_keys is for sqla pre 0.6.4 # if hasattr(property, "_foreign_keys"): class_attribute = list(property._foreign_keys)[0] else: class_attribute = list(property._calculated_foreign_keys)[0] def create_sort_decorator(class_attribute, order, join): def sort_decorator(query): if join: query = query.join(join) if order: return query.order_by(class_attribute.desc()) else: return query.order_by(class_attribute) return sort_decorator self._sort_decorator = create_sort_decorator(class_attribute, order, join) return self._rows return set_sort_decorator post(create_set_sort_decorator(column, order), self._refresh_content)
def revertRow(self, row): def create_refresh_entity(row): @model_function def refresh_entity(): o = self._get_object(row) self.admin.refresh(o) return row, o return refresh_entity post(create_refresh_entity(row), self._revert_row)
def exportToExcel( self ): from camelot.view.export.excel import open_data_with_excel def export(): title = self.admin.get_verbose_name_plural() columns = self.admin.get_columns() if self.model: data = list( self.model.getData() ) open_data_with_excel( title, columns, data ) post( export )
def __init__(self, gui_context, admin, search_text=None, parent=None): super(TableView, self).__init__(parent) assert object_thread(self) self.admin = admin self.application_gui_context = gui_context self.gui_context = gui_context post(self.get_title, self.change_title) widget_layout = QtGui.QVBoxLayout() if self.header_widget: self.header = self.header_widget(self, admin) widget_layout.addWidget(self.header) self.header.search.search_signal.connect(self.startSearch) self.header.search.cancel_signal.connect(self.cancelSearch) self.header.search.on_arrow_down_signal.connect(self.focusTable) if search_text: self.header.search.search(search_text) else: self.header = None widget_layout.setSpacing(0) widget_layout.setContentsMargins(0, 0, 0, 0) splitter = QtGui.QSplitter(self) splitter.setObjectName('splitter') widget_layout.addWidget(splitter) table_widget = QtGui.QWidget(self) filters_widget = QtGui.QWidget(self) self.table_layout = QtGui.QVBoxLayout() self.table_layout.setSpacing(0) self.table_layout.setContentsMargins(0, 0, 0, 0) self.table = None self.filters_layout = QtGui.QVBoxLayout() self.filters_layout.setSpacing(0) self.filters_layout.setContentsMargins(0, 0, 0, 0) self.actions = None table_widget.setLayout(self.table_layout) filters_widget.setLayout(self.filters_layout) #filters_widget.hide() self.set_admin(admin) splitter.addWidget(table_widget) splitter.addWidget(filters_widget) self.setLayout(widget_layout) self.search_filter = lambda q: q shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Find), self) shortcut.activated.connect(self.activate_search) if self.header_widget: self.header.filters_changed_signal.connect(self.rebuild_query) # give the table widget focus to prevent the header and its search control to # receive default focus, as this would prevent the displaying of 'Search...' in the # search control, but this conflicts with the MDI, resulting in the window not # being active and the menus not to work properly #table_widget.setFocus( QtCore.Qt.OtherFocusReason ) #self.setFocusProxy(table_widget) #self.setFocus( QtCore.Qt.OtherFocusReason ) post(self.admin.get_subclass_tree, self.setSubclassTree)