Coders Packet

A Document Viewer using Qt Creator and C++

By Saif Ali Khan

A Document Viewer made using Qt Creator and C++, with astonishing features, you can view a txt file, JSON file, and non-application/pdfs and you can print them as well.

A Document Viewer Using QT Creator and C++

1. Introduction:- This is a Document Viewer made using C++ on Qt Creator. It includes viewing txt files, JSON files, and pdf files and printing them as well, the app comes with some astonishing features like open documents, recent files, and many more. Cut and Paste options are also available within the app. You can edit the documents with the help of the edit option in the app. I used the latest version of Qt Creator to make the app, you can also use it as per your needs and requirements. 

2. Setup:- You just need to install Qt Creator from their official website and it is a must to have the MINGW compiler installed on your machine, as you have to build the app and then run it. I used version 6 of Qt Creator for the same, you can use similar versions or another as per your needs.

3. Creating Project:- Once you have installed Qt Creator successfully on your machine, go to create a project and let everything be the default, just make sure you have selected cmake build for the project and you are ready to go.

4. App Structure:- As we know this app is a desktop application, so we need to setup the project structure very well, do take a look at the images above to find out the correct structure for the app.

5. Code/Logic:- As you'll be provided with the source code within a zip file, extract it and open that project in your Qt Creator by dragging the project above the Qt Creator app on your machine. Logics that are very important are given below :-

Mainwindow Logic

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "viewerfactory.h"
#include "abstractviewer.h"
#include "recentfiles.h"
#include "recentfilemenu.h"

#include 
#include 
#include 

#include 
#include 

using namespace Qt::StringLiterals;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onActionOpenTriggered);
    connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::onActionAboutTriggered);
    connect(ui->actionAboutQt, &QAction::triggered, this, &MainWindow::onActionAboutQtTriggered);

    m_recentFiles.reset(new RecentFiles(ui->actionRecent));
    connect(m_recentFiles.get(), &RecentFiles::countChanged, this, [&](int count){
        ui->actionRecent->setText(tr("%n recent files", nullptr, count));
    });

    readSettings();
    m_factory.reset(new ViewerFactory(ui->viewArea, this));
    const QStringList &viewers = m_factory->viewerNames();

    const QString msg = tr("Available viewers: %1").arg(viewers.join(", "_L1));
    statusBar()->showMessage(msg);

    auto *menu = new RecentFileMenu(this, m_recentFiles.get());
    ui->actionRecent->setMenu(menu);
    connect(menu, &RecentFileMenu::fileOpened, this, &MainWindow::openFile);
    QWidget *w = ui->mainToolBar->widgetForAction(ui->actionRecent);
    auto *button = qobject_cast(w);
    if (button)
        connect(ui->actionRecent, &QAction::triggered, button, &QToolButton::showMenu);
}

MainWindow::~MainWindow()
{
    saveSettings();
}

void MainWindow::onActionOpenTriggered()
{
    QFileDialog fileDialog(this, tr("Open Document"), m_currentDir.absolutePath());
    while (fileDialog.exec() == QDialog::Accepted
           && !openFile(fileDialog.selectedFiles().constFirst())) {
    }
}

bool MainWindow::openFile(const QString &fileName)
{
    QFile *file = new QFile(fileName);
    if (!file->exists()) {
        statusBar()->showMessage(tr("File %1 could not be opened")
                                 .arg(QDir::toNativeSeparators(fileName)));
        delete file;
        return false;
    }

    QFileInfo fileInfo(*file);
    m_currentDir = fileInfo.dir();
    m_recentFiles->addFile(fileInfo.absoluteFilePath());

    // If a viewer is already open, clean it up and save its settings
    resetViewer();
    m_viewer = m_factory->viewer(file);
    if (!m_viewer) {
        statusBar()->showMessage(tr("File %1 can't be opened.")
                                 .arg(QDir::toNativeSeparators(fileName)));
        return false;
    }

    ui->actionPrint->setEnabled(m_viewer->hasContent());
    connect(m_viewer, &AbstractViewer::printingEnabledChanged, ui->actionPrint, &QAction::setEnabled);
    connect(ui->actionPrint, &QAction::triggered, m_viewer, &AbstractViewer::print);
    connect(m_viewer, &AbstractViewer::showMessage, statusBar(), &QStatusBar::showMessage);

    m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget);
    restoreViewerSettings();
    ui->scrollArea->setWidget(m_viewer->widget());
    return true;
}

