예제 #1
0
    def __init__(self):
        QtWidgets.QToolButton.__init__(self)

        # Init
        self.setIconSize(QtCore.QSize(*self.SIZE))
        self.setStyleSheet(
            "QToolButton{ border:none; padding:0px; margin:0px; }")
        self.setIcon(self.getCrossIcon1())
예제 #2
0
 def onSearchFinish(self, hits):
     if hits == 0:
         return
     hits = self._searchEngine.hits(0, hits)
     if not hits:
         return
     url = self.find_best_page(hits)
     self._helpBrowser.setSource(QtCore.QUrl(url))
예제 #3
0
    def _insertSnippet(self, body):

        if isinstance(body, list):
            txt = ""
            for i, t in enumerate(body):
                txt = body[i] + "\n"
            body = txt

        body = body.replace("\t", "    ")
        body = body.replace("$0", "")

        # Find patern ${num : text}
        # TODO: nested pattern ${2: ${3:Exception} as ${4:e}}
        d = {}
        pattern = "\$\{\d.*?}"
        match = re.findall(pattern, body)
        for fnd in match:
            fnd_split = fnd.split(":")
            # key
            key = fnd_split[0]
            key = key.replace("{", "")
            body = body.replace(fnd, key)
            # value
            value = fnd_split[1]
            value = value.replace("}", "")
            # dict
            d[key] = value

        # In snippet replace keys with values
        for i in reversed(range(10)):
            try:
                i = "$" + str(i)
                body = body.replace(i, d[i])
            except:
                pass

        # Write in the editor
        editor = pyzo.editors.getCurrentEditor()
        # cursor
        cursor = editor.textCursor()
        # current position
        pos = cursor.position()
        # insert the snippet
        cursor.insertText(body)

        # Find target text in the editor
        text = editor.toPlainText()
        pattern = "$1"

        if pattern in d:
            # find text
            regex = QtCore.QRegExp(d[pattern])
            index = regex.indexIn(text, pos)
            # select text
            cursor.setPosition(index)
            cursor.movePosition(QtGui.QTextCursor.EndOfWord, 1)
            editor.setTextCursor(cursor)
예제 #4
0
    def __init__(self, parent, **kwds):
        super().__init__(parent, showLineNumbers=True, **kwds)

        # Init filename and name
        self._filename = ''
        self._name = '<TMP>'

        # View settings
        self.setShowWhitespace(pyzo.config.view.showWhitespace)
        #TODO: self.setViewWrapSymbols(view.showWrapSymbols)
        self.setShowLineEndings(pyzo.config.view.showLineEndings)
        self.setShowIndentationGuides(pyzo.config.view.showIndentationGuides)
        #
        self.setWrap(bool(pyzo.config.view.wrap))
        self.setHighlightCurrentLine(pyzo.config.view.highlightCurrentLine)
        self.setLongLineIndicatorPosition(pyzo.config.view.edgeColumn)
        self.setHighlightMatchingBracket(
            pyzo.config.view.highlightMatchingBracket)
        #TODO: self.setFolding( int(view.codeFolding)*5 )
        # bracematch is set in baseTextCtrl, since it also applies to shells
        # dito for zoom and tabWidth

        # Set line endings to default
        self.lineEndings = pyzo.config.settings.defaultLineEndings

        # Set encoding to default
        self.encoding = 'UTF-8'

        # Modification time to test file change
        self._modifyTime = 0

        self.modificationChanged.connect(self._onModificationChanged)

        # To see whether the doc has changed to update the parser.
        self.textChanged.connect(self._onModified)

        # This timer is used to hide the marker that shows which code is executed
        self._showRunCursorTimer = QtCore.QTimer()

        # Add context menu (the offset is to prevent accidental auto-clicking)
        self._menu = EditorContextMenu(self)
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(lambda p: self._menu.popup(
            self.mapToGlobal(p) + QtCore.QPoint(0, 3)))
예제 #5
0
    def __init__(self, *args, padding=(4, 4, 6, 6), preventEqualTexts=True):
        QtWidgets.QTabBar.__init__(self, *args)

        # Put tab widget in document mode
        self.setDocumentMode(True)

        # Widget needs to draw its background (otherwise Mac has a dark bg)
        self.setDrawBase(False)
        if sys.platform == 'darwin':
            self.setAutoFillBackground(True)

        # Set whether we want to prevent eliding for names that start the same.
        self._preventEqualTexts = preventEqualTexts

        # Allow moving tabs around
        self.setMovable(True)

        # Get padding
        if isinstance(padding, (int, float)):
            padding = padding, padding, padding, padding
        elif isinstance(padding, (tuple, list)):
            pass
        else:
            raise ValueError('Invalid value for padding.')

        # Set style sheet
        stylesheet = STYLESHEET
        stylesheet = stylesheet.replace('PADDING_TOP', str(padding[0]))
        stylesheet = stylesheet.replace('PADDING_BOTTOM', str(padding[1]))
        stylesheet = stylesheet.replace('PADDING_LEFT', str(padding[2]))
        stylesheet = stylesheet.replace('PADDING_RIGHT', str(padding[3]))
        self.setStyleSheet(stylesheet)

        # We do our own eliding
        self.setElideMode(QtCore.Qt.ElideNone)

        # Make tabs wider if there's plenty space?
        self.setExpanding(False)

        # If there's not enough space, use scroll buttons
        self.setUsesScrollButtons(True)

        # When a tab is removed, select previous
        self.setSelectionBehaviorOnRemove(self.SelectPreviousTab)

        # Init alignment parameters
        self._alignWidth = MIN_NAME_WIDTH  # Width in characters
        self._alignWidthIsReducing = False  # Whether in process of reducing

        # Create timer for aligning
        self._alignTimer = QtCore.QTimer(self)
        self._alignTimer.setInterval(10)
        self._alignTimer.setSingleShot(True)
        self._alignTimer.timeout.connect(self._alignRecursive)
예제 #6
0
    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)

        # Set style/theme
        try:
            theme = pyzo.themes[pyzo.config.settings.theme.lower()]["data"]
            self.setStyle(theme)
            # autocomplete popup theme
            if pyzo.config.view.get("autoComplete_withTheme", False):
                editor_text_theme = theme["editor.text"].split(",")
                popup_background = editor_text_theme[1].split(":")[1]
                popup_text = editor_text_theme[0].split(":")[1]
                autoComplete_theme = "color: {}; background-color:{};".format(
                    popup_text, popup_background)
                self.completer().popup().setStyleSheet(autoComplete_theme)
        except Exception as err:
            print("Could not load theme: " + str(err))

        # Set font and zooming
        self.setFont(pyzo.config.view.fontname)
        self.setZoom(pyzo.config.view.zoom)
        self.setShowWhitespace(pyzo.config.view.showWhitespace)
        self.setHighlightMatchingBracket(
            pyzo.config.view.highlightMatchingBracket)

        # Create timer for autocompletion delay
        self._delayTimer = QtCore.QTimer(self)
        self._delayTimer.setSingleShot(True)
        self._delayTimer.timeout.connect(self._introspectNow)

        # For buffering autocompletion and calltip info
        self._callTipBuffer_name = ""
        self._callTipBuffer_time = 0
        self._callTipBuffer_result = ""
        self._autoCompBuffer_name = ""
        self._autoCompBuffer_time = 0
        self._autoCompBuffer_result = []

        self.setAutoCompletionAcceptKeysFromStr(
            pyzo.config.settings.autoComplete_acceptKeys)

        self.completer().highlighted.connect(self.updateHelp)
        self.setIndentUsingSpaces(
            pyzo.config.settings.defaultIndentUsingSpaces)
        self.setIndentWidth(pyzo.config.settings.defaultIndentWidth)
        self.setAutocompletPopupSize(*pyzo.config.view.autoComplete_popupSize)
        self.setAutocompleteMinChars(
            pyzo.config.settings.autoComplete_minChars)
        self.setCancelCallback(self.restoreHelp)
예제 #7
0
def setLanguage(languageName):
    """setLanguage(languageName)
    Set the language for the app. Loads qt and pyzo translations.
    Returns the QLocale instance to pass to the main widget.
    """

    # Get locale
    locale = getLocale(languageName)

    # Get paths were language files are
    qtTransPath = str(
        QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)
    )
    pyzoTransPath = os.path.join(pyzo.pyzoDir, "resources", "translations")

    # Get possible names for language files
    # (because Qt's .tr files may not have the language component.)
    localeName1 = locale.name()
    localeName2 = localeName1.split("_")[0]

    # Uninstall translators
    if not hasattr(QtCore, "_translators"):
        QtCore._translators = []
    for trans in QtCore._translators:
        QtWidgets.QApplication.removeTranslator(trans)

    # The default language
    if localeName1 == "C":
        return locale

    # Set Qt translations
    # Note that the translator instances must be stored
    # Note that the load() method is very forgiving with the file name
    for what, where in [("qt", qtTransPath), ("pyzo", pyzoTransPath)]:
        trans = QtCore.QTranslator()
        # Try loading both names
        for localeName in [localeName1, localeName2]:
            success = trans.load(what + "_" + localeName + ".tr", where)
            if success:
                QtWidgets.QApplication.installTranslator(trans)
                QtCore._translators.append(trans)
                print("loading %s %s: ok" % (what, languageName))
                break
        else:
            print("loading %s %s: failed" % (what, languageName))

    # Done
    return locale
