【发布时间】:2022-01-10 06:30:45
【问题描述】:
PyQt5:QAction -- QTextEdit.paste 不适用于 QTabWidget?
尝试学习python,我开始了一个项目来编写一个标签式markdown编辑器。在下面的代码中,一切都按预期工作,我可以:
- 添加和删除标签
- 使用键盘添加内容
- 使用RMB上下文菜单添加内容
- 从文件中加载内容
但是,当我尝试设置 QAction: QTextEdit.paste 时出现以下错误
Traceback (most recent call last):
File "~/Documents/Python/Mq/bin/Mq-post.py", line 170, in <module>
M = Main()
File "~/Documents/Python/Mq/bin/Mq-post.py", line 40, in __init__
self.initUi()
File "~/Documents/Python/Mq/bin/Mq-post.py", line 47, in initUi
configMainMenu(self)
File "~/Documents/Python/Mq/bin/ConfigMenu.py", line 47, in configMainMenu
Paste.triggered.connect(self.textArea.paste)
AttributeError: 'Main' object has no attribute 'textArea'
我已尝试将textArea 的定义从addTab 移动到__init__,但是,虽然没有报告错误,并且QTextEdit.paste 操作 按预期工作,但我无法创建新标签。
代码如下:
主要:
""" Mq editor main window """
import sys
from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget, QTabWidget,
QVBoxLayout, QFileDialog, QTextEdit)
from PyQt5.QtGui import (QIcon)
from ConfigMenu import (configMainMenu)
from Functions import (trimFileName, trimHome)
class Main(QMainWindow):
""" Main app """
def __init__(self):
super().__init__()
# Define initial window geometry
self.setWindowTitle('Mq Editor')
self.setWindowIcon(QIcon("Quill.png"))
self.left = 0
self.top = 0
self.width = 1024
self.height = 768
self.setGeometry(self.left, self.top, self.width, self.height)
# Setup a TabBar with movable closable tabs
self.tabs = QTabWidget()
self.tabs.tabCloseRequested.connect(self.closeTab)
self.tabs.setTabsClosable(True)
self.tabs.setMovable(True)
# Define the Layout
widget = QWidget(self)
self.setCentralWidget(widget)
self.layout = QVBoxLayout(widget)
# Defined in add tab, See pylint W0201, (disable to get traceback 2)
# self.textArea = None
self.initUi()
# #####################################
def initUi(self):
""" Set up user interface """
# Create a menu bar
configMainMenu(self)
# Adding the tab widget to the layout needs to come after calling
# 'configMainMenu' and 'createToolBar'
self.layout.addWidget(self.tabs)
# Create first tab
self.addTab()
# Open window in maximized size
self.showMaximized()
# #####################################
def addTab(self):
""" Create Tabs """
# Add tab with new editor instance, (this is only place it works)
self.textArea = QTextEdit()
self.textArea.textChanged.connect(self.textChanged)
self.textArea.createStandardContextMenu()
self.tabs.addTab(self.textArea, "Untitled")
# Switch to new tab
index = self.tabs.count() - 1
self.tabs.setCurrentIndex(index)
self.tabs.setTabToolTip(index, "Untitled")
# #####################################
def closeTab(self, index):
""" Close Tab Handler """
self.tabs.removeTab(index)
# #####################################
def getActiveTab(self):
""" Returns active tab 'index' and 'title' """
# Get the active tabs index
index = self.tabs.currentIndex()
# Get the active tabs title
title = self.tabs.tabText(index)
return index, title
# #####################################
def setActiveTabTitle(self, title, toolTip):
""" Sets the active tab 'title' and 'tooltip' """
# Get the active tabs index
index = self.tabs.currentIndex()
# Set the active tabs title
self.tabs.setTabText(index, title)
self.tabs.setTabToolTip(index, toolTip)
# #####################################
def textChanged(self):
""" textChanged handler """
# Get the active tab's details
index, title = self.getActiveTab()
# Check if last char of title is marked as 'Text Changed'
if title[-1] != '*':
# Not marked as 'Text Changed'
self.tabs.setTabText(index, title + '*')
# #####################################
def closeUnusedTabs(self):
""" Tab house-keeping """
tabIndex = self.tabs.count()
while tabIndex > 0:
index = tabIndex - 1
self.tabs.setCurrentIndex(index)
title = self.tabs.tabText(index)
if title == "Untitled":
self.closeTab(index)
tabIndex = tabIndex - 1
# #####################################
def openFile(self):
""" openFile handler """
# Open file dialogue
FileDialogue = QFileDialog.getOpenFileNames
filePath, _ = FileDialogue(None, "Open File", "",
"Markdown Files (*.md);;All Files (*)")
# check 'filePath' is not empty
if filePath:
# Keep the GUI clean
self.closeUnusedTabs()
# Iterate 'filePath' and open files
for i in filePath:
# Having removed unused tabs, we just need to add a new tab for
# each file 'i'
self.addTab()
# Read file 'i' and load into new tab instance of editor
with open(i, "r", encoding="utf8") as f:
fileContents = f.read()
self.textArea.setPlainText(fileContents)
# Get a trimmed path as the 'tab tooltip'
toolTip = trimHome(i)
# Get a trimmed file name to use as the 'tab title'
tabTitle = trimFileName(i)
# Set the new tabs 'title' and 'tootip'
self.setActiveTabTitle(tabTitle, toolTip)
def Paste(self):
" Test paste click handler "
print("Hit Paste")
m = QApplication(sys.argv)
M = Main()
M.show()
sys.exit(m.exec_())
配置菜单:
""" Module to create a menu bar """
from PyQt5.QtWidgets import (QAction)
def configMainMenu(self):
""" Creating a menu bar"""
# mainMenu Definition
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('File')
editMenu = mainMenu.addMenu('Edit')
# 'New'
New = QAction('New', self)
New.setShortcut('Ctrl+N')
New.setStatusTip('New File')
New.triggered.connect(self.addTab)
fileMenu.addAction(New)
# 'Close'
Close = QAction('Close', self)
Close.setShortcut('Ctrl+W')
Close.setStatusTip('Close File')
Close.triggered.connect(self.closeTab)
fileMenu.addAction(Close)
# 'Open'
Open = QAction('Open', self)
Open.setShortcut('Ctrl+O')
Open.setStatusTip('Open')
Open.triggered.connect(self.openFile)
fileMenu.addAction(Open)
# 'Paste'
Paste = QAction('Paste', self)
Paste.setShortcut('Ctrl+V')
Paste.setStatusTip('Paste text')
# ### The traceback leads here ###
Paste.triggered.connect(self.textArea.paste) # Fails
# Paste.triggered.connect(self.Paste) # For testing
editMenu.addAction(Paste)
功能:
""" Useful functions """
from pathlib import Path
from os.path import expanduser
def trimFileName(filePath):
""" Returns size limited 'tab title' based on 'file name' """
# FRST: Get the file name without extension
# Convert 'file path' to type 'path' without '.ext'
filePath = Path(filePath).with_suffix('')
# Convert the 'Path' back to 'string' and 'split' into a 'list'
pathStr = str(filePath).split("/")
# The file name is last element of the 'list'
tabTitle = pathStr[-1]
# SCOND: trim length to 12 characters.
# Nte: we use the tab 'tooltip' to show 'path' and 'file name'
length = len(tabTitle)
if length > 12:
tabTitle1 = tabTitle[0:3]
tabTitle2 = tabTitle[length - 6:length]
tabTitle = tabTitle1 + '..' + tabTitle2
return tabTitle
def trimHome(filePath):
""" Replace ${HOME} with '~' in 'filePath' """
# Note: This also works on Windows :)
home = expanduser("~")
trimmedPath = filePath.replace(home, "~")
return trimmedPath
欢迎任何关于如何在保持 Tab 功能的同时实现 Edit Menu 的建议或帮助。
【问题讨论】:
-
移动
configMainMenu()之后self.addTab().
标签: python python-3.x pyqt5 qtextedit qtabwidget