void MainWindow::onActionAboutTriggered()
{
    const QString viewerNames = m_factory->viewerNames().join(", "_L1);
    const QString mimeTypes = m_factory->supportedMimeTypes().join(u'\n');
    QString text = tr("A Widgets application to display and print JSON, "
                      "text and PDF files. Demonstrates various features to use "
                      "in widget applications: Using QSettings, query and save "
                      "user preferences, manage file histories and control cursor "
                      "behavior when hovering over widgets.\n\n"
                      "This version has loaded the following plugins:\n%1\n"
                      "\n\nIt supports the following mime types:\n%2")
                    .arg(viewerNames, mimeTypes);

    if (auto *def = m_factory->defaultViewer())
        text += tr("\n\nOther mime types will be displayed with %1.").arg(def->viewerName());

    QMessageBox::about(this, tr("About Document Viewer Demo"), text);
}

void MainWindow::onActionAboutQtTriggered()
{
    QMessageBox::aboutQt(this);
}

void MainWindow::readSettings()
{
    QSettings settings;

    // Restore working directory
    if (settings.contains(settingsDir))
        m_currentDir = QDir(settings.value(settingsDir).toString());
    else
        m_currentDir = QDir::current();

    // Restore QMainWindow state
    if (settings.contains(settingsMainWindow)) {
        QByteArray mainWindowState = settings.value(settingsMainWindow).toByteArray();
        restoreState(mainWindowState);
    }

    // Restore recent files
    m_recentFiles->restoreFromSettings(settings, settingsFiles);
}

void MainWindow::saveSettings() const
{
    QSettings settings;

    // Save working directory
    settings.setValue(settingsDir, m_currentDir.absolutePath());

    // Save QMainWindow state
    settings.setValue(settingsMainWindow, saveState());

    // Save recent files
    m_recentFiles->saveSettings(settings, settingsFiles);

    settings.sync();
}

void MainWindow::saveViewerSettings() const
{
    if (!m_viewer)
        return;

    QSettings settings;
    settings.beginGroup(settingsViewers);
        settings.setValue(m_viewer->viewerName(), m_viewer->saveState());
    settings.endGroup();
    settings.sync();
}

void MainWindow::resetViewer() const
{
    if (!m_viewer)
        return;

    saveViewerSettings();
    m_viewer->cleanup();
}

void MainWindow::restoreViewerSettings()
{
    if (!m_viewer)
        return;

    QSettings settings;
    settings.beginGroup(settingsViewers);
    QByteArray viewerSettings = settings.value(m_viewer->viewerName(), QByteArray()).toByteArray();
    settings.endGroup();
    if (!viewerSettings.isEmpty())
        m_viewer->restoreState(viewerSettings);
}

 

6. CMakeLists:- You should take a look at these txt files because they are a very important part of the project, as they contain the configurations, build settings, dependencies, imports, and many other cmake aspects.

CMakeLists.txt 

cmake_minimum_required(VERSION 3.16)
project(documentviewer LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

set(CMAKE_AUTORCC ON)

qt_standard_project_setup()

qt_add_library(abstractviewer STATIC
    abstractviewer.cpp abstractviewer.h
)

qt_add_executable(documentviewer
    main.cpp
    mainwindow.cpp mainwindow.h mainwindow.ui
    viewerfactory.cpp viewerfactory.h
    recentfiles.cpp recentfiles.h
    recentfilemenu.cpp recentfilemenu.h
    viewerinterfaces.h
    documentviewer.qrc
)

set_target_properties(documentviewer PROPERTIES
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)

target_link_libraries(documentviewer PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Widgets
    abstractviewer
)

target_link_libraries(abstractviewer PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Widgets
)

set(plugin_targets
    jsonviewer
    txtviewer
)

if(TARGET pdfviewer)
    list(APPEND plugin_targets pdfviewer)
endif()

if(QT6_IS_SHARED_LIBS_BUILD)
    add_dependencies(documentviewer ${plugin_targets})
else()
    target_link_libraries(documentviewer PRIVATE ${plugin_targets})
endif()

if(TARGET Qt6::PrintSupport)
    target_link_libraries(documentviewer PRIVATE Qt6::PrintSupport)
    target_link_libraries(abstractviewer PRIVATE Qt6::PrintSupport)
    add_compile_definitions(QT_DOCUMENTVIEWER_PRINTSUPPORT)
endif()

install(TARGETS documentviewer
    RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
    BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
    LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

 

7. Conclusion:- You'll find an about section for the project inside the help option on the title bar, go through it to understand each and every concept used in the project. Hope you will find this project and its description helpful. 

 

Download Complete Code

Comments

No comments yet

Download Packet

Reviews Report

Submitted by Saif Ali Khan (saifcore7)

Download packets of source code on Coders Packet