Текст в QLabel в PyQt5 выходит за границы
Я написал вот такой ридер для fb2-книг, смотрите ниже.
Когда я вставляю текст из списка pages[]
в textLabel
,
текст "выезжает" за ширину textLabel
.
setWordWrap
помогло, но текст стал нечитабельным.
Как это исправить без setWordWrap
?
main.py:
import sys
from config import *
from PyQt5.Qt import *
from src.Book import *
from src.styles.WhiteTheme import *
class BookViewer(object):
def __init__(self, appStyle: style = WhiteTheme, appStyleFromSystem="Windows",
app=QApplication(sys.argv)) -> None:
self.app = app
self.Book = None
# creating and configuring BookViewer window
self.content = QWidget()
self.content.setFixedSize(500, 500)
self.content.setWindowIcon(QIcon("./media/karfagen.png"))
self.content.setWindowTitle(f"Karfagen Book Viewer")
self.content.setStyleSheet(appStyle.style)
self.text_font = QFont(FONT_NAME, TEXT_SIZE)
self.text_height = QFontMetrics(self.text_font)
self.layout = QVBoxLayout(self.content)
self.pageNumber = 0
self.textLabel = QLabel()
self.textLabel.setFixedWidth(400)
self.textLabel.setFixedHeight(400)
self.navigationBox = QGroupBox()
self.navigationBox.setStyleSheet("""
QGroupBox {
border: 0px black solid;
}
""")
#creating navigation panel
self.navigationTopPanel = QWidget()
self.navigationTopPanelLayout = QHBoxLayout()
self.navigationTopPanel.setLayout(self.navigationTopPanelLayout)
self.openFile = QPushButton(text = "open file")
self.openFile.clicked.connect(self.openFileFunc)
self.openFile.setFixedWidth(70)
self.layout.addWidget(self.navigationTopPanel)
self.navigationTopPanelLayout.addWidget(self.openFile)
self.navigationTopPanelLayout.setAlignment(Qt.AlignLeft)
#creating elements for navigation in Book
self.navigationBoxLayout = QHBoxLayout()
self.btn_prev = QPushButton(text="<")
self.btn_prev.clicked.connect(self.prev_page)
self.pageNumberLabel = QLabel(text=str(self.pageNumber))
self.btn_next = QPushButton(text=">")
self.btn_next.clicked.connect(self.next_page)
self.navigationBoxLayout.addWidget(self.btn_prev)
self.navigationBoxLayout.setAlignment(Qt.AlignCenter)
self.navigationBoxLayout.addWidget(self.pageNumberLabel)
self.navigationBoxLayout.addWidget(self.btn_next)
self.navigationBox.setLayout(self.navigationBoxLayout)
self.layout.addWidget(self.textLabel)
self.layout.addStretch()
self.layout.addWidget(self.navigationBox)
self.content.setLayout(self.layout)
def start(self):
self.content.show()
self.app.exec_()
def render_page(self, pageNumber):
try:
self.pageNumberLabel.setText(str(pageNumber + 1))
self.textLabel.setText("".join(self.pages[self.pageNumber]))
except Exception as e:
self.show_messagebox(str(e))
def prev_page(self):
if self.pageNumber > 0 and self.Book:
self.pageNumber -= 1
self.render_page(self.pageNumber)
def next_page(self):
if self.pageNumber <= len(self.pages) and self.Book:
self.pageNumber += 1
print(self.pageNumber)
self.render_page(self.pageNumber)
def parseBookData(self):
"""
Parses raw book data into pages, handling paragraph wrapping and page breaks.
Returns:
A list of pages, where each page is a list of strings (paragraphs/lines).
"""
pages= []
page = []
current_text_height = 0
font_metrics = QFontMetrics(QFont(FONT_NAME, TEXT_SIZE))
for paragraph in self.Book.text_data:
# Split paragraph into lines that fit
lines = self.split_paragraph_into_lines(paragraph, font_metrics, self.textLabel.width())
for line in lines:
line_height = font_metrics.height() # Use actual line height
if current_text_height + line_height <= self.textLabel.height():
page.append(line + "<br>")
current_text_height += line_height
else:
pages.append(page)
page = [line]
current_text_height = line_height # Reset height to the current line's height
# Add the last page if it's not empty
if page:
pages.append(page)
return pages
def split_paragraph_into_lines(self, paragraph: str, font_metrics: QFontMetrics, max_width: int):
"""
Splits a paragraph into lines that fit within the maximum width, handling word wrapping.
Args:
paragraph: The paragraph to split.
font_metrics: QFontMetrics object.
max_width: The maximum width for a line.
Returns:
A list of strings, where each string is a line.
"""
if font_metrics.horizontalAdvance(paragraph) >= self.textLabel.width():
words = paragraph.split()
lines = []
current_line = ""
for word in words:
test_line = current_line + word + " " # Add word and a space to test
if font_metrics.horizontalAdvance(test_line) <= max_width:
current_line = test_line
else:
if current_line: # Add the current line if it's not empty
newString = ""
for i in range(len(current_line)):
newString += current_line[i]
if font_metrics.horizontalAdvance(newString) == max_width:
break
lines.append(newString)
current_line = word + " " + current_line[i:len(current_line)] # Start a new line with the current word
if current_line: # Add the last line
lines.append(current_line.strip())
return lines
else:
return [paragraph]
def openFileFunc(self):
options = QFileDialog.Options()
# Get the file names from the dialog
files, _ = QFileDialog.getOpenFileNames(self.content,
"Select Fiction Book Files",
"",
"Fiction Book 2 (*.fb2);;All Files (*)",
options=options)
if files:
self.Book = Book(files[0])
self.Book.parse()
self.content.setWindowTitle(self.Book.title + " " + self.Book.author + " " + "Karfagen Book Viewer")
self.pages = self.parseBookData()
self.render_page(self.pageNumber)
def show_messagebox(self, text):
msg = QMessageBox()
msg.setIcon(QMessageBox.Icon.Error)
msg.setText(text)
msg.setInformativeText("")
msg.setWindowTitle("error")
msg.setDetailedText("")
msg.exec()
Book.py:
from xml.dom.minidom import parse
class Book(object):
def __init__(self, filename, encoding="UTF-8"):
self.filename = filename
self.encoding = encoding
self.text_data = None
self.document = None
self.genre = None
self.author = None
self.title = None
self.lang = None
def parse(self):
document = parse(self.filename)
self.document = document
self.genre = self.loadTagValueFromXML("genre")
self.lang = self.loadTagValueFromXML("lang")
self.author = self.loadTagValueFromXML("last-name") + self.loadTagValueFromXML("first-name")
self.title = self.loadTagValueFromXML("book-title")
paragraphs = document.getElementsByTagName("section")
for paragraph in paragraphs:
text_nodes = [
node.childNodes[0].nodeValue for node in paragraph.childNodes
if node.nodeName == 'p' and node.childNodes[0].nodeValue
]
self.text_data = text_nodes
self.parsedTextData = []
def loadTagValueFromXML(self, tag_name):
try:
tag = self.document.getElementsByTagName(tag_name)[0].childNodes[0].nodeValue
return tag
except IndexError:
return ""
Ответы (1 шт):
Автор решения: S. Nick
→ Ссылка
Я попробовал сделать некоторые изменения для вас.
Заменил:
self.textLabel = QLabel()
на:
self.textLabel = QTextBrowser()
Добавил:
# +++ # +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
paragraph = " " + \
paragraph + "<br>" # +++
# +++ # +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Убрал + "<br>"
из строки
page.append(line + "<br>")
и еще кое-что по мелочи.
Попробуйте что получилось.
Вам есть еще над чем поработать.
bookviewer.py
import sys
from src.Book import *
from src.configParser import *
from PyQt5.Qt import *
class BookViewer(object):
def __init__(self,
app = QApplication(sys.argv),
appConfig = configParser()) -> None:
# +
app.setFont(QFont("Times", 14, QFont.Bold)) # +
self.app = app
self.Book = None
self.pages = []
self.pageNumber = 0
self.appConfig = appConfig
self.appStyle = self.appConfig.APP_THEME
# creating and configuring BookViewer window
self.content = QWidget()
#? self.content.setFixedSize(700, 500)
self.content.resize(700, 500) # +
self.content.setWindowIcon(QIcon("./media/karfagen.png"))
self.content.setWindowTitle(f"Karfagen Book Viewer")
self.content.setStyleSheet(self.appStyle)
self.text_font = QFont(self.appConfig.FONT_NAME,
self.appConfig.TEXT_SIZE)
self.text_height = QFontMetrics(self.text_font)
#? self.textLabel = QLabel()
self.textLabel = QTextBrowser() # +++
# self.textLabel.setWordWrap(True)
#? self.textLabel.setFixedWidth(400)
#? self.textLabel.setFixedHeight(400)
self.navigationBox = QGroupBox()
self.navigationBox.setStyleSheet("""
QGroupBox {
border: 2px black solid;
}
""")
#creating navigation panel
self.navigationTopPanel = QWidget()
self.openFile = QPushButton(text = "open file")
self.openFile.clicked.connect(self.openFileFunc)
#? self.openFile.setFixedWidth(70)
self.navigationTopPanelLayout = QHBoxLayout(
self.navigationTopPanel)
self.navigationTopPanelLayout.addWidget(self.openFile)
self.navigationTopPanelLayout.setAlignment(Qt.AlignLeft)
#creating elements for navigation in Book
self.btn_prev = QPushButton(text="<")
self.btn_prev.clicked.connect(self.prev_page)
self.pageNumberLabel = QLabel(text=str(self.pageNumber))
self.btn_next = QPushButton(text=">")
self.btn_next.clicked.connect(self.next_page)
self.goToPageField = QLineEdit()
self.goToPageField.setValidator(QIntValidator())
self.goToPageField.setPlaceholderText(
"Enter number of page to go")
self.goToPageField.setStyleSheet("""
QLineEdit {
border: 0px black solid;
background-color: #bbbbbb;
color: #000000;
}
""")
self.goToPageField.editingFinished.connect(self.pageByNumber)
self.navigationBoxLayout = QHBoxLayout()
self.navigationBoxLayout.addWidget(self.btn_prev)
self.navigationBoxLayout.setAlignment(Qt.AlignCenter)
self.navigationBoxLayout.addWidget(self.pageNumberLabel)
self.navigationBoxLayout.addWidget(self.btn_next)
self.navigationBoxLayout.addWidget(self.goToPageField)
self.navigationBox.setLayout(self.navigationBoxLayout)
self.layout = QVBoxLayout(self.content)
self.layout.addWidget(self.navigationTopPanel)
self.layout.addWidget(self.textLabel)
#? self.layout.addStretch()
self.layout.addWidget(self.navigationBox)
#? self.content.setLayout(self.layout)
def start(self):
self.content.show()
self.app.exec_()
def render_page(self, pageNumber):
try:
self.pageNumberLabel.setText(
f"{str(pageNumber + 1)}/{len(self.pages)}")
self.textLabel.setText("".join(self.pages[pageNumber]))
except Exception as e:
self.show_messagebox(str(e))
def pageByNumber(self):
pageNumber = int(self.goToPageField.text())
if pageNumber > 0 and pageNumber <= len(self.pages):
self.pageNumber = pageNumber
self.render_page(pageNumber - 1)
def prev_page(self):
if self.pageNumber > 0 and self.Book:
self.pageNumber -= 1
self.render_page(self.pageNumber)
def next_page(self):
if self.pageNumber <= len(self.pages) and self.Book:
self.pageNumber += 1
self.render_page(self.pageNumber)
def parseBookData(self):
"""
Parses raw book data into pages, handling paragraph
wrapping and page breaks.
Returns:
A list of pages, where each page is a list of
strings (paragraphs/lines).
"""
pages= []
page = []
current_text_height = 0
# font_metrics = QFontMetrics(
# QFont(self.appConfig.FONT_NAME, self.appConfig.TEXT_SIZE))
fm = QFontMetrics(self.textLabel.font()) # +
for paragraph in self.Book.text_data:
# +++ # +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
paragraph = " " + \
paragraph + "<br>" # +++
# +++ # +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Разделите абзац на строки, которые подходят
lines = self.split_paragraph_into_lines(
paragraph, fm, self.textLabel.width())
line_height = fm.height() # +
for line in lines:
if current_text_height + line_height <= \
self.textLabel.height() - 30:
# ^^^^ чтобы убрать прокрутку
#? page.append(line + "<br>") # заменил, смотри выше
page.append(line)
current_text_height += line_height
else:
pages.append(page)
page = [line]
current_text_height = line_height # Reset height to the current line's height
# +
QApplication.processEvents() # +
# Добавить последнюю страницу, если она не пустая
if page:
pages.append(page)
return pages
def split_paragraph_into_lines(self,
paragraph: str,
font_metrics: QFontMetrics,
max_width: int):
"""
Splits a paragraph into lines that fit within the maximum width, handling word wrapping.
Args:
paragraph: The paragraph to split.
font_metrics: QFontMetrics object.
max_width: The maximum width for a line.
Returns:
A list of strings, where each string is a line.
"""
if font_metrics.horizontalAdvance(paragraph) >= self.textLabel.width():
words = paragraph.split()
lines = []
current_line = ""
for word in words:
test_line = current_line + word + " " # Add word and a space to test
if font_metrics.horizontalAdvance(test_line) <= max_width:
current_line = test_line
else:
if current_line: # Add the current line if it's not empty
newString = ""
for i in range(len(current_line)):
newString += current_line[i]
if font_metrics.horizontalAdvance(newString) == max_width:
break
lines.append(newString)
current_line = word + " " + \
current_line[i:len(current_line)] # Start a new line with the current word
if current_line: # Add the last line
lines.append(current_line.strip())
return lines
else:
return [paragraph]
def openFileFunc(self):
options = QFileDialog.Options()
# Get the file names from the dialog
files, _ = QFileDialog.getOpenFileNames(
self.content,
"Select Fiction Book Files",
"",
"Fiction Book 2 (*.fb2);;All Files (*)",
options=options)
if files:
self.Book = Book(files[0])
self.Book.parse()
self.content.setWindowTitle(
self.Book.title + " " + self.Book.author + " " + "Karfagen Book Viewer")
self.pages = self.parseBookData()
self.render_page(self.pageNumber)
def show_messagebox(self, text):
msg = QMessageBox()
# msg.setIcon(QMessageBox.Icon.Error)
msg.setText(text)
msg.setInformativeText("")
msg.setWindowTitle("error")
msg.setDetailedText("")
msg.exec()