Ejemplo n.º 1
0
def pytest_configure(config):

    os.environ['GLUE_TESTING'] = 'True'

    if config.getoption('no_optional_skip'):
        from glue.tests import helpers
        for attr in helpers.__dict__:
            if attr.startswith('requires_'):
                # The following line replaces the decorators with a function
                # that does noting, effectively disabling it.
                setattr(helpers, attr, lambda f: f)

    # Make sure we don't affect the real glue config dir
    import tempfile
    from glue import config
    config.CFG_DIR = tempfile.mkdtemp()

    # Start up QApplication, if the Qt code is present
    try:
        from glue.utils.qt import get_qapp
    except Exception:
        # Note that we catch any exception, not just ImportError, because
        # QtPy can raise a PythonQtError.
        pass
    else:
        get_qapp()

    # Force loading of plugins
    from glue.main import load_plugins
    load_plugins()
Ejemplo n.º 2
0
def pytest_configure(config):

    os.environ['GLUE_TESTING'] = 'True'

    if config.getoption('no_optional_skip'):
        from glue.tests import helpers
        for attr in helpers.__dict__:
            if attr.startswith('requires_'):
                # The following line replaces the decorators with a function
                # that does noting, effectively disabling it.
                setattr(helpers, attr, lambda f: f)

    # Make sure we don't affect the real glue config dir
    import tempfile
    from glue import config
    config.CFG_DIR = tempfile.mkdtemp()

    # Start up QApplication, if the Qt code is present
    try:
        from glue.utils.qt import get_qapp
    except Exception:
        # Note that we catch any exception, not just ImportError, because
        # QtPy can raise a PythonQtError.
        pass
    else:
        get_qapp()

    # Force loading of plugins
    from glue.main import load_plugins
    load_plugins()
Ejemplo n.º 3
0
    def __exit__(self, exc_type, exc_val, tb):

        if exc_type is None:
            return

        # Make sure application has been started
        from glue.utils.qt import get_qapp  # Here to avoid circular import
        get_qapp()

        m = "%s\n%s" % (self.msg, exc_val)
        detail = ''.join(traceback.format_exception(exc_type, exc_val, tb))
        if len(m) > 500:
            detail = "Full message:\n\n%s\n\n%s" % (m, detail)
            m = m[:500] + '...'

        from qtpy import QtWidgets
        from qtpy.QtCore import Qt

        qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", m)
        qmb.setDetailedText(detail)
        qmb.resize(400, qmb.size().height())
        qmb.setTextInteractionFlags(Qt.TextSelectableByMouse)
        qmb.exec_()

        if self.exit:
            sys.exit(1)

        # Just for cases where we are testing and patching sys.exit
        return True
Ejemplo n.º 4
0
 def new_client(self, dc=None, canvas=None):
     from glue.utils.qt import get_qapp
     get_qapp()
     dc = dc or self.collect
     l = log.get_logger(name='ginga', log_stderr=True)
     canvas = ImageViewCanvas(l, render='widget')
     return GingaClient(dc, canvas)
Ejemplo n.º 5
0
def pytest_configure(config):

    if config.getoption('no_optional_skip'):
        from glue.tests import helpers
        for attr in helpers.__dict__:
            if attr.startswith('requires_'):
                # The following line replaces the decorators with a function
                # that does noting, effectively disabling it.
                setattr(helpers, attr, lambda f: f)

    # Make sure we don't affect the real glue config dir
    import tempfile
    from glue import config
    config.CFG_DIR = tempfile.mkdtemp()

    # Start up QApplication, if the Qt code is present
    try:
        from glue.utils.qt import get_qapp
    except ImportError:
        pass
    else:
        get_qapp()

    # Force loading of plugins
    from glue.main import load_plugins
    load_plugins()
Ejemplo n.º 6
0
    def __exit__(self, exc_type, exc_val, tb):

        if exc_type is None:
            return

        # Make sure application has been started
        from glue.utils.qt import get_qapp  # Here to avoid circular import
        get_qapp()

        m = "%s\n%s" % (self.msg, exc_val)
        detail = ''.join(traceback.format_exception(exc_type, exc_val, tb))
        if len(m) > 500:
            detail = "Full message:\n\n%s\n\n%s" % (m, detail)
            m = m[:500] + '...'

        from qtpy import QtWidgets
        from qtpy.QtCore import Qt

        qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", m)
        qmb.setDetailedText(detail)
        qmb.resize(400, qmb.size().height())
        qmb.setTextInteractionFlags(Qt.TextSelectableByMouse)
        qmb.exec_()

        if self.exit:
            sys.exit(1)

        # Just for cases where we are testing and patching sys.exit
        return True
Ejemplo n.º 7
0
    def new_client(self, dc=None, canvas=None):
        from glue.utils.qt import get_qapp

        get_qapp()
        dc = dc or self.collect
        l = log.get_logger(name="ginga", log_stderr=True)
        canvas = ImageViewCanvas(l, render="widget")
        return GingaClient(dc, canvas)
