DOM 북마크 애플리케이션

XML 북마크 교환 언어 파일용 리더를 제공합니다.

DOM 북마크 애플리케이션은 Qt의 DOM 기반 XML API를 사용하여 파일을 읽고 구문 분석하는 XML 북마크 교환 언어(XBEL) 파일용 리더를 제공합니다. QXmlStream 북마크 예제는 이러한 유형의 파일을 읽을 수 있는 다른 방법을 제공합니다.

XbelTree 클래스 정의

XbelTree 클래스에는 파일 시스템 읽기 및 쓰기 함수가 있습니다. QTreeWidget 클래스에서 상속되며, 북마크 표시 모델을 포함하고 편집할 수 있습니다.

class XbelTree : public QTreeWidget
{
    Q_OBJECT

public:
    explicit XbelTree(QWidget *parent = nullptr);

    bool read(QIODevice *device);
    bool write(QIODevice *device) const;

protected:
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
    void contextMenuEvent(QContextMenuEvent *event) override;
#endif

private slots:
    void updateDomElement(const QTreeWidgetItem *item, int column);

private:
    void parseFolderElement(const QDomElement &element,
                            QTreeWidgetItem *parentItem = nullptr);
    QTreeWidgetItem *createItem(const QDomElement &element,
                                QTreeWidgetItem *parentItem = nullptr);

    QDomDocument domDocument;
    QIcon folderIcon;
    QIcon bookmarkIcon;
};

XbelTree 클래스 구현

XbelTree 생성자는 QWidget 을 받아 그 안에 배치합니다. folderIcon 은 사용자가 아이콘과 상호 작용하지 않을 때만 픽셀맵이 표시되는 QIcon::Normal 모드로 설정됩니다. QStyle::SP_DirClosedIcon , QStyle::SP_DirOpenIcon, QStyle::SP_FileIcon 은 GUI 스타일을 따르는 표준 픽스맵에 해당합니다.

XbelTree::XbelTree(QWidget *parent)
    : QTreeWidget(parent)
{
    header()->setSectionResizeMode(QHeaderView::Stretch);
    setHeaderLabels({tr("Title"), tr("Location")});

    folderIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon),
                         QIcon::Normal, QIcon::Off);
    folderIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon),
                         QIcon::Normal, QIcon::On);
    bookmarkIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon));
}

read() 함수는 QDomDocument::setContent 을 사용하여 지정된 QIODevice 을 엽니다. 파일 열기에 성공하고 최상위 수준 헤더가 확인되면 모든 최상위 수준 XML 노드를 반복하고 각 노드에서 parseFolderElement() 을 호출하여 파일 내용을 구문 분석하기 전에 클래스의 내용을 지웁니다.

bool XbelTree::read(QIODevice *device)
{
    QDomDocument::ParseResult result =
            domDocument.setContent(device, QDomDocument::ParseOption::UseNamespaceProcessing);
    if (!result) {
        QMessageBox::information(window(), tr("DOM Bookmarks"),
                                 tr("Parse error at line %1, column %2:\n%3")
                                         .arg(result.errorLine)
                                         .arg(result.errorColumn)
                                         .arg(result.errorMessage));
        return false;
    }

    QDomElement root = domDocument.documentElement();
    if (root.tagName() != "xbel") {
        QMessageBox::information(window(), tr("DOM Bookmarks"),
                                 tr("The file is not an XBEL file."));
        return false;
    } else if (root.hasAttribute(versionAttribute)
               && root.attribute(versionAttribute) != "1.0"_L1) {
        QMessageBox::information(window(), tr("DOM Bookmarks"),
                                 tr("The file is not an XBEL version 1.0 "
                                    "file."));
        return false;
    }

    clear();

    disconnect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement);

    QDomElement child = root.firstChildElement(folderElement);
    while (!child.isNull()) {
        parseFolderElement(child);
        child = child.nextSiblingElement(folderElement);
    }

    connect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement);

    return true;
}

parseFolderElement() 함수는 다양한 요소 유형을 처리하고 요소가 하위 폴더인 경우 자신을 재귀적으로 호출합니다.

void XbelTree::parseFolderElement(const QDomElement &element,
                                  QTreeWidgetItem *parentItem)
{
    QTreeWidgetItem *item = createItem(element, parentItem);

    QString title = element.firstChildElement(titleElement).text();
    if (title.isEmpty())
        title = tr("Folder");

    item->setFlags(item->flags() | Qt::ItemIsEditable);
    item->setIcon(0, folderIcon);
    item->setText(0, title);

    bool folded = (element.attribute(foldedAttribute) != "no"_L1);
    item->setExpanded(!folded);

    constexpr char16_t midDot = u'\xB7';
    static const QString dots = QString(30, midDot);
    QDomElement child = element.firstChildElement();
    while (!child.isNull()) {
        if (child.tagName() == folderElement) {
            parseFolderElement(child, item);
        } else if (child.tagName() == bookmarkElement) {
            QTreeWidgetItem *childItem = createItem(child, item);

            QString title = child.firstChildElement(titleElement).text();
            if (title.isEmpty())
                title = tr("Folder");

            childItem->setFlags(item->flags() | Qt::ItemIsEditable);
            childItem->setIcon(0, bookmarkIcon);
            childItem->setText(0, title);
            childItem->setText(1, child.attribute(hrefAttribute));
        } else if (child.tagName() == "separator"_L1) {
            QTreeWidgetItem *childItem = createItem(child, item);
            childItem->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEditable));
            childItem->setText(0, dots);
        }
        child = child.nextSiblingElement();
    }
}