예제 #8
0
    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)

        # Set font and zooming
        self.setFont(pyzo.config.view.fontname)
        self.setZoom(pyzo.config.view.zoom)

        # Create timer for autocompletion delay
        self._delayTimer = QtCore.QTimer(self)
        self._delayTimer.setSingleShot(True)
        self._delayTimer.timeout.connect(self._introspectNow)

        # For buffering autocompletion and calltip info
        self._callTipBuffer_name = ''
        self._callTipBuffer_time = 0
        self._callTipBuffer_result = ''
        self._autoCompBuffer_name = ''
        self._autoCompBuffer_time = 0
        self._autoCompBuffer_result = []

        # The string with names given to SCI_AUTOCSHOW
        self._autoCompNameString = ''

        # Set autocomp accept key to default if necessary.
        # We force it to be string (see issue 134)
        if not isinstance(pyzo.config.settings.autoComplete_acceptKeys, str):
            pyzo.config.settings.autoComplete_acceptKeys = 'Tab'

        # Set autocomp accept keys
        qtKeys = []
        for key in pyzo.config.settings.autoComplete_acceptKeys.split(' '):
            if len(key) > 1:
                key = 'Key_' + key[0].upper() + key[1:].lower()
                qtkey = getattr(QtCore.Qt, key, None)
            else:
                qtkey = ord(key)
            if qtkey:
                qtKeys.append(qtkey)
        self.setAutoCompletionAcceptKeys(*qtKeys)

        self.completer().highlighted.connect(self.updateHelp)
        self.setIndentUsingSpaces(
            pyzo.config.settings.defaultIndentUsingSpaces)
        self.setIndentWidth(pyzo.config.settings.defaultIndentWidth)
        self.setAutocompletPopupSize(*pyzo.config.view.autoComplete_popupSize)
예제 #9
0
    def __init__(self, objectWithIcon):

        self._objectWithIcon = objectWithIcon

        # Motion properties
        self._index = 0
        self._level = 0
        self._count = 0  #  to count number of iters in level 1

        # Prepare blob pixmap
        self._blob = self._createBlobPixmap()
        self._legs = self._createLegsPixmap()

        # Create timer
        self._timer = QtCore.QTimer(None)
        self._timer.setInterval(150)
        self._timer.setSingleShot(False)
        self._timer.timeout.connect(self.onTimer)
예제 #10
0
    def __init__(self):
        QtWidgets.QToolButton.__init__(self)

        # Init
        self.setIconSize(QtCore.QSize(*self.SIZE))
        self.setStyleSheet("QToolButton{ border: none; }")

        # Create arrow pixmaps
        self._menuarrow1 = self._createMenuArrowPixmap(0)
        self._menuarrow2 = self._createMenuArrowPixmap(70)
        self._menuarrow = self._menuarrow1

        # Variable to keep icon
        self._icon = None

        # Variable to keep track of when the mouse was pressed, so that
        # we can allow dragging as well as clicking the menu.
        self._menuPressed = False
예제 #11
0
    def __init__(self, engine):
        super().__init__()
        self._engine = engine
        layout = QtWidgets.QVBoxLayout(self)
        add_button = QtWidgets.QPushButton("Add")
        del_button = QtWidgets.QPushButton("Delete")
        self._view = QtWidgets.QListView()
        layout.addWidget(self._view)
        layout2 = QtWidgets.QHBoxLayout()
        layout2.addWidget(add_button)
        layout2.addWidget(del_button)
        layout.addLayout(layout2)
        self._model = QtCore.QStringListModel()
        self._view.setModel(self._model)

        self._model.setStringList(self._engine.registeredDocumentations())

        add_button.clicked.connect(self.add_doc)
        del_button.clicked.connect(self.del_doc)
예제 #12
0
 def helpOnText(self, pos):
     hw = pyzo.toolManager.getTool("pyzointeractivehelp")
     if not hw:
         return
     name = self.textCursor().selectedText().strip()
     if name == "":
         cursor = self.cursorForPosition(
             pos - self.mapToGlobal(QtCore.QPoint(0, 0)))
         line = cursor.block().text()
         limit = cursor.positionInBlock()
         while limit < len(line) and (line[limit].isalnum()
                                      or line[limit] in (".", "_")):
             limit += 1
             cursor.movePosition(cursor.Right)
         _, tokens = self.getTokensUpToCursor(cursor)
         nameBefore, name = parseLine_autocomplete(tokens)
         if nameBefore:
             name = "%s.%s" % (nameBefore, name)
     if name != "":
         hw.setObjectName(name, True)
예제 #13
0
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)

        # create toolbar
        self._toolbar = QtGui.QToolBar(self)
        self._toolbar.setMaximumHeight(25)
        self._toolbar.setIconSize(QtCore.QSize(16, 16))

        # create stack
        self._stack = QtGui.QStackedWidget(self)

        # Populate toolbar
        self._shellButton = ShellControl(self._toolbar, self._stack)
        self._debugmode = 0
        self._dbs = DebugStack(self._toolbar)
        #
        self._toolbar.addWidget(self._shellButton)
        self._toolbar.addSeparator()
        # self._toolbar.addWidget(self._dbc) -> delayed, see addContextMenu()

        self._condahelp = CondaHelper(self)

        # widget layout
        layout = QtGui.QVBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self._toolbar)
        layout.addWidget(self._stack, 0)
        layout.addWidget(self._condahelp, 0)
        self.setLayout(layout)

        # make callbacks
        self._stack.currentChanged.connect(self.onCurrentChanged)

        # Make shared history (shared among shells)
        if PythonHistory is None:
            self.sharedHistory = None
        else:
            self.sharedHistory = PythonHistory('shellhistory.py')

        self.showCondaHelper()
예제 #14
0
    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)

        # Set style/theme
        try:
            self.setStyle(
                pyzo.themes[pyzo.config.settings.theme.lower()]['data'])
        except Exception as err:
            print("Could not load theme: " + str(err))

        # Set font and zooming
        self.setFont(pyzo.config.view.fontname)
        self.setZoom(pyzo.config.view.zoom)
        self.setShowWhitespace(pyzo.config.view.showWhitespace)
        self.setHighlightMatchingBracket(
            pyzo.config.view.highlightMatchingBracket)

        # Create timer for autocompletion delay
        self._delayTimer = QtCore.QTimer(self)
        self._delayTimer.setSingleShot(True)
        self._delayTimer.timeout.connect(self._introspectNow)

        # For buffering autocompletion and calltip info
        self._callTipBuffer_name = ''
        self._callTipBuffer_time = 0
        self._callTipBuffer_result = ''
        self._autoCompBuffer_name = ''
        self._autoCompBuffer_time = 0
        self._autoCompBuffer_result = []

        self.setAutoCompletionAcceptKeysFromStr(
            pyzo.config.settings.autoComplete_acceptKeys)

        self.completer().highlighted.connect(self.updateHelp)
        self.setIndentUsingSpaces(
            pyzo.config.settings.defaultIndentUsingSpaces)
        self.setIndentWidth(pyzo.config.settings.defaultIndentWidth)
        self.setAutocompletPopupSize(*pyzo.config.view.autoComplete_popupSize)
        self.setCancelCallback(self.restoreHelp)
예제 #15
0
    def __init__(self, parent):
        QtWidgets.QWidget.__init__(self, parent)

        # create toolbar
        self._toolbar = QtWidgets.QToolBar(self)
        self._toolbar.setMaximumHeight(26)
        self._toolbar.setIconSize(QtCore.QSize(16, 16))

        # create stack
        self._stack = QtWidgets.QStackedWidget(self)

        # Populate toolbar
        self._shellButton = ShellControl(self._toolbar, self._stack)
        self._debugmode = 0
        self._dbs = DebugStack(self._toolbar)
        #
        self._toolbar.addWidget(self._shellButton)
        self._toolbar.addSeparator()
        # self._toolbar.addWidget(self._dbc) -> delayed, see addContextMenu()

        self._interpreterhelp = InterpreterHelper(self)

        # widget layout
        layout = QtWidgets.QVBoxLayout()
        layout.setSpacing(0)
        # set margins
        margin = pyzo.config.view.widgetMargin
        layout.setContentsMargins(margin, margin, margin, margin)

        layout.addWidget(self._toolbar)
        layout.addWidget(self._stack, 0)
        layout.addWidget(self._interpreterhelp, 0)
        self.setLayout(layout)

        # make callbacks
        self._stack.currentChanged.connect(self.onCurrentChanged)

        self.showInterpreterHelper()
예제 #16
0
    def contextMenuEvent(self, event):
        """ contextMenuEvent(event)
        Show the context menu. 
        """

        QtWidgets.QTreeView.contextMenuEvent(self, event)

        # Get if an item is selected
        item = self.currentItem()
        if not item:
            return

        # Create menu
        self._menu.clear()
        for a in ['Show namespace', 'Show help', 'Delete']:
            action = self._menu.addAction(a)
            parts = splitName(self._proxy._name)
            parts.append(item.text(0))
            action._objectName = joinName(parts)
            action._item = item

        # Show
        self._menu.popup(QtGui.QCursor.pos() + QtCore.QPoint(3, 3))
예제 #17
0
    def __init__(self, parent, shellStack):
        QtGui.QToolButton.__init__(self, parent)

        # Store reference of shell stack
        self._shellStack = shellStack

        # Keep reference of actions corresponding to shells
        self._shellActions = []

        # Set text and tooltip
        self.setText('Warming up ...')
        self.setToolTip("Click to select shell.")
        self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        self.setPopupMode(self.InstantPopup)

        # Set icon
        self._iconMaker = ShellIconMaker(self)
        self._iconMaker.updateIcon('busy')  # Busy initializing

        # Create timer
        self._elapsedTimesTimer = QtCore.QTimer(self)
        self._elapsedTimesTimer.setInterval(200)
        self._elapsedTimesTimer.setSingleShot(False)
        self._elapsedTimesTimer.timeout.connect(self.onElapsedTimesTimer)
