Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. no pictures in helpfile
Qt 6.11 is out! See what's new in the release blog

no pictures in helpfile

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 3 Posters 2.0k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    django.Reinhard
    wrote on last edited by
    #1

    Hi,

    I'm currently trying to familiarize myself with the Qt help system. As I understand it, the help system is built on top of the qdoc system.

    So I have created some files for qdoc. The generation of the html files works and the files also look as expected.

    However, when I generate the help file and view it with QtAssistant, there is not a single image. I searched for additional information, but my uncle Ducky hardly finds any entries about qt-helpfiles.

    I vaguely remember reading somewhere that the images should be in the same directory as the html files, so I tried copying the images. Didn't make any difference, unfortunately.

    directory layout from QtCreator:

    Dirs.png

    My qdoc config file:

    project     = FalconView
    description = controlcenter for linuxcnc
    outputdir   = html
    headerdirs  = src
    sourcedirs  = src
    imagedirs   = src/images
    exampledirs = .
    version     = 0.1
    headers.fileextensions  = "*.h *.hpp"
    sources.fileextensions  = "*.cpp *.qdoc"
    qhp.FalconView.namespace     = de.schwarzrot.falconview.0.1
    qhp.FalconView.virtualFolder = FalconView
    qhp.FalconView.indexTitle    = User Guide
    

    the help project file:

    <?xml version="1.0" encoding="utf-8" ?>
    <QtHelpProject version="1.0">
        <namespace>de.schwarzrot.falconview.0.1</namespace>
        <virtualFolder>FalconView</virtualFolder>
        <filterSection>
            <filterAttribute>FalconView</filterAttribute>
            <filterAttribute>0.1</filterAttribute>
            <toc>
                <section title="User Guide" ref="index.html">
                    <section title="start the engine" ref="startup.html"/>
                    <section title="reference" ref="reference.html"/>
                    <section title="use case" ref="usage.html"/>
                </section>
            </toc>
            <keywords>
                <keyword name="fileManager" id="fileManager" ref="fileManager.html"/>
            </keywords>
            <files>
                <file>*.html</file>
            </files>
        </filterSection>
    </QtHelpProject>
    

    ... and here's how I build the help-file:

    #!/bin/bash
    project=FalconView
    
    qdoc ${project}.qdocconf
    cp ${project}.qhp html
    cd html
    cp -a images/* .
    qhelpgenerator ${project}.qhp
    mv ${project}.qch ..
    

    As mentioned - I tried with and without image copying.

    May be the biggest question for my understanding:
    Are the images put into the compressed helpfile, or do I have to provied them to the helpviewer, like any HTML-browser does?

    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      qdoc is an internal tool of Qt, do not use it for other projects. Use Doxygen instead.

      (Z(:^

      artwawA 1 Reply Last reply
      1
      • sierdzioS sierdzio

        qdoc is an internal tool of Qt, do not use it for other projects. Use Doxygen instead.

        artwawA Offline
        artwawA Offline
        artwaw
        wrote on last edited by
        #3

        @sierdzio said in no pictures in helpfile:

        qdoc is an internal tool of Qt

        That's new to me, it is not mentioned in the documentation or did I miss it through all the years?

        For more information please re-read.

        Kind Regards,
        Artur

        sierdzioS 1 Reply Last reply
        0
        • D Offline
          D Offline
          django.Reinhard
          wrote on last edited by
          #4

          Hi,

          thank you for your attention!

          @sierdzio said in no pictures in helpfile:

          qdoc is an internal tool of Qt, do not use it for other projects.

          Hm, my expectations are not very high, so qdoc works fine for me and I have no issues.

          Opposed to the helpengine.
          I tried to follow the Qt example "contextsensitivehelp", but it does not work at all for me. Documentation says, that I should test helpfile-creation by loading it in QtAssistant.

          Well, QtAssistant shows the pages, but without pictures. But when I use helpengine like Qt examples, I have no documents, no keywords, nothing.
          Even if I use the same url, that QtAssistant claims to use, I get no document in my app. But setup returns ok.

          So I don't know what's wrong.
          I'll gonna try to build my own help engine.

          1 Reply Last reply
          0
          • D Offline
            D Offline
            django.Reinhard
            wrote on last edited by
            #5

            @django-Reinhard said in no pictures in helpfile:

            I'll gonna try to build my own help engine.

            Done :)

            My uncle duck guided me to stackoverflow (last post), and with that info, it was straight forward. Qt already has all needed stuff - only had to assemble it new.

            According to my tests, I can answer my initial question:

            @django-Reinhard said in no pictures in helpfile:

            Are the images put into the compressed helpfile, or do I have to provied them to the helpviewer, like any HTML-browser does?

            Aperently not.

            Ok, what did I do?
            I have the qdoc-files in directory docs, which has a subdirectory images.
            qdoc then creates a directory 'html' with a subdirectory 'images' and copies all images to html/images.

            So I created a zip-file from the qdoc output - means all generated html-files and the image subdirectory.

            Base for help output is QTextBrowser. It already has complete functionality. Subclassing is anyway required to be able to override QTextBrowser::loadResource.

            So the minimal project with a working helpEngine looks like:

            MainWindow declaration:

            class MainWindow : public QMainWindow
            {
              Q_OBJECT
            public:
              explicit MainWindow(QWidget *parent = nullptr);
              virtual ~MainWindow();
            
              void createConnections();
              void loadPage(const QString& pgName);
              void loadPage(const QUrl& link);
            
            private:
              Ui::MainWindow* ui;
              HelpEngine*     he;
              QTextBrowser*   tb;
              };
            

            ... and MainWindow definition:

            MainWindow::MainWindow(QWidget *parent)
             : QMainWindow(parent)
             , ui(new Ui::MainWindow)
             , he(new HelpEngine(QApplication::applicationDirPath()
                               + "/../share/falconview/help/FalconView.qzh"
                               , this))
             , tb(new HTMLBrowser(*he, this)) {
              ui->setupUi(this);
              tb->setMinimumWidth(830);
            
              setCentralWidget(tb);
              createConnections();
              }
            
            
            MainWindow::~MainWindow() {
              }
            
            
            void MainWindow::createConnections() {
              connect(ui->actionExit,      &QAction::triggered, this, &QWidget::close);
              connect(ui->actionStartup,   &QAction::triggered, this, [=](){ loadPage("startup"); });
              connect(ui->actionReference, &QAction::triggered, this, [=](){ loadPage("reference"); });
              connect(ui->actionUsage,     &QAction::triggered, this, [=](){ loadPage("usage"); });
              }
            
            
            void MainWindow::loadPage(const QString &pgName) {
              qDebug() << "MW::loadPage(" << pgName << ")";
              tb->setSource(pgName + ".html");
              }
            
            
            void MainWindow::loadPage(const QUrl& link) {
              qDebug() << "MW::loadPage(" << link << ")";
              tb->setSource(link);
              }
            

            I renamed the zip-archive to *.qzh - shortcut for "Qt zipped helpfile" ;)

            For testing purpuse I added a menubar to mainwindow, and I used the menuentries to select pages from help-archive.

            HTMLBrowser is subclass of QTextBrowser:

            class HTMLBrowser : public QTextBrowser
            {
              Q_OBJECT
            public:
              explicit HTMLBrowser(HelpEngine& engine, QWidget* parent = nullptr);
              virtual ~HTMLBrowser();
            
              QVariant loadResource(int type, const QUrl &name);
            
            private:
              HelpEngine& engine;
              };
            

            Nothing special in implementation:

            HTMLBrowser::HTMLBrowser(HelpEngine& engine, QWidget* parent)
             : QTextBrowser(parent)
             , engine(engine) {
              }
            
            
            HTMLBrowser::~HTMLBrowser() {
              }
            
            
            QVariant HTMLBrowser::loadResource(int type, const QUrl& link) {
              qDebug() << "loadResource(" << type << "url:" << link;
            
              return engine.readFile(link.path());
              }
            

            ... and finally the "new" HelpEngine.
            First declaration:

            class HelpEngine : public QObject
            {
              Q_OBJECT
            public:
              explicit HelpEngine(const QString& helpFile, QObject *parent = nullptr);
            
              QVariant readFile(const QString& file);
              void     tellContent();
            
            protected:
              void     buildDir(const QVector<QZipReader::FileInfo> entries);
            
            private:
              QZipReader*        reader;
              QMap<QString, int> helpDir;
              };
            

            ... and here the implementation:

            HelpEngine::HelpEngine(const QString& helpFile, QObject *parent)
             : QObject(parent)
             , reader(new QZipReader(helpFile)) {
              buildDir(reader->fileInfoList());
              }
            
            
            void HelpEngine::buildDir(const QVector<QZipReader::FileInfo> entries) {
              int mx = entries.count();
            
              for (int i=0; i < mx; ++i) {
                  if (!entries[i].isFile) continue;
                  helpDir.insert(entries[i].filePath, i);
                  }
              }
            
            
            QVariant HelpEngine::readFile(const QString& file) {
              if (!helpDir.contains(file)) return QVariant();
              QByteArray ba = reader->fileData(file);
            
              return QVariant(ba);
              }
            
            
            void HelpEngine::tellContent() {
              for (const QZipReader::FileInfo& e : reader->fileInfoList()) {
                  qDebug() << "Helpfile-entry:" << e.filePath
                           << (e.isDir ? "Dir" : "")
                           << (e.isFile ? "File" : "")
                           << (e.isSymLink ? "SymLink" : "");
                  }
              }
            

            works like charming :)

            ... and in my humble opinion its fast enuf to use it without sqlite.
            Well, I have to find a solution for keywords and index, but I'm confident, that is feasible.

            Cheers

            1 Reply Last reply
            0
            • artwawA artwaw

              @sierdzio said in no pictures in helpfile:

              qdoc is an internal tool of Qt

              That's new to me, it is not mentioned in the documentation or did I miss it through all the years?

              sierdzioS Offline
              sierdzioS Offline
              sierdzio
              Moderators
              wrote on last edited by
              #6

              @artwaw said in no pictures in helpfile:

              @sierdzio said in no pictures in helpfile:

              qdoc is an internal tool of Qt

              That's new to me, it is not mentioned in the documentation or did I miss it through all the years?

              It's not mentioned in the docs, but it has been said repeatedly on the mailing list. I have no idea why the docs have never been updated with this info.

              (Z(:^

              artwawA 1 Reply Last reply
              1
              • sierdzioS sierdzio

                @artwaw said in no pictures in helpfile:

                @sierdzio said in no pictures in helpfile:

                qdoc is an internal tool of Qt

                That's new to me, it is not mentioned in the documentation or did I miss it through all the years?

                It's not mentioned in the docs, but it has been said repeatedly on the mailing list. I have no idea why the docs have never been updated with this info.

                artwawA Offline
                artwawA Offline
                artwaw
                wrote on last edited by
                #7

                @sierdzio Thank you!

                For more information please re-read.

                Kind Regards,
                Artur

                1 Reply Last reply
                0
                • D Offline
                  D Offline
                  django.Reinhard
                  wrote on last edited by
                  #8

                  Hi,

                  help from compressed archive works no matter, whether the *.html-files where built by qdoc, doxygen or are handwritten.
                  If firefox can display the page as local file, then it should be ready for help-browser as well.

                  New changes for index and keyword support: I added *.qhp to archive. That file already contains all necessary informations. So I did it like QHelpEngine: create Standard-widgets, which can be included in some page layout.

                  I created a help-"dialog" a dockable, which can be used floating or integrated into mainwindow.

                  HelpDialog::HelpDialog(QWidget* parent)
                   : QDockWidget(tr("Help"), parent)
                   , he(new HelpEngine(Core().helpFilename(), this))
                   , tb(new HTMLBrowser(*he))
                   , cw(static_cast<HelpContentWidget*>(he->contentWidget()))
                   , kw(static_cast<HelpKeywordWidget*>(he->keywordWidget())) {
                    setObjectName("HelpDialog");
                    tb->setMinimumWidth(830);
                    setMinimumWidth(1100);
                    QSplitter*  sh = new QSplitter(Qt::Horizontal, this);
                    QTabWidget* tw = new QTabWidget(sh);
                  
                    sh->addWidget(tw);
                    sh->addWidget(tb);
                    tw->addTab(cw, tr("Content"));
                    tw->addTab(kw, tr("Keywords"));
                    setWidget(sh);
                    connect(cw, &QTreeWidget::currentItemChanged, this, &HelpDialog::contentItemChanged);
                    connect(kw, &QListWidget::currentItemChanged, this, &HelpDialog::keywordItemChanged);
                    }
                  
                  
                  void HelpDialog::contentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *) {
                    const QString& page = current->data(1, Qt::DisplayRole).toString();
                  
                    tb->setSource(page);
                    }
                  
                  
                  void HelpDialog::keywordItemChanged(QListWidgetItem *current, QListWidgetItem *) {
                    const QString& page = current->toolTip();
                  
                    tb->setSource(page);
                    }
                  

                  FalconView_Help01.jpg

                  Here the sources. First the content-widget, a subclass of QTreeWidget:

                  class HelpContentWidget : public QTreeWidget
                  {
                    Q_OBJECT
                  public:
                    explicit HelpContentWidget(QWidget* parent = nullptr);
                    virtual ~HelpContentWidget();
                  
                    void setFolderIcon(const QIcon& ico);
                    void setHelpIcon(const QIcon& ico);
                    void parse(const QByteArray& ba);
                  
                  protected:
                    QTreeWidgetItem* createItem(const QDomElement& e, QTreeWidgetItem* parent);
                    QTreeWidgetItem* processElement(const QDomElement& e, QTreeWidgetItem* parent);
                    void processChildren(const QDomElement& e, QTreeWidgetItem* parentItem = nullptr);
                    void processAttributes(const QDomElement& e, QTreeWidgetItem* item);
                  
                  private:
                    int   level;
                    QIcon folderIcon;
                    QIcon helpIcon;
                    };
                  

                  ... and its implementation:

                  HelpContentWidget::HelpContentWidget(QWidget* parent)
                   : QTreeWidget(parent) {
                    header()->setStretchLastSection(true);
                    setHeaderLabel(tr("Title"));
                    }
                  
                  
                  HelpContentWidget::~HelpContentWidget() {
                    }
                  
                  
                  void HelpContentWidget::parse(const QByteArray& ba) {
                    QDomDocument doc;
                  
                    doc.setContent(ba);
                    QDomNodeList links = doc.elementsByTagName("toc");
                  
                    level = 0;
                    clear();
                    for (int i=0; i < links.count(); ++i) {
                        QDomNode link = links.item(i);
                  
                        qDebug() << "\n";
                        qDebug() << "check entry #" << i;
                        if (link.isElement()) {
                           QDomElement e = link.toElement();
                  
                           processChildren(e);
                           }
                        }
                    }
                  
                  
                  void HelpContentWidget::processAttributes(const QDomElement& e, QTreeWidgetItem* item) {
                    int mx = e.attributes().count();
                  
                    qDebug() << "processAttributes ...";
                    if (mx > 0) {
                       qDebug() << "element has" << mx << "attributes";
                       for (int i=0; i < mx; ++i) {
                           const QDomNode& n = e.attributes().item(i);
                  
                           qDebug() << "\tattribute:" << n.nodeName() << " => " << n.nodeValue();
                           if (n.nodeName() == "title")    item->setText(0, n.nodeValue());
                           else if (n.nodeName() == "ref") item->setText(1, n.nodeValue());
                           }
                       }
                    else qDebug() << "element has NO attributes";
                    }
                  
                  
                  void HelpContentWidget::processChildren(const QDomElement& e, QTreeWidgetItem* parent) {
                    qDebug() << "processChildren ... (level:" << level++ << ")";
                  
                    for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
                        if (n.isElement()) {
                           qDebug() << "child is Element";
                           processElement(n.toElement(), parent);
                           }
                        }
                    --level;
                    }
                  
                  
                  QTreeWidgetItem* HelpContentWidget::createItem(const QDomElement &e, QTreeWidgetItem *parent) {
                    QTreeWidgetItem* item;
                  
                    if (parent) item = new QTreeWidgetItem(parent);
                    else        item = new QTreeWidgetItem(this);
                    item->setIcon(0, folderIcon);
                    processAttributes(e, item);
                  
                    return item;
                    }
                  
                  
                  QTreeWidgetItem* HelpContentWidget::processElement(const QDomElement& e, QTreeWidgetItem* parent) {
                    qDebug() << "processElement - tag:" << e.tagName() << "text:" << e.text();
                    QTreeWidgetItem* item = nullptr;
                  
                    if (e.tagName() == "section") {
                       item = createItem(e, parent);
                  
                       processChildren(e, item);
                       }
                    return item;
                    }
                  
                  
                  void HelpContentWidget::setFolderIcon(const QIcon &ico) {
                    folderIcon = ico;
                    }
                  
                  
                  void HelpContentWidget::setHelpIcon(const QIcon &ico) {
                    helpIcon = ico;
                    }
                  

                  ... followed by the keywordWidget:

                  class HelpKeywordWidget : public QListWidget
                  {
                    Q_OBJECT
                  public:
                    explicit HelpKeywordWidget(QWidget* parent = nullptr);
                    virtual ~HelpKeywordWidget();
                  
                    void parse(const QByteArray& ba);
                    void setIcon(const QIcon& icon);
                  
                  protected:
                    void processChildren(const QDomElement& e);
                  
                  private:
                    QIcon icon;
                    };
                  

                  with this implementation:

                  HelpKeywordWidget::HelpKeywordWidget(QWidget* parent)
                   : QListWidget(parent) {
                    }
                  
                  
                  HelpKeywordWidget::~HelpKeywordWidget() {
                    }
                  
                  
                  void HelpKeywordWidget::parse(const QByteArray &ba) {
                    QDomDocument doc;
                  
                    doc.setContent(ba);
                    QDomNodeList links = doc.elementsByTagName("keywords");
                  
                    clear();
                    for (int i=0; i < links.count(); ++i) {
                        QDomNode link = links.item(i);
                  
                        qDebug() << "\n";
                        qDebug() << "check entry #" << i;
                        if (link.isElement()) {
                           QDomElement e = link.toElement();
                  
                           processChildren(e);
                           }
                        }
                    }
                  
                  
                  void HelpKeywordWidget::processChildren(const QDomElement& e) {
                    qDebug() << "processChildren ... ";
                  
                    for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
                        if (n.isElement()) {
                           qDebug() << "child is Element";
                           QDomElement      elem = n.toElement();
                           QListWidgetItem* item = new QListWidgetItem();
                           int              mx   = elem.attributes().count();
                  
                           item->setIcon(icon);
                           for (int i=0; i < mx; ++i) {
                               const QDomNode& n = elem.attributes().item(i);
                  
                               if (n.nodeName() == "name")     item->setText(n.nodeValue());
                               else if (n.nodeName() == "ref") item->setToolTip(n.nodeValue());
                               }
                           addItem(item);
                           }
                        else if (n.isEntity()) qDebug() << "child is Entity";
                        else if (n.isAttr())   qDebug() << "child is Attribute";
                        else if (n.isText())   qDebug() << "child is Text";
                        }
                    }
                  
                  
                  void HelpKeywordWidget::setIcon(const QIcon &icon) {
                    this->icon = icon;
                    }
                  

                  Creation of helpfile is done with a little bash-script:

                  #!/bin/bash
                  project=FalconView
                  
                  qdoc ${project}.qdocconf
                  cp ${project}.qhp html
                  cd html
                  zip -u9 ../FalconView.qzh *.html images/* FalconView.qhp
                  

                  I think that this solution can be used to create a helper solution for small applications with the least possible effort.

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved