Exemple #1
0
    def __init__(self,
                 parent=None,
                 namespace=None,
                 commands=[],
                 message=None,
                 max_line_count=300,
                 font=None,
                 exitfunc=None,
                 profile=False,
                 multithreaded=True,
                 light_background=True):
        PythonShellWidget.__init__(self, parent,
                                   get_conf_path('history_internal.py'),
                                   profile)

        self.set_light_background(light_background)
        self.multithreaded = multithreaded
        self.setMaximumBlockCount(max_line_count)

        if font is not None:
            self.set_font(font)

        # Allow raw_input support:
        self.input_loop = None
        self.input_mode = False

        # KeyboardInterrupt support
        self.interrupted = False  # used only for not-multithreaded mode
        self.sig_keyboard_interrupt.connect(self.keyboard_interrupt)

        # Code completion / calltips
        getcfg = lambda option: CONF.get('internal_console', option)
        case_sensitive = getcfg('codecompletion/case_sensitive')
        self.set_codecompletion_case(case_sensitive)

        # keyboard events management
        self.eventqueue = []

        # Init interpreter
        self.exitfunc = exitfunc
        self.commands = commands
        self.message = message
        self.interpreter = None
        self.start_interpreter(namespace)

        # Clear status bar
        self.status.emit('')

        # Embedded shell -- requires the monitor (which installs the
        # 'open_in_trex' function in builtins)
        if hasattr(builtins, 'open_in_trex'):
            self.go_to_error.connect(self.open_with_external_trex)
Exemple #2
0
def get_trexplugins_mods(io=False):
    """Import modules from plugins package and return the list"""
    # Create user directory
    user_plugin_path = osp.join(get_conf_path(), USER_PLUGIN_DIR)
    if not osp.isdir(user_plugin_path):
        os.makedirs(user_plugin_path)

    modlist, modnames = [], []

    # The user plugins directory is given the priority when looking for modules
    for plugin_path in [user_plugin_path] + sys.path:
        _get_trexplugins(plugin_path, io, modnames, modlist)
    return modlist
Exemple #3
0
    def __init__(self,
                 parent,
                 history_filename,
                 profile=False,
                 initial_message=None):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self)

        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True

        # History
        self.histidx = None
        self.hist_wholeline = False
        assert is_text_string(history_filename)
        self.history_filename = history_filename
        self.history = self.load_history()

        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))

        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile

        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        if initial_message:
            self.__buffer.append(initial_message)

        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()

        # Cursor width
        self.setCursorWidth(CONF.get('main', 'cursor/width'))
Exemple #4
0
 def load_plugin(self):
     """Load the Rope introspection plugin"""
     if not programs.is_module_installed('rope', ROPE_REQVER):
         raise ImportError('Requires Rope %s' % ROPE_REQVER)
     self.project = None
     self.create_rope_project(root_path=get_conf_path())
     submods = get_preferred_submodules()
     actual = []
     for submod in submods:
         try:
             imp.find_module(submod)
             actual.append(submod)
         except ImportError:
             pass
     if self.project is not None:
         self.project.prefs.set('extension_modules', actual)
Exemple #5
0
 def _filename_global(self):
     """Create a .ini filename located in user home directory.
     This .ini files stores the global trex preferences.
     """
     if self.subfolder is None:
         config_file = osp.join(get_home_dir(), '.%s.ini' % self.name)
         return config_file
     else:
         folder = get_conf_path()
         # Save defaults in a "defaults" dir of .trex2 to not pollute it
         if 'defaults' in self.name:
             folder = osp.join(folder, 'defaults')
             if not osp.isdir(folder):
                 os.mkdir(folder)
         config_file = osp.join(folder, '%s.ini' % self.name)
         return config_file
Exemple #6
0
import pkgutil
import re
from time import time
import sys
from zipimport import zipimporter

from trex.config.base import get_conf_path, running_in_mac_app

from pickleshare import PickleShareDB

#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------

# Path to the modules database
MODULES_PATH = get_conf_path('db')

# Time in seconds after which we give up
if os.name == 'nt':
    TIMEOUT_GIVEUP = 30
else:
    TIMEOUT_GIVEUP = 20

# Py2app only uses .pyc files for the stdlib when optimize=0,
# so we need to add it as another suffix here
if running_in_mac_app():
    suffixes = imp.get_suffixes() + [('.pyc', 'rb', '2')]
else:
    suffixes = imp.get_suffixes()

# Regular expression for the python import statement
Exemple #7
0
# Third party imports
from qtpy.QtCore import QObject, QTimer, Signal
from qtpy.QtWidgets import QApplication

# Local imports
from trex import dependencies
from trex.config.base import _, DEBUG, debug_print, get_conf_path
from trex.utils import sourcecode
from trex.utils.introspection.plugin_client import PluginClient
from trex.utils.introspection.utils import CodeInfo


PLUGINS = ['rope', 'jedi', 'fallback']

LOG_FILENAME = get_conf_path('introspection.log')
DEBUG_EDITOR = DEBUG >= 3
LEAD_TIME_SEC = 0.25


ROPE_REQVER = '>=0.9.4'
dependencies.add('rope',
                 _("Editor's code completion, go-to-definition and help"),
                 required_version=ROPE_REQVER)

JEDI_REQVER = '>=0.9.0'
dependencies.add('jedi',
                 _("Editor's code completion, go-to-definition and help"),
                 required_version=JEDI_REQVER)