예제 #18
0
class WorkspaceProxy(QtCore.QObject):
    """ WorkspaceProxy
    
    A proxy class to handle the asynchonous behaviour of getting information
    from the shell. The workspace tool asks for a certain name, and this
    class notifies when new data is available using a qt signal.
    
    """

    haveNewData = QtCore.Signal()

    def __init__(self):
        QtCore.QObject.__init__(self)

        # Variables
        self._variables = []

        # Element to get more info of
        self._name = ''

        # Bind to events
        pyzo.shells.currentShellChanged.connect(self.onCurrentShellChanged)
        pyzo.shells.currentShellStateChanged.connect(
            self.onCurrentShellStateChanged)

        # Initialize
        self.onCurrentShellStateChanged()

    def addNamePart(self, part):
        """ addNamePart(part)
        Add a part to the name.
        """
        parts = splitName(self._name)
        parts.append(part)
        self.setName(joinName(parts))

    def setName(self, name):
        """ setName(name)
        Set the name that we want to know more of.
        """
        self._name = name

        shell = pyzo.shells.getCurrentShell()
        if shell:
            future = shell._request.dir2(self._name)
            future.add_done_callback(self.processResponse)

    def goUp(self):
        """ goUp()
        Cut the last part off the name.
        """
        parts = splitName(self._name)
        if parts:
            parts.pop()
        self.setName(joinName(parts))

    def onCurrentShellChanged(self):
        """ onCurrentShellChanged()
        When no shell is selected now, update this. In all other cases,
        the onCurrentShellStateChange will be fired too.
        """
        shell = pyzo.shells.getCurrentShell()
        if not shell:
            self._variables = []
            self.haveNewData.emit()

    def onCurrentShellStateChanged(self):
        """ onCurrentShellStateChanged()
        Do a request for information!
        """
        shell = pyzo.shells.getCurrentShell()
        if not shell:
            # Should never happen I think, but just to be sure
            self._variables = []
        elif shell._state.lower() != 'busy':
            future = shell._request.dir2(self._name)
            future.add_done_callback(self.processResponse)

    def processResponse(self, future):
        """ processResponse(response)
        We got a response, update our list and notify the tree.
        """

        response = []

        # Process future
        if future.cancelled():
            pass  #print('Introspect cancelled') # No living kernel
        elif future.exception():
            print('Introspect-queryDoc-exception: ', future.exception())
        else:
            response = future.result()

        self._variables = response
        self.haveNewData.emit()
예제 #19
0
    def __init__(self, parent):
        QtWidgets.QWidget.__init__(self, parent)

        # Make sure there is a configuration entry for this tool
        # The pyzo tool manager makes sure that there is an entry in
        # config.tools before the tool is instantiated.
        toolId = self.__class__.__name__.lower()
        self._config = pyzo.config.tools[toolId]
        if not hasattr(self._config, 'hideTypes'):
            self._config.hideTypes = []
        # <kludge 2>
        # configuring the typeTranslation dictionary
        if not hasattr(self._config, 'typeTranslation'):
            # to prevent the exception to be raised, one could init to :
            # {"method": "function", "function": "function", "type": "type", "private": "private", "module": "module"}
            self._config.typeTranslation = {}
        # Defaults
        self._config.typeTranslation['method'] = 'function'
        self._config.typeTranslation['builtin_function_or_method'] = 'function'
        # <kludge 2>

        # Create tool button
        self._up = QtWidgets.QToolButton(self)
        style = QtWidgets.qApp.style()
        self._up.setIcon(style.standardIcon(style.SP_ArrowLeft))
        self._up.setIconSize(QtCore.QSize(16, 16))

        # Create "path" line edit
        self._line = QtWidgets.QLineEdit(self)
        self._line.setReadOnly(True)
        self._line.setStyleSheet("QLineEdit { background:#ddd; }")
        self._line.setFocusPolicy(QtCore.Qt.NoFocus)

        # Create options menu
        self._options = QtWidgets.QToolButton(self)
        self._options.setIcon(pyzo.icons.filter)
        self._options.setIconSize(QtCore.QSize(16, 16))
        self._options.setPopupMode(self._options.InstantPopup)
        self._options.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        #
        self._options._menu = QtWidgets.QMenu()
        self._options.setMenu(self._options._menu)
        self.onOptionsPress()  # create menu now

        # Create tree
        self._tree = WorkspaceTree(self)

        # Create message for when tree is empty
        self._initText = QtWidgets.QLabel(
            pyzo.translate(
                "pyzoWorkspace",
                """Lists the variables in the current shell's namespace.

Currently, there are none. Some of them may be hidden because of the filters you configured."""
            ), self)
        self._initText.setVisible(False)
        self._initText.setWordWrap(True)

        # Set layout
        layout = QtWidgets.QHBoxLayout()
        layout.addWidget(self._up, 0)
        layout.addWidget(self._line, 1)
        layout.addWidget(self._options, 0)
        #
        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.addLayout(layout, 0)
        mainLayout.addWidget(self._initText, 1)
        mainLayout.addWidget(self._tree, 2)
        mainLayout.setSpacing(2)
        # set margins
        margin = pyzo.config.view.widgetMargin
        mainLayout.setContentsMargins(margin, margin, margin, margin)
        self.setLayout(mainLayout)

        # Bind events
        self._up.pressed.connect(self._tree._proxy.goUp)
        self._options.pressed.connect(self.onOptionsPress)
        self._options._menu.triggered.connect(self.onOptionMenuTiggered)
예제 #20
0
    def __init__(self, parent):
        QtWidgets.QWidget.__init__(self, parent)

        # Create text field, checkbox, and button
        self._text = QtWidgets.QLineEdit(self)
        self._printBut = QtWidgets.QPushButton("Print", self)

        style = QtWidgets.qApp.style()

        self._backBut = QtWidgets.QToolButton(self)
        self._backBut.setIcon(style.standardIcon(style.SP_ArrowLeft))
        self._backBut.setIconSize(QtCore.QSize(16, 16))
        self._backBut.setPopupMode(self._backBut.DelayedPopup)
        self._backBut.setMenu(
            PyzoInteractiveHelpHistoryMenu("Backward menu", self, False))

        self._forwBut = QtWidgets.QToolButton(self)
        self._forwBut.setIcon(style.standardIcon(style.SP_ArrowRight))
        self._forwBut.setIconSize(QtCore.QSize(16, 16))
        self._forwBut.setPopupMode(self._forwBut.DelayedPopup)
        self._forwBut.setMenu(
            PyzoInteractiveHelpHistoryMenu("Forward menu", self, True))

        # Create options button
        self._options = QtWidgets.QToolButton(self)
        self._options.setIcon(pyzo.icons.wrench)
        self._options.setIconSize(QtCore.QSize(16, 16))
        self._options.setPopupMode(self._options.InstantPopup)
        self._options.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

        # Create options menu
        self._options._menu = QtWidgets.QMenu()
        self._options.setMenu(self._options._menu)

        # Create browser
        self._browser = QtWidgets.QTextBrowser(self)
        self._browser_text = initText
        self._browser.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self._browser.customContextMenuRequested.connect(self.showMenu)

        # Create two sizers
        self._sizer1 = QtWidgets.QVBoxLayout(self)
        self._sizer2 = QtWidgets.QHBoxLayout()

        # Put the elements together
        self._sizer2.addWidget(self._backBut, 1)
        self._sizer2.addWidget(self._forwBut, 2)
        self._sizer2.addWidget(self._text, 4)
        self._sizer2.addWidget(self._printBut, 0)
        self._sizer2.addWidget(self._options, 3)
        #
        self._sizer1.addLayout(self._sizer2, 0)
        self._sizer1.addWidget(self._browser, 1)
        #
        self._sizer1.setSpacing(2)
        # set margins
        margin = pyzo.config.view.widgetMargin
        self._sizer1.setContentsMargins(margin, margin, margin, margin)

        self.setLayout(self._sizer1)

        # Set config
        toolId = self.__class__.__name__.lower()
        self._config = config = pyzo.config.tools[toolId]
        #
        if not hasattr(config, "smartNewlines"):
            config.smartNewlines = True
        if not hasattr(config, "fontSize"):
            if sys.platform == "darwin":
                config.fontSize = 12
            else:
                config.fontSize = 10

        # Create callbacks
        self._text.returnPressed.connect(self.queryDoc)
        self._printBut.clicked.connect(self.printDoc)
        self._backBut.clicked.connect(self.goBack)
        self._forwBut.clicked.connect(self.goForward)
        #
        self._options.pressed.connect(self.onOptionsPress)
        self._options._menu.triggered.connect(self.onOptionMenuTiggered)

        # Start
        self._history = []
        self._histindex = 0
        self.setText()  # Set default text
        self.onOptionsPress()  # Fill menu
예제 #21
0
    def _alignRecursive(self):
        """ _alignRecursive()
        
        Recursive alignment of the items. The alignment process
        should be initiated from alignTabs().
        
        """

        # Only if visible
        if not self.isVisible():
            return

        # Get tab bar and number of items
        N = self.count()

        # Get right edge of last tab and left edge of corner widget
        pos1 = self.tabRect(0).topLeft()
        pos2 = self.tabRect(N - 1).topRight()
        cornerWidget = self.parent().cornerWidget()
        if cornerWidget:
            pos3 = cornerWidget.pos()
        else:
            pos3 = QtCore.QPoint(self.width(), 0)
        x1 = pos1.x()
        x2 = pos2.x()
        x3 = pos3.x()
        alignMargin = x3 - (x2 - x1) - 3  # Must be positive (has margin)

        # Are the tabs too wide?
        if alignMargin < 0:
            # Tabs extend beyond corner widget

            # Reduce width then
            self._alignWidth -= 1
            self._alignWidth = max(self._alignWidth, MIN_NAME_WIDTH)

            # Apply
            self._setMaxWidthOfAllItems()
            self._alignWidthIsReducing = True

            # Try again if there's still room for reduction
            if self._alignWidth > MIN_NAME_WIDTH:
                self._alignTimer.start()

        elif alignMargin > 10 and not self._alignWidthIsReducing:
            # Gap between tabs and corner widget is a bit large

            # Increase width then
            self._alignWidth += 1
            self._alignWidth = min(self._alignWidth, MAX_NAME_WIDTH)

            # Apply
            itemsElided = self._setMaxWidthOfAllItems()

            # Try again if there's still room for increment
            if itemsElided and self._alignWidth < MAX_NAME_WIDTH:
                self._alignTimer.start()
                #self._alignTimer.timeout.emit()

        else:
            pass  # margin is good