Ejemplo n.º 8
0
    def test_fit_polynomial(self):

        # TODO: need to deterministically set to polynomial fitter

        self.viewer.add_data(self.data)
        self.profile_tools.ui.tabs.setCurrentIndex(1)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        assert_allclose(self.profile_tools.rng_mode.state.x_range, (0.9, 15.1))

        self.profile_tools.ui.button_fit.click()
        self.profile_tools.wait_for_fit()
        app = get_qapp()
        app.processEvents()

        pixel_log = self.profile_tools.text_log.toPlainText().splitlines()
        assert pixel_log[0] == 'd1'
        assert pixel_log[1] == 'Coefficients:'
        assert pixel_log[-2] == '8.000000e+00'
        assert pixel_log[-1] == '3.500000e+00'

        self.profile_tools.ui.button_clear.click()
        assert self.profile_tools.text_log.toPlainText() == ''

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        assert_allclose(self.profile_tools.rng_mode.state.x_range, (1.9, 30.1))

        self.profile_tools.ui.button_fit.click()
        self.profile_tools.wait_for_fit()
        app = get_qapp()
        app.processEvents()

        world_log = self.profile_tools.text_log.toPlainText().splitlines()
        assert world_log[0] == 'd1'
        assert world_log[1] == 'Coefficients:'
        assert world_log[-2] == '4.000000e+00'
        assert world_log[-1] == '3.500000e+00'
Ejemplo n.º 9
0
def pytest_configure(config):

    if config.getoption('no_optional_skip'):
        from glue.tests import helpers
        for attr in helpers.__dict__:
            if attr.startswith('requires_'):
                # The following line replaces the decorators with a function
                # that does noting, effectively disabling it.
                setattr(helpers, attr, lambda f: f)

    # Make sure we don't affect the real glue config dir
    import tempfile
    from glue import config
    config.CFG_DIR = tempfile.mkdtemp()

    # Start up QApplication, if the Qt code is present
    try:
        from glue.utils.qt import get_qapp
    except ImportError:
        pass
    else:
        app = get_qapp()

    # Force loading of plugins
    from glue.main import load_plugins
    load_plugins()
Ejemplo n.º 10
0
 def wrapper():
     from glue.utils.qt import get_qapp
     app = get_qapp()
     # Make sure that any window/dialog that needs to be shown is shown
     app.processEvents()
     dialog = app.activeWindow()
     function(dialog)
Ejemplo n.º 11
0
    def __init__(self, layer, parent=None):

        super(ScatterLayerStyleEditor, self).__init__(parent=parent)

        self.ui = load_ui('layer_style_editor_scatter.ui', self,
                          directory=os.path.dirname(__file__))

        # The following is needed because of a bug in Qt which means that
        # tab titles don't get scaled right.
        if platform.system() == 'Darwin':
            app = get_qapp()
            app_font = app.font()
            self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize()))

        self.layer_state = layer.state

        self.layer_state.add_callback('xerr_visible', self._update_xerr_att_combo)
        self.layer_state.add_callback('yerr_visible', self._update_yerr_att_combo)
        self.layer_state.add_callback('size_mode', self._update_size_mode)
        self.layer_state.add_callback('cmap_mode', self._update_cmap_mode)
        self.layer_state.add_callback('layer', self._update_warnings)

        self._update_xerr_att_combo()
        self._update_yerr_att_combo()
        self._update_size_mode()
        self._update_cmap_mode()
        self._update_warnings()
Ejemplo n.º 12
0
def in_process_console(console_class=RichJupyterWidget, **kwargs):
    """
    Create a console widget, connected to an in-process Kernel

    Keyword arguments will be added to the namespace of the shell.

    Parameters
    ----------
    console_class : `type`
        The class of the console widget to create
    """

    global kernel_manager, kernel_client

    if kernel_manager is None:
        start_in_process_kernel()

    app = get_qapp()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = console_class()
    control._display_banner = False
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.shell = kernel_manager.kernel.shell
    control.shell.user_ns.update(**kwargs)
    control.setWindowTitle('IPython Terminal - type howto() for instructions')

    return control
Ejemplo n.º 13
0
    def __init__(self, session, parent=None):
        """
        :type session: :class:`~glue.core.Session`
        """
        QtWidgets.QMainWindow.__init__(self, parent)
        ViewerBase.__init__(self, session)
        self.setWindowIcon(get_qapp().windowIcon())
        self._view = LayerArtistWidget(
            layer_style_widget_cls=self._layer_style_widget_cls,
            hub=session.hub)
        self._view.layer_list.setModel(self._layer_artist_container.model)
        self._tb_vis = {}  # store whether toolbars are enabled
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setAcceptDrops(True)
        self.setAnimated(False)
        self.toolbar = None
        self._toolbars = []
        self._warn_close = True
        self.setContentsMargins(2, 2, 2, 2)
        self._mdi_wrapper = None  # GlueMdiSubWindow that self is embedded in
        self.statusBar().setStyleSheet("QStatusBar{font-size:10px}")

        # close window when last plot layer deleted
        if self._close_on_last_layer_removed:
            self._layer_artist_container.on_empty(
                lambda: self.close(warn=False))
        self._layer_artist_container.on_changed(self.update_window_title)
Ejemplo n.º 14
0
def in_process_console(console_class=RichJupyterWidget, **kwargs):
    """
    Create a console widget, connected to an in-process Kernel

    Keyword arguments will be added to the namespace of the shell.

    Parameters
    ----------
    console_class : `type`
        The class of the console widget to create
    """

    global kernel_manager, kernel_client

    if kernel_manager is None:
        start_in_process_kernel()

    app = get_qapp()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = console_class()
    control._display_banner = False
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.shell = kernel_manager.kernel.shell
    control.shell.user_ns.update(**kwargs)
    control.setWindowTitle('IPython Terminal - type howto() for instructions')

    return control
