Search code examples
pythonc++qtsubprocesspopen

How to add my open files with my Qt app in Pythons subprocess.Popen()


I have made a simple notepad editor using Qt that I try to open with:

p = subprocess.Popen(["myNotepadApp.exe", "myFile.py"])

this opens my app but not with the specified file myFile.py open. Is there any changes I need to make to my Qt code for my Qt open function? Does opening files with this subprocess.Popen() function even call my Qt open function?

void objectDetector::on_actionOpen_triggered()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open file");
    QFile file(fileName);
    currentFile = fileName;
    if(!file.open(QIODevice::ReadOnly| QFile::Text)){
        QMessageBox::warning(this, "Warning", "Cannot open file : " + file.errorString());
        return;
    }
    setWindowTitle("Object Editor");
    QTextStream in(&file);
    QString text = in.readAll();
    ui->textEdit->setText(text);
    file.close();

}

Here is the constructor of the UI object if it is releveant

objectDetector::objectDetector(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::objectDetector)
{
    setWindowTitle("Object Editor");
    ui->setupUi(this);
    this->setCentralWidget(ui->textEdit);
}

Here is the current main.cpp:

#include "objectdetector.h"

#include <QApplication>


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    auto* od = new objectDetector(&a);
     is left in here is for us.
    const auto args = a.arguments();
    if (args.size() > 1)
    {
        
        const auto fileName = args[1];
        // Pass the first arg to objectDetector as file name to open it
        od->openFile(fileName);
    }
    return a.exec();
}

Solution

  • What your call to Popen does is about the same as if you type the following into your shell:

    myNotepadApp.exe myFile.py
    

    So, "myFile.py" is passed as an argument to your executable but it fails to do anything with it. The functionality is missing from your Qt application and the whole issue is unrelated to Python's Popen.

    In C++ (like in C), you can access these arguments in main() using argc, or the count of arguments and argv, the vector of arguments themselves. Qt also processes arguments for some Qt-specific flags and offers QCoreApplication::arguments() as a convenient way to access them.

    As you did not share your main.cpp, I'll have to guess what it looks like. In order to make it work, you'll need to do something like the following:

    int main(int argc, char** argv)
    {
        // Pass argc and argv to QApplication
        QApplication app(argc, argv);
        // Create your classes
        objectDetector od;
        // Get arguments back from QApplication
        // Qt will have stripped argv of all Qt-specific flags so whatever is left in here is for us.
        const auto args = app.arguments();
        if (args.size() > 1)
        {
            // args[0] is the name of your executable, we don't want that
            const auto fileName = args[1];
            // Pass the first arg to objectDetector as file name to open it
            od.openFile(fileName);
        }
        od.show();
        return app.exec();
    }
    

    And you will have to give your class objectDetector a method to handle a file name. Luckily you already wrote all the code we need:

    void objectDetector::on_actionOpen_triggered()
    {
        QString fileName = QFileDialog::getOpenFileName(this, "Open file");
        openFile(fileName);
    }
    
    // This needs to be public
    void objectDetector::openFile(const QString& fileName)
    {
        // Looks familiar? It's your own code extracted as a method
        QFile file(fileName);
        currentFile = fileName;
        if(!file.open(QIODevice::ReadOnly| QFile::Text)){
            QMessageBox::warning(this, "Warning", "Cannot open file : " + file.errorString());
            return;
        }
        setWindowTitle("Object Editor");
        QTextStream in(&file);
        QString text = in.readAll();
        ui->textEdit->setText(text);
        file.close();
    }
    

    This way, the first argument will be treated as a file name and your application will attempt to open it.

    There's room for improvement, however. It will not handle multiple file names and passing a string that does not name a file will result in a "Cannot open file" message box. For anything more complex than getting the first argument, consider setting up QCommandLineParser.

    Qt features an example text editor. This seems to do largely what you are trying to accomplish. It's using a QCommandLineParser. You may be able to get inspiration from there.