예제 #22
0
class CompactTabBar(QtWidgets.QTabBar):
    """ CompactTabBar(parent, *args, padding=(4,4,6,6), preventEqualTexts=True)
    
    Tab bar corresponcing to the CompactTabWidget.
    
    With the "padding" argument the padding of the tabs can be chosen.
    It should be an integer, or a 4 element tuple specifying the padding
    for top, bottom, left, right. When a tab has a button,
    the padding is the space between button and text.
    
    With preventEqualTexts to True, will reduce the amount of eliding if
    two tabs have (partly) the same name, so that they can always be
    distinguished.
    
    """

    # Add signal to be notified of double clicks on tabs
    tabDoubleClicked = QtCore.Signal(int)
    barDoubleClicked = QtCore.Signal()

    def __init__(self, *args, padding=(4, 4, 6, 6), preventEqualTexts=True):
        QtWidgets.QTabBar.__init__(self, *args)

        # Put tab widget in document mode
        self.setDocumentMode(True)

        # Widget needs to draw its background (otherwise Mac has a dark bg)
        self.setDrawBase(False)
        if sys.platform == 'darwin':
            self.setAutoFillBackground(True)

        # Set whether we want to prevent eliding for names that start the same.
        self._preventEqualTexts = preventEqualTexts

        # Allow moving tabs around
        self.setMovable(True)

        # Get padding
        if isinstance(padding, (int, float)):
            padding = padding, padding, padding, padding
        elif isinstance(padding, (tuple, list)):
            pass
        else:
            raise ValueError('Invalid value for padding.')

        # Set style sheet
        stylesheet = STYLESHEET
        stylesheet = stylesheet.replace('PADDING_TOP', str(padding[0]))
        stylesheet = stylesheet.replace('PADDING_BOTTOM', str(padding[1]))
        stylesheet = stylesheet.replace('PADDING_LEFT', str(padding[2]))
        stylesheet = stylesheet.replace('PADDING_RIGHT', str(padding[3]))
        self.setStyleSheet(stylesheet)

        # We do our own eliding
        self.setElideMode(QtCore.Qt.ElideNone)

        # Make tabs wider if there's plenty space?
        self.setExpanding(False)

        # If there's not enough space, use scroll buttons
        self.setUsesScrollButtons(True)

        # When a tab is removed, select previous
        self.setSelectionBehaviorOnRemove(self.SelectPreviousTab)

        # Init alignment parameters
        self._alignWidth = MIN_NAME_WIDTH  # Width in characters
        self._alignWidthIsReducing = False  # Whether in process of reducing

        # Create timer for aligning
        self._alignTimer = QtCore.QTimer(self)
        self._alignTimer.setInterval(10)
        self._alignTimer.setSingleShot(True)
        self._alignTimer.timeout.connect(self._alignRecursive)

    def _compactTabBarData(self, i):
        """ _compactTabBarData(i)
        
        Get the underlying tab data for tab i. Only for internal use.
        
        """

        # Get current TabData instance
        tabData = QtWidgets.QTabBar.tabData(self, i)
        if (tabData is not None) and hasattr(tabData, 'toPyObject'):
            tabData = tabData.toPyObject()  # Older version of Qt

        # If none, make it as good as we can
        if not tabData:
            name = str(QtWidgets.QTabBar.tabText(self, i))
            tabData = TabData(name)
            QtWidgets.QTabBar.setTabData(self, i, tabData)

        # Done
        return tabData

    ## Overload a few methods

    def mouseDoubleClickEvent(self, event):
        i = self.tabAt(event.pos())
        if i == -1:
            # There was no tab under the cursor
            self.barDoubleClicked.emit()
        else:
            # Tab was double clicked
            self.tabDoubleClicked.emit(i)

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.MiddleButton:
            i = self.tabAt(event.pos())
            if i >= 0:
                self.parent().tabCloseRequested.emit(i)
                return
        super().mousePressEvent(event)

    def setTabData(self, i, data):
        """ setTabData(i, data)
        
        Set the given object at the tab with index 1.
        
        """
        # Get underlying python instance
        tabData = self._compactTabBarData(i)

        # Attach given data
        tabData.data = data

    def tabData(self, i):
        """ tabData(i)
        
        Get the tab data at item i. Always returns a Python object.
        
        """

        # Get underlying python instance
        tabData = self._compactTabBarData(i)

        # Return stored data
        return tabData.data

    def setTabText(self, i, text):
        """ setTabText(i, text)
        
        Set the text for tab i.
        
        """
        tabData = self._compactTabBarData(i)
        if text != tabData.name:
            tabData.name = text
            self.alignTabs()

    def tabText(self, i):
        """ tabText(i)
        
        Get the title of the tab at index i.
        
        """
        tabData = self._compactTabBarData(i)
        return tabData.name

    ## Overload events and protected functions

    def tabInserted(self, i):
        QtWidgets.QTabBar.tabInserted(self, i)

        # Is called when a tab is inserted

        # Get given name and store
        name = str(QtWidgets.QTabBar.tabText(self, i))
        tabData = TabData(name)
        QtWidgets.QTabBar.setTabData(self, i, tabData)

        # Update
        self.alignTabs()

    def tabRemoved(self, i):
        QtWidgets.QTabBar.tabRemoved(self, i)

        # Update
        self.alignTabs()

    def resizeEvent(self, event):
        QtWidgets.QTabBar.resizeEvent(self, event)
        self.alignTabs()

    def showEvent(self, event):
        QtWidgets.QTabBar.showEvent(self, event)
        self.alignTabs()

    ## For aligning

    def alignTabs(self):
        """ alignTabs()
        
        Align the tab items. Their names are ellided if required so that
        all tabs fit on the tab bar if possible. When there is too little
        space, the QTabBar will kick in and draw scroll arrows.
        
        """

        # Set name widths correct (in case new names were added)
        self._setMaxWidthOfAllItems()

        # Start alignment process
        self._alignWidthIsReducing = False
        self._alignTimer.start()

    def _alignRecursive(self):
        """ _alignRecursive()
        
        Recursive alignment of the items. The alignment process
        should be initiated from alignTabs().
        
        """

        # Only if visible
        if not self.isVisible():
            return

        # Get tab bar and number of items
        N = self.count()

        # Get right edge of last tab and left edge of corner widget
        pos1 = self.tabRect(0).topLeft()
        pos2 = self.tabRect(N - 1).topRight()
        cornerWidget = self.parent().cornerWidget()
        if cornerWidget:
            pos3 = cornerWidget.pos()
        else:
            pos3 = QtCore.QPoint(self.width(), 0)
        x1 = pos1.x()
        x2 = pos2.x()
        x3 = pos3.x()
        alignMargin = x3 - (x2 - x1) - 3  # Must be positive (has margin)

        # Are the tabs too wide?
        if alignMargin < 0:
            # Tabs extend beyond corner widget

            # Reduce width then
            self._alignWidth -= 1
            self._alignWidth = max(self._alignWidth, MIN_NAME_WIDTH)

            # Apply
            self._setMaxWidthOfAllItems()
            self._alignWidthIsReducing = True

            # Try again if there's still room for reduction
            if self._alignWidth > MIN_NAME_WIDTH:
                self._alignTimer.start()

        elif alignMargin > 10 and not self._alignWidthIsReducing:
            # Gap between tabs and corner widget is a bit large

            # Increase width then
            self._alignWidth += 1
            self._alignWidth = min(self._alignWidth, MAX_NAME_WIDTH)

            # Apply
            itemsElided = self._setMaxWidthOfAllItems()

            # Try again if there's still room for increment
            if itemsElided and self._alignWidth < MAX_NAME_WIDTH:
                self._alignTimer.start()
                #self._alignTimer.timeout.emit()

        else:
            pass  # margin is good

    def _getAllNames(self):
        """ _getAllNames()
        
        Get a list of all (full) tab names.
        
        """
        return [self._compactTabBarData(i).name for i in range(self.count())]

    def _setMaxWidthOfAllItems(self):
        """ _setMaxWidthOfAllItems()
        
        Sets the maximum width of all items now, by eliding the names.
        Returns whether any items were elided.
        
        """

        # Prepare for measuring font sizes
        font = self.font()
        metrics = QtGui.QFontMetrics(font)

        # Get whether an item was reduced in size
        itemReduced = False

        for i in range(self.count()):

            # Get width
            w = self._alignWidth

            # Get name
            name = name0 = self._compactTabBarData(i).name

            # Check if we can reduce the name size, correct w if necessary
            if ((w + 1) < len(name0)) and self._preventEqualTexts:

                # Increase w untill there are no names that start the same
                allNames = self._getAllNames()
                hasSimilarNames = True
                diff = 2
                w -= 1
                while hasSimilarNames and w < len(name0):
                    w += 1
                    w2 = w - (diff - 1)
                    shortName = name[:w2]
                    similarnames = [n for n in allNames if n[:w2] == shortName]
                    hasSimilarNames = len(similarnames) > 1

            # Check again, with corrected w
            if (w + 1) < len(name0):
                name = name[:w] + ELLIPSIS
                itemReduced = True

            # Set text now
            QtWidgets.QTabBar.setTabText(self, i, name)

        # Done
        return itemReduced