Ejemplo n.º 15
0
    def test_navigate_sync_image(self):

        self.viewer.add_data(self.data)
        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)
        assert image_viewer.state.slices == (0, 0, 0)

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        app = get_qapp()
        app.processEvents()

        x, y = self.viewer.axes.transData.transform([[1, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (1, 0, 0)

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[10, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (5, 0, 0)
Ejemplo n.º 16
0
    def test_same_data(self):

        # Test that we can't set the same data twice

        app = get_qapp()

        dialog = LinkEditor(self.data_collection)
        dialog.show()
        link_widget = dialog.link_widget

        link_widget.state.data1 = self.data1
        link_widget.state.data2 = self.data2

        assert link_widget.state.data1 == self.data1
        assert link_widget.state.data2 == self.data2

        link_widget.state.data1 = self.data2

        assert link_widget.state.data1 == self.data2
        assert link_widget.state.data2 == self.data1

        link_widget.state.data2 = self.data2

        assert link_widget.state.data1 == self.data1
        assert link_widget.state.data2 == self.data2
Ejemplo n.º 17
0
    def __init__(self, layer, parent=None):

        super(ScatterLayerStyleEditor, self).__init__(parent=parent)

        self.ui = load_ui('layer_style_editor_scatter.ui', self,
                          directory=os.path.dirname(__file__))

        # The following is needed because of a bug in Qt which means that
        # tab titles don't get scaled right.
        app = get_qapp()
        app_font = app.font()
        self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize()))

        self.layer_state = layer.state

        self.layer_state.add_callback('xerr_visible', self._update_xerr_att_combo)
        self.layer_state.add_callback('yerr_visible', self._update_yerr_att_combo)
        self.layer_state.add_callback('size_mode', self._update_size_mode)
        self.layer_state.add_callback('cmap_mode', self._update_cmap_mode)
        self.layer_state.add_callback('layer', self._update_warnings)

        self._update_xerr_att_combo()
        self._update_yerr_att_combo()
        self._update_size_mode()
        self._update_cmap_mode()
        self._update_warnings()
Ejemplo n.º 18
0
def test_table_widget_session_no_subset(tmpdir):

    # Regression test for a bug that caused table viewers with no subsets to
    # not be restored correctly and instead raise an exception.

    app = get_qapp()  # noqa

    d = Data(a=[1, 2, 3, 4, 5],
             b=[3.2, 1.2, 4.5, 3.3, 2.2],
             c=['e', 'b', 'c', 'a', 'f'], label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

    widget = gapp.new_data_viewer(TableViewer)
    widget.add_data(d)

    session_file = tmpdir.join('table.glu').strpath

    gapp.save_session(session_file)

    gapp2 = GlueApplication.restore_session(session_file)
    gapp2.show()

    gapp2.data_collection[0]
    gapp2.viewers[0][0]
Ejemplo n.º 19
0
    def test_navigate_sync_image(self):

        self.viewer.add_data(self.data)
        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)
        assert image_viewer.state.slices == (0, 0, 0)

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        app = get_qapp()
        app.processEvents()

        x, y = self.viewer.axes.transData.transform([[1, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (1, 0, 0)

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[10, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (5, 0, 0)
Ejemplo n.º 20
0
def test_table_widget_session_no_subset(tmpdir):

    # Regression test for a bug that caused table viewers with no subsets to
    # not be restored correctly and instead raise an exception.

    app = get_qapp()  # noqa

    d = Data(a=[1, 2, 3, 4, 5],
             b=[3.2, 1.2, 4.5, 3.3, 2.2],
             c=['e', 'b', 'c', 'a', 'f'],
             label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

    widget = gapp.new_data_viewer(TableViewer)
    widget.add_data(d)

    session_file = tmpdir.join('table.glu').strpath

    gapp.save_session(session_file)

    gapp2 = GlueApplication.restore_session(session_file)
    gapp2.show()

    gapp2.data_collection[0]
    gapp2.viewers[0][0]
Ejemplo n.º 21
0
    def __init__(self, axes, **kwargs):
        super(RoiMode, self).__init__(axes, **kwargs)

        self._start_event = None
        self._drag = False
        app = get_qapp()
        self._drag_dist = app.startDragDistance()
Ejemplo n.º 22
0
 def fix_tab_widget_fontsize(tab_widget):
     """
     Because of a bug in Qt, tab titles on MacOS X don't have the right font size
     """
     if platform.system() == 'Darwin':
         app = get_qapp()
         app_font = app.font()
         tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize()))
Ejemplo n.º 23
0
 def result(*args, **kwargs):
     from glue.utils.qt import get_qapp  # Here to avoid circ import
     app = get_qapp()
     app.setOverrideCursor(shape)
     try:
         return func(*args, **kwargs)
     finally:
         app.restoreOverrideCursor()
Ejemplo n.º 24
0
    def __init__(self, viewer, **kwargs):

        super(RoiMode, self).__init__(viewer, **kwargs)

        self._start_event = None
        self._drag = False
        app = get_qapp()
        self._drag_dist = app.startDragDistance()
Ejemplo n.º 25
0
 def result(*args, **kwargs):
     from glue.utils.qt import get_qapp  # Here to avoid circ import
     app = get_qapp()
     app.setOverrideCursor(shape)
     try:
         return func(*args, **kwargs)
     finally:
         app.restoreOverrideCursor()
Ejemplo n.º 26
0
 def wait(self):
     if QT_INSTALLED:
         # Wait 0.5 seconds to make sure that the computation has properly started
         time.sleep(0.5)
         while self._worker.running:
             time.sleep(1 / 25)
         from glue.utils.qt import get_qapp
         app = get_qapp()
         app.processEvents()
Ejemplo n.º 27
0
def set_cursor_cm(shape):
    """Context manager equivalent for :func:`set_cursor`."""
    from glue.utils.qt import get_qapp
    app = get_qapp()
    app.setOverrideCursor(shape)
    try:
        yield
    finally:
        app.restoreOverrideCursor()
Ejemplo n.º 28
0
 def wait(self):
     if QT_INSTALLED:
         # Wait 0.5 seconds to make sure that the computation has properly started
         time.sleep(0.5)
         while self._worker.running:
             time.sleep(1 / 25)
         from glue.utils.qt import get_qapp
         app = get_qapp()
         app.processEvents()
Ejemplo n.º 29
0
def set_cursor_cm(shape):
    """Context manager equivalent for :func:`set_cursor`."""
    from glue.utils.qt import get_qapp
    app = get_qapp()
    app.setOverrideCursor(shape)
    try:
        yield
    finally:
        app.restoreOverrideCursor()
Ejemplo n.º 30
0
    def __init__(self, data_collection=None, session=None):

        # At this point we need to check if a Qt application already exists -
        # this happens for example if using the %gui qt/qt5 mode in Jupyter. We
        # should keep a reference to the original icon so that we can restore it
        # later
        self._original_app = QtWidgets.QApplication.instance()
        if self._original_app is not None:
            self._original_icon = self._original_app.windowIcon()

        self._export_helper = ExportHelper(self)
        self._import_helper = ImportHelper(self)

        # Now we can get the application instance, which involves setting it
        # up if it doesn't already exist.
        self.app = get_qapp()

        QtWidgets.QMainWindow.__init__(self)
        Application.__init__(self,
                             data_collection=data_collection,
                             session=session)

        # Pull in any keybindings from an external file
        self.keybindings = keyboard_shortcut

        icon = get_icon('app_icon')
        self.app.setWindowIcon(icon)

        # Even though we loaded the plugins in start_glue, we re-load them here
        # in case glue was started directly by initializing this class.
        load_plugins(require_qt_plugins=True)

        self.setWindowTitle("Glue")
        self.setWindowIcon(icon)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self._actions = {}
        self._terminal = None
        self._setup_ui()
        self.tab_widget.setMovable(True)
        self.tab_widget.setTabsClosable(True)

        # The following is a counter that never goes down, even if tabs are
        # deleted (this is by design, to avoid having two tabs called the
        # same if a tab is removed then a new one added again)
        self._total_tab_count = 0

        lwidget = self._layer_widget
        a = PlotAction(lwidget, self)
        lwidget.ui.layerTree.addAction(a)

        self._tweak_geometry()
        self._create_actions()
        self._create_menu()
        self._connect()
        self.new_tab()
        self._update_viewer_in_focus()
Ejemplo n.º 31
0
def main(argv=sys.argv):
    """
    The majority of the code in this function was taken from start_glue() in main.py after a discussion with
    Tom Robataille. We wanted the ability to get command line arguments and use them in here and this seemed
    to be the cleanest way to do it.
    """
    # Make sure the mosviz startup item is registered
    from .startup import mosviz_setup  # noqa

    parser = argparse.ArgumentParser()
    parser.add_argument('data_files', nargs=argparse.REMAINDER)
    args = parser.parse_known_args(argv[1:])

    import glue
    from glue.utils.qt import get_qapp
    app = get_qapp()

    # Splash screen
    splash = get_splash()
    splash.image = QtGui.QPixmap(MOSVIZ_SPLASH_PATH)
    splash.show()

    # Start off by loading plugins. We need to do this before restoring
    # the session or loading the configuration since these may use existing
    # plugins.
    load_plugins(splash=splash)

    datafiles = args[0].data_files

    # # Show the splash screen for 2 seconds
    timer = QTimer()
    timer.setInterval(2000)
    timer.setSingleShot(True)
    timer.timeout.connect(splash.close)
    timer.start()

    data_collection = glue.core.DataCollection()
    hub = data_collection.hub

    splash.set_progress(100)

    session = glue.core.Session(data_collection=data_collection, hub=hub)
    ga = GlueApplication(session=session)
    qapp = QtWidgets.QApplication.instance()
    ga.setWindowTitle('MOSViz v{0}'.format(__version__))
    qapp.setWindowIcon(QtGui.QIcon(MOSVIZ_ICON_PATH))
    ga.setWindowIcon(QtGui.QIcon(MOSVIZ_ICON_PATH))

    # Load the data files.
    if datafiles:
        datasets = load_data_files(datafiles)
        ga.add_datasets(data_collection, datasets, auto_merge=False)

    ga.run_startup_action('mosviz')

    sys.exit(ga.start(maximized=True))
Ejemplo n.º 32
0
def pytest_configure(config):

    global app

    # We need to import PyWWT before setting up a QApplication since the
    # WebEngine widgets require this.
    from pywwt.qt import WWTQtClient  # noqa

    from glue.utils.qt import get_qapp
    app = get_qapp()
Ejemplo n.º 33
0
    def __init__(self, data_collection=None, session=None):

        # At this point we need to check if a Qt application already exists -
        # this happens for example if using the %gui qt/qt5 mode in Jupyter. We
        # should keep a reference to the original icon so that we can restore it
        # later
        self._original_app = QtWidgets.QApplication.instance()
        if self._original_app is not None:
            self._original_icon = self._original_app.windowIcon()

        self._export_helper = ExportHelper(self)
        self._import_helper = ImportHelper(self)

        # Now we can get the application instance, which involves setting it
        # up if it doesn't already exist.
        self.app = get_qapp()

        QtWidgets.QMainWindow.__init__(self)
        Application.__init__(self, data_collection=data_collection,
                             session=session)

        # Pull in any keybindings from an external file
        self.keybindings = keyboard_shortcut

        icon = get_icon('app_icon')
        self.app.setWindowIcon(icon)

        # Even though we loaded the plugins in start_glue, we re-load them here
        # in case glue was started directly by initializing this class.
        load_plugins()

        self.setWindowTitle("Glue")
        self.setWindowIcon(icon)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self._actions = {}
        self._terminal = None
        self._setup_ui()
        self.tab_widget.setMovable(True)
        self.tab_widget.setTabsClosable(True)

        # The following is a counter that never goes down, even if tabs are
        # deleted (this is by design, to avoid having two tabs called the
        # same if a tab is removed then a new one added again)
        self._total_tab_count = 0

        lwidget = self._layer_widget
        a = PlotAction(lwidget, self)
        lwidget.ui.layerTree.addAction(a)

        self._tweak_geometry()
        self._create_actions()
        self._create_menu()
        self._connect()
        self.new_tab()
        self._update_viewer_in_focus()
Ejemplo n.º 34
0
    def test_aspect_resize(self):

        # Make sure that the limits are adjusted appropriately when resizing
        # depending on the aspect ratio mode. Note that we don't add any data
        # here since it isn't needed for this test.

        # This test works with Matplotlib 2.0 and 2.2 but not 2.1, hence we
        # skip it with Matplotlib 2.1 above.

        app = get_qapp()

        # Set initial limits to deterministic values
        self.viewer.state.aspect = 'auto'
        self.viewer.state.x_min = 0.
        self.viewer.state.x_max = 1.
        self.viewer.state.y_min = 0.
        self.viewer.state.y_max = 1.

        self.viewer.state.aspect = 'equal'

        # Resize events only work if widget is visible
        self.viewer.show()
        app.processEvents()

        def limits(viewer):
            return (viewer.state.x_min, viewer.state.x_max, viewer.state.y_min,
                    viewer.state.y_max)

        # Set viewer to an initial size and save limits
        self.viewer.viewer_size = (800, 400)
        app.processEvents()
        initial_limits = limits(self.viewer)

        # Change the viewer size, and make sure the limits are adjusted
        self.viewer.viewer_size = (400, 400)
        app.processEvents()
        with pytest.raises(AssertionError):
            assert_allclose(limits(self.viewer), initial_limits)

        # Now change the viewer size a number of times and make sure if we
        # return to the original size, the limits match the initial ones.
        self.viewer.viewer_size = (350, 800)
        app.processEvents()
        self.viewer.viewer_size = (900, 300)
        app.processEvents()
        self.viewer.viewer_size = (600, 600)
        app.processEvents()
        self.viewer.viewer_size = (800, 400)
        app.processEvents()
        assert_allclose(limits(self.viewer), initial_limits)

        # Now check that the limits don't change in 'auto' mode
        self.viewer.state.aspect = 'auto'
        self.viewer.viewer_size = (900, 300)
        assert_allclose(limits(self.viewer), initial_limits)
Ejemplo n.º 35
0
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                # Make sure application has been started
                from glue.utils.qt import get_qapp  # Here to avoid circ import
                get_qapp()

                m = "%s\n%s" % (msg, e)
                detail = str(traceback.format_exc())
                if len(m) > 500:
                    detail = "Full message:\n\n%s\n\n%s" % (m, detail)
                    m = m[:500] + '...'

                qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", m)
                qmb.setDetailedText(detail)
                qmb.show()
                qmb.raise_()
                qmb.exec_()
                sys.exit(1)
Ejemplo n.º 36
0
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                # Make sure application has been started
                from glue.utils.qt import get_qapp  # Here to avoid circ import
                get_qapp()

                m = "%s\n%s" % (msg, e)
                detail = str(traceback.format_exc())
                if len(m) > 500:
                    detail = "Full message:\n\n%s\n\n%s" % (m, detail)
                    m = m[:500] + '...'

                qmb = QMessageBox(QMessageBox.Critical, "Error", m)
                qmb.setDetailedText(detail)
                qmb.show()
                qmb.raise_()
                qmb.exec_()
                sys.exit(1)
Ejemplo n.º 37
0
    def test_collapse(self):

        self.viewer.add_data(self.data)

        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)

        self.profile_tools.ui.tabs.setCurrentIndex(2)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        app = get_qapp()
        app.processEvents()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        app.processEvents()

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean
Ejemplo n.º 38
0
    def test_collapse(self):

        self.viewer.add_data(self.data)

        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)

        self.profile_tools.ui.tabs.setCurrentIndex(2)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        app = get_qapp()
        app.processEvents()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        app.processEvents()

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean
Ejemplo n.º 39
0
def main():
    import numpy as np
    from glue.utils.qt import get_qapp
    from glue.core import Data, DataCollection

    app = get_qapp()

    x = np.array([1, 2, 3])
    d = Data(label='data', x=x, y=x * 2)
    dc = DataCollection(d)

    LinkEditor.update_links(dc)
Ejemplo n.º 40
0
def pytest_configure(config):

    os.environ['QTWEBENGINE_CHROMIUM_FLAGS'] = '--single-process'

    global app

    # We need to import PyWWT before setting up a QApplication since the
    # WebEngine widgets require this.
    from pywwt.qt import WWTQtClient  # noqa

    from glue.utils.qt import get_qapp
    app = get_qapp()
Ejemplo n.º 41
0
def test_logger_close():

    # Regression test to make sure that when closing an application, sys.stderr
    # no longer points to GlueLogger.

    app = GlueApplication()
    app.close()

    qapp = get_qapp()
    qapp.processEvents()

    assert not isinstance(sys.stderr, GlueLogger)
Ejemplo n.º 42
0
def test_main():

    app = get_qapp()

    w = QMessageBox(QMessageBox.Critical, "Error", "An error occurred")
    w.setDetailedText("Spam")
    w.select_all()
    w.copy_detailed()

    assert app.clipboard().text() == "Spam"

    app.quit()
Ejemplo n.º 43
0
def test_logger_close():

    # Regression test to make sure that when closing an application, sys.stderr
    # no longer points to GlueLogger.

    app = GlueApplication()
    app.close()

    qapp = get_qapp()
    qapp.processEvents()

    assert not isinstance(sys.stderr, GlueLogger)
Ejemplo n.º 44
0
def main():
    import numpy as np
    from glue.utils.qt import get_qapp
    from glue.core import Data, DataCollection

    app = get_qapp()

    x = np.array([1, 2, 3])
    d = Data(label='data', x=x, y=x * 2)
    dc = DataCollection(d)

    LinkEditor.update_links(dc)
Ejemplo n.º 45
0
    def test_resize(self):

        # Regression test for a bug that caused images to not be shown at
        # full resolution after resizing a widget.

        # This test only runs correctly on Linux on Travis at the moment,
        # although it works fine locally on MacOS X. I have not yet tracked
        # down the cause of the failure, but essentially the first time that
        # self.widget.client._view_window is accessed below, it is still None.
        # The issue is made more complicated by the fact that whether the test
        # succeeds or not (after removing code in ImageWidget) depends on
        # whether another test is run first - in particular I tried with
        # test_resize from test_application.py. I was able to then get the
        # test here to pass if the other test_resize was *not* run first.
        # This should be investigated more in future, but for now, it's most
        # important that we get the fix in.

        # What appears to happen when the test fails is that the QTimer gets
        # started but basically never ends up triggering the timeout.

        large = core.Data(label='largeim', x=np.random.random((1024, 1024)))
        self.collect.append(large)

        app = get_qapp()
        self.widget.add_data(large)
        self.widget.show()

        self.widget.resize(300, 300)
        time.sleep(0.5)
        app.processEvents()

        extx0, exty0 = self.widget.client._view_window[4:]

        # While resizing, the view window should not change until we've
        # waited for a bit, to avoid resampling the data every time.
        for res in range(10):

            self.widget.resize(300 + res * 30, 300 + res * 30)
            app.processEvents()

            extx, exty = self.widget.client._view_window[4:]
            assert extx == extx0
            assert exty == exty0

        time.sleep(0.5)
        app.processEvents()

        extx, exty = self.widget.client._view_window[4:]
        assert extx != extx0
        assert exty != exty0

        self.widget.close()
Ejemplo n.º 46
0
    def test_resize(self):

        # Regression test for a bug that caused images to not be shown at
        # full resolution after resizing a widget.

        # This test only runs correctly on Linux on Travis at the moment,
        # although it works fine locally on MacOS X. I have not yet tracked
        # down the cause of the failure, but essentially the first time that
        # self.widget.client._view_window is accessed below, it is still None.
        # The issue is made more complicated by the fact that whether the test
        # succeeds or not (after removing code in ImageWidget) depends on
        # whether another test is run first - in particular I tried with
        # test_resize from test_application.py. I was able to then get the
        # test here to pass if the other test_resize was *not* run first.
        # This should be investigated more in future, but for now, it's most
        # important that we get the fix in.

        # What appears to happen when the test fails is that the QTimer gets
        # started but basically never ends up triggering the timeout.

        large = core.Data(label='largeim', x=np.random.random((1024, 1024)))
        self.collect.append(large)

        app = get_qapp()
        self.widget.add_data(large)
        self.widget.show()

        self.widget.resize(300, 300)
        time.sleep(0.5)
        app.processEvents()

        extx0, exty0 = self.widget.client._view_window[4:]

        # While resizing, the view window should not change until we've
        # waited for a bit, to avoid resampling the data every time.
        for res in range(10):

            self.widget.resize(300 + res * 30, 300 + res * 30)
            app.processEvents()

            extx, exty = self.widget.client._view_window[4:]
            assert extx == extx0
            assert exty == exty0

        time.sleep(0.5)
        app.processEvents()

        extx, exty = self.widget.client._view_window[4:]
        assert extx != extx0
        assert exty != exty0

        self.widget.close()
Ejemplo n.º 47
0
    def activate(self):

        app = get_qapp()

        filename, _ = compat.getsavefilename(
            caption='Save File',
            basedir='mytour.wtt',
            filters='WWT Tour File (*.wtt);;',
            selectedfilter='WWT Tour File (*.wtt);;')

        # This indicates that the user cancelled
        if not filename:
            return

        if not filename.endswith('.wtt'):
            filename = filename + '.wtt'

        self.viewer._wwt.widget.page.runJavaScript("tourxml = '';",
                                                   asynchronous=False)
        tourxml = self.viewer._wwt.widget.page.runJavaScript(
            'tourxml;', asynchronous=False)

        self.viewer._wwt.widget.page.runJavaScript(SAVE_TOUR_CODE)

        start = time.time()
        tourxml = None
        while time.time() - start < 10:
            time.sleep(0.1)
            app.processEvents()
            tourxml = self.viewer._wwt.widget.page.runJavaScript(
                'tourxml;', asynchronous=False)
            if tourxml:
                break

        if not tourxml:
            raise Exception("Failed to save tour")

        # Patch the altUnit so that it is correct for the Windows client (since
        # the web client currently has other bugs with relation to loading tours).
        # https://github.com/WorldWideTelescope/wwt-web-client/issues/248
        for unit_int in range(1, 11):
            altunit_str = 'AltUnit="{0}"'.format(unit_int)
            if altunit_str in tourxml:
                altunit_str_new = 'AltUnit="{0}"'.format(unit_int - 1)
                print('Changing {0} to {1} in {2}'.format(
                    altunit_str, altunit_str_new, filename))
                tourxml = tourxml.replace(altunit_str, altunit_str_new)

        with io.open(filename, 'w', newline='') as f:
            f.write(tourxml)
Ejemplo n.º 48
0
    def setup_method(self, method):

        self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1')
        self.data2 = Data(a=[1, 2, 3], b=[2, 3, 4], label='data2')
        self.dc = DataCollection([self.data1, self.data2])

        self.x = self.data1.id['x']
        self.y = self.data1.id['y']
        self.a = self.data2.id['a']
        self.b = self.data2.id['b']

        self.app = get_qapp()

        self.dialog = SaveDataDialog(data_collection=self.dc)
Ejemplo n.º 49
0
def main():
    import numpy as np
    from glue.utils.qt import get_qapp
    from glue.core import Data, DataCollection

    app = get_qapp()

    dc = DataCollection()

    for i in range(10):
        x = np.array([1, 2, 3])
        d = Data(label='data_{0:02d}'.format(i), x=x, y=x * 2)
        dc.append(d)

    LinkEditor.update_links(dc)
Ejemplo n.º 50
0
    def __init__(self, application, parent=None):

        super(PreferencesDialog, self).__init__(parent=parent)

        self._app = weakref.ref(application)

        self.ui = load_ui('preferences.ui', self,
                          directory=os.path.dirname(__file__))

        self.ui.cancel.clicked.connect(self.reject)
        self.ui.ok.clicked.connect(self.accept)

        self.ui.combo_theme.currentIndexChanged.connect(self._update_colors_from_theme)

        self.ui.button_reset_dialogs.clicked.connect(self._reset_dialogs)

        # The following is needed because of a bug in Qt which means that
        # tab titles don't get scaled right.
        if platform.system() == 'Darwin':
            app = get_qapp()
            app_font = app.font()
            self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize()))

        from glue.config import settings
        self.background = settings.BACKGROUND_COLOR
        self.foreground = settings.FOREGROUND_COLOR
        self.data_color = settings.DATA_COLOR
        self.data_alpha = settings.DATA_ALPHA
        self.font_size = settings.FONT_SIZE

        self._update_theme_from_colors()

        self._autolink_pane = AutolinkPreferencesPane()
        self.ui.tab_widget.addTab(self._autolink_pane, 'Autolinking')

        self.panes = []

        from glue.config import preference_panes
        for label, widget_cls in sorted(preference_panes):
            pane = widget_cls()
            self.ui.tab_widget.addTab(pane, label)
            self.panes.append(pane)
Ejemplo n.º 51
0
def test_change_components():

    # Regression test for a bug that caused table viewers to not update when
    # adding/removing components. For now, this does not work with Qt 5.7.

    app = get_qapp()  # noqa

    d = Data(a=[1, 2, 3, 4, 5],
             b=[3.2, 1.2, 4.5, 3.3, 2.2],
             c=['e', 'b', 'c', 'a', 'f'], label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

    viewer = gapp.new_data_viewer(TableViewer)
    viewer.add_data(d)

    data_changed = MagicMock()
    viewer.model.dataChanged.connect(data_changed)

    # layoutChanged needs to be emitted for the new/removed columns to be
    # registered (dataChanged is not enough)
    layout_changed = MagicMock()
    viewer.model.layoutChanged.connect(layout_changed)

    assert data_changed.call_count == 0
    assert layout_changed.call_count == 0
    viewer.model.columnCount() == 2

    d.add_component([3, 4, 5, 6, 2], 'z')

    assert data_changed.call_count == 1
    assert layout_changed.call_count == 1
    viewer.model.columnCount() == 3

    d.remove_component(d.id['z'])

    assert data_changed.call_count == 2
    assert layout_changed.call_count == 2
    viewer.model.columnCount() == 2
Ejemplo n.º 52
0
    def __init__(self, data_collection=None, session=None, maximized=True):

        self.app = get_qapp()

        QtWidgets.QMainWindow.__init__(self)
        Application.__init__(self, data_collection=data_collection,
                             session=session)

        self.app.setQuitOnLastWindowClosed(True)
        icon = get_icon('app_icon')
        self.app.setWindowIcon(icon)

        # Even though we loaded the plugins in start_glue, we re-load them here
        # in case glue was started directly by initializing this class.
        load_plugins()

        self.setWindowTitle("Glue")
        self.setWindowIcon(icon)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self._actions = {}
        self._terminal = None
        self._setup_ui()
        self.tab_widget.setMovable(True)
        self.tab_widget.setTabsClosable(True)

        # The following is a counter that never goes down, even if tabs are
        # deleted (this is by design, to avoid having two tabs called the
        # same if a tab is removed then a new one added again)
        self._total_tab_count = 0

        lwidget = self._layer_widget
        a = PlotAction(lwidget, self)
        lwidget.ui.layerTree.addAction(a)
        lwidget.bind_selection_to_edit_subset()

        self._tweak_geometry(maximized=maximized)
        self._create_actions()
        self._create_menu()
        self._connect()
        self.new_tab()
        self._update_plot_dashboard(None)
Ejemplo n.º 53
0
    def __init__(self, parent=None):
        """
        :type session: :class:`~glue.core.Session`
        """

        super(BaseQtViewerWidget, self).__init__(parent)

        self.setWindowIcon(get_qapp().windowIcon())

        status_bar = self.statusBar()
        status_bar.setSizeGripEnabled(False)
        status_bar.setStyleSheet("QStatusBar{font-size:10px}")

        self.setFocusPolicy(Qt.StrongFocus)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setAcceptDrops(True)
        self.setAnimated(False)
        self.setContentsMargins(2, 2, 2, 2)

        self._mdi_wrapper = None  # GlueMdiSubWindow that self is embedded in
        self._warn_close = True
Ejemplo n.º 54
0
def create_app(datafiles=[], interactive=True):

    app = get_qapp()

    if interactive:
        # Splash screen
        splash = get_splash()
        splash.image = QtGui.QPixmap(MOSVIZ_SPLASH_PATH)
        splash.show()
    else:
        splash = None

    # Start off by loading plugins. We need to do this before restoring
    # the session or loading the configuration since these may use existing
    # plugins.
    load_plugins(splash=splash)

    # # Show the splash screen for 2 seconds
    if interactive:
        timer = QTimer()
        timer.setInterval(2000)
        timer.setSingleShot(True)
        timer.timeout.connect(splash.close)
        timer.start()

    data_collection = glue.core.DataCollection()
    hub = data_collection.hub

    if interactive:
        splash.set_progress(100)

    ga = _create_glue_app(data_collection, hub)
    ga.run_startup_action('mosviz')

    # Load the data files.
    if datafiles:
        datasets = load_data_files(datafiles)
        ga.add_datasets(data_collection, datasets, auto_merge=False)

    return ga
Ejemplo n.º 55
0
    def test_close_on_last_layer_remove(self):

        # regression test for 391

        # Note: processEvents is needed for things to work correctly with PySide2
        qtapp = get_qapp()

        d1 = Data(x=np.random.random((2,) * self.ndim))
        d2 = Data(y=np.random.random((2,) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.add_data(d2)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 1
        dc.remove(d1)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 1
        dc.remove(d2)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 0
        app.close()
Ejemplo n.º 56
0
def test_table_title():

    app = get_qapp()  # noqa

    data1 = Data(a=[1, 2, 3, 4, 5], label='test1')
    data2 = Data(a=[1, 2, 3, 4, 5], label='test2')

    dc = DataCollection([data1, data2])

    gapp = GlueApplication(dc)

    viewer = gapp.new_data_viewer(TableViewer)

    assert viewer.windowTitle() == 'Table'

    viewer.add_data(data1)

    assert viewer.windowTitle() == 'Table: test1'

    viewer.add_data(data2)

    assert viewer.windowTitle() == 'Table: test2'
Ejemplo n.º 57
0
    def _setup_ctxbar(self):
        l = self.widget.centralWidget().layout()
        self._contexts = [NavContext(self),
                          FitContext(self),
                          CollapseContext(self)]

        tabs = QtWidgets.QTabWidget(parent=self.widget)

        # The following is needed because of a bug in Qt which means that
        # tab titles don't get scaled right.
        if platform.system() == 'Darwin':
            app = get_qapp()
            app_font = app.font()
            tabs.setStyleSheet('font-size: {0}px'.format(app_font.pointSize()))

        tabs.addTab(self._contexts[0].widget, 'Navigate')
        tabs.addTab(self._contexts[1].widget, 'Fit')
        tabs.addTab(self._contexts[2].widget, 'Collapse')
        self._tabs = tabs
        self._tabs.setVisible(False)
        l.addWidget(tabs)
        l.setStretchFactor(tabs, 0)