def load_translations(self, application): """Fill the QApplication with the needed translations :param application: the QApplication on which to install the translator """ from camelot.core.utils import load_translations from camelot.view.model_thread import get_model_thread get_model_thread().post(load_translations) for translator in self.application_admin.get_translator(): application.installTranslator(translator)
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 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 closeEvent( self, event ): from camelot.view.model_thread import get_model_thread model_thread = get_model_thread() self.workspace.close_all_views() self.write_settings() logger.info( 'closing mainwindow' ) model_thread.stop() super( MainWindow, self ).closeEvent( event ) QtCore.QCoreApplication.exit(0)
def gui_run(self, gui_context): from camelot.view.model_thread import get_model_thread model_thread = get_model_thread() # we might exit the application when the workspace is not even there if gui_context.workspace != None: gui_context.workspace.close_all_views() if model_thread != None: model_thread.stop() QtCore.QCoreApplication.exit(0)
def start_model_thread(self): """Launch the second thread where the model lives""" from camelot.view.model_thread import get_model_thread, construct_model_thread from camelot.view.remote_signals import construct_signal_handler construct_model_thread() construct_signal_handler() mt = get_model_thread() mt.setup_exception_signal.connect(self.setup_exception) mt.start()
def closeEvent(self, event): from camelot.view.model_thread import get_model_thread model_thread = get_model_thread() self.workspace.close_all_views() self.write_settings() logger.info('closing mainwindow') model_thread.stop() super(MainWindow, self).closeEvent(event) QtCore.QCoreApplication.exit(0)
def __init__(self, parent): QtGui.QStatusBar.__init__(self, parent) from camelot.view.controls.busy_widget import BusyWidget self.busy_widget = BusyWidget(self) self.busy_widget.setMinimumWidth(100) self.addPermanentWidget(self.busy_widget, 0) mt = get_model_thread() mt.thread_busy_signal.connect(self.busy_widget.set_busy) # the model thread might allready be busy before we connected to it self.busy_widget.set_busy(mt.busy())
def __init__(self, parent): QtGui.QStatusBar.__init__(self, parent) from camelot.view.controls.busy_widget import BusyWidget self.busy_widget = BusyWidget(self) self.busy_widget.setMinimumWidth(100) self.addPermanentWidget(self.busy_widget, 0) mt = get_model_thread() mt.thread_busy_signal.connect( self.busy_widget.set_busy ) # the model thread might allready be busy before we connected to it self.busy_widget.set_busy(mt.busy())
def start_model_thread(self): """Launch the second thread where the model lives""" from camelot.view.model_thread import get_model_thread, construct_model_thread from camelot.view.remote_signals import construct_signal_handler from camelot.core.conf import settings from camelot.core.sql import metadata metadata.bind = settings.ENGINE() construct_model_thread() construct_signal_handler() mt = get_model_thread() mt.setup_exception_signal.connect( self.initialization_exception ) mt.start()
def __init__(self, parent=None): from camelot.view.controls.busy_widget import BusyWidget from camelot.view.model_thread import get_model_thread super(Dashboard, self).__init__(parent) desktop = QCoreApplication.instance().desktop() self.resize(desktop.width() * Dashboard.SCALE, desktop.height() * Dashboard.SCALE) self.closemark = CloseMark(QPixmap('close-mark.png'), self) self.setBGColor(Qt.white) busy_widget = BusyWidget(self) busy_widget.setMinimumSize( desktop.width() * Dashboard.SCALE, desktop.height() * Dashboard.SCALE ) #self.addPermanentWidget(busy_widget, 0) mt = get_model_thread() mt.thread_busy_signal.connect( busy_widget.set_busy ) busy_widget.set_busy(mt.busy())
def __init__(self, parent=None): from camelot.view.controls.busy_widget import BusyWidget from camelot.view.model_thread import get_model_thread super(Dashboard, self).__init__(parent) desktop = QCoreApplication.instance().desktop() self.resize(desktop.width() * Dashboard.SCALE, desktop.height() * Dashboard.SCALE) self.closemark = CloseMark(QPixmap('close-mark.png'), self) self.setBGColor(Qt.white) busy_widget = BusyWidget(self) busy_widget.setMinimumSize(desktop.width() * Dashboard.SCALE, desktop.height() * Dashboard.SCALE) #self.addPermanentWidget(busy_widget, 0) mt = get_model_thread() mt.thread_busy_signal.connect(busy_widget.set_busy) busy_widget.set_busy(mt.busy())
def setUp(self): self.app = get_application() from camelot.view import model_thread from camelot.view.model_thread.no_thread_model_thread import NoThreadModelThread from camelot.view.model_thread import get_model_thread, has_model_thread from camelot.view.remote_signals import construct_signal_handler, has_signal_handler if not has_model_thread(): # # Run the tests without real threading, to avoid timing problems with screenshots etc. # model_thread._model_thread_.insert(0, NoThreadModelThread()) if not has_signal_handler(): construct_signal_handler() self.mt = get_model_thread() if not self.mt.isRunning(): self.mt.start() # make sure the startup sequence has passed self.process()
def launch_meta_camelot(): import sys from camelot.view.model_thread import construct_model_thread, get_model_thread from camelot.admin.action import GuiContext from PyQt4 import QtGui app = QtGui.QApplication([a for a in sys.argv if a]) construct_model_thread() mt = get_model_thread() mt.start() settings.append( MetaSettings() ) new_project = CreateNewProject() gui_context = GuiContext() admin = MetaCamelotAdmin() admin.get_stylesheet() gui_context.admin = admin new_project.gui_run( gui_context ) # keep app alive during running of app return app
def setUp(self): self.app = get_application() from camelot.view import model_thread from camelot.view.model_thread.no_thread_model_thread import NoThreadModelThread from camelot.view.model_thread import get_model_thread, has_model_thread from camelot.view.remote_signals import construct_signal_handler, has_signal_handler if not has_model_thread(): # # Run the tests without real threading, to avoid timing problems with screenshots etc. # model_thread._model_thread_.insert( 0, NoThreadModelThread() ) if not has_signal_handler(): construct_signal_handler() self.mt = get_model_thread() if not self.mt.isRunning(): self.mt.start() # make sure the startup sequence has passed self.process()
def launch_meta_camelot(): import sys from camelot.view.model_thread import construct_model_thread, get_model_thread from camelot.admin.action import GuiContext from PyQt4 import QtGui app = QtGui.QApplication([a for a in sys.argv if a]) construct_model_thread() mt = get_model_thread() mt.start() settings.append(MetaSettings()) new_project = CreateNewProject() gui_context = GuiContext() admin = MetaCamelotAdmin() admin.get_stylesheet() gui_context.admin = admin new_project.gui_run(gui_context) # keep app alive during running of app return app
def __init__(self, parent = None): super( BusyWidget, self ).__init__( parent ) palette = QtGui.QPalette( self.palette() ) palette.setColor( palette.Background, Qt.transparent ) self.setPalette( palette ) self.setAttribute( Qt.WA_TransparentForMouseEvents ) pixmap = working_pixmap.getQPixmap() rows = 4 self.cols = 8 self.frame_height = pixmap.height() / rows self.frame_width = pixmap.width() / self.cols self.orbs = rows * self.cols self.highlighted_orb = 0 self.timer = None self.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding ) mt = get_model_thread() mt.thread_busy_signal.connect( self.set_busy ) # the model thread might already be busy before we connected to it self.set_busy( mt.busy() )
def __init__(self, parent=None): super(BusyWidget, self).__init__(parent) palette = QtGui.QPalette(self.palette()) palette.setColor(palette.Background, Qt.transparent) self.setPalette(palette) self.setAttribute(Qt.WA_TransparentForMouseEvents) pixmap = working_pixmap.getQPixmap() rows = 4 self.cols = 8 self.frame_height = pixmap.height() / rows self.frame_width = pixmap.width() / self.cols self.orbs = rows * self.cols self.highlighted_orb = 0 self.timer = None self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) mt = get_model_thread() mt.thread_busy_signal.connect(self.set_busy) # the model thread might already be busy before we connected to it self.set_busy(mt.busy())
def __init__(self, app_admin, entity): """ :param app_admin: the application admin object for this application, if None, then the default application_admin is taken :param entity: the entity class for which this admin instance is to be used """ from camelot.view.remote_signals import get_signal_handler if not app_admin: from camelot.admin.application_admin import get_application_admin self.app_admin = get_application_admin() else: self.app_admin = app_admin self.rsh = get_signal_handler() if entity: from camelot.view.model_thread import get_model_thread self.entity = entity self.mt = get_model_thread() # # caches to prevent recalculation of things # self._field_attributes = dict() self._subclasses = None
def __init__( self, admin, max_number_of_rows=10, flush_changes=True, cache_collection_proxy=None, ): """ :param admin: the admin interface for the items in the collection :param cache_collection_proxy: the CollectionProxy on which this CollectionProxy will reuse the cache. Passing a cache has the advantage that objects that were present in the original cache will remain at the same row in the new cache This is used when a form is created from a tableview. Because between the last query of the tableview, and the first of the form, the object might have changed position in the query. """ super(CollectionProxy, self).__init__() assert object_thread(self) from camelot.view.model_thread import get_model_thread # # The source model will contain the actual data stripped from the # objects in the collection. # self.source_model = QtGui.QStandardItemModel() self.setSourceModel(self.source_model) self.logger = logging.getLogger(logger.name + '.%s' % id(self)) self.logger.debug('initialize query table for %s' % (admin.get_verbose_name())) # the mutex is recursive to avoid blocking during unittest, when # model and view are used in the same thread self._mutex = QtCore.QMutex(QtCore.QMutex.Recursive) self.admin = admin self.list_action = admin.list_action self.row_model_context = RowModelContext() self.row_model_context.admin = admin self.settings = self.admin.get_settings() self._horizontal_header_height = QtGui.QFontMetrics( self._header_font_required).height() + 10 self._header_font_metrics = QtGui.QFontMetrics(self._header_font) vertical_header_font_height = QtGui.QFontMetrics( self._header_font).height() self._vertical_header_height = vertical_header_font_height * self.admin.lines_per_row + 10 self.vertical_header_size = QtCore.QSize(16 + 10, self._vertical_header_height) self.validator = admin.get_validator(self) self._collection = [] self.flush_changes = flush_changes self.mt = get_model_thread() # Set database connection and load data self._rows = None self._columns = [] self._static_field_attributes = [] self._max_number_of_rows = max_number_of_rows max_cache = 10 * self.max_number_of_rows if cache_collection_proxy: cached_entries = len(cache_collection_proxy.display_cache) max_cache = max(cached_entries, max_cache) self.display_cache = cache_collection_proxy.display_cache.shallow_copy( max_cache) self.edit_cache = cache_collection_proxy.edit_cache.shallow_copy( max_cache) self.attributes_cache = cache_collection_proxy.attributes_cache.shallow_copy( max_cache) self.action_state_cache = cache_collection_proxy.action_state_cache.shallow_copy( max_cache) else: self.display_cache = Fifo(max_cache) self.edit_cache = Fifo(max_cache) self.attributes_cache = Fifo(max_cache) self.action_state_cache = Fifo(max_cache) # The rows in the table for which a cache refill is under request self.rows_under_request = set() self._update_requests = list() self._rowcount_requests = list() # The rows that have unflushed changes self.unflushed_rows = set() self._sort_and_filter = SortingRowMapper() self.row_changed_signal.connect(self._emit_changes) self._rows_about_to_be_inserted_signal.connect( self._rows_about_to_be_inserted, Qt.QueuedConnection) self._rows_inserted_signal.connect(self._rows_inserted, Qt.QueuedConnection) self.rsh = get_signal_handler() self.rsh.connect_signals(self) # # the initial collection might contain unflushed rows post(self._update_unflushed_rows) # # in that way the number of rows is requested as well if cache_collection_proxy: self.setRowCount(cache_collection_proxy.rowCount()) self.logger.debug('initialization finished')
def gui_run(self, gui_context): from camelot.view.model_thread import get_model_thread model_thread = get_model_thread() gui_context.workspace.close_all_views() model_thread.stop() QtCore.QCoreApplication.exit(0)
def __init__( self, admin, collection_getter, columns_getter, max_number_of_rows = 10, edits = None, flush_changes = True, cache_collection_proxy = None ): """ :param admin: the admin interface for the items in the collection :param collection_getter: a function that takes no arguments and returns the collection that will be visualized. This function will be called inside the model thread, to prevent delays when this function causes the database to be hit. If the collection is a list, it should not contain any duplicate elements. :param columns_getter: a function that takes no arguments and returns the columns that will be cached in the proxy. This function will be called inside the model thread. :param cache_collection_proxy: the CollectionProxy on which this CollectionProxy will reuse the cache. Passing a cache has the advantage that objects that were present in the original cache will remain at the same row in the new cache This is used when a form is created from a tableview. Because between the last query of the tableview, and the first of the form, the object might have changed position in the query. """ super(CollectionProxy, self).__init__() from camelot.view.model_thread import get_model_thread self.logger = logging.getLogger(logger.name + '.%s'%id(self)) self.logger.debug('initialize query table for %s' % (admin.get_verbose_name())) self._mutex = QtCore.QMutex() self.admin = admin self._horizontal_header_height = QtGui.QFontMetrics( self._header_font_required ).height() + 10 vertical_header_font_height = QtGui.QFontMetrics( self._header_font ).height() self._vertical_header_height = vertical_header_font_height * self.admin.lines_per_row + 10 self.iconSize = QtCore.QSize( vertical_header_font_height, vertical_header_font_height ) if self.header_icon: self.form_icon = QtCore.QVariant( self.header_icon.getQIcon().pixmap( self.iconSize ) ) else: self.form_icon = QtCore.QVariant() self.validator = admin.create_validator( self ) self._collection_getter = collection_getter self.column_count = 0 self.flush_changes = flush_changes self.delegate_manager = None self.mt = get_model_thread() # Set database connection and load data self._rows = 0 self._columns = [] self._static_field_attributes = [] self._max_number_of_rows = max_number_of_rows if cache_collection_proxy: self.display_cache = cache_collection_proxy.display_cache.shallow_copy( 10 * self.max_number_of_rows ) self.edit_cache = cache_collection_proxy.edit_cache.shallow_copy( 10 * self.max_number_of_rows ) self.attributes_cache = cache_collection_proxy.attributes_cache.shallow_copy( 10 * self.max_number_of_rows ) else: self.display_cache = Fifo( 10 * self.max_number_of_rows ) self.edit_cache = Fifo( 10 * self.max_number_of_rows ) self.attributes_cache = Fifo( 10 * self.max_number_of_rows ) # The rows in the table for which a cache refill is under request self.rows_under_request = set() self._update_requests = list() # The rows that have unflushed changes self.unflushed_rows = set() self._sort_and_filter = SortingRowMapper() # Set edits self.edits = edits or [] self.row_changed_signal.connect( self._emit_changes ) self.rsh = get_signal_handler() self.rsh.connect_signals( self ) def get_columns(): self._columns = columns_getter() self._static_field_attributes = list(self.admin.get_static_field_attributes([c[0] for c in self._columns])) return self._columns post( get_columns, self.setColumns ) # # the initial collection might contain unflushed rows post( self.updateUnflushedRows ) # # in that way the number of rows is requested as well if cache_collection_proxy: self.setRowCount( cache_collection_proxy.rowCount() ) else: post( self.getRowCount, self.setRowCount ) self.logger.debug( 'initialization finished' )
def gui_run( self, gui_context ): from camelot.view.model_thread import get_model_thread model_thread = get_model_thread() gui_context.workspace.close_all_views() model_thread.stop() QtCore.QCoreApplication.exit(0)
def main(self): """the main function of the application, this will call all other functions before starting the event loop""" import logging logger = logging.getLogger("camelot.view.main") try: # # before anything else happens or is imported, the splash screen should be there # import sys from PyQt4 import QtGui, QtCore app = QtGui.QApplication([a for a in sys.argv if a]) splash_window = self.show_splashscreen() self.show_splash_message(splash_window, _("Initialize application")) # regularly call processEvents to keep the splash alive app.processEvents() # font = app.font() # font.setStyleStrategy(QtGui.QFont.PreferAntialias) # font.setPointSize(font.pointSize()+1) # app.setFont(font) QT_MAJOR_VERSION = float(".".join(str(QtCore.QT_VERSION_STR).split(".")[0:2])) logger.debug("qt version %s, pyqt version %s" % (QtCore.QT_VERSION_STR, QtCore.PYQT_VERSION_STR)) logger.debug("qt major version %f" % QT_MAJOR_VERSION) app.processEvents() import sqlalchemy, elixir logger.debug("sqlalchemy version %s" % sqlalchemy.__version__) logger.debug("elixir version %s" % elixir.__version__) app.processEvents() self.set_application_attributes(app) self.pre_initialization() app.processEvents() # regularly call processEvents to keep the splash alive self.show_splash_message(splash_window, _("Setup database")) app.processEvents() self.start_model_thread() app.processEvents() # # WEIRD, if we put this code in a method, the translations # don't work # from camelot.core.utils import load_translations from camelot.view.model_thread import get_model_thread self.show_splash_message(splash_window, _("Load translations")) app.processEvents() get_model_thread().post(load_translations) self.show_splash_message(splash_window, _("Create translator")) app.processEvents() translator = self.application_admin.get_translator() self.show_splash_message(splash_window, _("Install translator")) if isinstance(translator, list): for t in translator: app.installTranslator(t) else: app.installTranslator(translator) app.processEvents() # self.load_translations(app) # Set the style sheet self.show_splash_message(splash_window, _("Create main window")) app.processEvents() stylesheet = self.application_admin.get_stylesheet() if stylesheet: app.setStyleSheet(stylesheet) app.processEvents() self.initialization() app.processEvents() main_window = self.create_main_window() self.close_splashscreen(splash_window, main_window) return self.start_event_loop(app) except Exception, e: logger.error("exception in initialization", exc_info=e) import traceback, cStringIO sio = cStringIO.StringIO() traceback.print_exc(file=sio) traceback_print = sio.getvalue() sio.close() exception_info = (e, traceback_print) self.initialization_exception(exception_info)
def __init__(self, admin, collection_getter, columns_getter, max_number_of_rows=10, edits=None, flush_changes=True, cache_collection_proxy=None): """ :param admin: the admin interface for the items in the collection :param collection_getter: a function that takes no arguments and returns the collection that will be visualized. This function will be called inside the model thread, to prevent delays when this function causes the database to be hit. If the collection is a list, it should not contain any duplicate elements. :param columns_getter: a function that takes no arguments and returns the columns that will be cached in the proxy. This function will be called inside the model thread. :param cache_collection_proxy: the CollectionProxy on which this CollectionProxy will reuse the cache. Passing a cache has the advantage that objects that were present in the original cache will remain at the same row in the new cache This is used when a form is created from a tableview. Because between the last query of the tableview, and the first of the form, the object might have changed position in the query. """ super(CollectionProxy, self).__init__() from camelot.view.model_thread import get_model_thread self.logger = logging.getLogger(logger.name + '.%s' % id(self)) self.logger.debug('initialize query table for %s' % (admin.get_verbose_name())) self._mutex = QtCore.QMutex() self.admin = admin self._horizontal_header_height = QtGui.QFontMetrics( self._header_font_required).height() + 10 vertical_header_font_height = QtGui.QFontMetrics( self._header_font).height() self._vertical_header_height = vertical_header_font_height * self.admin.lines_per_row + 10 self.iconSize = QtCore.QSize(vertical_header_font_height, vertical_header_font_height) if self.header_icon: self.form_icon = QtCore.QVariant( self.header_icon.getQIcon().pixmap(self.iconSize)) else: self.form_icon = QtCore.QVariant() self.validator = admin.create_validator(self) self._collection_getter = collection_getter self.column_count = 0 self.flush_changes = flush_changes self.delegate_manager = None self.mt = get_model_thread() # Set database connection and load data self._rows = 0 self._columns = [] self._static_field_attributes = [] self._max_number_of_rows = max_number_of_rows if cache_collection_proxy: self.display_cache = cache_collection_proxy.display_cache.shallow_copy( 10 * self.max_number_of_rows) self.edit_cache = cache_collection_proxy.edit_cache.shallow_copy( 10 * self.max_number_of_rows) self.attributes_cache = cache_collection_proxy.attributes_cache.shallow_copy( 10 * self.max_number_of_rows) else: self.display_cache = Fifo(10 * self.max_number_of_rows) self.edit_cache = Fifo(10 * self.max_number_of_rows) self.attributes_cache = Fifo(10 * self.max_number_of_rows) # The rows in the table for which a cache refill is under request self.rows_under_request = set() self._update_requests = list() # The rows that have unflushed changes self.unflushed_rows = set() self._sort_and_filter = SortingRowMapper() # Set edits self.edits = edits or [] self.row_changed_signal.connect(self._emit_changes) self.rsh = get_signal_handler() self.rsh.connect_signals(self) def get_columns(): self._columns = columns_getter() self._static_field_attributes = list( self.admin.get_static_field_attributes( [c[0] for c in self._columns])) return self._columns post(get_columns, self.setColumns) # # the initial collection might contain unflushed rows post(self.updateUnflushedRows) # # in that way the number of rows is requested as well if cache_collection_proxy: self.setRowCount(cache_collection_proxy.rowCount()) else: post(self.getRowCount, self.setRowCount) self.logger.debug('initialization finished')
def main(self): """the main function of the application, this will call all other functions before starting the event loop""" import logging logger = logging.getLogger('camelot.view.main') try: # # before anything else happens or is imported, the splash screen should be there # import sys from PyQt4 import QtGui, QtCore app = QtGui.QApplication([a for a in sys.argv if a]) splash_window = self.show_splashscreen() self.show_splash_message(splash_window, _('Initialize application')) # regularly call processEvents to keep the splash alive app.processEvents() # font = app.font() # font.setStyleStrategy(QtGui.QFont.PreferAntialias) # font.setPointSize(font.pointSize()+1) # app.setFont(font) QT_MAJOR_VERSION = float('.'.join( str(QtCore.QT_VERSION_STR).split('.')[0:2])) logger.debug('qt version %s, pyqt version %s' % (QtCore.QT_VERSION_STR, QtCore.PYQT_VERSION_STR)) logger.debug('qt major version %f' % QT_MAJOR_VERSION) app.processEvents() import sqlalchemy, elixir logger.debug('sqlalchemy version %s' % sqlalchemy.__version__) logger.debug('elixir version %s' % elixir.__version__) app.processEvents() self.set_application_attributes(app) self.pre_initialization() app.processEvents() # regularly call processEvents to keep the splash alive self.show_splash_message(splash_window, _('Setup database')) app.processEvents() self.start_model_thread() app.processEvents() # # WEIRD, if we put this code in a method, the translations # don't work # from camelot.core.utils import load_translations from camelot.view.model_thread import get_model_thread self.show_splash_message(splash_window, _('Load translations')) app.processEvents() get_model_thread().post(load_translations) self.show_splash_message(splash_window, _('Create translator')) app.processEvents() translator = self.application_admin.get_translator() self.show_splash_message(splash_window, _('Install translator')) if isinstance(translator, list): for t in translator: app.installTranslator(t) else: app.installTranslator(translator) app.processEvents() #self.load_translations(app) # Set the style sheet self.show_splash_message(splash_window, _('Create main window')) app.processEvents() stylesheet = self.application_admin.get_stylesheet() if stylesheet: app.setStyleSheet(stylesheet) app.processEvents() self.initialization() app.processEvents() main_window = self.create_main_window() self.close_splashscreen(splash_window, main_window) return self.start_event_loop(app) except Exception, e: logger.error('exception in initialization', exc_info=e) import traceback, cStringIO sio = cStringIO.StringIO() traceback.print_exc(file=sio) traceback_print = sio.getvalue() sio.close() exception_info = (e, traceback_print) self.initialization_exception(exception_info)