Exemple #8
0
class WorkingDirectory(QToolBar, TRexPluginMixin):
    """
    Working directory changer widget
    """
    CONF_SECTION = 'workingdir'
    CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage
    LOG_PATH = get_conf_path(CONF_SECTION)

    sig_option_changed = Signal(str, object)
    set_previous_enabled = Signal(bool)
    set_next_enabled = Signal(bool)
    redirect_stdio = Signal(bool)
    set_explorer_cwd = Signal(str)
    refresh_findinfiles = Signal()
    set_current_console_wd = Signal(str)

    def __init__(self, parent, workdir=None, **kwds):
        if PYQT5:
            super(WorkingDirectory, self).__init__(parent, **kwds)
        else:
            QToolBar.__init__(self, parent)
            TRexPluginMixin.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

        self.setWindowTitle(self.get_plugin_title())  # Toolbar title
        self.setObjectName(
            self.get_plugin_title())  # Used to save Window state

        # Previous dir action
        self.history = []
        self.histindex = None
        self.previous_action = create_action(self,
                                             "previous",
                                             None,
                                             ima.icon('previous'),
                                             _('Back'),
                                             triggered=self.previous_directory)
        self.addAction(self.previous_action)

        # Next dir action
        self.history = []
        self.histindex = None
        self.next_action = create_action(self,
                                         "next",
                                         None,
                                         ima.icon('next'),
                                         _('Next'),
                                         triggered=self.next_directory)
        self.addAction(self.next_action)

        # Enable/disable previous/next actions
        self.set_previous_enabled.connect(self.previous_action.setEnabled)
        self.set_next_enabled.connect(self.next_action.setEnabled)

        # Path combo box
        adjust = self.get_option('working_dir_adjusttocontents')
        self.pathedit = PathComboBox(self, adjust_to_contents=adjust)
        self.pathedit.setToolTip(
            _("This is the working directory for newly\n"
              "opened consoles (Python/IPython consoles and\n"
              "terminals), for the file explorer, for the\n"
              "find in files plugin and for new files\n"
              "created in the editor"))
        self.pathedit.open_dir.connect(self.chdir)
        self.pathedit.activated[str].connect(self.chdir)
        self.pathedit.setMaxCount(self.get_option('working_dir_history'))
        wdhistory = self.load_wdhistory(workdir)
        if workdir is None:
            if self.get_option('startup/use_last_directory'):
                if wdhistory:
                    workdir = wdhistory[0]
                else:
                    workdir = "."
            else:
                workdir = self.get_option('startup/fixed_directory', ".")
                if not osp.isdir(workdir):
                    workdir = "."
        self.chdir(workdir)
        self.pathedit.addItems(wdhistory)
        self.pathedit.selected_text = self.pathedit.currentText()
        self.refresh_plugin()
        self.addWidget(self.pathedit)

        # Browse action
        browse_action = create_action(self,
                                      "browse",
                                      None,
                                      ima.icon('DirOpenIcon'),
                                      _('Browse a working directory'),
                                      triggered=self.select_directory)
        self.addAction(browse_action)

        # Parent dir action
        parent_action = create_action(self,
                                      "parent",
                                      None,
                                      ima.icon('up'),
                                      _('Change to parent directory'),
                                      triggered=self.parent_directory)
        self.addAction(parent_action)

    #------ TRexPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Global working directory')

    def get_plugin_icon(self):
        """Return widget icon"""
        return ima.icon('DirOpenIcon')

    def get_plugin_actions(self):
        """Setup actions"""
        return (None, None)

    def register_plugin(self):
        """Register plugin in TRex's main window"""
        self.redirect_stdio.connect(self.main.redirect_internalshell_stdio)
        self.main.console.shell.refresh.connect(self.refresh_plugin)
        iconsize = 24
        self.setIconSize(QSize(iconsize, iconsize))
        self.main.addToolBar(self)

    def refresh_plugin(self):
        """Refresh widget"""
        curdir = getcwd()
        self.pathedit.add_text(curdir)
        self.save_wdhistory()
        self.set_previous_enabled.emit(self.histindex is not None
                                       and self.histindex > 0)
        self.set_next_enabled.emit(self.histindex is not None and \
                                   self.histindex < len(self.history)-1)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        pass

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    #------ Public API ---------------------------------------------------------
    def load_wdhistory(self, workdir=None):
        """Load history from a text file in user home directory"""
        if osp.isfile(self.LOG_PATH):
            wdhistory, _ = encoding.readlines(self.LOG_PATH)
            wdhistory = [name for name in wdhistory if os.path.isdir(name)]
        else:
            if workdir is None:
                workdir = get_home_dir()
            wdhistory = [workdir]
        return wdhistory

    def save_wdhistory(self):
        """Save history to a text file in user home directory"""
        text = [ to_text_string( self.pathedit.itemText(index) ) \
                 for index in range(self.pathedit.count()) ]
        encoding.writelines(text, self.LOG_PATH)

    @Slot()
    def select_directory(self):
        """Select directory"""
        self.redirect_stdio.emit(False)
        directory = getexistingdirectory(self.main, _("Select directory"),
                                         getcwd())
        if directory:
            self.chdir(directory)
        self.redirect_stdio.emit(True)

    @Slot()
    def previous_directory(self):
        """Back to previous directory"""
        self.histindex -= 1
        self.chdir(directory='', browsing_history=True)

    @Slot()
    def next_directory(self):
        """Return to next directory"""
        self.histindex += 1
        self.chdir(directory='', browsing_history=True)

    @Slot()
    def parent_directory(self):
        """Change working directory to parent directory"""
        self.chdir(os.path.join(getcwd(), os.path.pardir))

    @Slot(str)
    @Slot(str, bool)
    @Slot(str, bool, bool)
    def chdir(self, directory, browsing_history=False, refresh_explorer=True):
        """Set directory as working directory"""
        if directory:
            directory = osp.abspath(to_text_string(directory))

        # Working directory history management
        if browsing_history:
            directory = self.history[self.histindex]
        elif directory in self.history:
            self.histindex = self.history.index(directory)
        else:
            if self.histindex is None:
                self.history = []
            else:
                self.history = self.history[:self.histindex + 1]
            self.history.append(directory)
            self.histindex = len(self.history) - 1

        # Changing working directory
        os.chdir(to_text_string(directory))
        self.refresh_plugin()
        if refresh_explorer:
            self.set_explorer_cwd.emit(directory)
            self.set_as_current_console_wd()
        self.refresh_findinfiles.emit()

    def set_as_current_console_wd(self):
        """Set as current console working directory"""
        self.set_current_console_wd.emit(getcwd())
