2016-11-02 37 views
1

メモ帳++のようにPyQtの左側に行番号をつけてtextEditを作成します。私は別のtextEditを追加しようとしましたが、スクロールが止まっています。私はこれを検索して見つけましたが、questionですが、それには良い解決策はありません。PyQtの行番号を使ってテキストエリア(textEdit)を作成する

+0

[PyQtは中CodeEditor例]の可能性のある重複します(http:// stackoverflo w.com/questions/33243852/codeeditor-example-in-pyqt) – ekhumoro

答えて

3

では、C++ http://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html

は(ない5 PyQt4を使用してイムを私はそれが似ていると思います)のpython3のために一緒にそれを置く(およびQTextEdit vs QPlainTextEditを参照してくださいQTextEditないQPlainTextEditを使用)に基づいてCodeEditor example in pyqt探しているものがこれです:

ここ
import sys 
from PyQt4.QtGui import * 
from PyQt4.QtCore import * 

import numpy as np 

class LineNumberArea(QWidget): 


    def __init__(self, editor): 
     super().__init__(editor) 
     self.myeditor = editor 


    def sizeHint(self): 
     return Qsize(self.editor.lineNumberAreaWidth(), 0) 


    def paintEvent(self, event): 
     self.myeditor.lineNumberAreaPaintEvent(event) 


class CodeEditor(QPlainTextEdit): 
    def __init__(self): 
     super().__init__() 
     self.lineNumberArea = LineNumberArea(self) 

     self.connect(self, SIGNAL('blockCountChanged(int)'), self.updateLineNumberAreaWidth) 
     self.connect(self, SIGNAL('updateRequest(QRect,int)'), self.updateLineNumberArea) 
     self.connect(self, SIGNAL('cursorPositionChanged()'), self.highlightCurrentLine) 

     self.updateLineNumberAreaWidth(0) 


    def lineNumberAreaWidth(self): 
     digits = 1 
     count = max(1, self.blockCount()) 
     while count >= 10: 
      count /= 10 
      digits += 1 
     space = 3 + self.fontMetrics().width('9') * digits 
     return space 


    def updateLineNumberAreaWidth(self, _): 
     self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) 


    def updateLineNumberArea(self, rect, dy): 

     if dy: 
      self.lineNumberArea.scroll(0, dy) 
     else: 
      self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), 
         rect.height()) 

     if rect.contains(self.viewport().rect()): 
      self.updateLineNumberAreaWidth(0) 


    def resizeEvent(self, event): 
     super().resizeEvent(event) 

     cr = self.contentsRect(); 
     self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), 
        self.lineNumberAreaWidth(), cr.height())) 


    def lineNumberAreaPaintEvent(self, event): 
     mypainter = QPainter(self.lineNumberArea) 

     mypainter.fillRect(event.rect(), Qt.lightGray) 

     block = self.firstVisibleBlock() 
     blockNumber = block.blockNumber() 
     top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top() 
     bottom = top + self.blockBoundingRect(block).height() 

     # Just to make sure I use the right font 
     height = self.fontMetrics().height() 
     while block.isValid() and (top <= event.rect().bottom()): 
      if block.isVisible() and (bottom >= event.rect().top()): 
       number = str(blockNumber + 1) 
       mypainter.setPen(Qt.black) 
       mypainter.drawText(0, top, self.lineNumberArea.width(), height, 
       Qt.AlignRight, number) 

      block = block.next() 
      top = bottom 
      bottom = top + self.blockBoundingRect(block).height() 
      blockNumber += 1 


    def highlightCurrentLine(self): 
     extraSelections = [] 

     if not self.isReadOnly(): 
      selection = QTextEdit.ExtraSelection() 

      lineColor = QColor(Qt.yellow).lighter(160) 

      selection.format.setBackground(lineColor) 
      selection.format.setProperty(QTextFormat.FullWidthSelection, True) 
      selection.cursor = self.textCursor() 
      selection.cursor.clearSelection() 
      extraSelections.append(selection) 
     self.setExtraSelections(extraSelections) 