예제 #23
0
 def mimeData(self, indexes):
     mimeData = QtCore.QMimeData()
     mimeData.setData("text/plain", self.plainText(indexes))
     return mimeData
예제 #24
0
class PyzoEditor(BaseTextCtrl):

    # called when dirty changed or filename changed, etc
    somethingChanged = QtCore.Signal()

    def __init__(self, parent, **kwds):
        super().__init__(parent, showLineNumbers=True, **kwds)

        # Init filename and name
        self._filename = ''
        self._name = '<TMP>'

        # View settings
        self.setShowWhitespace(pyzo.config.view.showWhitespace)
        #TODO: self.setViewWrapSymbols(view.showWrapSymbols)
        self.setShowLineEndings(pyzo.config.view.showLineEndings)
        self.setShowIndentationGuides(pyzo.config.view.showIndentationGuides)
        #
        self.setWrap(bool(pyzo.config.view.wrap))
        self.setHighlightCurrentLine(pyzo.config.view.highlightCurrentLine)
        self.setLongLineIndicatorPosition(pyzo.config.view.edgeColumn)
        self.setHighlightMatchingBracket(
            pyzo.config.view.highlightMatchingBracket)
        #TODO: self.setFolding( int(view.codeFolding)*5 )
        # bracematch is set in baseTextCtrl, since it also applies to shells
        # dito for zoom and tabWidth

        # Set line endings to default
        self.lineEndings = pyzo.config.settings.defaultLineEndings

        # Set encoding to default
        self.encoding = 'UTF-8'

        # Modification time to test file change
        self._modifyTime = 0

        self.modificationChanged.connect(self._onModificationChanged)

        # To see whether the doc has changed to update the parser.
        self.textChanged.connect(self._onModified)

        # This timer is used to hide the marker that shows which code is executed
        self._showRunCursorTimer = QtCore.QTimer()

        # Add context menu (the offset is to prevent accidental auto-clicking)
        self._menu = EditorContextMenu(self)
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(lambda p: self._menu.popup(
            self.mapToGlobal(p) + QtCore.QPoint(0, 3)))

    ## Properties

    @property
    def name(self):
        return self._name

    @property
    def filename(self):
        return self._filename

    @property
    def lineEndings(self):
        """
        Line-endings style of this file. Setter accepts machine-readable (e.g. '\r') and human-readable (e.g. 'CR') input
        """
        return self._lineEndings

    @lineEndings.setter
    def lineEndings(self, value):
        if value in ('\r', '\n', '\r\n'):
            self._lineEndings = value
            return
        try:
            self._lineEndings = {'CR': '\r', 'LF': '\n', 'CRLF': '\r\n'}[value]
        except KeyError:
            raise ValueError('Invalid line endings style %r' % value)

    @property
    def lineEndingsHumanReadable(self):
        """
        Current line-endings style, human readable (e.g. 'CR')
        """
        return {'\r': 'CR', '\n': 'LF', '\r\n': 'CRLF'}[self.lineEndings]

    @property
    def encoding(self):
        """ Encoding used to convert the text of this file to bytes.
        """
        return self._encoding

    @encoding.setter
    def encoding(self, value):
        # Test given value, correct name if it exists
        try:
            c = codecs.lookup(value)
            value = c.name
        except Exception:
            value = codecs.lookup('UTF-8').name
        # Store
        self._encoding = value

    ##

    def justifyText(self):
        """ Overloaded version of justifyText to make it use our
        configurable justificationwidth.
        """
        super().justifyText(pyzo.config.settings.justificationWidth)

    def showRunCursor(self, cursor):
        """
        Momentarily highlight a piece of code to show that this is being executed
        """

        extraSelection = QtWidgets.QTextEdit.ExtraSelection()
        extraSelection.cursor = cursor
        extraSelection.format.setBackground(QtCore.Qt.gray)
        self.setExtraSelections([extraSelection])

        self._showRunCursorTimer.singleShot(
            200, lambda: self.setExtraSelections([]))

    def id(self):
        """ Get an id of this editor. This is the filename,
        or for tmp files, the name. """
        if self._filename:
            return self._filename
        else:
            return self._name

    def focusInEvent(self, event):
        """ Test whether the file has been changed 'behind our back'
        """
        # Act normally to the focus event
        BaseTextCtrl.focusInEvent(self, event)
        # Test file change
        self.testWhetherFileWasChanged()

    def testWhetherFileWasChanged(self):
        """ testWhetherFileWasChanged()
        Test to see whether the file was changed outside our backs,
        and let the user decide what to do.
        Returns True if it was changed.
        """

        # get the path
        path = self._filename
        if not os.path.isfile(path):
            # file is deleted from the outside
            return

        # test the modification time...
        mtime = os.path.getmtime(path)
        if mtime != self._modifyTime:

            # ask user
            dlg = QtWidgets.QMessageBox(self)
            dlg.setWindowTitle('File was changed')
            dlg.setText("File has been modified outside of the editor:\n" +
                        self._filename)
            dlg.setInformativeText("Do you want to reload?")
            t = dlg.addButton("Reload", QtWidgets.QMessageBox.AcceptRole)  #0
            dlg.addButton("Keep this version",
                          QtWidgets.QMessageBox.RejectRole)  #1
            dlg.setDefaultButton(t)

            # whatever the result, we will reset the modified time
            self._modifyTime = os.path.getmtime(path)

            # get result and act
            result = dlg.exec_()
            if result == QtWidgets.QMessageBox.AcceptRole:
                self.reload()
            else:
                pass  # when cancelled or explicitly said, do nothing

            # Return that indeed the file was changes
            return True

    def _onModificationChanged(self, changed):
        """Handler for the modificationChanged signal. Emit somethingChanged
        for the editorStack to update the modification notice."""
        self.somethingChanged.emit()

    def _onModified(self):
        pyzo.parser.parseThis(self)

    def dragMoveEvent(self, event):
        """ Otherwise cursor can get stuck.
        https://bitbucket.org/iep-project/iep/issue/252
        https://qt-project.org/forums/viewthread/3180
        """
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
        else:
            BaseTextCtrl.dropEvent(self, event)

    def dropEvent(self, event):
        """ Drop files in the list. """
        if event.mimeData().hasUrls():
            # file: let the editorstack do the work.
            pyzo.editors.dropEvent(event)
        else:
            # text: act normal
            BaseTextCtrl.dropEvent(self, event)

    def showEvent(self, event=None):
        """ Capture show event to change title. """
        # Act normally
        if event:
            BaseTextCtrl.showEvent(self, event)

        # Make parser update
        pyzo.parser.parseThis(self)

    def setTitleInMainWindow(self):
        """ set the title  text in the main window to show filename. """

        # compose title
        name, path = self._name, self._filename
        if path:
            pyzo.main.setMainTitle(path)
        else:
            pyzo.main.setMainTitle(name)

    def save(self, filename=None):
        """ Save the file. No checking is done. """

        # get filename
        if filename is None:
            filename = self._filename
        if not filename:
            raise ValueError("No filename specified, and no filename known.")

        # Test whether it was changed without us knowing. If so, dont save now.
        if self.testWhetherFileWasChanged():
            return

        # Get text and remember where we are
        text = self.toPlainText()
        cursor = self.textCursor()
        linenr = cursor.blockNumber() + 1
        index = cursor.positionInBlock()
        scroll = self.verticalScrollBar().value()

        # Convert line endings (optionally remove trailing whitespace
        if pyzo.config.settings.removeTrailingWhitespaceWhenSaving:
            lines = [line.rstrip() for line in text.split('\n')]
            if lines[-1]:
                lines.append('')  # Ensure the file ends in an empty line
            text = self.lineEndings.join(lines)
            self.setPlainText(text)
            # Go back to where we were
            cursor = self.textCursor()
            cursor.movePosition(cursor.Start)  # move to begin of the document
            cursor.movePosition(cursor.NextBlock,
                                n=linenr - 1)  # n blocks down
            index = min(index, cursor.block().length() - 1)
            cursor.movePosition(cursor.Right, n=index)  # n chars right
            self.setTextCursor(cursor)
            self.verticalScrollBar().setValue(scroll)
        else:
            text = text.replace('\n', self.lineEndings)

        # Make bytes
        bb = text.encode(self.encoding)

        # Store
        f = open(filename, 'wb')
        try:
            f.write(bb)
        finally:
            f.close()

        # Update stats
        self._filename = normalizePath(filename)
        self._name = os.path.split(self._filename)[1]
        self.document().setModified(False)
        self._modifyTime = os.path.getmtime(self._filename)

        # update title (in case of a rename)
        self.setTitleInMainWindow()

        # allow item to update its texts (no need: onModifiedChanged does this)
        #self.somethingChanged.emit()

    def reload(self):
        """ Reload text using the self._filename.
        We do not have a load method; we first try to load the file
        and only when we succeed create an editor to show it in...
        This method is only for reloading in case the file was changed
        outside of the editor. """

        # We can only load if the filename is known
        if not self._filename:
            return
        filename = self._filename

        # Remember where we are
        cursor = self.textCursor()
        linenr = cursor.blockNumber() + 1

        # Load file (as bytes)
        with open(filename, 'rb') as f:
            bb = f.read()

        # Convert to text
        text = bb.decode('UTF-8')

        # Process line endings (before setting the text)
        self.lineEndings = determineLineEnding(text)

        # Set text
        self.setPlainText(text)
        self.document().setModified(False)

        # Go where we were (approximately)
        self.gotoLine(linenr)

    def deleteLines(self):
        cursor = self.textCursor()
        # Find start and end of selection
        start = cursor.selectionStart()
        end = cursor.selectionEnd()
        # Expand selection: from start of first block to start of next block
        cursor.setPosition(start)
        cursor.movePosition(cursor.StartOfBlock)
        cursor.setPosition(end, cursor.KeepAnchor)
        cursor.movePosition(cursor.NextBlock, cursor.KeepAnchor)

        cursor.removeSelectedText()

    def duplicateLines(self):
        cursor = self.textCursor()
        # Find start and end of selection
        start = cursor.selectionStart()
        end = cursor.selectionEnd()
        # Expand selection: from start of first block to start of next block
        cursor.setPosition(start)
        cursor.movePosition(cursor.StartOfBlock)
        cursor.setPosition(end, cursor.KeepAnchor)
        cursor.movePosition(cursor.NextBlock, cursor.KeepAnchor)

        text = cursor.selectedText()
        cursor.setPosition(start)
        cursor.movePosition(cursor.StartOfBlock)
        cursor.insertText(text)

    def commentCode(self):
        """
        Comment the lines that are currently selected
        """
        indents = []

        def getIndent(cursor):
            text = cursor.block().text().rstrip()
            if text:
                indents.append(len(text) - len(text.lstrip()))

        def commentBlock(cursor):
            cursor.setPosition(cursor.block().position() + minindent)
            cursor.insertText('# ')

        self.doForSelectedBlocks(getIndent)
        minindent = min(indents) if indents else 0
        self.doForSelectedBlocks(commentBlock)

    def uncommentCode(self):
        """
        Uncomment the lines that are currently selected
        """

        #TODO: this should not be applied to lines that are part of a multi-line string

        #Define the uncomment function to be applied to all blocks
        def uncommentBlock(cursor):
            """
            Find the first # on the line; if there is just whitespace before it,
            remove the # and if it is followed by a space remove the space, too
            """
            text = cursor.block().text()
            commentStart = text.find('#')
            if commentStart == -1:
                return  #No comment on this line
            if text[:commentStart].strip() != '':
                return  #Text before the #
            #Move the cursor to the beginning of the comment
            cursor.setPosition(cursor.block().position() + commentStart)
            cursor.deleteChar()
            if text[commentStart:].startswith('# '):
                cursor.deleteChar()

        #Apply this function to all blocks
        self.doForSelectedBlocks(uncommentBlock)

    def gotoDef(self):
        """
        Goto the definition for the word under the cursor
        """

        # Get name of object to go to
        cursor = self.textCursor()
        if not cursor.hasSelection():
            cursor.select(cursor.WordUnderCursor)
        word = cursor.selection().toPlainText()

        # Send the open command to the shell
        s = pyzo.shells.getCurrentShell()
        if s is not None:
            if word and word.isidentifier():
                s.executeCommand('open %s\n' % word)
            else:
                s.write('Invalid identifier %r\n' % word)

    ## Introspection processing methods

    def processCallTip(self, cto):
        """ Processes a calltip request using a CallTipObject instance.
        """
        # Try using buffer first
        if cto.tryUsingBuffer():
            return

        # Try obtaining calltip from the source
        sig = pyzo.parser.getFictiveSignature(cto.name, self, True)
        if sig:
            # Done
            cto.finish(sig)
        else:
            # Try the shell
            shell = pyzo.shells.getCurrentShell()
            if shell:
                shell.processCallTip(cto)

    def processAutoComp(self, aco):
        """ Processes an autocomp request using an AutoCompObject instance.
        """

        # Try using buffer first
        if aco.tryUsingBuffer():
            return

        # Init name to poll by remote process (can be changed!)
        nameForShell = aco.name

        # Get normal fictive namespace
        fictiveNS = pyzo.parser.getFictiveNameSpace(self)
        fictiveNS = set(fictiveNS)

        # Add names
        if not aco.name:
            # "root" names
            aco.addNames(fictiveNS)
            # imports
            importNames, importLines = pyzo.parser.getFictiveImports(self)
            aco.addNames(importNames)
        else:
            # Prepare list of class names to check out
            classNames = [aco.name]
            handleSelf = True
            # Unroll supers
            while classNames:
                className = classNames.pop(0)
                if not className:
                    continue
                if handleSelf or (className in fictiveNS):
                    # Only the self list (only first iter)
                    fictiveClass = pyzo.parser.getFictiveClass(
                        className, self, handleSelf)
                    handleSelf = False
                    if fictiveClass:
                        aco.addNames(fictiveClass.members)
                        classNames.extend(fictiveClass.supers)
                else:
                    nameForShell = className
                    break

        # If there's a shell, let it finish the autocompletion
        shell = pyzo.shells.getCurrentShell()
        if shell:
            aco.name = nameForShell  # might be the same or a base class
            shell.processAutoComp(aco)
        else:
            # Otherwise we finish it ourselves
            aco.finish()
예제 #25
0
class CommandHistory(QtCore.QObject):
    """Keep track of a (global) history of commands.

    This kinda assumes Python commands, but should be easy enough to generalize.
    """

    max_commands = 2000

    command_added = QtCore.Signal(str)
    command_removed = QtCore.Signal(int)
    commands_reset = QtCore.Signal()

    def __init__(self, fname):
        super().__init__()
        self._commands = []
        self._fname = fname
        self._last_date = datetime.date(2000, 1, 1)
        self._load()

    def _load(self):
        """Load commands from file."""
        if not self._fname:
            return

        assert not self._commands

        try:
            filename = os.path.join(pyzo.appDataDir, self._fname)
            if not os.path.isfile(filename):
                with open(filename, "wb"):
                    pass

            # Load lines and add to commands
            lines = open(filename, "r", encoding="utf-8").read().splitlines()
            self._commands.extend(
                [line.rstrip() for line in lines[-self.max_commands :]]
            )

            # Resolve last date
            for c in reversed(lines):
                if c.startswith("# ==== "):
                    try:
                        c = c.split("====")[1].strip()
                        self._last_date = datetime.datetime.strptime(
                            c, "%Y-%m-%d"
                        ).date()
                        break
                    except Exception:
                        pass

        except Exception as e:
            print("An error occurred while loading the history: " + str(e))

    def save(self):
        """Save the commands to disk."""
        if not self._fname:
            return
        filename = os.path.join(pyzo.appDataDir, self._fname)
        try:
            with open(filename, "wt", encoding="utf-8") as f:
                f.write("\n".join(self._commands))
        except Exception:
            print("Could not save command history")

    def get_commands(self):
        """Get a list of all commands (latest last)."""
        return self._commands.copy()

    def append(self, command):
        """Add a command to the list."""
        command = command.rstrip()
        if not command:
            return

        # Add date?
        today = datetime.date.today()
        if today > self._last_date:
            self._last_date = today
            self._commands.append("# ==== " + today.strftime("%Y-%m-%d"))
            self.command_added.emit(self._commands[-1])

        # Clear it
        try:
            index = self._commands.index(command)
        except ValueError:
            pass
        else:
            self._commands.pop(index)
            self.command_removed.emit(index)

        # Append
        self._commands.append(command)
        self.command_added.emit(self._commands[-1])

        # Reset?
        if len(self._commands) > self.max_commands:
            self._commands[: self.max_commands // 2] = []
            self.commands_reset.emit()

    def pop(self, index):
        """Remove a command by index."""
        self._commands.pop(index)
        self.command_removed.emit(index)

    def find_starting_with(self, firstpart, n=1):
        """Find the nth (1-based) command that starts with firstpart, or None."""
        count = 0
        for c in reversed(self._commands):
            if c.startswith(firstpart):
                count += 1
                if count >= n:
                    return c
        return None

    def find_all(self, needle):
        """Find all commands that contain the given text. In order
        of being used.
        """
        commands = []
        for c in reversed(self._commands):
            if needle in c:
                commands.append(c)
        return commands
예제 #26
0
파일: __init__.py 프로젝트: ghisvail/pyzo
class ToolManager(QtCore.QObject):
    """ Manages the tools. """

    # This signal indicates a change in the loaded tools
    toolInstanceChange = QtCore.Signal()

    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)

        # list of ToolDescription instances
        self._toolInfo = None
        self._activeTools = {}

    def loadToolInfo(self):
        """ (re)load the tool information. 
        """
        # Get paths to load files from
        toolDir1 = os.path.join(pyzo.pyzoDir, 'tools')
        toolDir2 = os.path.join(pyzo.appDataDir, 'tools')

        # Create list of tool files
        toolfiles = []
        for toolDir in [toolDir1, toolDir2]:
            tmp = [os.path.join(toolDir, f) for f in os.listdir(toolDir)]
            toolfiles.extend(tmp)

        # Note: we do not use the code below anymore, since even the frozen
        # app makes use of the .py files.
#         # Get list of files, also when we're in a zip file.
#         i = tooldir.find('.zip')
#         if i>0:
#             # Get list of files from zipfile
#             tooldir = tooldir[:i+4]
#             import zipfile
#             z = zipfile.ZipFile(tooldir)
#             toolfiles = [os.path.split(i)[1] for i in z.namelist()
#                         if i.startswith('visvis') and i.count('functions')]
#         else:
#             # Get list of files from file system
#             toolfiles = os.listdir(tooldir)

