Development/Tutorials/KCmdLineArgs: Difference between revisions

    From KDE TechBase
    No edit summary
    No edit summary
    (3 intermediate revisions by the same user not shown)
    Line 5: Line 5:
    series=Beginner Tutorial|
    series=Beginner Tutorial|


    name=Command line arguments (Under construction [[User:milliams]])|
    name=Command line arguments|


    pre=[[Development/Tutorials/Saving and loading|Tutorial 4 - Loading and saving]]|
    pre=[[Development/Tutorials/Saving and loading|Tutorial 4 - Loading and saving]]|


    next=[[Development/Tutorials/###|Tutorial 6 - ###]] (TODO [[User:milliams]])|
    }}


    reading={{class|KCmdLineArgs}} {{class|KCmdLineOptions}}
    }}


    ==Abstract==
    ==Abstract==
    Line 18: Line 16:
    Now that we have a text editor which can open and save files. We will now make the editor act more like a desktop application by enabling it to open files from command line arguments or even using ''Open with'' from within Dolphin.
    Now that we have a text editor which can open and save files. We will now make the editor act more like a desktop application by enabling it to open files from command line arguments or even using ''Open with'' from within Dolphin.


    [[image:introtokdetutorial5.png|frame|center]]
    [[image:tutorial5-kf5.png|frame|center]]


    == The Code ==
    == The Code ==
    Line 24: Line 22:
    ===main.cpp===
    ===main.cpp===
    <syntaxhighlight lang="cpp-qt">
    <syntaxhighlight lang="cpp-qt">
    #include <KApplication>
    #include <cstdlib>
    #include <QApplication>
    #include <QCommandLineParser>
    #include <QUrl>
    #include <QDir>
     
    #include <KAboutData>
    #include <KAboutData>
    #include <KCmdLineArgs>
    #include <KLocalizedString>
    #include <KUrl> //new
     
    #include "mainwindow.h"
    #include "mainwindow.h"
       
       
    int main (int argc, char *argv[])
    int main (int argc, char *argv[])
    {
    {
      KAboutData aboutData( "tutorial5", "tutorial5",
        QApplication app(argc, argv);
          ki18n("Tutorial 5"), "1.0",
       
          ki18n("A simple text area which can load and save."),
        KLocalizedString::setApplicationDomain("tutorial5");
          KAboutData::License_GPL,
       
          ki18n("Copyright (c) 2007 Developer") );
        KAboutData aboutData(
      KCmdLineArgs::init( argc, argv, &aboutData );
                            // The program name used internally. (componentName)
     
                            QStringLiteral("tutorial5"),
      KCmdLineOptions options; //new
                            // A displayable program name string. (displayName)
      options.add("+[file]", ki18n("Document to open")); //new
                            i18n("Tutorial 5"),
      KCmdLineArgs::addCmdLineOptions(options); //new
                            // The program version string. (version)
     
                            QStringLiteral("1.0"),
      KApplication app;
                            // Short description of what the app does. (shortDescription)
                            i18n("A simple text area which can load and save."),
                            // The license this code is released under
                            KAboutLicense::GPL,
                            // Copyright Statement (copyrightStatement = QString())
                            i18n("(c) 2015"),
                            // Optional text shown in the About box.
                            // Can contain any information desired. (otherText)
                            i18n("Some text..."),
                            // The program homepage string. (homePageAddress = QString())
                            QStringLiteral("http://example.com/"),
                            // The bug report email address
                            // (bugsEmailAddress = QLatin1String("[email protected]")
                            QStringLiteral("[email protected]"));
       
        aboutData.addAuthor(i18n("Name"), i18n("Task"), QStringLiteral("[email protected]"),
                            QStringLiteral("http://your.website.com"), QStringLiteral("OSC Username"));
       
        KAboutData::setApplicationData(aboutData);
       
       
      MainWindow* window = new MainWindow();
        QCommandLineParser parser;
      window->show();
        parser.addHelpOption();
     
        parser.addVersionOption();
      KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); //new
        parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
      if(args->count()) //new
       
      {
        aboutData.setupCommandLine(&parser);
        window->openFile(args->url(0).url()); //new
        parser.process(app);
      }
       
     
        aboutData.processCommandLine(&parser);
      return app.exec();
       
        MainWindow* window = new MainWindow();
        window->show();
       
        if (parser.positionalArguments().count() > 0)
        {
            window->openFile(QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath()));
        }
       
        return app.exec();
    }
    }
    </syntaxhighlight>
    </syntaxhighlight>
    Line 65: Line 95:
       
       
    #include <KXmlGuiWindow>
    #include <KXmlGuiWindow>
    #include <KTextEdit>
     
    class KTextEdit;
    class KJob;
       
       
    class MainWindow : public KXmlGuiWindow
    class MainWindow : public KXmlGuiWindow
    {
    {
      Q_OBJECT
        Q_OBJECT
     
       
       public:
       public:
         MainWindow(QWidget *parent=0);
         MainWindow(QWidget *parent=0);
         void openFile(const QString &inputFileName); //new
         void openFile(const QUrl &inputFileName);
     
       private:
       private:
         KTextEdit* textArea;
         KTextEdit* textArea;
         void setupActions();
         void setupActions();
       
         QString fileName;
         QString fileName;
       
       
    Line 86: Line 119:
         void saveFileAs();
         void saveFileAs();
         void saveFileAs(const QString &outputFileName);
         void saveFileAs(const QString &outputFileName);
       
        void downloadFinished(KJob* job);
    };
    };
       
       
    Line 93: Line 128:
    ===mainwindow.cpp===
    ===mainwindow.cpp===
    <syntaxhighlight lang="cpp-qt">
    <syntaxhighlight lang="cpp-qt">
    #include "mainwindow.h"
    #include <QApplication>
    #include <QAction>
    #include <KApplication>
    #include <QSaveFile>
    #include <KAction>
    #include <QFileDialog>
    #include <KLocale>
    #include <QTextStream>
    #include <QByteArray>
     
    #include <KTextEdit>
    #include <KLocalizedString>
    #include <KActionCollection>
    #include <KActionCollection>
    #include <KStandardAction>
    #include <KStandardAction>
    #include <KFileDialog>
    #include <KMessageBox>
    #include <KMessageBox>
    #include <KIO/NetAccess>
    #include <KIO/Job>
    #include <KSaveFile>
     
    #include <QTextStream>
    #include "mainwindow.h"
     
    MainWindow::MainWindow(QWidget *parent)
    MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent), fileName(QString())
        : KXmlGuiWindow(parent),
          fileName(QString())
    {
    {
       textArea = new KTextEdit;
       textArea = new KTextEdit();
       setCentralWidget(textArea);
       setCentralWidget(textArea);
     
       setupActions();
       setupActions();
    }
    }
     
    void MainWindow::setupActions()
    void MainWindow::setupActions()
    {
    {
      KAction* clearAction = new KAction(this);
        QAction* clearAction = new QAction(this);
      clearAction->setText(i18n("Clear"));
        clearAction->setText(i18n("&Clear"));
      clearAction->setIcon(KIcon("document-new"));
        clearAction->setIcon(QIcon::fromTheme("document-new"));
      clearAction->setShortcut(Qt::CTRL + Qt::Key_W);
        actionCollection()->setDefaultShortcut(clearAction, Qt::CTRL + Qt::Key_W);
      actionCollection()->addAction("clear", clearAction);
        actionCollection()->addAction("clear", clearAction);
      connect(clearAction, SIGNAL(triggered(bool)),
        connect(clearAction, SIGNAL(triggered(bool)), textArea, SLOT(clear()));
              textArea, SLOT(clear()));
       
        KStandardAction::quit(qApp, SLOT(quit()), actionCollection());
       
        KStandardAction::open(this, SLOT(openFile()), actionCollection());
       
       
      KStandardAction::quit(kapp, SLOT(quit()),
        KStandardAction::save(this, SLOT(saveFile()), actionCollection());
                            actionCollection());
       
       
      KStandardAction::open(this, SLOT(openFile()),
        KStandardAction::saveAs(this, SLOT(saveFileAs()), actionCollection());
                            actionCollection());
       
       
      KStandardAction::save(this, SLOT(saveFile()),
        KStandardAction::openNew(this, SLOT(newFile()), actionCollection());
                            actionCollection());
       
        setupGUI(Default, "tutorial5ui.rc");
      KStandardAction::saveAs(this, SLOT(saveFileAs()),
                            actionCollection());
      KStandardAction::openNew(this, SLOT(newFile()),
                            actionCollection());
      setupGUI();
    }
    }
     
    void MainWindow::newFile()
    void MainWindow::newFile()
    {
    {
      fileName.clear();
        fileName.clear();
      textArea->clear();
        textArea->clear();
    }
    }
     
    void MainWindow::saveFileAs(const QString &outputFileName)
    void MainWindow::saveFileAs(const QString &outputFileName)
    {
    {
      KSaveFile file(outputFileName);
        if (!outputFileName.isNull())
      file.open();
        {
     
            QSaveFile file(outputFileName);
      QByteArray outputByteArray;
            file.open(QIODevice::WriteOnly);
      outputByteArray.append(textArea->toPlainText());
           
      file.write(outputByteArray);
            QByteArray outputByteArray;
      file.finalize();
            outputByteArray.append(textArea->toPlainText().toUtf8());
      file.close();
            file.write(outputByteArray);
     
            file.commit();
      fileName = outputFileName;
     
            fileName = outputFileName;
        }
    }
    }
     
    void MainWindow::saveFileAs()
    void MainWindow::saveFileAs()
    {
    {
      saveFileAs(KFileDialog::getSaveFileName());
        saveFileAs(QFileDialog::getSaveFileName(this, i18n("Save File As")));
    }
    }
     
    void MainWindow::saveFile()
    void MainWindow::saveFile()
    {
    {
      if(!fileName.isEmpty())
        if (!fileName.isEmpty())
      {
        {
        saveFileAs(fileName);
            saveFileAs(fileName);
      }
        }
      else
        else
      {
        {
        saveFileAs();
            saveFileAs();
      }
        }
    }
    }
     
    void MainWindow::openFile() //changed
     
    void MainWindow::openFile()
    {
    {
      openFile(KFileDialog::getOpenFileName());
        openFile(QFileDialog::getOpenFileUrl(this, i18n("Open File")));
    }
    }
       
    void MainWindow::openFile(const QUrl &inputFileName)
    {
        if (!inputFileName.isEmpty())
        {
            KIO::Job* job = KIO::storedGet(inputFileName);
            fileName = inputFileName.toLocalFile();


    void MainWindow::openFile(const QString &inputFileName) //new
            connect(job, SIGNAL(result(KJob*)), this, SLOT(downloadFinished(KJob*)));
           
            job->exec();
        }
    }
     
    void MainWindow::downloadFinished(KJob* job)
    {
    {
      QString tmpFile;
        if (job->error())
      if(KIO::NetAccess::download(inputFileName, tmpFile,
        {
            this))
            KMessageBox::error(this, job->errorString());
      {
            fileName.clear();
        QFile file(tmpFile);
            return;
        file.open(QIODevice::ReadOnly);
         }
        textArea->setPlainText(QTextStream(&file).readAll());
       
         fileName = inputFileName;
         KIO::StoredTransferJob* storedJob = (KIO::StoredTransferJob*)job;
         textArea->setPlainText(QTextStream(storedJob->data(), QIODevice::ReadOnly).readAll());
         KIO::NetAccess::removeTempFile(tmpFile);
      }
      else
      {
         KMessageBox::error(this,  
            KIO::NetAccess::lastErrorString());
      }
    }
    }
    </syntaxhighlight>
    </syntaxhighlight>
    Line 216: Line 255:
         xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
         xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
                             http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd" >
                             http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd" >
     
       <MenuBar>
       <MenuBar>
         <Menu name="file" >
         <Menu name="file" >
    Line 222: Line 261:
         </Menu>
         </Menu>
       </MenuBar>
       </MenuBar>
     
       <ToolBar name="mainToolBar" >
       <ToolBar name="mainToolBar" >
         <text>Main Toolbar</text>
         <text>Main Toolbar</text>
         <Action name="clear" />
         <Action name="clear" />
       </ToolBar>
       </ToolBar>
     
    </gui>
    </gui>
    </syntaxhighlight>
    </syntaxhighlight>
    This is identical to the <tt>tutorial''x''ui.rc</tt> from the last two tutorials except <tt>tutorial''x''</tt> is now <tt>tutorial5</tt>.


    ==Explanation==
    ==Explanation==
    Line 236: Line 274:
    ===mainwindow.h===
    ===mainwindow.h===


    Here we have done nothing but add a new <tt>openFile</tt> function which takes a <tt>QString</tt>
    Here we have done nothing but add a new <tt>openFile</tt> function which takes a <tt>QUrl</tt>. Again, we use a QUrl instead of a QString so that we can also work with remote files as if they were local.
    <syntaxhighlight lang="cpp-qt">
    <syntaxhighlight lang="cpp-qt">
    void openFile(const QString &inputFileName);
    void openFile(const QUrl &inputFileName);
    </syntaxhighlight>
    </syntaxhighlight>


    ===mainwindow.cpp===
    ===mainwindow.cpp===


    There's no new code here, only rearranging. Everything from <tt>void openFile()</tt> has been moved into <tt>void openFile(const QString &inputFileName)</tt> except the call to <tt>KFileDialog::getOpenFileName()</tt>.
    There's no new code here, only rearranging. Everything from <tt>void openFile()</tt> has been moved into <tt>void openFile(const QUrl &inputFileName)</tt> except the call to <tt>QFileDialog::getOpenFileUrl()</tt>.


    This way, we can call <tt>openFile()</tt> if we want to display a dialog, or we can call <tt>openFile(QString)</tt> if we know the name of the file already.
    This way, we can call <tt>openFile()</tt> if we want to display a dialog, or we can call <tt>openFile(QString)</tt> if we know the name of the file already. Which will be the case when we feed the file name through the command line.


    ===main.cpp===
    ===main.cpp===


    This is where all the {{class|KCmdLineArgs}} magic happens.
    This is where all the [http://doc.qt.io/qt-5/qcommandlineparser.html QCommandLineParser] magic happens. In previous examples, we only used the class to feed QApplication the necessary data for using flags like <tt>--version</tt> or <tt>--author</tt>. Now we actually get to use it to process command line arguments.
     
    First, we tell QCommandLineParser that we want to add a new positional arguments. In a nutshell, these are arguments that are not options. <tt>-h</tt> or <tt>--version</tt> are options, <tt>file</tt> is an argument.
    <syntaxhighlight lang="cpp-qt">
    parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
    </syntaxhighlight>


    ==Make, Install And Run==
     
    Later on, we start processing positional arguments, but only if there is one. Otherwise, we proceed as usual. In our case we can only open one file at a time, so only the first file is of interest to us. We call the <tt>openFile()</tt> function and feed it the URL of the file we want to open, whether it is a local file like {{path|$HOME/foo}} or a remote one like {{path|ftp.mydomain.com/bar}}. We use the overloaded form of <tt>[http://doc.qt.io/qt-5/qurl.html#fromUserInput-1 QUrl::fromUserInput()]</tt> in order to set the current path. This is needed in order to work with relative paths like <tt>"../baz"</tt>.
    <syntaxhighlight lang="cpp-qt">
    if (parser.positionalArguments().count() > 0)
    {
        window->openFile(QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath()));
    }
    </syntaxhighlight>
     
    ==Make, Install and Run==


    ===CMakeLists.txt===
    ===CMakeLists.txt===
    <syntaxhighlight lang="cmake">
    <syntaxhighlight lang="cmake">
    project(tutorial5)
    project (tutorial5)
     
    find_package(KDE4 REQUIRED)
    cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
    include_directories( ${KDE4_INCLUDES} )
    set(QT_MIN_VERSION "5.3.0")
    set(KF5_MIN_VERSION "5.2.0")
    set(tutorial5_SRCS  
     
      main.cpp
    find_package(ECM 1.0.0 REQUIRED NO_MODULE)
      mainwindow.cpp
    set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
     
    include(KDEInstallDirs)
    include(KDECMakeSettings)
    include(KDECompilerSettings)
    include(FeatureSummary)
     
    find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
        Core    # QCommandLineParser, QStringLiteral, QSaveFile, QTextStream, QByteArray
        Widgets # QApplication, QAction, QFileDialog
    )
     
    find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
        CoreAddons      # KAboutData
        I18n            # KLocalizedString
        XmlGui          # KXmlGuiWindow, KActionCollection
        TextWidgets    # KTextEdit
        ConfigWidgets  # KStandardActions
        WidgetsAddons  # KMessageBox
        KIO            # KIO
    )
       
    feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
       
    set(tutorial5_SRCS main.cpp mainwindow.cpp)
     
    add_executable(tutorial5 ${tutorial5_SRCS})
     
    target_link_libraries(tutorial5
        Qt5::Widgets
        KF5::CoreAddons
        KF5::I18n
        KF5::XmlGui
        KF5::TextWidgets
        KF5::ConfigWidgets
        KF5::WidgetsAddons
        KF5::KIOCore
    )
    )
     
    kde4_add_executable(tutorial5 ${tutorial5_SRCS})
    install(TARGETS tutorial5  ${INSTALL_TARGETS_DEFAULT_ARGS})
       
     
    target_link_libraries(tutorial5 ${KDE4_KDEUI_LIBS}
    install(FILES tutorial5ui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/tutorial5)
                                    ${KDE4_KIO_LIBS})
    install(TARGETS tutorial5 DESTINATION ${BIN_INSTALL_DIR})
    install( FILES tutorial5ui.rc  
            DESTINATION ${DATA_INSTALL_DIR}/tutorial5 )
    </syntaxhighlight>
    </syntaxhighlight>


    With this file, the tutorial can built and run in the same way as tutorial 3 and 4. For more information, see tutorial 3.
    With this file, the tutorial can built and run in the same way as tutorial 3 and 4. For more information, see tutorial 3.
    Line 280: Line 364:
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME
    make install
    make install
    $HOME/bin/tutorial5
    XDG_DATA_DIRS=$HOME/share:$XDG_DATA_DIRS $HOME/bin/tutorial5
    </syntaxhighlight>
    </syntaxhighlight>


    ==Moving On==
     
    Now you can move on to the ### (TODO [[User:milliams]]) tutorial.
    {{Attention||The source code on this page applies only the current KDE Frameworks 5 ("KF5") version. For the older KDE Development Platform ("KDE4"), See [[Development/Tutorials/First_program/KDE4]]}}


    [[Category:C++]]
    [[Category:C++]]

    Revision as of 08:42, 16 January 2016


    Command line arguments
    Tutorial Series   Beginner Tutorial
    Previous   Tutorial 4 - Loading and saving
    What's Next   n/a
    Further Reading   n/a


    Abstract

    Now that we have a text editor which can open and save files. We will now make the editor act more like a desktop application by enabling it to open files from command line arguments or even using Open with from within Dolphin.

    The Code

    main.cpp

    #include <cstdlib>
     
    #include <QApplication>
    #include <QCommandLineParser>
    #include <QUrl>
    #include <QDir>
    
    #include <KAboutData>
    #include <KLocalizedString>
    
    #include "mainwindow.h"
     
    int main (int argc, char *argv[])
    {
        QApplication app(argc, argv);
        
        KLocalizedString::setApplicationDomain("tutorial5");
        
        KAboutData aboutData(
                             // The program name used internally. (componentName)
                             QStringLiteral("tutorial5"),
                             // A displayable program name string. (displayName)
                             i18n("Tutorial 5"),
                             // The program version string. (version)
                             QStringLiteral("1.0"),
                             // Short description of what the app does. (shortDescription)
                             i18n("A simple text area which can load and save."),
                             // The license this code is released under
                             KAboutLicense::GPL,
                             // Copyright Statement (copyrightStatement = QString())
                             i18n("(c) 2015"),
                             // Optional text shown in the About box.
                             // Can contain any information desired. (otherText)
                             i18n("Some text..."),
                             // The program homepage string. (homePageAddress = QString())
                             QStringLiteral("http://example.com/"),
                             // The bug report email address
                             // (bugsEmailAddress = QLatin1String("[email protected]")
                             QStringLiteral("[email protected]"));
        
        aboutData.addAuthor(i18n("Name"), i18n("Task"), QStringLiteral("[email protected]"),
                            QStringLiteral("http://your.website.com"), QStringLiteral("OSC Username"));
        
        KAboutData::setApplicationData(aboutData);
     
        QCommandLineParser parser;
        parser.addHelpOption();
        parser.addVersionOption();
        parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
        
        aboutData.setupCommandLine(&parser);
        parser.process(app);
        
        aboutData.processCommandLine(&parser);
        
        MainWindow* window = new MainWindow();
        window->show();
        
        if (parser.positionalArguments().count() > 0)
        {
            window->openFile(QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath()));
        }
        
        return app.exec();
    }
    

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
     
    #include <KXmlGuiWindow>
    
    class KTextEdit;
    class KJob;
     
    class MainWindow : public KXmlGuiWindow
    {
        Q_OBJECT
        
      public:
        MainWindow(QWidget *parent=0);
        void openFile(const QUrl &inputFileName);
     
      private:
        KTextEdit* textArea;
        void setupActions();
        
        QString fileName;
     
      private slots:
        void newFile();
        void openFile();
        void saveFile();
        void saveFileAs();
        void saveFileAs(const QString &outputFileName);
        
        void downloadFinished(KJob* job);
    };
     
    #endif
    

    mainwindow.cpp

    #include <QApplication>
    #include <QAction>
    #include <QSaveFile>
    #include <QFileDialog>
    #include <QTextStream>
    #include <QByteArray>
    
    #include <KTextEdit>
    #include <KLocalizedString>
    #include <KActionCollection>
    #include <KStandardAction>
    #include <KMessageBox>
    #include <KIO/Job>
    
    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent), fileName(QString())
    {
      textArea = new KTextEdit();
      setCentralWidget(textArea);
      
      setupActions();
    }
    
    void MainWindow::setupActions()
    {
        QAction* clearAction = new QAction(this);
        clearAction->setText(i18n("&Clear"));
        clearAction->setIcon(QIcon::fromTheme("document-new"));
        actionCollection()->setDefaultShortcut(clearAction, Qt::CTRL + Qt::Key_W);
        actionCollection()->addAction("clear", clearAction);
        connect(clearAction, SIGNAL(triggered(bool)), textArea, SLOT(clear()));
        
        KStandardAction::quit(qApp, SLOT(quit()), actionCollection());
        
        KStandardAction::open(this, SLOT(openFile()), actionCollection());
     
        KStandardAction::save(this, SLOT(saveFile()), actionCollection());
     
        KStandardAction::saveAs(this, SLOT(saveFileAs()), actionCollection());
     
        KStandardAction::openNew(this, SLOT(newFile()), actionCollection());
        
        setupGUI(Default, "tutorial5ui.rc");
    }
    
    void MainWindow::newFile()
    {
        fileName.clear();
        textArea->clear();
    }
    
    void MainWindow::saveFileAs(const QString &outputFileName)
    {
        if (!outputFileName.isNull())
        {
            QSaveFile file(outputFileName);
            file.open(QIODevice::WriteOnly);
            
            QByteArray outputByteArray;
            outputByteArray.append(textArea->toPlainText().toUtf8());
            file.write(outputByteArray);
            file.commit();
    
            fileName = outputFileName;
        }
    }
    
    void MainWindow::saveFileAs()
    {
        saveFileAs(QFileDialog::getSaveFileName(this, i18n("Save File As")));
    }
    
    void MainWindow::saveFile()
    {
        if (!fileName.isEmpty())
        {
            saveFileAs(fileName);
        }
        else
        {
            saveFileAs();
        }
    }
    
    
    void MainWindow::openFile()
    {
        openFile(QFileDialog::getOpenFileUrl(this, i18n("Open File")));
    }
        
    void MainWindow::openFile(const QUrl &inputFileName)
    {
        if (!inputFileName.isEmpty())
        {
            KIO::Job* job = KIO::storedGet(inputFileName);
            fileName = inputFileName.toLocalFile();
    
            connect(job, SIGNAL(result(KJob*)), this, SLOT(downloadFinished(KJob*)));
            
            job->exec();
        }
    }
    
    void MainWindow::downloadFinished(KJob* job)
    {
        if (job->error())
        {
            KMessageBox::error(this, job->errorString());
            fileName.clear();
            return;
        }
        
        KIO::StoredTransferJob* storedJob = (KIO::StoredTransferJob*)job;
        textArea->setPlainText(QTextStream(storedJob->data(), QIODevice::ReadOnly).readAll());
    }
    

    tutorial5ui.rc

    <?xml version="1.0" encoding="UTF-8"?>
    <gui name="tutorial5"
         version="1"
         xmlns="http://www.kde.org/standards/kxmlgui/1.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
                             http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd" >
     
      <MenuBar>
        <Menu name="file" >
          <Action name="clear" />
        </Menu>
      </MenuBar>
     
      <ToolBar name="mainToolBar" >
        <text>Main Toolbar</text>
        <Action name="clear" />
      </ToolBar>
     
    </gui>
    

    Explanation

    mainwindow.h

    Here we have done nothing but add a new openFile function which takes a QUrl. Again, we use a QUrl instead of a QString so that we can also work with remote files as if they were local.

    void openFile(const QUrl &inputFileName);
    

    mainwindow.cpp

    There's no new code here, only rearranging. Everything from void openFile() has been moved into void openFile(const QUrl &inputFileName) except the call to QFileDialog::getOpenFileUrl().

    This way, we can call openFile() if we want to display a dialog, or we can call openFile(QString) if we know the name of the file already. Which will be the case when we feed the file name through the command line.

    main.cpp

    This is where all the QCommandLineParser magic happens. In previous examples, we only used the class to feed QApplication the necessary data for using flags like --version or --author. Now we actually get to use it to process command line arguments.

    First, we tell QCommandLineParser that we want to add a new positional arguments. In a nutshell, these are arguments that are not options. -h or --version are options, file is an argument.

    parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
    


    Later on, we start processing positional arguments, but only if there is one. Otherwise, we proceed as usual. In our case we can only open one file at a time, so only the first file is of interest to us. We call the openFile() function and feed it the URL of the file we want to open, whether it is a local file like $HOME/foo or a remote one like ftp.mydomain.com/bar. We use the overloaded form of QUrl::fromUserInput() in order to set the current path. This is needed in order to work with relative paths like "../baz".

    if (parser.positionalArguments().count() > 0)
    {
        window->openFile(QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath()));
    }
    

    Make, Install and Run

    CMakeLists.txt

    project (tutorial5)
    
    cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
    set(QT_MIN_VERSION "5.3.0")
    set(KF5_MIN_VERSION "5.2.0")
    
    find_package(ECM 1.0.0 REQUIRED NO_MODULE)
    set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
    
    include(KDEInstallDirs)
    include(KDECMakeSettings)
    include(KDECompilerSettings)
    include(FeatureSummary)
    
    find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS 
        Core    # QCommandLineParser, QStringLiteral, QSaveFile, QTextStream, QByteArray
        Widgets # QApplication, QAction, QFileDialog
    )
    
    find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
        CoreAddons      # KAboutData
        I18n            # KLocalizedString
        XmlGui          # KXmlGuiWindow, KActionCollection
        TextWidgets     # KTextEdit
        ConfigWidgets   # KStandardActions
        WidgetsAddons   # KMessageBox
        KIO             # KIO
    )
        
    feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
        
    set(tutorial5_SRCS main.cpp mainwindow.cpp)
    
    add_executable(tutorial5 ${tutorial5_SRCS})
    
    target_link_libraries(tutorial5
        Qt5::Widgets
        KF5::CoreAddons
        KF5::I18n
        KF5::XmlGui
        KF5::TextWidgets
        KF5::ConfigWidgets
        KF5::WidgetsAddons
        KF5::KIOCore
    )
    
    install(TARGETS tutorial5  ${INSTALL_TARGETS_DEFAULT_ARGS})
    
    install(FILES tutorial5ui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/tutorial5)
    


    With this file, the tutorial can built and run in the same way as tutorial 3 and 4. For more information, see tutorial 3.

    mkdir build && cd build
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME
    make install
    XDG_DATA_DIRS=$HOME/share:$XDG_DATA_DIRS $HOME/bin/tutorial5
    


    The source code on this page applies only the current KDE Frameworks 5 ("KF5") version. For the older KDE Development Platform ("KDE4"), See Development/Tutorials/First_program/KDE4