Exemple #9
0
class OnlineHelp(PydocBrowser, TRexPluginMixin):
    """
    Online Help Plugin
    """
    sig_option_changed = Signal(str, object)
    CONF_SECTION = 'onlinehelp'
    LOG_PATH = get_conf_path(CONF_SECTION)

    def __init__(self, parent):
        self.main = parent
        PydocBrowser.__init__(self, parent)
        TRexPluginMixin.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

        self.register_widget_shortcuts(self.find_widget)

        self.webview.set_zoom_factor(self.get_option('zoom_factor'))
        self.url_combo.setMaxCount(self.get_option('max_history_entries'))
        self.url_combo.addItems(self.load_history())

    #------ Public API ---------------------------------------------------------
    def load_history(self, obj=None):
        """Load history from a text file in user home directory"""
        if osp.isfile(self.LOG_PATH):
            history = [
                line.replace('\n', '')
                for line in open(self.LOG_PATH, 'r').readlines()
            ]
        else:
            history = []
        return history

    def save_history(self):
        """Save history to a text file in user home directory"""
        open(self.LOG_PATH, 'w').write("\n".join( \
                [to_text_string(self.url_combo.itemText(index))
                 for index in range(self.url_combo.count())] ))

    #------ TRexPluginMixin API ---------------------------------------------
    def visibility_changed(self, enable):
        """DockWidget visibility has changed"""
        TRexPluginMixin.visibility_changed(self, enable)
        if enable and not self.is_server_running():
            self.initialize()

    #------ TRexPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Online help')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        self.url_combo.lineEdit().selectAll()
        return self.url_combo

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        self.save_history()
        self.set_option('zoom_factor', self.webview.get_zoom_factor())
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def register_plugin(self):
        """Register plugin in TRex's main window"""
        self.main.add_dockwidget(self)
