DOM Bookmarks Example

Provides a reader for XML Bookmark Exchange Language files.

The DOM Bookmarks example provides a reader for XML Bookmark Exchange Language (XBEL) files that uses Qt’s DOM-based XML API to read and parse the files. The SAX Bookmarks example provides an alternative way to read this type of file.

DOM Bookmark Screenshot
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xbel>
<xbel version="1.0">
    <folder folded="no">
        <title>Qt Resources</title>
        <folder folded="yes">
            <title>Trolltech Partners</title>
            <bookmark href="http://partners.trolltech.com/partners/training.html">
                <title>Training Partners</title>
            </bookmark>
            <bookmark href="http://partners.trolltech.com/partners/service.html">
                <title>Consultants and System Integrators</title>
            </bookmark>
            <bookmark href="http://partners.trolltech.com/partners/tech.html">
                <title>Technology Partners</title>
            </bookmark>
            <bookmark href="http://partners.trolltech.com/partners/resellers.html">
                <title>Value Added Resellers (VARs)</title>
            </bookmark>
        </folder>
        <folder folded="yes">
            <title>Community Resources</title>
            <bookmark href="http://www.qtforum.org/">
                <title>QtForum.org</title>
            </bookmark>
            <bookmark href="http://www.digitalfanatics.org/projects/qt_tutorial/">
                <title>The Independent Qt Tutorial</title>
            </bookmark>
            <bookmark href="http://prog.qt.free.fr/">
                <title>French PROG.Qt</title>
            </bookmark>
            <bookmark href="http://www.qtforum.de/">
                <title>German Qt Forum</title>
            </bookmark>
            <bookmark href="http://www.korone.net/">
                <title>Korean Qt Community Site</title>
            </bookmark>
            <bookmark href="http://prog.org.ru/forum/forum_14.html">
                <title>Russian Qt Forum</title>
            </bookmark>
            <bookmark href="http://qt4.digitalfanatics.org/">
                <title>Digitalfanatics: The QT 4 Resource Center</title>
            </bookmark>
            <bookmark href="http://www.qtquestions.org/">
                <title>QtQuestions</title>
            </bookmark>
        </folder>
        <bookmark href="http://doc.trolltech.com/qq/">
            <title>Qt Quarterly</title>
        </bookmark>
        <bookmark href="http://www.trolltech.com/">
            <title>Trolltech's home page</title>
        </bookmark>
        <bookmark href="http://doc.trolltech.com/4.0/">
            <title>Qt 4.0 documentation</title>
        </bookmark>
        <bookmark href="http://www.trolltech.com/developer/faqs/">
            <title>Frequently Asked Questions</title>
        </bookmark>
    </folder>
    <folder folded="no">
        <title>Online Dictionaries</title>
        <bookmark href="http://www.dictionary.com/">
            <title>Dictionary.com</title>
        </bookmark>
        <bookmark href="http://www.m-w.com/">
            <title>Merriam-Webster Online</title>
        </bookmark>
        <bookmark href="http://dictionary.cambridge.org/">
            <title>Cambridge Dictionaries Online</title>
        </bookmark>
        <bookmark href="http://www.onelook.com/">
            <title>OneLook Dictionary Search</title>
        </bookmark>
        <separator/>
        <bookmark href="www.iee.et.tu-dresden.de/">
            <title>The New English-German Dictionary</title>
        </bookmark>
        <bookmark href="http://dict.tu-chemnitz.de/">
            <title>TU Chemnitz German-English Dictionary</title>
        </bookmark>
        <separator/>
        <bookmark href="http://atilf.atilf.fr/tlf.htm">
            <title>Trésor de la Langue Française informatisé</title>
        </bookmark>
        <bookmark href="http://dictionnaires.atilf.fr/dictionnaires/ACADEMIE/">
            <title>Dictionnaire de l'Académie Française</title>
        </bookmark>
        <bookmark href="http://elsap1.unicaen.fr/cgi-bin/cherches.cgi">
            <title>Dictionnaire des synonymes</title>
        </bookmark>
    </folder>