# Iterate over tool modules
        newlist = []
        for file in toolfiles:
            modulePath = file

            # Check
            if os.path.isdir(file):
                file = os.path.join(file, '__init__.py')  # A package perhaps
                if not os.path.isfile(file):
                    continue
            elif file.endswith('__.py') or not file.endswith('.py'):
                continue
            elif file.endswith('pyzoFileBrowser.py'):
                # Skip old file browser (the file can be there from a previous install)
                continue

            #
            toolName = ""
            toolSummary = ""
            # read file to find name or summary
            linecount = 0
            for line in open(file, encoding='utf-8'):
                linecount += 1
                if linecount > 50:
                    break
                if line.startswith("tool_name"):
                    i = line.find("=")
                    if i < 0: continue
                    line = line.rstrip("\n").rstrip("\r")
                    line = line[i + 1:].strip(" ")
                    toolName = line.strip("'").strip('"')
                elif line.startswith("tool_summary"):
                    i = line.find("=")
                    if i < 0: continue
                    line = line.rstrip("\n").rstrip("\r")
                    line = line[i + 1:].strip(" ")
                    toolSummary = line.strip("'").strip('"')
                else:
                    pass

            # Add stuff
            tmp = ToolDescription(modulePath, toolName, toolSummary)
            newlist.append(tmp)

        # Store and return
        self._toolInfo = sorted(newlist, key=lambda x: x.id)
        self.updateToolInstances()
        return self._toolInfo

    def updateToolInstances(self):
        """ Make tool instances up to date, so that it can be seen what
        tools are now active. """
        for toolDes in self.getToolInfo():
            if toolDes.id in self._activeTools:
                toolDes.instance = self._activeTools[toolDes.id]
            else:
                toolDes.instance = None

        # Emit update signal
        self.toolInstanceChange.emit()

    def getToolInfo(self):
        """ Like loadToolInfo(), but use buffered instance if available.
        """
        if self._toolInfo is None:
            self.loadToolInfo()
        return self._toolInfo

    def getToolClass(self, toolId):
        """ Get the class of the tool.
        It will import (and reload) the module and get the class.
        Some checks are performed, like whether the class inherits 
        from QWidget.
        Returns the class or None if failed...
        """

        # Make sure we have the info
        if self._toolInfo is None:
            self.loadToolInfo()

        # Get module name and path
        for toolDes in self._toolInfo:
            if toolDes.id == toolId:
                moduleName = toolDes.moduleName
                modulePath = toolDes.modulePath
                break
        else:
            print("WARNING: could not find module for tool", repr(toolId))
            return None

        # Remove from sys.modules, to force the module to reload
        for key in [key for key in sys.modules]:
            if key and key.startswith('pyzo.tools.' + moduleName):
                del sys.modules[key]

        # Load module
        try:
            m_file, m_fname, m_des = imp.find_module(
                moduleName, [os.path.dirname(modulePath)])
            mod = imp.load_module('pyzo.tools.' + moduleName, m_file, m_fname,
                                  m_des)
        except Exception as why:
            print("Invalid tool " + toolId + ":", why)
            return None

        # Is the expected class present?
        className = ""
        for member in dir(mod):
            if member.lower() == toolId:
                className = member
                break
        else:
            print("Invalid tool, Classname must match module name '%s'!" %
                  toolId)
            return None

        # Does it inherit from QWidget?
        plug = mod.__dict__[className]
        if not (isinstance(plug, type) and issubclass(plug, QtGui.QWidget)):
            print("Invalid tool, tool class must inherit from QWidget!")
            return None

        # Succes!
        return plug

    def loadTool(self, toolId, splitWith=None):
        """ Load a tool by creating a dock widget containing the tool widget.
        """

        # A tool id should always be lower case
        toolId = toolId.lower()

        # Close old one
        if toolId in self._activeTools:
            old = self._activeTools[toolId].widget()
            self._activeTools[toolId].setWidget(QtGui.QWidget(pyzo.main))
            if old:
                old.close()
                old.deleteLater()

        # Get tool class (returns None on failure)
        toolClass = self.getToolClass(toolId)
        if toolClass is None:
            return

        # Already loaded? reload!
        if toolId in self._activeTools:
            self._activeTools[toolId].reload(toolClass)
            return

        # Obtain name from buffered list of names
        for toolDes in self._toolInfo:
            if toolDes.id == toolId:
                name = toolDes.name
                break
        else:
            name = toolId

        # Make sure there is a config entry for this tool
        if not hasattr(pyzo.config.tools, toolId):
            pyzo.config.tools[toolId] = ssdf.new()

        # Create dock widget and add in the main window
        dock = ToolDockWidget(pyzo.main, self)
        dock.setTool(toolId, name, toolClass)

        if splitWith and splitWith in self._activeTools:
            otherDock = self._activeTools[splitWith]
            pyzo.main.splitDockWidget(otherDock, dock, QtCore.Qt.Horizontal)
        else:
            pyzo.main.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)

        # Add to list
        self._activeTools[toolId] = dock
        self.updateToolInstances()

    def reloadTools(self):
        """ Reload all tools. """
        for id in self.getLoadedTools():
            self.loadTool(id)

    def closeTool(self, toolId):
        """ Close the tool with specified id.
        """
        if toolId in self._activeTools:
            dock = self._activeTools[toolId]
            dock.close()

    def getTool(self, toolId):
        """ Get the tool widget instance, or None
        if not available. """
        if toolId in self._activeTools:
            return self._activeTools[toolId].widget()
        else:
            return None

    def onToolClose(self, toolId):
        # Remove from dict
        self._activeTools.pop(toolId, None)
        # Set instance to None
        self.updateToolInstances()

    def getLoadedTools(self):
        """ Get a list with id's of loaded tools. """
        tmp = []
        for toolDes in self.getToolInfo():
            if toolDes.id in self._activeTools:
                tmp.append(toolDes.id)
        return tmp
예제 #27
0
파일: main.py 프로젝트: stonebig/pyzo
 def postEventWithCallback(self, callback, *args):
     self.queue.put((callback, args))
     QtWidgets.qApp.postEvent(self, QtCore.QEvent(QtCore.QEvent.User))
예제 #28
0
    def __init__(self, parent):
        QtWidgets.QFrame.__init__(self, parent)

        # Init config
        toolId = self.__class__.__name__.lower()
        self._config = pyzo.config.tools[toolId]
        if not hasattr(self._config, 'zoomFactor'):
            self._config.zoomFactor = 1.0
        if not hasattr(self._config, 'bookMarks'):
            self._config.bookMarks = []
        for item in default_bookmarks:
            if item not in self._config.bookMarks:
                self._config.bookMarks.append(item)

        # Get style object (for icons)
        style = QtWidgets.QApplication.style()

        # Create some buttons
        self._back = QtWidgets.QToolButton(self)
        self._back.setIcon(style.standardIcon(style.SP_ArrowBack))
        self._back.setIconSize(QtCore.QSize(16, 16))
        #
        self._forward = QtWidgets.QToolButton(self)
        self._forward.setIcon(style.standardIcon(style.SP_ArrowForward))
        self._forward.setIconSize(QtCore.QSize(16, 16))

        # Create address bar
        #self._address = QtWidgets.QLineEdit(self)
        self._address = QtWidgets.QComboBox(self)
        self._address.setEditable(True)
        self._address.setInsertPolicy(self._address.NoInsert)
        #
        for a in self._config.bookMarks:
            self._address.addItem(a)
        self._address.setEditText('')

        # Create web view
        if imported_qtwebkit:
            self._view = QtWebKit.QWebView(self)
        else:
            self._view = WebView(self)
        #
#         self._view.setZoomFactor(self._config.zoomFactor)
#         settings = self._view.settings()
#         settings.setAttribute(settings.JavascriptEnabled, True)
#         settings.setAttribute(settings.PluginsEnabled, True)

# Layout
        self._sizer1 = QtWidgets.QVBoxLayout(self)
        self._sizer2 = QtWidgets.QHBoxLayout()
        #
        self._sizer2.addWidget(self._back, 0)
        self._sizer2.addWidget(self._forward, 0)
        self._sizer2.addWidget(self._address, 1)
        #
        self._sizer1.addLayout(self._sizer2, 0)
        self._sizer1.addWidget(self._view, 1)
        #
        self._sizer1.setSpacing(2)
        self.setLayout(self._sizer1)

        # Bind signals
        self._back.clicked.connect(self.onBack)
        self._forward.clicked.connect(self.onForward)
        self._address.lineEdit().returnPressed.connect(self.go)
        self._address.activated.connect(self.go)
        self._view.loadFinished.connect(self.onLoadEnd)
        self._view.loadStarted.connect(self.onLoadStart)

        # Start
        self._view.show()
        self.go('http://docs.python.org')