Exemple #10
0
class Help(TRexPluginWidget):
    """
    Docstrings viewer widget
    """
    CONF_SECTION = 'help'
    CONFIGWIDGET_CLASS = HelpConfigPage
    LOG_PATH = get_conf_path(CONF_SECTION)
    FONT_SIZE_DELTA = DEFAULT_SMALL_DELTA

    # Signals
    focus_changed = Signal()

    def __init__(self, parent):
        if PYQT5:
            TRexPluginWidget.__init__(self, parent, main=parent)
        else:
            TRexPluginWidget.__init__(self, parent)

        self.internal_shell = None

        # Initialize plugin
        self.initialize_plugin()

        self.no_doc_string = _("No documentation available")

        self._last_console_cb = None
        self._last_editor_cb = None

        self.plain_text = PlainText(self)
        self.rich_text = RichText(self)

        color_scheme = self.get_color_scheme()
        self.set_plain_text_font(self.get_plugin_font(), color_scheme)
        self.plain_text.editor.toggle_wrap_mode(self.get_option('wrap'))

        # Add entries to read-only editor context-menu
        self.wrap_action = create_action(self,
                                         _("Wrap lines"),
                                         toggled=self.toggle_wrap_mode)
        self.wrap_action.setChecked(self.get_option('wrap'))
        self.plain_text.editor.readonly_menu.addSeparator()
        add_actions(self.plain_text.editor.readonly_menu, (self.wrap_action, ))

        self.set_rich_text_font(self.get_plugin_font('rich_text'))

        self.shell = None

        # locked = disable link with Console
        self.locked = False
        self._last_texts = [None, None]
        self._last_editor_doc = None

        # Object name
        layout_edit = QHBoxLayout()
        layout_edit.setContentsMargins(0, 0, 0, 0)
        txt = _("Source")
        if sys.platform == 'darwin':
            source_label = QLabel("  " + txt)
        else:
            source_label = QLabel(txt)
        layout_edit.addWidget(source_label)
        self.source_combo = QComboBox(self)
        self.source_combo.addItems([_("Console"), _("Editor")])
        self.source_combo.currentIndexChanged.connect(self.source_changed)
        if (not programs.is_module_installed('rope')
                and not programs.is_module_installed('jedi', '>=0.8.1')):
            self.source_combo.hide()
            source_label.hide()
        layout_edit.addWidget(self.source_combo)
        layout_edit.addSpacing(10)
        layout_edit.addWidget(QLabel(_("Object")))
        self.combo = ObjectComboBox(self)
        layout_edit.addWidget(self.combo)
        self.object_edit = QLineEdit(self)
        self.object_edit.setReadOnly(True)
        layout_edit.addWidget(self.object_edit)
        self.combo.setMaxCount(self.get_option('max_history_entries'))
        self.combo.addItems(self.load_history())
        self.combo.setItemText(0, '')
        self.combo.valid.connect(lambda valid: self.force_refresh())

        # Plain text docstring option
        self.docstring = True
        self.rich_help = self.get_option('rich_mode', True)
        self.plain_text_action = create_action(self,
                                               _("Plain Text"),
                                               toggled=self.toggle_plain_text)

        # Source code option
        self.show_source_action = create_action(
            self, _("Show Source"), toggled=self.toggle_show_source)

        # Rich text option
        self.rich_text_action = create_action(self,
                                              _("Rich Text"),
                                              toggled=self.toggle_rich_text)

        # Add the help actions to an exclusive QActionGroup
        help_actions = QActionGroup(self)
        help_actions.setExclusive(True)
        help_actions.addAction(self.plain_text_action)
        help_actions.addAction(self.rich_text_action)

        # Automatic import option
        self.auto_import_action = create_action(
            self, _("Automatic import"), toggled=self.toggle_auto_import)
        auto_import_state = self.get_option('automatic_import')
        self.auto_import_action.setChecked(auto_import_state)

        # Lock checkbox
        self.locked_button = create_toolbutton(self,
                                               triggered=self.toggle_locked)
        layout_edit.addWidget(self.locked_button)
        self._update_lock_icon()

        # Option menu
        options_button = create_toolbutton(self,
                                           text=_('Options'),
                                           icon=ima.icon('tooloptions'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, [
            self.rich_text_action, self.plain_text_action,
            self.show_source_action, None, self.auto_import_action
        ])
        options_button.setMenu(menu)
        layout_edit.addWidget(options_button)

        if self.rich_help:
            self.switch_to_rich_text()
        else:
            self.switch_to_plain_text()
        self.plain_text_action.setChecked(not self.rich_help)
        self.rich_text_action.setChecked(self.rich_help)
        self.source_changed()

        # Main layout
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addLayout(layout_edit)
        layout.addWidget(self.plain_text)
        layout.addWidget(self.rich_text)
        self.setLayout(layout)

        # Add worker thread for handling rich text rendering
        self._sphinx_thread = SphinxThread(
            html_text_no_doc=warning(self.no_doc_string))
        self._sphinx_thread.html_ready.connect(
            self._on_sphinx_thread_html_ready)
        self._sphinx_thread.error_msg.connect(self._on_sphinx_thread_error_msg)

        # Handle internal and external links
        view = self.rich_text.webview
        if not WEBENGINE:
            view.page().setLinkDelegationPolicy(
                QWebEnginePage.DelegateAllLinks)
        view.linkClicked.connect(self.handle_link_clicks)

        self._starting_up = True

    #------ TRexPluginWidget API ---------------------------------------------
    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.main.tabify_plugins(self.main.variableexplorer, self)

    def get_plugin_title(self):
        """Return widget title"""
        return _('Help')

    def get_plugin_icon(self):
        """Return widget icon"""
        return ima.icon('help')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        self.combo.lineEdit().selectAll()
        return self.combo

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def register_plugin(self):
        """Register plugin in TRex's main window"""
        self.focus_changed.connect(self.main.plugin_focus_changed)
        self.main.add_dockwidget(self)
        self.main.console.set_help(self)
        self.internal_shell = self.main.console.shell

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        if self._starting_up:
            self._starting_up = False
            self.switch_to_rich_text()
            self.show_intro_message()

    def update_font(self):
        """Update font from Preferences"""
        color_scheme = self.get_color_scheme()
        font = self.get_plugin_font()
        rich_font = self.get_plugin_font(rich_text=True)

        self.set_plain_text_font(font, color_scheme=color_scheme)
        self.set_rich_text_font(rich_font)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        color_scheme_n = 'color_scheme_name'
        color_scheme_o = self.get_color_scheme()
        connect_n = 'connect_to_oi'
        wrap_n = 'wrap'
        wrap_o = self.get_option(wrap_n)
        self.wrap_action.setChecked(wrap_o)
        math_n = 'math'
        math_o = self.get_option(math_n)

        if color_scheme_n in options:
            self.set_plain_text_color_scheme(color_scheme_o)
        if wrap_n in options:
            self.toggle_wrap_mode(wrap_o)
        if math_n in options:
            self.toggle_math_mode(math_o)

        # To make auto-connection changes take place instantly
        self.main.editor.apply_plugin_settings(options=[connect_n])
        self.main.extconsole.apply_plugin_settings(options=[connect_n])
        if self.main.ipyconsole is not None:
            self.main.ipyconsole.apply_plugin_settings(options=[connect_n])

    #------ Public API (related to Help's source) -------------------------
    def source_is_console(self):
        """Return True if source is Console"""
        return self.source_combo.currentIndex() == 0

    def switch_to_editor_source(self):
        self.source_combo.setCurrentIndex(1)

    def switch_to_console_source(self):
        self.source_combo.setCurrentIndex(0)

    def source_changed(self, index=None):
        if self.source_is_console():
            # Console
            self.combo.show()
            self.object_edit.hide()
            self.show_source_action.setEnabled(True)
            self.auto_import_action.setEnabled(True)
        else:
            # Editor
            self.combo.hide()
            self.object_edit.show()
            self.show_source_action.setDisabled(True)
            self.auto_import_action.setDisabled(True)
        self.restore_text()

    def save_text(self, callback):
        if self.source_is_console():
            self._last_console_cb = callback
        else:
            self._last_editor_cb = callback

    def restore_text(self):
        if self.source_is_console():
            cb = self._last_console_cb
        else:
            cb = self._last_editor_cb
        if cb is None:
            if self.is_plain_text_mode():
                self.plain_text.clear()
            else:
                self.rich_text.clear()
        else:
            func = cb[0]
            args = cb[1:]
            func(*args)
            if get_meth_class_inst(func) is self.rich_text:
                self.switch_to_rich_text()
            else:
                self.switch_to_plain_text()

    #------ Public API (related to rich/plain text widgets) --------------------
    @property
    def find_widget(self):
        if self.plain_text.isVisible():
            return self.plain_text.find_widget
        else:
            return self.rich_text.find_widget

    def set_rich_text_font(self, font):
        """Set rich text mode font"""
        self.rich_text.set_font(font, fixed_font=self.get_plugin_font())

    def set_plain_text_font(self, font, color_scheme=None):
        """Set plain text mode font"""
        self.plain_text.set_font(font, color_scheme=color_scheme)

    def set_plain_text_color_scheme(self, color_scheme):
        """Set plain text mode color scheme"""
        self.plain_text.set_color_scheme(color_scheme)

    @Slot(bool)
    def toggle_wrap_mode(self, checked):
        """Toggle wrap mode"""
        self.plain_text.editor.toggle_wrap_mode(checked)
        self.set_option('wrap', checked)

    def toggle_math_mode(self, checked):
        """Toggle math mode"""
        self.set_option('math', checked)

    def is_plain_text_mode(self):
        """Return True if plain text mode is active"""
        return self.plain_text.isVisible()

    def is_rich_text_mode(self):
        """Return True if rich text mode is active"""
        return self.rich_text.isVisible()

    def switch_to_plain_text(self):
        """Switch to plain text mode"""
        self.rich_help = False
        self.plain_text.show()
        self.rich_text.hide()
        self.plain_text_action.setChecked(True)

    def switch_to_rich_text(self):
        """Switch to rich text mode"""
        self.rich_help = True
        self.plain_text.hide()
        self.rich_text.show()
        self.rich_text_action.setChecked(True)
        self.show_source_action.setChecked(False)

    def set_plain_text(self, text, is_code):
        """Set plain text docs"""

        # text is coming from utils.dochelpers.getdoc
        if type(text) is dict:
            name = text['name']
            if name:
                rst_title = ''.join([
                    '=' * len(name), '\n', name, '\n', '=' * len(name), '\n\n'
                ])
            else:
                rst_title = ''

            if text['argspec']:
                definition = ''.join(
                    ['Definition: ', name, text['argspec'], '\n'])
            else:
                definition = ''

            if text['note']:
                note = ''.join(['Type: ', text['note'], '\n\n----\n\n'])
            else:
                note = ''

            full_text = ''.join(
                [rst_title, definition, note, text['docstring']])
        else:
            full_text = text

        self.plain_text.set_text(full_text, is_code)
        self.save_text([self.plain_text.set_text, full_text, is_code])

    def set_rich_text_html(self, html_text, base_url):
        """Set rich text"""
        self.rich_text.set_html(html_text, base_url)
        self.save_text([self.rich_text.set_html, html_text, base_url])

    def show_intro_message(self):
        intro_message = _("Here you can get help of any object by pressing "
                          "%s in front of it, either on the Editor or the "
                          "Console.%s"
                          "Help can also be shown automatically after writing "
                          "a left parenthesis next to an object. You can "
                          "activate this behavior in %s.")
        prefs = _("Preferences > Help")
        if sys.platform == 'darwin':
            shortcut = "Cmd+I"
        else:
            shortcut = "Ctrl+I"

        if self.is_rich_text_mode():
            title = _("Usage")
            tutorial_message = _("New to TRex? Read our")
            tutorial = _("tutorial")
            intro_message = intro_message % (
                "<b>" + shortcut + "</b>", "<br><br>", "<i>" + prefs + "</i>")
            self.set_rich_text_html(
                usage(title, intro_message, tutorial_message, tutorial),
                QUrl.fromLocalFile(CSS_PATH))
        else:
            install_sphinx = "\n\n%s" % _("Please consider installing Sphinx "
                                          "to get documentation rendered in "
                                          "rich text.")
            intro_message = intro_message % (shortcut, "\n\n", prefs)
            intro_message += install_sphinx
            self.set_plain_text(intro_message, is_code=False)

    def show_rich_text(self, text, collapse=False, img_path=''):
        """Show text in rich mode"""
        self.visibility_changed(True)
        self.raise_()
        self.switch_to_rich_text()
        context = generate_context(collapse=collapse, img_path=img_path)
        self.render_sphinx_doc(text, context)

    def show_plain_text(self, text):
        """Show text in plain mode"""
        self.visibility_changed(True)
        self.raise_()
        self.switch_to_plain_text()
        self.set_plain_text(text, is_code=False)

    @Slot()
    def show_tutorial(self):
        tutorial_path = get_module_source_path('trex.utils.help')
        tutorial = osp.join(tutorial_path, 'tutorial.rst')
        text = open(tutorial).read()
        self.show_rich_text(text, collapse=True)

    def handle_link_clicks(self, url):
        url = to_text_string(url.toString())
        if url == "spy://tutorial":
            self.show_tutorial()
        elif url.startswith('http'):
            programs.start_file(url)
        else:
            self.rich_text.webview.load(QUrl(url))

    #------ Public API ---------------------------------------------------------
    def force_refresh(self):
        if self.source_is_console():
            self.set_object_text(None, force_refresh=True)
        elif self._last_editor_doc is not None:
            self.set_editor_doc(self._last_editor_doc, force_refresh=True)

    def set_object_text(self, text, force_refresh=False, ignore_unknown=False):
        """Set object analyzed by Help"""
        if (self.locked and not force_refresh):
            return
        self.switch_to_console_source()

        add_to_combo = True
        if text is None:
            text = to_text_string(self.combo.currentText())
            add_to_combo = False

        found = self.show_help(text, ignore_unknown=ignore_unknown)
        if ignore_unknown and not found:
            return

        if add_to_combo:
            self.combo.add_text(text)
        if found:
            self.save_history()

        if self.dockwidget is not None:
            self.dockwidget.blockSignals(True)
        self.__eventually_raise_help(text, force=force_refresh)
        if self.dockwidget is not None:
            self.dockwidget.blockSignals(False)

    def set_editor_doc(self, doc, force_refresh=False):
        """
        Use the help plugin to show docstring dictionary computed
        with introspection plugin from the Editor plugin
        """
        if (self.locked and not force_refresh):
            return
        self.switch_to_editor_source()
        self._last_editor_doc = doc
        self.object_edit.setText(doc['obj_text'])

        if self.rich_help:
            self.render_sphinx_doc(doc)
        else:
            self.set_plain_text(doc, is_code=False)

        if self.dockwidget is not None:
            self.dockwidget.blockSignals(True)
        self.__eventually_raise_help(doc['docstring'], force=force_refresh)
        if self.dockwidget is not None:
            self.dockwidget.blockSignals(False)

    def __eventually_raise_help(self, text, force=False):
        index = self.source_combo.currentIndex()
        if hasattr(self.main, 'tabifiedDockWidgets'):
            # 'QMainWindow.tabifiedDockWidgets' was introduced in PyQt 4.5
            if self.dockwidget and (force or self.dockwidget.isVisible()) \
               and not self.ismaximized \
               and (force or text != self._last_texts[index]):
                dockwidgets = self.main.tabifiedDockWidgets(self.dockwidget)
                if self.main.console.dockwidget not in dockwidgets and \
                   (hasattr(self.main, 'extconsole') and \
                    self.main.extconsole.dockwidget not in dockwidgets):
                    self.dockwidget.show()
                    self.dockwidget.raise_()
        self._last_texts[index] = text

    def load_history(self, obj=None):
        """Load history from a text file in user home directory"""
        if osp.isfile(self.LOG_PATH):
            history = [
                line.replace('\n', '')
                for line in open(self.LOG_PATH, 'r').readlines()
            ]
        else:
            history = []
        return history

    def save_history(self):
        """Save history to a text file in user home directory"""
        open(self.LOG_PATH, 'w').write("\n".join( \
                [to_text_string(self.combo.itemText(index))
                 for index in range(self.combo.count())] ))

    @Slot(bool)
    def toggle_plain_text(self, checked):
        """Toggle plain text docstring"""
        if checked:
            self.docstring = checked
            self.switch_to_plain_text()
            self.force_refresh()
        self.set_option('rich_mode', not checked)

    @Slot(bool)
    def toggle_show_source(self, checked):
        """Toggle show source code"""
        if checked:
            self.switch_to_plain_text()
        self.docstring = not checked
        self.force_refresh()
        self.set_option('rich_mode', not checked)

    @Slot(bool)
    def toggle_rich_text(self, checked):
        """Toggle between sphinxified docstrings or plain ones"""
        if checked:
            self.docstring = not checked
            self.switch_to_rich_text()
        self.set_option('rich_mode', checked)

    @Slot(bool)
    def toggle_auto_import(self, checked):
        """Toggle automatic import feature"""
        self.combo.validate_current_text()
        self.set_option('automatic_import', checked)
        self.force_refresh()

    @Slot()
    def toggle_locked(self):
        """
        Toggle locked state
        locked = disable link with Console
        """
        self.locked = not self.locked
        self._update_lock_icon()

    def _update_lock_icon(self):
        """Update locked state icon"""
        icon = ima.icon('lock') if self.locked else ima.icon('lock_open')
        self.locked_button.setIcon(icon)
        tip = _("Unlock") if self.locked else _("Lock")
        self.locked_button.setToolTip(tip)

    def set_shell(self, shell):
        """Bind to shell"""
        self.shell = shell

    def get_shell(self):
        """
        Return shell which is currently bound to Help,
        or another running shell if it has been terminated
        """
        if not hasattr(self.shell, 'get_doc') or not self.shell.is_running():
            self.shell = None
            if self.main.ipyconsole is not None:
                shell = self.main.ipyconsole.get_current_shellwidget()
                if shell is not None and shell.kernel_client is not None:
                    self.shell = shell
            if self.shell is None and self.main.extconsole is not None:
                self.shell = self.main.extconsole.get_running_python_shell()
            if self.shell is None:
                self.shell = self.internal_shell
        return self.shell

    def render_sphinx_doc(self, doc, context=None):
        """Transform doc string dictionary to HTML and show it"""
        # Math rendering option could have changed
        fname = self.parent().parent().editor.get_current_filename()
        dname = osp.dirname(fname)
        self._sphinx_thread.render(doc, context, self.get_option('math'),
                                   dname)

    def _on_sphinx_thread_html_ready(self, html_text):
        """Set our sphinx documentation based on thread result"""
        self._sphinx_thread.wait()
        self.set_rich_text_html(html_text, QUrl.fromLocalFile(CSS_PATH))

    def _on_sphinx_thread_error_msg(self, error_msg):
        """ Display error message on Sphinx rich text failure"""
        self._sphinx_thread.wait()
        self.plain_text_action.setChecked(True)
        sphinx_ver = programs.get_module_version('sphinx')
        QMessageBox.critical(
            self, _('Help'),
            _("The following error occured when calling "
              "<b>Sphinx %s</b>. <br>Incompatible Sphinx "
              "version or doc string decoding failed."
              "<br><br>Error message:<br>%s") % (sphinx_ver, error_msg))

    def show_help(self, obj_text, ignore_unknown=False):
        """Show help"""
        shell = self.get_shell()
        if shell is None:
            return
        obj_text = to_text_string(obj_text)

        if not shell.is_defined(obj_text):
            if self.get_option('automatic_import') and \
               self.internal_shell.is_defined(obj_text, force_import=True):
                shell = self.internal_shell
            else:
                shell = None
                doc = None
                source_text = None

        if shell is not None:
            doc = shell.get_doc(obj_text)
            source_text = shell.get_source(obj_text)

        is_code = False

        if self.rich_help:
            self.render_sphinx_doc(doc)
            return doc is not None
        elif self.docstring:
            hlp_text = doc
            if hlp_text is None:
                hlp_text = source_text
                if hlp_text is None:
                    hlp_text = self.no_doc_string
                    if ignore_unknown:
                        return False
        else:
            hlp_text = source_text
            if hlp_text is None:
                hlp_text = doc
                if hlp_text is None:
                    hlp_text = _("No source code available.")
                    if ignore_unknown:
                        return False
            else:
                is_code = True
        self.set_plain_text(hlp_text, is_code=is_code)
        return True
Exemple #11
0
# Standard library imports
import errno
import os
import socket
import threading

# Third party imports
from qtpy.QtCore import QThread, Signal

# Local imports
from trex.config.base import get_conf_path, DEBUG
from trex.utils.debug import log_last_error
from trex.utils.bsdsocket import read_packet, write_packet
from trex.utils.misc import select_port

LOG_FILENAME = get_conf_path('introspection.log')

DEBUG_INTROSPECTION = DEBUG >= 2

if DEBUG_INTROSPECTION:
    import logging
    logging.basicConfig(filename=get_conf_path('introspection_debug.log'),
                        level=logging.DEBUG)

TREX_PORT = 20128


class IntrospectionServer(threading.Thread):
    """Introspection server"""
    def __init__(self):
        threading.Thread.__init__(self)
Exemple #12
0
    def __init__(self,
                 parent=None,
                 fname=None,
                 wdir=None,
                 history_filename=None,
                 show_icontext=True,
                 light_background=True,
                 menu_actions=None,
                 show_buttons_inside=True,
                 show_elapsed_time=True):
        QWidget.__init__(self, parent)

        self.menu_actions = menu_actions
        self.write_lock = QMutex()
        self.buffer_lock = QMutex()
        self.buffer = []

        self.run_button = None
        self.kill_button = None
        self.options_button = None
        self.icontext_action = None

        self.show_elapsed_time = show_elapsed_time

        self.fname = fname
        if wdir is None:
            wdir = osp.dirname(osp.abspath(fname))
        self.wdir = wdir if osp.isdir(wdir) else None
        self.arguments = ""

        self.shell = self.SHELL_CLASS(parent, get_conf_path(history_filename))
        self.shell.set_light_background(light_background)
        self.shell.execute.connect(self.send_to_process)
        self.shell.sig_keyboard_interrupt.connect(self.keyboard_interrupt)
        # Redirecting some SIGNALs:
        self.shell.redirect_stdio.connect(
            lambda state: self.redirect_stdio.emit(state))

        self.state_label = None
        self.time_label = None

        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        if show_buttons_inside:
            self.state_label = QLabel()
            hlayout = QHBoxLayout()
            hlayout.addWidget(self.state_label)
            hlayout.addStretch(0)
            hlayout.addWidget(self.create_time_label())
            hlayout.addStretch(0)
            for button in toolbar_buttons:
                hlayout.addWidget(button)
            vlayout.addLayout(hlayout)
        else:
            vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.get_shell_widget())
        self.setLayout(vlayout)
        self.resize(640, 480)
        if parent is None:
            self.setWindowIcon(self.get_icon())
            self.setWindowTitle(_("Console"))

        self.t0 = None
        self.timer = QTimer(self)

        self.process = None

        self.is_closing = False

        if show_buttons_inside:
            self.update_time_label_visibility()