write() 함수는 QDomDocument::save를 사용하여 지정된 QIODevice 에 domDocument를 저장합니다.

bool XbelTree::write(QIODevice *device) const
{
    const int IndentSize = 4;

    QTextStream out(device);
    domDocument.save(out, IndentSize);
    return true;
}

MainWindow 클래스 정의

MainWindow 클래스는 QMainWindow 의 하위 클래스이며 File 메뉴와 Help 메뉴가 있습니다.

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

public slots:
    void open();
    void saveAs();
    void about();

private:
    void createMenus();

    XbelTree *xbelTree;
};

MainWindow 클래스 구현

MainWindow 생성자는 멤버 XbelTree 객체를 인스턴스화하고 헤더를 QStringList 객체 labels 로 설정합니다. 또한 생성자는 createMenus() 를 호출하여 메뉴를 설정합니다. statusBar() 은 "준비됨" 메시지를 표시하는 데 사용됩니다.

MainWindow::MainWindow()
{
    xbelTree = new XbelTree;
    setCentralWidget(xbelTree);

    createMenus();

    statusBar()->showMessage(tr("Ready"));

    setWindowTitle(tr("DOM Bookmarks"));
    const QSize availableSize = screen()->availableGeometry().size();
    resize(availableSize.width() / 2, availableSize.height() / 3);
}

createMenus() 함수는 메뉴를 채우고 키보드 단축키를 설정합니다.

void MainWindow::createMenus()
{
    QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
    QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open);
    openAct->setShortcuts(QKeySequence::Open);

    QAction *saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &MainWindow::saveAs);
    saveAsAct->setShortcuts(QKeySequence::SaveAs);

    QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
    exitAct->setShortcuts(QKeySequence::Quit);

    menuBar()->addSeparator();

    QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
    helpMenu->addAction(tr("&About"), this, &MainWindow::about);
    helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
}

open() 함수는 사용자가 QFileDialog 을 사용하여 XBEL 파일을 열 수 있도록 합니다. 파일을 읽을 수 없거나 구문 분석 오류가 있는 경우 fileNameerrorString 과 함께 경고 메시지가 표시됩니다. 성공하면 XbelTree::read() 을 호출합니다.

void MainWindow::open()
{
    QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath());
    fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
    if (fileDialog.exec() != QDialog::Accepted)
        return;

    const QString fileName = fileDialog.selectedFiles().constFirst();
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("DOM Bookmarks"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(QDir::toNativeSeparators(fileName),
                                  file.errorString()));
        return;
    }

    if (xbelTree->read(&file))
        statusBar()->showMessage(tr("File loaded"), 2000);
}

saveAs() 함수는 QFileDialog 을 표시하고 사용자에게 fileName 을 입력하라는 메시지를 표시합니다. open() 함수와 마찬가지로 이 함수 역시 파일을 쓸 수 없는 경우 경고 메시지를 표시합니다. 성공하면 XbelTree::write() 을 호출합니다.

void MainWindow::saveAs()
{
    QFileDialog fileDialog(this, tr("Save Bookmark File"), QDir::currentPath());
    fileDialog.setAcceptMode(QFileDialog::AcceptSave);
    fileDialog.setDefaultSuffix("xbel"_L1);
    fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
    if (fileDialog.exec() != QDialog::Accepted)
        return;

    const QString fileName = fileDialog.selectedFiles().constFirst();
    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("DOM Bookmarks"),
                             tr("Cannot write file %1:\n%2.")
                             .arg(QDir::toNativeSeparators(fileName),
                                  file.errorString()));
        return;
    }

    if (xbelTree->write(&file))
        statusBar()->showMessage(tr("File saved"), 2000);
}

about() 함수는 예제에 대한 간략한 설명과 함께 QMessageBox 을 표시합니다.

void MainWindow::about()
{
   QMessageBox::about(this, tr("About DOM Bookmarks"),
                      tr("The <b>DOM Bookmarks</b> example demonstrates how to "
                         "use Qt's DOM classes to read and write XML "
                         "documents."));
}

XBEL 파일에 대한 자세한 내용은 XML 북마크 교환 언어 리소스 페이지를 참조하세요.

예제 프로젝트 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.