Skip to content

Commit

Permalink
Merge pull request #488 from mattgibbs/embedded_display_load_when_shown
Browse files Browse the repository at this point in the history
Add option to defer loading embedded displays until they become visible.
  • Loading branch information
mattgibbs authored Jun 10, 2019
2 parents ad54a15 + 87501b4 commit 7bfb9dc
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 122 deletions.
95 changes: 6 additions & 89 deletions pydm/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,21 @@
our PyDMMainWindow class with navigation logic.
"""
import os
import imp
import sys
import uuid
import signal
import subprocess
import json
import inspect
import logging
import warnings
from .display_module import Display

from qtpy.QtCore import Qt, QTimer, Slot
from qtpy.QtWidgets import QApplication, QWidget
from qtpy.QtGui import QColor
from qtpy import uic
from .main_window import PyDMMainWindow

from .utilities import macro, which, path_info, find_display_in_path
from .utilities.stylesheet import apply_stylesheet
from .utilities.display_loading import load_ui_file, load_py_file
from .utilities import connection
from . import data_plugins
from .widgets.rules import RulesDispatcher
Expand Down Expand Up @@ -278,87 +275,7 @@ def close_window(self, window):
# it means that we already closed it.
pass

def load_ui_file(self, uifile, macros=None):
"""
Load a .ui file, perform macro substitution, then return the resulting QWidget.
This is an internal method, users will usually want to use `open_file` instead.
Parameters
----------
uifile : str
The path to a .ui file to load.
macros : dict, optional
A dictionary of macro variables to supply to the file
to be opened.
Returns
-------
QWidget
"""
if macros is not None and len(macros) > 0:
f = macro.substitute_in_file(uifile, macros)
else:
f = uifile
return uic.loadUi(f)

def load_py_file(self, pyfile, args=None, macros=None):
"""
Load a .py file, performs some sanity checks to try and determine
if the file actually contains a valid PyDM Display subclass, and if
the checks pass, create and return an instance.
This is an internal method, users will usually want to use `open_file` instead.
Parameters
----------
pyfile : str
The path to a .ui file to load.
args : list, optional
A list of command-line arguments to pass to the
loaded display subclass.
macros : dict, optional
A dictionary of macro variables to supply to the
loaded display subclass.
Returns
-------
pydm.Display
"""
# Add the intelligence module directory to the python path, so that submodules can be loaded. Eventually, this should go away, and intelligence modules should behave as real python modules.
module_dir = os.path.dirname(os.path.abspath(pyfile))
sys.path.append(module_dir)
temp_name = str(uuid.uuid4())

# Now load the intelligence module.
module = imp.load_source(temp_name, pyfile)
if hasattr(module, 'intelclass'):
cls = module.intelclass
if not issubclass(cls, Display):
raise ValueError("Invalid class definition at file {}. {} does not inherit from Display. Nothing to open at this time.".format(pyfile, cls.__name__))
else:
classes = [obj for name, obj in inspect.getmembers(module) if inspect.isclass(obj) and issubclass(obj, Display) and obj != Display]
if len(classes) == 0:
raise ValueError("Invalid File Format. {} has no class inheriting from Display. Nothing to open at this time.".format(pyfile))
if len(classes) > 1:
warnings.warn("More than one Display class in file {}. The first occurence (in alphabetical order) will be opened: {}".format(pyfile, classes[0].__name__), RuntimeWarning, stacklevel=2)
cls = classes[0]

try:
# This only works in python 3 and up.
module_params = inspect.signature(cls).parameters
except AttributeError:
# Works in python 2, deprecated in 3.0 and up.
module_params = inspect.getargspec(cls.__init__).args

# Because older versions of Display may not have the args parameter or the macros parameter, we check
# to see if it does before trying to use them.
kwargs = {}
if 'args' in module_params:
kwargs['args'] = args
if 'macros' in module_params:
kwargs['macros'] = macros
return cls(**kwargs)


def open_file(self, ui_file, macros=None, command_line_args=None, defer_connections=False, **kwargs):
"""
Expand Down Expand Up @@ -403,9 +320,9 @@ def open_file(self, ui_file, macros=None, command_line_args=None, defer_connecti
self.macro_stack.append(merged_macros)
with data_plugins.connection_queue(defer_connections=defer_connections):
if extension == '.ui':
widget = self.load_ui_file(filepath, merged_macros)
widget = load_ui_file(filepath, merged_macros)
elif extension == '.py':
widget = self.load_py_file(filepath, args, merged_macros)
widget = load_py_file(filepath, args, merged_macros)
else:
self.directory_stack.pop()
self.macro_stack.pop()
Expand Down Expand Up @@ -565,6 +482,6 @@ def widget_from_template(self, template, macros):
merged_macros = self.macro_stack[-1].copy()
merged_macros.update(macros)
f = macro.replace_macros_in_template(template, merged_macros)
w = self.load_ui_file(f)
w = load_ui_file(f)
w.base_macros = merged_macros
return w
91 changes: 91 additions & 0 deletions pydm/utilities/display_loading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import os
import sys
import imp
import uuid
import inspect
import warnings
from ..display_module import Display
from qtpy import uic
from . import macro

def load_ui_file(uifile, macros=None):
"""
Load a .ui file, perform macro substitution, then return the resulting QWidget.
This is an internal method, users will usually want to use `open_file` instead.
Parameters
----------
uifile : str
The path to a .ui file to load.
macros : dict, optional
A dictionary of macro variables to supply to the file
to be opened.
Returns
-------
QWidget
"""
if macros is not None and len(macros) > 0:
f = macro.substitute_in_file(uifile, macros)
else:
f = uifile
return uic.loadUi(f)

def load_py_file(pyfile, args=None, macros=None):
"""
Load a .py file, performs some sanity checks to try and determine
if the file actually contains a valid PyDM Display subclass, and if
the checks pass, create and return an instance.
This is an internal method, users will usually want to use `open_file` instead.
Parameters
----------
pyfile : str
The path to a .ui file to load.
args : list, optional
A list of command-line arguments to pass to the
loaded display subclass.
macros : dict, optional
A dictionary of macro variables to supply to the
loaded display subclass.
Returns
-------
pydm.Display
"""
# Add the intelligence module directory to the python path, so that submodules can be loaded. Eventually, this should go away, and intelligence modules should behave as real python modules.
module_dir = os.path.dirname(os.path.abspath(pyfile))
sys.path.append(module_dir)
temp_name = str(uuid.uuid4())

# Now load the intelligence module.
module = imp.load_source(temp_name, pyfile)
if hasattr(module, 'intelclass'):
cls = module.intelclass
if not issubclass(cls, Display):
raise ValueError("Invalid class definition at file {}. {} does not inherit from Display. Nothing to open at this time.".format(pyfile, cls.__name__))
else:
classes = [obj for name, obj in inspect.getmembers(module) if inspect.isclass(obj) and issubclass(obj, Display) and obj != Display]
if len(classes) == 0:
raise ValueError("Invalid File Format. {} has no class inheriting from Display. Nothing to open at this time.".format(pyfile))
if len(classes) > 1:
warnings.warn("More than one Display class in file {}. The first occurence (in alphabetical order) will be opened: {}".format(pyfile, classes[0].__name__), RuntimeWarning, stacklevel=2)
cls = classes[0]

try:
# This only works in python 3 and up.
module_params = inspect.signature(cls).parameters
except AttributeError:
# Works in python 2, deprecated in 3.0 and up.
module_params = inspect.getargspec(cls.__init__).args

# Because older versions of Display may not have the args parameter or the macros parameter, we check
# to see if it does before trying to use them.
kwargs = {}
if 'args' in module_params:
kwargs['args'] = args
if 'macros' in module_params:
kwargs['macros'] = macros
return cls(**kwargs)
Loading

0 comments on commit 7bfb9dc

Please sign in to comment.