예제 #29
0
class Installer(QtWidgets.QDialog):
    
    lineFromStdOut = QtCore.Signal(str)
    
    def __init__(self, parent=None):
        QtWidgets.QDialog.__init__(self, parent)
        self.setWindowTitle('Install miniconda')
        self.setModal(True)
        self.resize(500, 500)
        
        text = translate('bootstrapconda', 'This will download and install miniconda on your computer.')
        
        self._label = QtWidgets.QLabel(text, self)
        
        self._scipystack = QtWidgets.QCheckBox(translate('bootstrapconda', 'Also install scientific packages'), self)
        self._scipystack.setChecked(True)
        self._path = QtWidgets.QLineEdit(default_conda_dir, self)
        self._progress = QtWidgets.QProgressBar(self)
        self._outputLine = QtWidgets.QLabel(self)
        self._output = QtWidgets.QPlainTextEdit(self)
        self._output.setReadOnly(True)
        self._button = QtWidgets.QPushButton('Install', self)
        
        self._outputLine.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
        
        vbox = QtWidgets.QVBoxLayout(self)
        self.setLayout(vbox)
        vbox.addWidget(self._label, 0)
        vbox.addWidget(self._path, 0)
        vbox.addWidget(self._scipystack, 0)
        vbox.addWidget(self._progress, 0)
        vbox.addWidget(self._outputLine, 0)
        vbox.addWidget(self._output, 1)
        vbox.addWidget(self._button, 0)
        
        self._button.clicked.connect(self.go)
        
        self.addOutput(translate('bootstrapconda', 'Waiting to start installation.\n'))
        self._progress.setVisible(False)
        
        self.lineFromStdOut.connect(self.setStatus)
    
    def setStatus(self, line):
        self._outputLine.setText(line)
    
    def addOutput(self, text):
        #self._output.setPlainText(self._output.toPlainText() + '\n' + text)
        cursor = self._output.textCursor()
        cursor.movePosition(cursor.End, cursor.MoveAnchor)
        cursor.insertText(text)
        cursor.movePosition(cursor.End, cursor.MoveAnchor)
        self._output.setTextCursor(cursor)
        self._output.ensureCursorVisible()
    
    def addStatus(self, line):
        self.addOutput('\n' + line)
        self.setStatus(line)
    
    def go(self):
        
        # Check if we can install
        try:
            self._conda_dir = self._path.text()
            if not os.path.isabs(self._conda_dir):
                raise ValueError('Given installation path must be absolute.')
            if os.path.exists(self._conda_dir):
                raise ValueError('The given installation path already exists.')
        except Exception as err:
            self.addOutput('\nCould not install:\n' + str(err))
            return
        
        ok = False
        
        try:
            
            # Disable user input, get ready for installation
            self._progress.setVisible(True)
            self._button.clicked.disconnect()
            self._button.setEnabled(False)
            self._scipystack.setEnabled(False)
            self._path.setEnabled(False)
            
            if not os.path.exists(self._conda_dir):
                self.addStatus('Downloading installer ... ')
                self._progress.setMaximum(100)
                self.download()
                self.addStatus('Done downloading installer.')
                self.make_done()
                
                self.addStatus('Installing (this can take a minute) ... ')
                self._progress.setMaximum(0)
                ret = self.install()
                self.addStatus(('Failed' if ret else 'Done') + ' installing.')
                self.make_done()
            
            self.post_install()
            
            if self._scipystack.isChecked():
                self.addStatus('Installing scientific packages ... ')
                self._progress.setMaximum(0)
                ret = self.install_scipy()
                self.addStatus('Done installing scientific packages')
                self.make_done()
            
            self.addStatus('Verifying ... ')
            self._progress.setMaximum(100)
            ret = self.verify()
            if ret:
                self.addOutput('Error\n' + ret)
                self.addStatus('Verification Failed!')
            else:
                self.addOutput('Done verifying')
                self.addStatus('Ready to go!')
                self.make_done()
                ok = True
        
        except Exception as err:
            self.addStatus('Installation failed ...')
            self.addOutput('\n\nException!\n' + str(err))
        
        if not ok:
            self.addOutput('\n\nWe recommend installing miniconda or anaconda, ')
            self.addOutput('and making Pyzo aware if it via the shell configuration.')
        else:
            self.addOutput('\n\nYou can install additional packages by running "conda install" in the shell.')
        
        # Wrap up, allow user to exit
        self._progress.hide()
        self._button.setEnabled(True)
        self._button.setText('Close')
        self._button.clicked.connect(self.close)
    
    def make_done(self):
        self._progress.setMaximum(100)
        self._progress.setValue(100)
        etime = time.time() + 0.2
        while time.time() < etime:
            time.sleep(0.01)
            QtWidgets.qApp.processEvents()
    
    def download(self):
        
        # Installer already downloaded?
        if os.path.isfile(miniconda_path):
            self.addOutput('Already downloaded.')
            return  # os.remove(miniconda_path)
        
        # Get url key
        key = ''
        if sys.platform.startswith('win'):
            key = 'win'
        elif sys.platform.startswith('darwin'):
            key = 'osx'
        elif sys.platform.startswith('linux'):
            key = 'linux'
        key += '64' if is_64bit() else '32'
        
        # Get url
        if key not in links:
            raise RuntimeError('Cannot download miniconda for this platform.')
        url = base_url + links[key]
        
        _fetch_file(url, miniconda_path, self._progress)
    
    def install(self):
        dest = self._conda_dir
        
        # Clear dir 
        assert not os.path.isdir(dest), 'Miniconda dir already exists'
        assert ' ' not in dest, 'miniconda dest path must not contain spaces'
        
        if sys.platform.startswith('win'):
            return self._run_process([miniconda_path, '/S', '/D=%s' % dest])
        else:
            os.chmod(miniconda_path, os.stat(miniconda_path).st_mode | stat.S_IEXEC)
            return self._run_process([miniconda_path, '-b', '-p', dest])
    
    def post_install(self):
        
        exe = py_exe(self._conda_dir)
        
        # Add Pyzo channel
        cmd = [exe, '-m', 'conda', 'config', '--system', '--add', 'channels', 'pyzo']
        subprocess.check_call(cmd, shell=sys.platform.startswith('win'))
        self.addStatus('Added Pyzo channel to conda env')
        
        # Add to pyzo shell config
        first = None
        if pyzo.config.shellConfigs2 and pyzo.config.shellConfigs2[0]['exe'] == exe:
            pass
        else:
            s = pyzo.ssdf.new()
            s.name = 'Py3-conda'
            s.exe = exe
            s.gui='PyQt4'
            pyzo.config.shellConfigs2.insert(0, s)
            pyzo.saveConfig()
            self.addStatus('Prepended new env to Pyzo shell configs.')
    
    def install_scipy(self):
        
        packages = ['numpy', 'scipy', 'pandas', 'matplotlib', 'sympy',
                    #'scikit-image', 'scikit-learn', 
                    'pyopengl', # 'visvis', 'imageio',
                    'tornado', 'pyqt', #'ipython', 'jupyter',
                    #'requests', 'pygments','pytest', 
                    ]
        exe = py_exe(self._conda_dir)
        cmd = [exe, '-m', 'conda', 'install', '--yes'] + packages
        return self._run_process(cmd)
    
    def _run_process(self, cmd):
        """ Run command in a separate process, catch stdout, show lines
        in the output label. On fail, show all output in output text.
        """
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=sys.platform.startswith('win'))
        catcher = StreamCatcher(p.stdout, self.lineFromStdOut)
        
        while p.poll() is None:
            time.sleep(0.01)
            QtWidgets.qApp.processEvents()
        
        catcher.join()
        if p.poll():
            self.addOutput(catcher.output())
        return p.poll()
    
    def verify(self):
        
        self._progress.setValue(1)
        if not os.path.isdir(self._conda_dir):
            return 'Conda dir not created.'
        
        self._progress.setValue(11)
        exe = py_exe(self._conda_dir)
        if not os.path.isfile(exe):
           return 'Conda dir does not have Python exe' 
        
        self._progress.setValue(21)
        try:
            ver = subprocess.check_output([exe, '-c', 'import sys; print(sys.version)'])
        except Exception as err:
            return 'Error getting Python version: ' + str(err)
        
        self._progress.setValue(31)
        if ver.decode() < '3.4':
            return 'Expected Python version 3.4 or higher'
        
        self._progress.setValue(41)
        try:
            ver = subprocess.check_output([exe, '-c', 'import conda; print(conda.__version__)'])
        except Exception as err:
            return 'Error calling Python exe: ' + str(err)
        
        self._progress.setValue(51)
        if ver.decode() < '3.16':
            return 'Expected Conda version 3.16 or higher'
        
        # Smooth toward 100%
        for i in range(self._progress.value(), 100, 5):
            time.sleep(0.05)
            self._progress.setValue(i)
            QtWidgets.qApp.processEvents()
예제 #30
0
class WebView(QtWidgets.QTextBrowser):
    """ Inherit the webview class to implement zooming using
    the mouse wheel. 
    """

    loadStarted = QtCore.Signal()
    loadFinished = QtCore.Signal(bool)

    def __init__(self, parent):
        QtWidgets.QTextBrowser.__init__(self, parent)

        # Current url
        self._url = ''
        self._history = []
        self._history2 = []

        # Connect
        self.anchorClicked.connect(self.load)

    def wheelEvent(self, event):
        # Zooming does not work for this widget
        if QtCore.Qt.ControlModifier & QtWidgets.qApp.keyboardModifiers():
            self.parent().wheelEvent(event)
        else:
            QtWidgets.QTextBrowser.wheelEvent(self, event)

    def url(self):
        return self._url

    def _getUrlParts(self):
        r = urllib.parse.urlparse(self._url)
        base = r.scheme + '://' + r.netloc
        return base, r.path, r.fragment


#
#     def loadCss(self, urls=[]):
#         urls.append('http://docs.python.org/_static/default.css')
#         urls.append('http://docs.python.org/_static/pygments.css')
#         text = ''
#         for url in urls:
#             tmp = urllib.request.urlopen(url).read().decode('utf-8')
#             text += '\n' + tmp
#         self.document().setDefaultStyleSheet(text)

    def back(self):

        # Get url and update forward history
        url = self._history.pop()
        self._history2.append(self._url)

        # Go there
        url = self._load(url)

    def forward(self):

        if not self._history2:
            return

        # Get url and update forward history
        url = self._history2.pop()
        self._history.append(self._url)

        # Go there
        url = self._load(url)

    def load(self, url):

        # Clear forward history
        self._history2 = []

        # Store current url in history
        while self._url in self._history:
            self._history.remove(self._url)
        self._history.append(self._url)

        # Load
        url = self._load(url)

    def _load(self, url):
        """ _load(url)
        Convert url and load page, returns new url.
        """
        # Make url a string
        if isinstance(url, QtCore.QUrl):
            url = str(url.toString())

        # Compose relative url to absolute
        if url.startswith('#'):
            base, path, frag = self._getUrlParts()
            url = base + path + url
        elif not '//' in url:
            base, path, frag = self._getUrlParts()
            url = base + '/' + url.lstrip('/')

        # Try loading
        self.loadStarted.emit()
        self._url = url
        try:
            #print('URL:', url)
            text = urllib.request.urlopen(url).read().decode('utf-8')
            self.setHtml(text)
            self.loadFinished.emit(True)
        except Exception as err:
            self.setHtml(str(err))
            self.loadFinished.emit(False)

        # Set
        return url