Вставка изображения из XML (fb2) в PyQt5

Я написал данный код для парсинга fb2-книги:

import sys
from xml.dom.minidom import parse
from PyQt5.Qt import *
import base64


class Book(object):
    def __init__(self, filename, encoding, app):

        self.app = app
        self.parsedTextData = None
        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):
        global text_nodes
        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")
        text_nodes = []
        for paragraph in paragraphs:
            # text_nodes = [
            #     node.childNodes[0].nodeValue for node in paragraph.childNodes
            #     if node.nodeName == 'p' and node.childNodes[0].nodeValue
            # ]
            for node in paragraph.childNodes:
                if node.nodeName == "p" and node.childNodes[0].nodeValue:
                    text_nodes.append(node.childNodes[0].nodeValue)
                else:
                    for ii in node.childNodes:
                        if ii.nodeName == "image":
                            ImageObject = QPixmap.fromImage(self.getImageFromXMLById(ii.getAttribute("l:href")[1:]))
                            text_nodes.append(ImageObject)

        self.text_data = text_nodes
        self.parsedTextData = []

    def getImageFromXMLById(self, imgId):
        for tag in self.document.getElementsByTagName("binary"):
            if tag.getAttribute("id") == imgId:
                img = QImage()
                binStr = tag.childNodes[0].nodeValue.encode("utf-8")
                img.loadFromData(binStr)
                return img
    def loadTagValueFromXML(self, tag_name):
        try:
            tag = self.document.getElementsByTagName(tag_name)[0].childNodes[0].nodeValue
            return tag
        except IndexError:
            return ""

    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(self.app.appFont)

        for paragraph in self.text_data:
            # Split paragraph into lines that fit

            paragraph = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + paragraph + "<br>"

            lines = self.split_paragraph_into_lines(paragraph, font_metrics, self.app.textLabel.width())
            for line in lines:
                line_height = font_metrics.height()  # Use actual line height

                if current_text_height + line_height <= self.app.textLabel.height() - 30:
                    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

        # 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.app.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]


if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = QWidget()
    b = Book("C:\\Users\\Treska\\Documents\\projects\\karfagen\\samples\\sampleWithImage.fb2", "UTF-8", app)
    b.parse()
    img = QPixmap.fromImage( = b.loadTagValueFromXML("img_0"))
    label = QLabel()
    label.setPixmap(img.scaledToWidth(500, Qt.SmoothTransformation))
    layout = QHBoxLayout()
    layout.addWidget(label)
    widget.setLayout(layout)
    widget.show()
    app.exec_()

При попытке вывести на экран изображение через QPixmap.fromImage(b.loadTagValueFromXML("img_0")) вознкает ошибка:

TypeError: fromImage(image: QImage, flags: Union[Qt.ImageConversionFlags, Qt.ImageConversionFlag] = Qt.AutoColor): argument 1 has unexpected type 'str'

Подскажите, как можно было бы ее исправить?

Весь код проекта - https://github.com/kotKolil/karfagen

Файл, из которого я загружал изображение - https://github.com/kotKolil/karfagen/blob/master/samples/sampleWithImage.fb2

Ответы (1 шт):

Автор решения: S. Nick

Я отметил для вас изменения и дополнения, которые внес в код.

main.py

import sys
from xml.dom.minidom import parse
from PyQt5.Qt import *
#? import base64


class Book(object):
    def __init__(self, filename, encoding, app):
        self.filename = filename
        self.encoding = encoding
        self.app = app

        self.parsedTextData = None
        self.text_data = None
        self.document = None
        self.genre = None
        self.author = None
        self.title = None
        self.lang = None

# !!! vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    def parse(self):
#?        global text_nodes
        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")
        text_nodes = []
        for paragraph in paragraphs:
            # text_nodes = [
            #     node.childNodes[0].nodeValue for node in paragraph.childNodes
            #     if node.nodeName == 'p' and node.childNodes[0].nodeValue
            # ]
            for node in paragraph.childNodes:
                if node.nodeName == "p" and node.childNodes[0].nodeValue:
                    text_nodes.append(node.childNodes[0].nodeValue)
                else:
                    for ii in node.childNodes:
                        if ii.nodeName == "image":
#-                          ImageObject = QPixmap.fromImage(self.getImageFromXMLById(ii.getAttribute("l:href")[1:]))
# !!! +++                   vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                            binStr = self.getImageFromXMLById(
                                    ii.getAttribute("l:href")[1:])
                                    
#-                          ImageObject = QPixmap.fromImage(img)
# !!! +++                   vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                            pixmap = QPixmap()
                            pixmap.loadFromData(QByteArray.fromBase64(binStr))
# !!! +++                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

#-                          text_nodes.append(ImageObject)
                            text_nodes.append(pixmap)
# !!! +++                                     ^^^^^^

        self.text_data = text_nodes
        self.parsedTextData = []

    def getImageFromXMLById(self, imgId):
        for tag in self.document.getElementsByTagName("binary"):
            if tag.getAttribute("id") == imgId:
                img = QImage()
                binStr = tag.childNodes[0].nodeValue.encode("utf-8")

#-              img.loadFromData(binStr)

#-              return img
                return binStr 
# !!! +++              ^^^^^^ 
               
# !!! +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                
    def loadTagValueFromXML(self, tag_name):
        try:
            tag = self.document.getElementsByTagName(tag_name)[0].childNodes[0].nodeValue
            return tag
        except IndexError:
            return ""

    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(self.app.appFont)

        for paragraph in self.text_data:
            # Split paragraph into lines that fit

            paragraph = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + paragraph + "<br>"

            lines = self.split_paragraph_into_lines(paragraph, font_metrics, self.app.textLabel.width())
            for line in lines:
                line_height = font_metrics.height()  # Use actual line height

                if current_text_height + line_height <= self.app.textLabel.height() - 30:
                    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

        # 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.app.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]


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

# !!! установите свой путь к sampleWithImage.fb2
#   b = Book("C:\\Users\\Treska\\Documents\\projects\\karfagen\\samples\\sampleWithImage.fb2", "UTF-8", app)
    book = Book("sampleWithImage.fb2", "UTF-8", app)

    book.parse()
    
#?  img = QPixmap.fromImage(b.loadTagValueFromXML("img_0"))
    
    label = QLabel()
    
#-  label.setPixmap(img.scaledToWidth(500, Qt.SmoothTransformation))
# !!! +++           vvvvvvvvvvvvvvvvv
    label.setPixmap(book.text_data[0].scaledToWidth(
        500, Qt.SmoothTransformation))

    layout = QHBoxLayout()
    layout.addWidget(label)
    widget.setLayout(layout)
    widget.show()
    sys.exit(app.exec())

введите сюда описание изображения

→ Ссылка