</xbel>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xbel>
<xbel version="1.0">
    <folder folded="yes">
        <title>Literate Programming</title>
        <bookmark href="http://www.vivtek.com/litprog.html">
            <title>Synopsis of Literate Programming</title>
        </bookmark>
        <bookmark href="http://vasc.ri.cmu.edu/old_help/Programming/Literate/literate.html">
            <title>Literate Programming: Propaganda and Tools</title>
        </bookmark>
        <bookmark href="http://www.isy.liu.se/%7Eturbell/litprog/">
            <title>Literate Programming by Henrik Turbell</title>
        </bookmark>
        <bookmark href="http://www.desy.de/user/projects/LitProg.html">
            <title>Literate Programming Library</title>
        </bookmark>
        <bookmark href="http://www.loria.fr/services/tex/english/litte.html">
            <title>Literate Programming Basics</title>
        </bookmark>
        <bookmark href="http://ei.cs.vt.edu/%7Ecs5014/courseNotes/4.LiterateProgramming/literate_prog.html">
            <title>Literate Programming Overview</title>
        </bookmark>
        <bookmark href="http://www.perl.com/pub/a/tchrist/litprog.html">
            <title>POD is not Literate Programming</title>
        </bookmark>
        <bookmark href="http://www.cornellcollege.edu/%7Eltabak/publications/articles/swsafety.html">
            <title>Computers That We Can Count On</title>
        </bookmark>
        <bookmark href="http://www.cs.auc.dk/%7Enormark/litpro/issues-and-problems.html">
            <title>Literate Programming - Issues and Problems</title>
        </bookmark>
        <bookmark href="http://c2.com/cgi/wiki?LiterateProgramming">
            <title>Literate Programming - Wiki Pages</title>
        </bookmark>
        <bookmark href="http://developers.slashdot.org/developers/02/05/19/2216233.shtml">
            <title>What is well-commented code?</title>
        </bookmark>
        <bookmark href="http://liinwww.ira.uka.de/bibliography/SE/litprog.html">
            <title>Bibliography on literate programming - A searchable bibliography</title>
        </bookmark>
        <bookmark href="http://www2.umassd.edu/SWPI/ProcessBibliography/bib-codereading.html">
            <title>Program comprehension and code reading bibliography</title>
        </bookmark>
        <bookmark href="http://www.cs.auc.dk/%7Enormark/elucidative-programming/">
            <title>Elucidative Programming</title>
        </bookmark>
        <bookmark href="http://www.msu.edu/%7Epfaffben/avl/index.html">
            <title>AVL Trees (TexiWeb)</title>
        </bookmark>
        <bookmark href="http://literate-programming.wikiverse.org/">
            <title>Literate Programming on Wikiverse</title>
        </bookmark>
        <bookmark href="http://www.pbrt.org/">
            <title>Physically Based Rendering: From Theory to Implementation</title>
        </bookmark>
    </folder>
    <folder folded="no">
        <title>Useful C++ Links</title>
        <folder folded="no">
            <title>STL</title>
            <bookmark href="http://www.sgi.com/tech/stl/table_of_contents.html">
                <title>STL Reference Documentation</title>
            </bookmark>
            <bookmark href="http://www.yrl.co.uk/~phil/stl/stl.htmlx">
                <title>STL Tutorial</title>
            </bookmark>
            <bookmark href="http://www.cppreference.com/cpp_stl.html">
                <title>STL Reference</title>
            </bookmark>
        </folder>
        <folder folded="no">
            <title>Qt</title>
            <bookmark href="http://doc.trolltech.com/2.3/">
                <title>Qt 2.3 Reference</title>
            </bookmark>
            <bookmark href="http://doc.trolltech.com/3.3/">
                <title>Qt 3.3 Reference</title>
            </bookmark>
            <bookmark href="http://doc.trolltech.com/4.0/">
                <title>Qt 4.0 Reference</title>
            </bookmark>
            <bookmark href="http://www.trolltech.com/">
                <title>Trolltech Home Page</title>
            </bookmark>
        </folder>
        <folder folded="yes">
            <title>IOStreams</title>
            <bookmark href="http://www.cplusplus.com/ref/iostream/index.html">
                <title>IO Stream Library</title>
            </bookmark>
            <bookmark href="http://courses.cs.vt.edu/~cs2604/fall01/binio.html">
                <title>Binary I/O</title>
            </bookmark>
            <bookmark href="http://www.parashift.com/c++-faq-lite/input-output.html">
                <title>I/O Stream FAQ</title>
            </bookmark>
        </folder>
        <folder folded="yes">
            <title>gdb</title>
            <bookmark href="http://www.cs.princeton.edu/~benjasik/gdb/gdbtut.html">
                <title>GDB Tutorial</title>
            </bookmark>
            <bookmark href="http://www.gnu.org/manual/gdb-4.17/html_mono/gdb.html">
                <title>Debugging with GDB</title>
            </bookmark>
            <bookmark href="http://www.cs.washington.edu/orgs/acm/tutorials/dev-in-unix/gdb-refcard.pdf">
                <title>GDB Quick Reference Page (PDF) (Handy)</title>
            </bookmark>
        </folder>
        <folder folded="yes">
            <title>Classes and Constructors</title>
            <bookmark href="http://www.parashift.com/c++-faq-lite/ctors.html">
                <title>Constructor FAQ</title>
            </bookmark>
            <bookmark href="http://www.juicystudio.com/tutorial/cpp/index.html">
                <title>Organizing Classes</title>
            </bookmark>
        </folder>
    </folder>
    <folder folded="yes">
        <title>Software Documentation or System Documentation</title>
        <bookmark href="http://www.martinfowler.com/distributedComputing/thud.html">
            <title>The Almighty Thud</title>
        </bookmark>
        <bookmark href="http://msdn.microsoft.com/library/techart/cfr.htm">
            <title>Microsoft Coding Techniques and Programming Practices</title>
        </bookmark>
        <bookmark href="http://www.bearcave.com/software/prog_docs.html">
            <title>Software and Documentation</title>
        </bookmark>
        <bookmark href="http://c2.com/cgi/wiki?TheSourceCodeIsTheDesign">
            <title>The Source Code is the Design</title>
        </bookmark>
        <bookmark href="http://www.bleading-edge.com/Publications/C++Journal/Cpjour2.htm">
            <title>What is Software Design?</title>
        </bookmark>
        <bookmark href="http://www.mindprod.com/unmain.html">
            <title>How To Write Unmaintainable Code</title>
        </bookmark>
        <bookmark href="http://www.idinews.com/selfDoc.html">
            <title>Self Documenting Program Code Remains a Distant Goal</title>
        </bookmark>
        <bookmark href="http://www.sdmagazine.com/documents/s=730/sdm0106m/0106m.htm">
            <title>Place Tab A in Slot B</title>
        </bookmark>
        <bookmark href="http://www.holub.com/class/uml/uml.html">
            <title>UML Reference Card</title>
        </bookmark>
    </folder>
    <folder folded="yes">
        <title>TeX Resources</title>
        <bookmark href="http://www.tug.org/">
            <title>The TeX User's Group</title>
        </bookmark>
        <bookmark href="http://www.miktex.org/">
            <title>MikTeX website</title>
        </bookmark>
        <bookmark href="http://cm.bell-labs.com/who/hobby/MetaPost.html">
            <title>MetaPost website</title>
        </bookmark>
        <bookmark href="http://pauillac.inria.fr/%7Emaranget/hevea/">
            <title>HEVEA is a quite complete and fast LATEX to HTML translator</title>
        </bookmark>
    </folder>
    <folder folded="no">
        <title>Portable Document Format (PDF)</title>
        <bookmark href="http://www.adobe.com/">
            <title>Adobe - The postscript and PDF standards</title>
        </bookmark>
        <bookmark href="http://partners.adobe.com/asn/developer/technotes/acrobatpdf.html">
            <title>Reference Manual Portable Document Format</title>
        </bookmark>
        <bookmark href="http://partners.adobe.com/asn/developer/acrosdk/main.html">
            <title>Adobe Acrobat Software Development Kit</title>
        </bookmark>
    </folder>
    <folder folded="yes">
        <title>Literature Sites</title>
        <bookmark href="http://www.cc.columbia.edu/cu/libraries/subjects/speccol.html">
            <title>Guide to Special Collections (Columbia University)</title>
        </bookmark>
        <bookmark href="http://www.ipl.org/ref/litcrit/">
            <title>Literary Criticism on the Web from the Internet Public Library</title>
        </bookmark>
        <bookmark href="http://www.victorianweb.org/">
            <title>Victorian Web.</title>
        </bookmark>
        <bookmark href="http://vos.ucsb.edu/">
            <title>Voice of the Shuttle.</title>
        </bookmark>
        <bookmark href="http://www.modjourn.brown.edu/">
            <title>Modernist Journals Project</title>
        </bookmark>
        <bookmark href="http://www.poetspath.com">
            <title>Museum of American Poetics</title>
        </bookmark>
        <bookmark href="http://www.english.uiuc.edu/maps/">
            <title>Modern American Poetry</title>
        </bookmark>
        <bookmark href="http://www.findarticles.com/">
            <title>FindArticles.com</title>
        </bookmark>
        <bookmark href="http://www.literaryhistory.com">
            <title>Literary History</title>
        </bookmark>
        <bookmark href="http://www.litencyc.com/LitEncycFrame.htm">
            <title>Literary Encyclopedia</title>
        </bookmark>
        <separator/>
        <bookmark href="http://texts.cdlib.org/ucpress/">
            <title>The University of California Press</title>
        </bookmark>
        <bookmark href="http://www.letrs.indiana.edu/web/w/wright2/">
            <title>Wright American Fiction, 1851-1875</title>
        </bookmark>
        <bookmark href="http://docsouth.unc.edu/">
            <title>Documenting the American South: Beginnings to 1920</title>
        </bookmark>
        <bookmark href="http://etext.lib.virginia.edu/eng-on.html">
            <title>Electronic Text Center at the University of Virginia</title>
        </bookmark>
        <bookmark href="http://digital.nypl.org/schomburg/writers_aa19/">
            <title>The Schomburg Center for Research in Black Culture</title>
        </bookmark>
        <bookmark href="http://www.infomotions.com/alex2/">
            <title>Alex Catalog of Electronic Texts.</title>
        </bookmark>
    </folder>