if __name__ == "__main__": 
    app = QApplication(sys.argv) 

    txt = CodeEditor() 
    txt.show() 

    sys.exit(app.exec_()) 
+0

多くのありがとう、兄弟、これは実際に私が欲しいものです。 – bzimor

0

PyQt5のバージョン(メニュー付きなど...)

#!/usr/bin/python3 
# -- coding: utf-8 -- 

from PyQt5.QtWidgets import QPlainTextEdit, QWidget, QVBoxLayout, QApplication, QFileDialog, QMessageBox, QHBoxLayout, \ 
         QFrame, QTextEdit, QToolBar, QComboBox, QLabel, QAction, QLineEdit, QToolButton, QMenu, QMainWindow 
from PyQt5.QtGui import QIcon, QPainter, QTextFormat, QColor, QTextCursor, QKeySequence, QClipboard, QTextCharFormat, QPalette 
from PyQt5.QtCore import Qt, QVariant, QRect, QDir, QFile, QFileInfo, QTextStream, QRegExp, QSettings 
import sys, os 

lineBarColor = QColor("#ACDED5") 
lineHighlightColor = QColor("#ACDED5") 

class NumberBar(QWidget): 
    def __init__(self, parent = None): 
     super(NumberBar, self).__init__(parent) 
     self.editor = parent 
     layout = QVBoxLayout() 
     self.setLayout(layout) 
     self.editor.blockCountChanged.connect(self.update_width) 
     self.editor.updateRequest.connect(self.update_on_scroll) 
     self.update_width('1') 

    def update_on_scroll(self, rect, scroll): 
     if self.isVisible(): 
      if scroll: 
       self.scroll(0, scroll) 
      else: 
       self.update() 

    def update_width(self, string): 
     width = self.fontMetrics().width(str(string)) + 10 
     if self.width() != width: 
      self.setFixedWidth(width) 

    def paintEvent(self, event): 
     if self.isVisible(): 
      block = self.editor.firstVisibleBlock() 
      height = self.fontMetrics().height() 
      number = block.blockNumber() 
      painter = QPainter(self) 
      painter.fillRect(event.rect(), lineBarColor) 
      painter.drawRect(0, 0, event.rect().width() - 1, event.rect().height() - 1) 
      font = painter.font() 

      current_block = self.editor.textCursor().block().blockNumber() + 1 

      condition = True 
      while block.isValid() and condition: 
       block_geometry = self.editor.blockBoundingGeometry(block) 
       offset = self.editor.contentOffset() 
       block_top = block_geometry.translated(offset).top() 
       number += 1 

       rect = QRect(0, block_top, self.width() - 5, height) 

       if number == current_block: 
        font.setBold(True) 
       else: 
        font.setBold(False) 

       painter.setFont(font) 
       painter.drawText(rect, Qt.AlignRight, '%i'%number) 

       if block_top > event.rect().bottom(): 
        condition = False 

       block = block.next() 

      painter.end() 