Exemple #13
0
def main():
    """
    Start TRex application.

    If single instance mode is turned on (default behavior) and an instance of
    TRex is already running, this will just parse and send command line
    options to the application.
    """
    # Parse command line options
    options, args = get_options()

    # Store variable to be used in self.restart (restart trex instance)
    os.environ['TREX_ARGS'] = str(sys.argv[1:])

    if CONF.get('main', 'single_instance') and not options.new_instance \
      and not options.reset_config_files and not running_in_mac_app():
        # Minimal delay (0.1-0.2 secs) to avoid that several
        # instances started at the same time step in their
        # own foots while trying to create the lock file
        time.sleep(random.randrange(1000, 2000, 90) / 10000.)

        # Lock file creation
        lock_file = get_conf_path('trex.lock')
        lock = lockfile.FilesystemLock(lock_file)

        # Try to lock trex.lock. If it's *possible* to do it, then
        # there is no previous instance running and we can start a
        # new one. If *not*, then there is an instance already
        # running, which is locking that file
        try:
            lock_created = lock.lock()
        except:
            # If locking fails because of errors in the lockfile
            # module, try to remove a possibly stale trex.lock.
            # This is reported to solve all problems with
            # lockfile (See issue 2363)
            try:
                if os.name == 'nt':
                    if osp.isdir(lock_file):
                        import shutil
                        shutil.rmtree(lock_file, ignore_errors=True)
                else:
                    if osp.islink(lock_file):
                        os.unlink(lock_file)
            except:
                pass

            # Then start TRex as usual and *don't* continue
            # executing this script because it doesn't make
            # sense
            from trex.app import mainwindow
            mainwindow.main()
            return

        if lock_created:
            # Start a new instance
            from trex.app import mainwindow
            mainwindow.main()
        else:
            # Pass args to TRex or print an informative
            # message
            if args:
                send_args_to_trex(args)
            else:
                print("TRex is already running. If you want to open a new \n"
                      "instance, please pass to it the --new-instance option")
    else:
        from trex.app import mainwindow
        mainwindow.main()