</xbel>
"""PySide6 port of the xml/dombookmarks example from Qt v5.x"""

import sys

from PySide6.QtCore import QDir, QFile, Qt
from PySide6.QtGui import QAction, QIcon
from PySide6.QtWidgets import (QApplication, QFileDialog, QHeaderView, QMainWindow, QMessageBox, QStyle, QTreeWidget, QTreeWidgetItem, QWidget)
from PySide6.QtXml import QDomDocument


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._xbel_tree = XbelTree()
        self.setCentralWidget(self._xbel_tree)

        self.create_actions()
        self.create_menus()

        self.statusBar().showMessage("Ready")

        self.setWindowTitle("DOM Bookmarks")
        self.resize(480, 320)

    def open(self):
        file_name = QFileDialog.getOpenFileName(self,
                "Open Bookmark File", QDir.currentPath(),
                "XBEL Files (*.xbel *.xml)")[0]

        if not file_name:
            return

        in_file = QFile(file_name)
        if not in_file.open(QFile.ReadOnly | QFile.Text):
            reason = in_file.errorString()
            QMessageBox.warning(self, "DOM Bookmarks",
                    f"Cannot read file {file_name}:\n{reason}.")
            return

        if self._xbel_tree.read(in_file):
            self.statusBar().showMessage("File loaded", 2000)

    def save_as(self):
        file_name = QFileDialog.getSaveFileName(self,
                "Save Bookmark File", QDir.currentPath(),
                "XBEL Files (*.xbel *.xml)")[0]

        if not file_name:
            return

        out_file = QFile(file_name)
        if not out_file.open(QFile.WriteOnly | QFile.Text):
            reason = out_file.errorString()
            QMessageBox.warning(self, "DOM Bookmarks",
                    "Cannot write file {fileName}:\n{reason}.")
            return

        if self._xbel_tree.write(out_file):
            self.statusBar().showMessage("File saved", 2000)

    def about(self):
        QMessageBox.about(self, "About DOM Bookmarks",
            "The <b>DOM Bookmarks</b> example demonstrates how to use Qt's "
            "DOM classes to read and write XML documents.")

    def create_actions(self):
        self._open_act = QAction("&Open...", self, shortcut="Ctrl+O",
                triggered=self.open)

        self._save_as_act = QAction("&Save As...", self, shortcut="Ctrl+S",
                triggered=self.save_as)

        self._exit_act = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self._about_act = QAction("&About", self, triggered=self.about)

        self._about_qt_act = QAction("About &Qt", self,
                triggered=qApp.aboutQt)

    def create_menus(self):
        self._file_menu = self.menuBar().addMenu("&File")
        self._file_menu.addAction(self._open_act)
        self._file_menu.addAction(self._save_as_act)
        self._file_menu.addAction(self._exit_act)

        self.menuBar().addSeparator()

        self._help_menu = self.menuBar().addMenu("&Help")
        self._help_menu.addAction(self._about_act)
        self._help_menu.addAction(self._about_qt_act)


