From 9a3527ea79f513d1cb04791d653de4ed2b0efdca Mon Sep 17 00:00:00 2001 From: Hugo Slepicka Date: Tue, 12 Dec 2017 18:29:31 -0800 Subject: [PATCH] ENH: Open in Designer now also opens the PY file associated with the screen. (#187) * ENH: Open in Designer now also opens the PY file associated with the screen. * ENH: Changing the Menu text accordingly for Open in Editor and Text Editor as suggested by @mgibbs. FIX: We cant use 'intelclass' as a generic name when loading files, this was causing problems with Open File. Changed to uuid.uuid4() * ENH: Adding better way to find the QT Binary location thanks to @mgibbs --- examples/code_only/code_only.py | 21 ++++++++++ examples/home.ui | 52 +++++++++++------------ pydm/application.py | 11 ++++- pydm/display_module.py | 11 ++--- pydm/main_window.py | 73 +++++++++++++++++++++++++-------- 5 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 examples/code_only/code_only.py diff --git a/examples/code_only/code_only.py b/examples/code_only/code_only.py new file mode 100644 index 000000000..a0c85a518 --- /dev/null +++ b/examples/code_only/code_only.py @@ -0,0 +1,21 @@ +from pydm import Display +from pydm.PyQt.QtGui import QLabel, QVBoxLayout, QHBoxLayout + +class MyDisplay(Display): + def __init__(self, parent=None, args=[]): + super(MyDisplay, self).__init__(parent=parent, args=args) + self.setup_ui() + + def setup_ui(self): + main = QHBoxLayout() + sub = QVBoxLayout() + for i in range(10): + sub.addWidget(QLabel(str(i))) + main.addLayout(sub) + self.setLayout(main) + + def ui_filename(self): + return None + + def ui_filepath(self): + return None diff --git a/examples/home.ui b/examples/home.ui index 5a56a6119..18dda4d35 100644 --- a/examples/home.ui +++ b/examples/home.ui @@ -76,8 +76,8 @@ 0 0 - 216 - 782 + 213 + 560 @@ -95,7 +95,7 @@ - + Byte Indicator @@ -117,7 +117,7 @@ - + Checkbox @@ -139,7 +139,7 @@ - + Drawing Widgets @@ -161,7 +161,7 @@ - + Embedded Display @@ -183,7 +183,7 @@ - + Enum Combo Box @@ -205,7 +205,7 @@ - + Image View @@ -227,7 +227,7 @@ - + Label @@ -249,7 +249,7 @@ - + Line Edit @@ -265,7 +265,7 @@ - + Push Button @@ -281,7 +281,7 @@ - + Related Display Button @@ -297,7 +297,7 @@ - + Shell Command Button @@ -313,7 +313,7 @@ - + Slider @@ -329,7 +329,7 @@ - + Spinbox @@ -345,7 +345,7 @@ - + Symbol @@ -361,7 +361,7 @@ - + Time Plot @@ -377,7 +377,7 @@ - + Waveform Plot @@ -393,7 +393,7 @@ - + Waveform Table @@ -436,7 +436,7 @@ 0 0 - 358 + 366 443 @@ -458,7 +458,7 @@ - + Macro Variables in Embedded Displays @@ -480,7 +480,7 @@ - + Macro Variables and Related Display Buttons @@ -502,7 +502,7 @@ - + Nested Embedded Displays @@ -524,7 +524,7 @@ - + Python-based Display: Image Processing @@ -546,7 +546,7 @@ - + Python-based Display: Camera View Application @@ -581,7 +581,7 @@ PyDMRelatedDisplayButton - QFrame + QPushButton
pydm.widgets.related_display_button
diff --git a/pydm/application.py b/pydm/application.py index 76e50b69b..984ee0c53 100644 --- a/pydm/application.py +++ b/pydm/application.py @@ -7,6 +7,7 @@ import os import imp import sys +import uuid import signal import subprocess import re @@ -250,7 +251,12 @@ def make_window(self, ui_file, macros=None, command_line_args=None): main_window.move(main_window.x() + 10, main_window.y() + 10) def close_window(self, window): - del self.windows[window] + try: + del self.windows[window] + except KeyError: + # If window is no longer at self.windows + # it means that we already closed it. + pass def load_ui_file(self, uifile, macros=None): """ @@ -310,9 +316,10 @@ def load_py_file(self, pyfile, args=None, macros=None): # 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('intelclass', pyfile) + module = imp.load_source(temp_name, pyfile) self.__sanity_check_pyqt(module) if hasattr(module, 'intelclass'): cls = module.intelclass diff --git a/pydm/display_module.py b/pydm/display_module.py index a651809bb..1bedca211 100644 --- a/pydm/display_module.py +++ b/pydm/display_module.py @@ -18,8 +18,9 @@ def ui_filename(self): def load_ui(self, parent=None, macros=None): if self.ui: return self.ui - if macros is not None: - f = macro.substitute_in_file(self.ui_filepath(), macros) - else: - f = self.ui_filepath() - self.ui = uic.loadUi(f, baseinstance=self) + if self.ui_filepath() is not None and self.ui_filepath() != "": + if macros is not None: + f = macro.substitute_in_file(self.ui_filepath(), macros) + else: + f = self.ui_filepath() + self.ui = uic.loadUi(f, baseinstance=self) diff --git a/pydm/main_window.py b/pydm/main_window.py index 10218ec10..da1dc2d7b 100644 --- a/pydm/main_window.py +++ b/pydm/main_window.py @@ -2,13 +2,16 @@ from os import path, environ from functools import partial from .PyQt.QtGui import QApplication, QMainWindow, QFileDialog, QWidget, QShortcut, QKeySequence -from .PyQt.QtCore import Qt, QTimer, pyqtSlot, QSize +from .PyQt.QtCore import Qt, QTimer, pyqtSlot, QSize, QLibraryInfo from .utilities import IconFont from .pydm_ui import Ui_MainWindow +from .display_module import Display import subprocess import platform + class PyDMMainWindow(QMainWindow): + def __init__(self, parent=None, hide_nav_bar=False, hide_menu_bar=False, hide_status_bar=False): super(PyDMMainWindow, self).__init__(parent) self.app = QApplication.instance() @@ -63,16 +66,18 @@ def __init__(self, parent=None, hide_nav_bar=False, hide_menu_bar=False, hide_st if hide_status_bar: self.toggle_status_bar(False) self.designer_path = None - if environ.get('QTHOME') is None: - self.ui.actionEdit_in_Designer.setEnabled(False) + designer_bin = QLibraryInfo.location(QLibraryInfo.BinariesPath) + + if platform.system() == 'Darwin': + self.designer_path = os.path.join(designer_bin, 'Designer.app/Contents/MacOS/Designer') + elif platform.system() == 'Linux': + self.designer_path = os.path.join(designer_bin, 'designer') else: - qt_path = environ.get('QTHOME') - if platform.system() == 'Darwin': - # On OS X we have to launch designer in this ugly way if we want it to get access to environment variables. Ugh. - self.designer_path = path.join(qt_path, 'Designer.app/Contents/MacOS/Designer') - else: - # This assumes some non-OS X unix. No windows support right now. - self.designer_path = path.join(qt_path, 'bin/designer') + self.designer_path = os.path.join(designer_bin, 'designer.exe') + + # Ensure that the file exists + if not os.path.isfile(self.designer_path): + self.designer_path = None def set_display_widget(self, new_widget): if new_widget == self._display_widget: @@ -119,6 +124,16 @@ def open_abs_file(self, filename, macros=None, command_line_args=None): self.ui.actionBack.setEnabled(len(self.back_stack) > 1) if self.home_file is None: self.home_file = (filename, merged_macros, command_line_args) + # Update here the Menu Editor text... + ui_file, py_file = self.get_files_in_display() + edit_in_text = "Open in " + editors = [] + if ui_file is not None and ui_file != "": + editors.append("Designer") + if py_file is not None and py_file != "": + editors.append("Text Editor") + edit_in_text += ' and '.join(editors) + self.ui.actionEdit_in_Designer.setText(edit_in_text) def new_window(self, ui_file, macros=None, command_line_args=None): filename = self.join_to_current_file_path(ui_file) @@ -243,16 +258,38 @@ def toggle_menu_bar(self, checked=None): def toggle_status_bar(self, checked): self.ui.statusbar.setHidden(not checked) - @pyqtSlot(bool) - def edit_in_designer(self, checked): - if not self.designer_path: - return - filename, extension = path.splitext(self.current_file()) + def get_files_in_display(self): + _, extension = path.splitext(self.current_file()) if extension == '.ui': - process = subprocess.Popen('{0} "{1}"'.format(self.designer_path, self.current_file()), shell=True) - self.statusBar().showMessage("Launching '{0}' in Qt Designer...".format(self.current_file()), 5000) + return self.current_file(), None else: - self.statusBar().showMessage("{0} is a Python file, and cannot be edited in Qt Designer.".format(self.current_file()), 5000) + central_widget = self.centralWidget() if isinstance(self.centralWidget(), Display) else None + if central_widget is not None: + ui_file = central_widget.ui_filepath() + return ui_file, self.current_file() + + @pyqtSlot(bool) + def edit_in_designer(self, checked): + + def open_editor_ui(fname): + if self.designer_path is None or fname is None or fname == "": + return + self.statusBar().showMessage("Launching '{0}' in Qt Designer...".format(fname), 5000) + _ = subprocess.Popen('{0} "{1}"'.format(self.designer_path, fname), shell=True) + + def open_editor_generic(fname): + if platform.system() == "Darwin": + subprocess.call(('open', fname)) + elif platform.system() == "Linux": + subprocess.call(('xdg-open', fname)) + elif platform.system() == "Windows": + os.startfile(fname) + + ui_file, py_file = self.get_files_in_display() + if ui_file is not None and ui_file != "": + open_editor_ui(fname=ui_file) + if py_file is not None and py_file != "": + open_editor_generic(fname=py_file) @pyqtSlot(bool) def open_file_action(self, checked):