Exemple #14
0
    def __init__(self,
                 plugin,
                 name,
                 history_filename,
                 config_options,
                 additional_options,
                 interpreter_versions,
                 connection_file=None,
                 hostname=None,
                 menu_actions=None,
                 slave=False,
                 external_kernel=False):
        super(ClientWidget, self).__init__(plugin)
        SaveHistoryMixin.__init__(self)

        # --- Init attrs
        self.name = name
        self.history_filename = get_conf_path(history_filename)
        self.connection_file = connection_file
        self.hostname = hostname
        self.menu_actions = menu_actions
        self.slave = slave

        # --- Other attrs
        self.options_button = None
        self.stop_button = None
        self.stop_icon = ima.icon('stop')
        self.history = []

        # --- Widgets
        self.shellwidget = ShellWidget(
            config=config_options,
            ipyclient=self,
            additional_options=additional_options,
            interpreter_versions=interpreter_versions,
            external_kernel=external_kernel,
            local_kernel=True)
        self.infowidget = WebView(self)
        self.set_infowidget_font()
        self.loading_page = self._create_loading_page()
        self._show_loading_page()

        # --- Layout
        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        hlayout = QHBoxLayout()
        for button in toolbar_buttons:
            hlayout.addWidget(button)
        vlayout.addLayout(hlayout)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.shellwidget)
        vlayout.addWidget(self.infowidget)
        self.setLayout(vlayout)

        # --- Exit function
        self.exit_callback = lambda: plugin.close_client(client=self)

        # --- Signals
        # As soon as some content is printed in the console, stop
        # our loading animation
        document = self.get_control().document()
        document.contentsChange.connect(self._hide_loading_page)