class myEditor(QMainWindow): 
    def __init__(self, parent = None): 
     super(myEditor, self).__init__(parent) 
     self.MaxRecentFiles = 5 
     self.windowList = [] 
     self.recentFileActs = [] 
     self.setAttribute(Qt.WA_DeleteOnClose) 
     # Editor Widget ... 
     QIcon.setThemeName('Faenza-Dark') 
     self.editor = QPlainTextEdit() 
     self.editor.setStyleSheet(stylesheet2(self)) 
     self.editor.setFrameStyle(QFrame.NoFrame) 
     self.editor.setTabStopWidth(14) 
     self.extra_selections = [] 
     self.fname = "" 
     self.filename = "" 
     # Line Numbers ... 
     self.numbers = NumberBar(self.editor) 

     self.createActions() 
     # Laying out... 
     layoutH = QHBoxLayout() 
     layoutH.setSpacing(1.5) 
     layoutH.addWidget(self.numbers) 
     layoutH.addWidget(self.editor) 

     ### begin toolbar 
     tb = QToolBar(self) 
     tb.setWindowTitle("File Toolbar")   

     self.newAct = QAction("&New", self, shortcut=QKeySequence.New, 
       statusTip="Create a new file", triggered=self.newFile) 
     self.newAct.setIcon(QIcon.fromTheme("document-new")) 

     self.openAct = QAction("&Open", self, shortcut=QKeySequence.Open, 
       statusTip="open file", triggered=self.openFile) 
     self.openAct.setIcon(QIcon.fromTheme("document-open")) 

     self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save, 
       statusTip="save file", triggered=self.fileSave) 
     self.saveAct.setIcon(QIcon.fromTheme("document-save")) 

     self.saveAsAct = QAction("&Save as ...", self, shortcut=QKeySequence.SaveAs, 
       statusTip="save file as ...", triggered=self.fileSaveAs) 
     self.saveAsAct.setIcon(QIcon.fromTheme("document-save-as")) 

     self.exitAct = QAction("Exit", self, shortcut=QKeySequence.Quit, 
       toolTip="Exit", triggered=self.handleQuit) 
     self.exitAct.setIcon(QIcon.fromTheme("application-exit")) 

     ### find/replace toolbar 
     self.tbf = QToolBar(self) 
     self.tbf.setWindowTitle("Find Toolbar") 
     self.findfield = QLineEdit() 
     self.findfield.addAction(QIcon.fromTheme("edit-find"), QLineEdit.LeadingPosition) 
     self.findfield.setClearButtonEnabled(True) 
     self.findfield.setFixedWidth(150) 
     self.findfield.setPlaceholderText("find") 
     self.findfield.setToolTip("press RETURN to find") 
     self.findfield.setText("") 
     ft = self.findfield.text() 
     self.findfield.returnPressed.connect(self.findText) 
     self.tbf.addWidget(self.findfield) 
     self.replacefield = QLineEdit() 
     self.replacefield.addAction(QIcon.fromTheme("edit-find-and-replace"), QLineEdit.LeadingPosition) 
     self.replacefield.setClearButtonEnabled(True) 
     self.replacefield.setFixedWidth(150) 
     self.replacefield.setPlaceholderText("replace with") 
     self.replacefield.setToolTip("press RETURN to replace the first") 
     self.replacefield.returnPressed.connect(self.replaceOne) 
     self.tbf.addSeparator() 
     self.tbf.addWidget(self.replacefield) 
     self.tbf.addSeparator() 

     self.tbf.addAction("replace all", self.replaceAll) 
     self.tbf.addSeparator() 

     layoutV = QVBoxLayout() 

     bar=self.menuBar() 

     self.filemenu=bar.addMenu("File") 
     self.separatorAct = self.filemenu.addSeparator() 
     self.filemenu.addAction(self.newAct) 
     self.filemenu.addAction(self.openAct) 
     self.filemenu.addAction(self.saveAct) 
     self.filemenu.addAction(self.saveAsAct) 
     self.filemenu.addSeparator() 
     for i in range(self.MaxRecentFiles): 
      self.filemenu.addAction(self.recentFileActs[i]) 
     self.updateRecentFileActions() 
     self.filemenu.addSeparator() 
     self.filemenu.addAction(self.exitAct) 
     bar.setStyleSheet(stylesheet2(self)) 
     editmenu = bar.addMenu("Edit") 
     editmenu.addAction(QAction(QIcon.fromTheme('edit-copy'), "Copy", self, triggered = self.editor.copy, shortcut = QKeySequence.Copy)) 
     editmenu.addAction(QAction(QIcon.fromTheme('edit-cut'), "Cut", self, triggered = self.editor.cut, shortcut = QKeySequence.Cut)) 
     editmenu.addAction(QAction(QIcon.fromTheme('edit-paste'), "Paste", self, triggered = self.editor.paste, shortcut = QKeySequence.Paste)) 
     editmenu.addAction(QAction(QIcon.fromTheme('edit-delete'), "Delete", self, triggered = self.editor.cut, shortcut = QKeySequence.Delete)) 
     editmenu.addSeparator() 
     editmenu.addAction(QAction(QIcon.fromTheme('edit-select-all'), "Select All", self, triggered = self.editor.selectAll, shortcut = QKeySequence.SelectAll)) 

     layoutV.addWidget(bar)  
     layoutV.addWidget(self.tbf) 
     layoutV.addLayout(layoutH) 

     ### main window 
     mq = QWidget(self) 
     mq.setLayout(layoutV) 
     self.setCentralWidget(mq) 

     # Event Filter ... 
     self.installEventFilter(self) 
     self.editor.setFocus() 
     self.cursor = QTextCursor() 
     self.editor.setPlainText("hello") 
     self.editor.moveCursor(self.cursor.End) 
     self.editor.document().modificationChanged.connect(self.setWindowModified) 

     # Brackets ExtraSelection ... 
     self.left_selected_bracket = QTextEdit.ExtraSelection() 
     self.right_selected_bracket = QTextEdit.ExtraSelection() 

    def createActions(self): 
     for i in range(self.MaxRecentFiles): 
      self.recentFileActs.append(
        QAction(self, visible=False, 
          triggered=self.openRecentFile)) 


    def openRecentFile(self): 
     action = self.sender() 
     if action: 
      if (self.maybeSave()): 
       self.openFileOnStart(action.data()) 

     ### New File 
    def newFile(self): 
     if self.maybeSave(): 
      self.editor.clear() 
      self.editor.setPlainText("") 
      self.filename = "" 
      self.setModified(False) 
      self.editor.moveCursor(self.cursor.End) 

     ### open File 
    def openFileOnStart(self, path=None): 
     if path: 
      inFile = QFile(path) 
      if inFile.open(QFile.ReadWrite | QFile.Text): 
       text = inFile.readAll() 

       try: 
         # Python v3. 
        text = str(text, encoding = 'utf8') 
       except TypeError: 
         # Python v2. 
        text = str(text) 
       self.editor.setPlainText(text) 
       self.filename = path 
       self.setModified(False) 
       self.fname = QFileInfo(path).fileName() 
       self.setWindowTitle(self.fname + "[*]") 
       self.document = self.editor.document() 
       self.setCurrentFile(self.filename) 

     ### open File 
    def openFile(self, path=None): 
     if self.maybeSave(): 
      if not path: 
       path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Documents/", 
        "Text Files (*.txt *.csv *.py);;All Files (*.*)") 

      if path: 
       inFile = QFile(path) 
       if inFile.open(QFile.ReadWrite | QFile.Text): 
        text = inFile.readAll() 

        try: 
         # Python v3. 
         text = str(text, encoding = 'utf8') 
        except TypeError: 
         # Python v2. 
         text = str(text) 
        self.editor.setPlainText(text) 
        self.filename = path 
        self.setModified(False) 
        self.fname = QFileInfo(path).fileName() 
        self.setWindowTitle(self.fname + "[*]") 
        self.document = self.editor.document() 
        self.setCurrentFile(self.filename) 

    def fileSave(self): 
     if (self.filename != ""): 
      file = QFile(self.filename) 
      print(self.filename) 
      if not file.open(QFile.WriteOnly | QFile.Text): 
       QMessageBox.warning(self, "Error", 
         "Cannot write file %s:\n%s." % (self.filename, file.errorString())) 
       return 

      outstr = QTextStream(file) 
      QApplication.setOverrideCursor(Qt.WaitCursor) 
      outstr << self.editor.toPlainText() 
      QApplication.restoreOverrideCursor()     
      self.setModified(False) 
      self.fname = QFileInfo(self.filename).fileName() 
      self.setWindowTitle(self.fname + "[*]") 
      self.setCurrentFile(self.filename) 


     else: 
      self.fileSaveAs() 

      ### save File 
    def fileSaveAs(self): 
     fn, _ = QFileDialog.getSaveFileName(self, "Save as...", self.filename, 
       "Python files (*.py)") 

     if not fn: 
      print("Error saving") 
      return False 

     lfn = fn.lower() 
     if not lfn.endswith('.py'): 
      fn += '.py' 

     self.filename = fn 
     self.fname = os.path.splitext(str(fn))[0].split("/")[-1] 
     return self.fileSave() 

    def closeEvent(self, e): 
     if self.maybeSave(): 
      e.accept() 
     else: 
      e.ignore() 

     ### ask to save 
    def maybeSave(self): 
     if not self.isModified(): 
      return True 

     if self.filename.startswith(':/'): 
      return True 

     ret = QMessageBox.question(self, "Message", 
       "<h4><p>The document was modified.</p>\n" \ 
       "<p>Do you want to save changes?</p></h4>", 
       QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) 

     if ret == QMessageBox.Yes: 
      if self.filename == "": 
       self.fileSaveAs() 
       return False 
      else: 
       self.fileSave() 
       return True 

     if ret == QMessageBox.Cancel: 
      return False 

     return True 

    def findText(self): 
     ft = self.findfield.text() 
     if self.editor.find(ft): 
      return 
     else: 
      self.editor.moveCursor(1) 
      if self.editor.find(ft): 
       self.editor.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor) 

    def handleQuit(self): 
     print("Goodbye ...") 
     app.quit() 

    def set_numbers_visible(self, value = True): 
     self.numbers.setVisible(False) 

    def match_left(self, block, character, start, found): 
     map = {'{': '}', '(': ')', '[': ']'} 

     while block.isValid(): 
      data = block.userData() 
      if data is not None: 
       braces = data.braces 
       N = len(braces) 

       for k in range(start, N): 
        if braces[k].character == character: 
         found += 1 

        if braces[k].character == map[character]: 
         if not found: 
          return braces[k].position + block.position() 
         else: 
          found -= 1 

       block = block.next() 
       start = 0 

    def match_right(self, block, character, start, found): 
     map = {'}': '{', ')': '(', ']': '['} 

     while block.isValid(): 
      data = block.userData() 

      if data is not None: 
       braces = data.braces 

       if start is None: 
        start = len(braces) 
       for k in range(start - 1, -1, -1): 
        if braces[k].character == character: 
         found += 1 
        if braces[k].character == map[character]: 
         if found == 0: 
          return braces[k].position + block.position() 
         else: 
          found -= 1 
      block = block.previous() 
      start = None 