class XbelTree(QTreeWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.header().setSectionResizeMode(QHeaderView.Stretch)
        self.setHeaderLabels(("Title", "Location"))

        self._dom_document = QDomDocument()

        self._dom_element_for_item = {}

        self._folder_icon = QIcon()
        self._bookmark_icon = QIcon()

        self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirClosedIcon),
                QIcon.Normal, QIcon.Off)
        self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirOpenIcon),
                QIcon.Normal, QIcon.On)
        self._bookmark_icon.addPixmap(self.style().standardPixmap(QStyle.SP_FileIcon))

    def read(self, device):
        ok, errorStr, errorLine, errorColumn = self._dom_document.setContent(device, True)
        if not ok:
            QMessageBox.information(self.window(), "DOM Bookmarks",
                    f"Parse error at line {errorLine}, column {errorColumn}:\n{errorStr}")
            return False

        root = self._dom_document.documentElement()
        if root.tagName() != 'xbel':
            QMessageBox.information(self.window(), "DOM Bookmarks",
                    "The file is not an XBEL file.")
            return False
        elif root.hasAttribute('version') and root.attribute('version') != '1.0':
            QMessageBox.information(self.window(), "DOM Bookmarks",
                    "The file is not an XBEL version 1.0 file.")
            return False

        self.clear()

        # It might not be connected.
        try:
            self.itemChanged.disconnect(self.update_dom_element)
        except:
            pass

        child = root.firstChildElement('folder')
        while not child.isNull():
            self.parse_folder_element(child)
            child = child.nextSiblingElement('folder')

        self.itemChanged.connect(self.update_dom_element)

        return True

    def write(self, device):
        INDENT_SIZE = 4

        out = QTextStream(device)
        self._dom_document.save(out, INDENT_SIZE)
        return True

    def update_dom_element(self, item, column):
        element = self._dom_element_for_item.get(id(item))
        if not element.isNull():
            if column == 0:
                old_title_element = element.firstChildElement('title')
                new_title_element = self._dom_document.createElement('title')

                new_title_text = self._dom_document.createTextNode(item.text(0))
                new_title_element.appendChild(new_title_text)

                element.replaceChild(new_title_element, old_title_element)
            else:
                if element.tagName() == 'bookmark':
                    element.setAttribute('href', item.text(1))

    def parse_folder_element(self, element, parentItem=None):
        item = self.create_item(element, parentItem)

        title = element.firstChildElement('title').text()
        if not title:
            title = "Folder"

        item.setFlags(item.flags() | Qt.ItemIsEditable)
        item.setIcon(0, self._folder_icon)
        item.setText(0, title)

        folded = (element.attribute('folded') != 'no')
        item.setExpanded(not folded)

        child = element.firstChildElement()
        while not child.isNull():
            if child.tagName() == 'folder':
                self.parse_folder_element(child, item)
            elif child.tagName() == 'bookmark':
                child_item = self.create_item(child, item)

                title = child.firstChildElement('title').text()
                if not title:
                    title = "Folder"

                child_item.setFlags(item.flags() | Qt.ItemIsEditable)
                child_item.setIcon(0, self._bookmark_icon)
                child_item.setText(0, title)
                child_item.setText(1, child.attribute('href'))
            elif child.tagName() == 'separator':
                child_item = self.create_item(child, item)
                child_item.setFlags(item.flags() & ~(Qt.ItemIsSelectable | Qt.ItemIsEditable))
                child_item.setText(0, 30 * "\xb7")

            child = child.nextSiblingElement()

    def create_item(self, element, parentItem=None):
        item = QTreeWidgetItem()

        if parentItem is not None:
            item = QTreeWidgetItem(parentItem)
        else:
            item = QTreeWidgetItem(self)

        self._dom_element_for_item[id(item)] = element
        return item


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_win = MainWindow()
    main_win.show()
    main_win.open()
    sys.exit(app.exec())