Exemple #15
0
import threading

# Local imports
from trex.config.base import get_conf_path, DEBUG
from trex.py3compat import getcwd, is_text_string, pickle, _thread
from trex.utils.misc import fix_reference_name
from trex.utils.debug import log_last_error
from trex.utils.dochelpers import (getargtxt, getdoc, getsource, getobjdir,
                                   isdefined)
from trex.utils.bsdsocket import (communicate, read_packet, write_packet,
                                  PACKET_NOT_RECEIVED, PICKLE_HIGHEST_PROTOCOL)
from trex.utils.introspection.module_completion import module_completion
from trex.widgets.variableexplorer.utils import (get_remote_data,
                                                 make_remote_view)

LOG_FILENAME = get_conf_path('monitor.log')
DEBUG_MONITOR = DEBUG >= 2
if DEBUG_MONITOR:
    import logging
    logging.basicConfig(filename=get_conf_path('monitor_debug.log'),
                        level=logging.DEBUG)


def monitor_save_globals(sock, settings, filename):
    """Save globals() to file"""
    return communicate(sock,
                       '__save_globals__()',
                       settings=[settings, filename])


def monitor_load_globals(sock, filename, ext):
Exemple #16
0
class ProfilerWidget(QWidget):
    """
    Profiler widget
    """
    DATAPATH = get_conf_path('profiler.results')
    VERSION = '0.0.1'
    redirect_stdio = Signal(bool)

    def __init__(self, parent, max_entries=100):
        QWidget.__init__(self, parent)

        self.setWindowTitle("Profiler")

        self.output = None
        self.error_output = None

        self._last_wdir = None
        self._last_args = None
        self._last_pythonpath = None

        self.filecombo = PythonModulesComboBox(self)

        self.start_button = create_toolbutton(self,
                                              icon=ima.icon('run'),
                                              text=_("Profile"),
                                              tip=_("Run profiler"),
                                              triggered=lambda: self.start(),
                                              text_beside_icon=True)
        self.stop_button = create_toolbutton(self,
                                             icon=ima.icon('stop'),
                                             text=_("Stop"),
                                             tip=_("Stop current profiling"),
                                             text_beside_icon=True)
        self.filecombo.valid.connect(self.start_button.setEnabled)
        #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data)
        # FIXME: The combobox emits this signal on almost any event
        #        triggering show_data() too early, too often.

        browse_button = create_toolbutton(self,
                                          icon=ima.icon('fileopen'),
                                          tip=_('Select Python script'),
                                          triggered=self.select_file)

        self.datelabel = QLabel()

        self.log_button = create_toolbutton(self,
                                            icon=ima.icon('log'),
                                            text=_("Output"),
                                            text_beside_icon=True,
                                            tip=_("Show program's output"),
                                            triggered=self.show_log)

        self.datatree = ProfilerDataTree(self)

        self.collapse_button = create_toolbutton(
            self,
            icon=ima.icon('collapse'),
            triggered=lambda dD: self.datatree.change_view(-1),
            tip=_('Collapse one level up'))
        self.expand_button = create_toolbutton(
            self,
            icon=ima.icon('expand'),
            triggered=lambda dD: self.datatree.change_view(1),
            tip=_('Expand one level down'))

        self.save_button = create_toolbutton(self,
                                             text_beside_icon=True,
                                             text=_("Save data"),
                                             icon=ima.icon('filesave'),
                                             triggered=self.save_data,
                                             tip=_('Save profiling data'))
        self.load_button = create_toolbutton(
            self,
            text_beside_icon=True,
            text=_("Load data"),
            icon=ima.icon('fileimport'),
            triggered=self.compare,
            tip=_('Load profiling data for comparison'))
        self.clear_button = create_toolbutton(self,
                                              text_beside_icon=True,
                                              text=_("Clear comparison"),
                                              icon=ima.icon('editdelete'),
                                              triggered=self.clear)

        hlayout1 = QHBoxLayout()
        hlayout1.addWidget(self.filecombo)
        hlayout1.addWidget(browse_button)
        hlayout1.addWidget(self.start_button)
        hlayout1.addWidget(self.stop_button)

        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(self.collapse_button)
        hlayout2.addWidget(self.expand_button)
        hlayout2.addStretch()
        hlayout2.addWidget(self.datelabel)
        hlayout2.addStretch()
        hlayout2.addWidget(self.log_button)
        hlayout2.addWidget(self.save_button)
        hlayout2.addWidget(self.load_button)
        hlayout2.addWidget(self.clear_button)

        layout = QVBoxLayout()
        layout.addLayout(hlayout1)
        layout.addLayout(hlayout2)
        layout.addWidget(self.datatree)
        self.setLayout(layout)

        self.process = None
        self.set_running_state(False)
        self.start_button.setEnabled(False)
        self.clear_button.setEnabled(False)

        if not is_profiler_installed():
            # This should happen only on certain GNU/Linux distributions
            # or when this a home-made Python build because the Python
            # profilers are included in the Python standard library
            for widget in (self.datatree, self.filecombo, self.start_button,
                           self.stop_button):
                widget.setDisabled(True)
            url = 'http://docs.python.org/library/profile.html'
            text = '%s <a href=%s>%s</a>' % (_('Please install'), url,
                                             _("the Python profiler modules"))
            self.datelabel.setText(text)
        else:
            pass  # self.show_data()

    def save_data(self):
        """Save data"""
        title = _("Save profiler result")
        filename, _selfilter = getsavefilename(
            self, title, getcwd(),
            _("Profiler result") + " (*.Result)")
        if filename:
            self.datatree.save_data(filename)

    def compare(self):
        filename, _selfilter = getopenfilename(
            self, _("Select script to compare"), getcwd(),
            _("Profiler result") + " (*.Result)")
        if filename:
            self.datatree.compare(filename)
            self.show_data()
            self.clear_button.setEnabled(True)

    def clear(self):
        self.datatree.compare(None)
        self.datatree.hide_diff_cols(True)
        self.show_data()
        self.clear_button.setEnabled(False)

    def analyze(self, filename, wdir=None, args=None, pythonpath=None):
        if not is_profiler_installed():
            return
        self.kill_if_running()
        #index, _data = self.get_data(filename)
        index = None  # FIXME: storing data is not implemented yet
        if index is None:
            self.filecombo.addItem(filename)
            self.filecombo.setCurrentIndex(self.filecombo.count() - 1)
        else:
            self.filecombo.setCurrentIndex(self.filecombo.findText(filename))
        self.filecombo.selected()
        if self.filecombo.is_valid():
            if wdir is None:
                wdir = osp.dirname(filename)
            self.start(wdir, args, pythonpath)

    def select_file(self):
        self.redirect_stdio.emit(False)
        filename, _selfilter = getopenfilename(
            self, _("Select Python script"), getcwd(),
            _("Python scripts") + " (*.py ; *.pyw)")
        self.redirect_stdio.emit(True)
        if filename:
            self.analyze(filename)

    def show_log(self):
        if self.output:
            TextEditor(self.output,
                       title=_("Profiler output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    def show_errorlog(self):
        if self.error_output:
            TextEditor(self.error_output,
                       title=_("Profiler output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    def start(self, wdir=None, args=None, pythonpath=None):
        filename = to_text_string(self.filecombo.currentText())
        if wdir is None:
            wdir = self._last_wdir
            if wdir is None:
                wdir = osp.basename(filename)
        if args is None:
            args = self._last_args
            if args is None:
                args = []
        if pythonpath is None:
            pythonpath = self._last_pythonpath
        self._last_wdir = wdir
        self._last_args = args
        self._last_pythonpath = pythonpath

        self.datelabel.setText(_('Profiling, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(wdir)
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(
            lambda: self.read_output(error=True))
        self.process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self.finished(ec, es))
        self.stop_button.clicked.connect(self.process.kill)

        if pythonpath is not None:
            env = [
                to_text_string(_pth)
                for _pth in self.process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, separator, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)
            self.process.setProcessEnvironment(processEnvironment)

        self.output = ''
        self.error_output = ''

        p_args = ['-m', 'cProfile', '-o', self.DATAPATH]
        if os.name == 'nt':
            # On Windows, one has to replace backslashes by slashes to avoid
            # confusion with escape characters (otherwise, for example, '\t'
            # will be interpreted as a tabulation):
            p_args.append(osp.normpath(filename).replace(os.sep, '/'))
        else:
            p_args.append(filename)
        if args:
            p_args.extend(shell_split(args))
        executable = sys.executable
        if executable.endswith("trex.exe"):
            # py2exe distribution
            executable = "python.exe"
        self.process.start(executable, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))

    def set_running_state(self, state=True):
        self.start_button.setEnabled(not state)
        self.stop_button.setEnabled(state)

    def read_output(self, error=False):
        if error:
            self.process.setReadChannel(QProcess.StandardError)
        else:
            self.process.setReadChannel(QProcess.StandardOutput)
        qba = QByteArray()
        while self.process.bytesAvailable():
            if error:
                qba += self.process.readAllStandardError()
            else:
                qba += self.process.readAllStandardOutput()
        text = to_text_string(locale_codec.toUnicode(qba.data()))
        if error:
            self.error_output += text
        else:
            self.output += text

    def finished(self, exit_code, exit_status):
        self.set_running_state(False)
        self.show_errorlog()  # If errors occurred, show them.
        self.output = self.error_output + self.output
        # FIXME: figure out if show_data should be called here or
        #        as a signal from the combobox
        self.show_data(justanalyzed=True)

    def kill_if_running(self):
        if self.process is not None:
            if self.process.state() == QProcess.Running:
                self.process.kill()
                self.process.waitForFinished()

    def show_data(self, justanalyzed=False):
        if not justanalyzed:
            self.output = None
        self.log_button.setEnabled(self.output is not None \
                                   and len(self.output) > 0)
        self.kill_if_running()
        filename = to_text_string(self.filecombo.currentText())
        if not filename:
            return

        self.datelabel.setText(_('Sorting data, please wait...'))
        QApplication.processEvents()

        self.datatree.load_data(self.DATAPATH)
        self.datatree.show_tree()

        text_style = "<span style=\'color: #444444\'><b>%s </b></span>"
        date_text = text_style % time.strftime("%d %b %Y %H:%M",
                                               time.localtime())
        self.datelabel.setText(date_text)