# ''' 

     cursor = self.editor.textCursor() 
     block = cursor.block() 
     data = block.userData() 
     previous, next = None, None 

     if data is not None: 
      position = cursor.position() 
      block_position = cursor.block().position() 
      braces = data.braces 
      N = len(braces) 

      for k in range(0, N): 
       if braces[k].position == position - block_position or braces[k].position == position - block_position - 1: 
        previous = braces[k].position + block_position 
        if braces[k].character in ['{', '(', '[']: 
         next = self.match_left(block, 
               braces[k].character, 
               k + 1, 0) 
        elif braces[k].character in ['}', ')', ']']: 
         next = self.match_right(block, 
               braces[k].character, 
               k, 0) 
        if next is None: 
         next = -1 

     if next is not None and next > 0: 
      if next == 0 and next >= 0: 
       format = QTextCharFormat() 

      cursor.setPosition(previous) 
      cursor.movePosition(QTextCursor.NextCharacter, 
           QTextCursor.KeepAnchor) 

      format.setBackground(QColor('white')) 
      self.left_selected_bracket.format = format 
      self.left_selected_bracket.cursor = cursor 

      cursor.setPosition(next) 
      cursor.movePosition(QTextCursor.NextCharacter, 
           QTextCursor.KeepAnchor) 

      format.setBackground(QColor('white')) 
      self.right_selected_bracket.format = format 
      self.right_selected_bracket.cursor = cursor 
#   ''' 
    def paintEvent(self, event): 
     highlighted_line = QTextEdit.ExtraSelection() 
     highlighted_line.format.setBackground(lineHighlightColor) 
     highlighted_line.format.setProperty(QTextFormat 
               .FullWidthSelection, 
                QVariant(True)) 
     highlighted_line.cursor = self.editor.textCursor() 
     highlighted_line.cursor.clearSelection() 
     self.editor.setExtraSelections([highlighted_line, 
             self.left_selected_bracket, 
             self.right_selected_bracket]) 

    def document(self): 
     return self.editor.document 

    def isModified(self): 
     return self.editor.document().isModified() 

    def setModified(self, modified): 
     self.editor.document().setModified(modified) 

    def setLineWrapMode(self, mode): 
     self.editor.setLineWrapMode(mode) 

    def clear(self): 
     self.editor.clear() 

    def setPlainText(self, *args, **kwargs): 
     self.editor.setPlainText(*args, **kwargs) 

    def setDocumentTitle(self, *args, **kwargs): 
     self.editor.setDocumentTitle(*args, **kwargs) 

    def set_number_bar_visible(self, value): 
     self.numbers.setVisible(value) 

    def replaceAll(self): 
     print("replacing all") 
     oldtext = self.editor.document().toPlainText() 
     newtext = oldtext.replace(self.findfield.text(), self.replacefield.text()) 
     self.editor.setPlainText(newtext) 
     self.setModified(True) 

    def replaceOne(self): 
     print("replacing all") 
     oldtext = self.editor.document().toPlainText() 
     newtext = oldtext.replace(self.findfield.text(), self.replacefield.text(), 1) 
     self.editor.setPlainText(newtext) 
     self.setModified(True) 

    def setCurrentFile(self, fileName): 
     self.curFile = fileName 
     if self.curFile: 
      self.setWindowTitle("%s - Recent Files" % self.strippedName(self.curFile)) 
     else: 
      self.setWindowTitle("Recent Files") 

     settings = QSettings('Axel Schneider', 'PTEdit') 
     files = settings.value('recentFileList') 

     try: 
      files.remove(fileName) 
     except ValueError: 
      pass 

     files.insert(0, fileName) 
     del files[self.MaxRecentFiles:] 

     settings.setValue('recentFileList', files) 

     for widget in QApplication.topLevelWidgets(): 
      if isinstance(widget, myEditor): 
       widget.updateRecentFileActions() 

    def updateRecentFileActions(self): 
     mytext = "" 
     settings = QSettings('Axel Schneider', 'PTEdit') 
     files = settings.value('recentFileList') 

     numRecentFiles = min(len(files), self.MaxRecentFiles) 

     for i in range(numRecentFiles): 
      text = "&%d %s" % (i + 1, self.strippedName(files[i])) 
      self.recentFileActs[i].setText(text) 
      self.recentFileActs[i].setData(files[i]) 
      self.recentFileActs[i].setVisible(True) 

     for j in range(numRecentFiles, self.MaxRecentFiles): 
      self.recentFileActs[j].setVisible(False) 

     self.separatorAct.setVisible((numRecentFiles > 0)) 

    def clearRecentFileList(self, fileName): 
     self.rmenu.clear() 


    def strippedName(self, fullFileName): 
     return QFileInfo(fullFileName).fileName() 

def stylesheet2(self): 
    return """ 
QPlainTextEdit 
{ 
background: #ECECEC; 
color: #202020; 
border: 1px solid #1EAE3D; 
selection-background-color: #505050; 
selection-color: #ACDED5; 
} 
QMenu 
{ 
background: #F2F2F2; 
color: #0E185F; 
border: 1px solid #1EAE3D; 
selection-background-color: #ACDED5; 
} 
    """  

if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    win = myEditor() 
    win.setWindowIcon(QIcon.fromTheme("application-text")) 
    win.setWindowTitle("Plain Text Edit" + "[*]") 
    win.setMinimumSize(640,250) 
    win.showMaximized() 
    if len(sys.argv) > 1: 
     print(sys.argv[1]) 
     win.openFileOnStart(sys.argv[1]) 
    app.exec_() 
関連する問題