【问题标题】:GUI project in QT Creator not working after tinkering it togheterQT Creator 中的 GUI 项目在修补后无法正常工作
【发布时间】:2020-02-21 14:29:37
【问题描述】:

已经从其他脚本的 sn-ps 中组合了一些脚本。我正在尝试为解密备份的python脚本(HHDB.py)创建一个GUI。此脚本需要密码、加密文件所在文件夹的位置以及写入加密文件的目的地等参数。

使用 QT Creator 制作了一个漂亮的 gui,带有一个按钮可以浏览到包含加密文件的文件夹。它的路径显示在文本编辑字段中。 目的地也一样。 然后有一个字段可以输入已知密码。 最后有一个按钮可以运行 HHBD 脚本,参数为密码、起点和终点。

由于我不知道哪里出了问题,并且调试器(不再)给出任何错误,我需要一个程序员的眼睛来指出我的(许多)缺陷。

感谢您查看下面的代码:

谷歌搜索了很多,但许多发现的解决方案都给出了调试、查找和替换的错误:-(

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QDir>
#include <QString>
#include <QLabel>
#include <QProcess>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;

}

void MainWindow::on_pushButton_clicked()
{

    QString backup_dir = QFileDialog::getExistingDirectory(this,tr("Choose HiSuite Backup Folder"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    ui->textEdit->setPlainText(backup_dir.replace('/','\\'));
}

void MainWindow::on_pushButton_2_clicked()
{

    QString destination_dir = QFileDialog::getExistingDirectory(this,tr("Choose Destination Folder"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    ui->textEdit_2->setPlainText(destination_dir.replace('/','\\'));

}

void MainWindow::on_pushButton_3_clicked()
{
    QString passwordstring = ui->password->toPlainText();

    QProcess process;
    QString scriptFile =  QCoreApplication::applicationDirPath() + "./HHBD.py";

    QString pythonCommand = "python " + scriptFile +
                        " passwordstring" +
                        " backup_dir.replace('/','\\')" +
                        " destination_dir.replace('/','\\')";

    printf("PyCommand: %s\n", pythonCommand.toStdString().c_str());
    process.start (pythonCommand);

}

我想推送“pushButton_3”脚本执行带有给定参数的 HHBD.py 脚本,例如“python HHBD.py 123456789 C:\test\ c:\dest”,其中“123456789”来自密码字段, “C:\test\”来自backup_dir 字段,“C:\dest”来自destination_dir 字段

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

    void on_pushButton_3_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

主窗口.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>395</width>
    <height>303</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Huawei HiSuite Backup Decrypter</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>J:/Downloads_2/huawei.png</normaloff>J:/Downloads_2/huawei.png</iconset>
  </property>
  <property name="autoFillBackground">
   <bool>true</bool>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>10</y>
      <width>151</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Select HiSuite Backup Folder</string>
    </property>
   </widget>
   <widget class="QTextEdit" name="textEdit">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>40</y>
      <width>381</width>
      <height>23</height>
     </rect>
    </property>
    <property name="sizePolicy">
     <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
      <horstretch>0</horstretch>
      <verstretch>0</verstretch>
     </sizepolicy>
    </property>
    <property name="verticalScrollBarPolicy">
     <enum>Qt::ScrollBarAlwaysOff</enum>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_2">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>70</y>
      <width>151</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Select Destination Folder</string>
    </property>
   </widget>
   <widget class="QTextEdit" name="textEdit_2">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>100</y>
      <width>381</width>
      <height>21</height>
     </rect>
    </property>
    <property name="verticalScrollBarPolicy">
     <enum>Qt::ScrollBarAlwaysOff</enum>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>150</y>
      <width>381</width>
      <height>31</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>10</pointsize>
     </font>
    </property>
    <property name="autoFillBackground">
     <bool>true</bool>
    </property>
    <property name="lineWidth">
     <number>2</number>
    </property>
    <property name="text">
     <string>Type here the password given during the creation of the backup of the mobile device with Huawei HiSuite.</string>
    </property>
    <property name="wordWrap">
     <bool>true</bool>
    </property>
   </widget>
   <widget class="QTextEdit" name="password">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>190</y>
      <width>191</width>
      <height>21</height>
     </rect>
    </property>
    <property name="verticalScrollBarPolicy">
     <enum>Qt::ScrollBarAlwaysOff</enum>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_3">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>220</y>
      <width>151</width>
      <height>31</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="text">
     <string>Decrypt HiSuite Backup</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>270</x>
      <y>170</y>
      <width>141</width>
      <height>111</height>
     </rect>
    </property>
    <property name="text">
     <string/>
    </property>
    <property name="pixmap">
     <pixmap>J:/Downloads_2/Aantekening 2019-10-24 141833.png</pixmap>
    </property>
   </widget>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

【问题讨论】:

  • 除了"...everything goes wrong" 之外,您还没有真正描述过您遇到的问题。话虽如此,一个明显的问题是processMainWindow::on_pushButton_3_clicked 中被声明为局部变量,因此它将超出范围并在调用process.start(pythonCommand) 后立即调用其析构函数。
  • 嘿@G.M.,作为一名非程序员/编码员,而且还不是python 和QT Creator 的新手,我在你的回复中迷失了方向。澄清“一切”是我没有得到我想要的结果。除非“QProcess: Destroyed while process ("python") is still running”,否则什么都不会发生。我刚刚注意到。你能用noob语言解释一下什么是错的,为什么?
  • 第二眼看去:" backup_dir.replace('/','\\')"" destination_dir.replace('/','\\')" 是常量字符串,按字面意思传递。相反,您可以使用例如从ui-&gt;textEditui-&gt;textEdit2 中提取字符串。
  • 谢夫怎么说。我也总是使用 start() 重载将参数作为 QStringList,因为它避免了引用问题
  • @Scheff:编辑了标签,我做了“backup_dir.replace('/','\\')”和“destination_dir.replace('/','\\')”以 windows 格式显示文件夹路径。我猜 HHBD.py 脚本需要 windows 格式的参数?

标签: python c++ qt


【解决方案1】:

这是一个如此小的程序,将它分布在三个文件中是没有意义的。你可以把它全部放在main.cpp

下面的示例是完整且可编译的。整个文件长 129 行。您可以从https://github.com/KubaO/stackoverflown/tree/master/questions/script-frontend-58555092下载完整的项目。

看起来像这样:

首先,让我们从主窗口开始。你不需要使用QMainWindow,因为那是用于带有菜单等的“大”应用程序。你只需要一个普通的QWidget

// https://github.com/KubaO/stackoverflown/tree/master/questions/script-frontend-58555092
#include <QtWidgets>
#include <initializer_list>

class MainWindow : public QWidget {
   Q_OBJECT

QProcess 实例在窗口的生命周期内被保留,因此我们可以监控它的状态和输出,而不是启动一个分离的进程:

   QProcess process;

用户界面非常简单,它的元素可以很容易地作为成员提供:

   QGridLayout layout{this};
   QPushButton selectSource{tr("Select HiSuite Backup Folder")};
   QLineEdit source;
   QPushButton selectDestination{tr("Select Destination Folder")};
   QLineEdit destination;
   QLabel instruction{tr(
       "Type here the password given during the creation of "
       "the backup of the mobile device with Huawei HiSuite.")};
   QLineEdit password;
   QPushButton decrypt{tr("Decrypt HiSuite Backup")};
   QTextBrowser output;

   QFileSystemModel fsModel;
   QCompleter fsCompleter{&fsModel};

   QTextCharFormat statusFormat, commandFormat, stdoutFormat, stderrFormat;

   void setupUi();
   void doDecrypt();
   void onStateChanged(QProcess::ProcessState state);

  public:
   explicit MainWindow(QWidget *parent = nullptr);
};

注意所有用户可见的字符串是如何用tr() 包装的,以便可以使用 Qt Linguist 轻松翻译。

UI 设置被排除在外,而且没有争议。路径输入字段有一个文件系统完成器,以便于手动输入路径。

void MainWindow::setupUi() {
   setWindowTitle(tr("Huawei HiSuite Backup Decrypter"));
   QFont boldFont;
   boldFont.setBold(true);
   decrypt.setFont(boldFont);
   instruction.setWordWrap(true);

   commandFormat.setForeground(QColor(Qt::green).darker());
   statusFormat.setForeground(QColor(Qt::blue));
   stderrFormat.setForeground(QColor(Qt::red).darker());

   int row = 0;
   for (auto *widget : std::initializer_list<QWidget *>{
            &selectSource, &source, &selectDestination, &destination, &instruction,
            &password, &decrypt, &output}) {
      layout.addWidget(widget, row++, 0);
   }

   for (auto *button : {&selectSource, &selectDestination, &decrypt})
      button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);

   fsModel.setRootPath("");
   fsModel.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
   fsCompleter.setCompletionMode(QCompleter::PopupCompletion);
   source.setCompleter(&fsCompleter);
   destination.setCompleter(&fsCompleter);
}

大多数插槽都很小,可以在 lambdas 中提供:

MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
   setupUi();

   connect(&selectSource, &QPushButton::clicked, [this] {
      auto dir = QFileDialog::getExistingDirectory(
          this, tr("Choose HiSuite Backup Folder"), QDir::homePath(),
          QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
      source.setText(dir);
   });

   connect(&selectDestination, &QPushButton::clicked, [this] {
      auto dir = QFileDialog::getExistingDirectory(
          this, tr("Choose Destination Folder"), QDir::homePath(),
          QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
      destination.setText(dir);
   });

   connect(&decrypt, &QPushButton::clicked, this, &MainWindow::doDecrypt);

   connect(&process, &QProcess::stateChanged, this, &MainWindow::onStateChanged);

该过程的输出显示在文本浏览器中,彩色化以便于识别错误:

   connect(&process, &QProcess::readyReadStandardOutput, [this] {
      output.setCurrentCharFormat(stdoutFormat);
      output.append(process.readAllStandardOutput());
   });
   connect(&process, &QProcess::readyReadStandardError, [this] {
      output.setCurrentCharFormat(stderrFormat);
      output.append(process.readAllStandardError());
   });  
}

为了完整起见,指示了进程的状态;

void MainWindow::onStateChanged(QProcess::ProcessState state) {
   output.setCurrentCharFormat(statusFormat);
   switch (state) {
      case QProcess::Starting:
         output.append(tr("(starting)"));
         break;
      case QProcess::Running:
         output.append(tr("(started)"));
         break;
      case QProcess::NotRunning:
         output.append(tr("(not running)"));
         break;
   }
}

解密使用QProcessprogramarguments 属性显式设置进程及其参数。这样,路径中的空格等就没有问题了。Qt 为我们处理了这一切。请注意,applicationDirPath() + "./foo" 有一个句点会给出类似C:/path./foo 的路径 - 这样的路径无效,句点不应该在那里。

void MainWindow::doDecrypt() {
   output.clear();
   auto password = this->password.text();

   QString scriptFile = QCoreApplication::applicationDirPath() + "/HHBD.py";
   process.setProgram("python");
   process.setArguments({QDir::toNativeSeparators(scriptFile), password,
                         QDir::toNativeSeparators(source.text()),
                         QDir::toNativeSeparators(destination.text())});
   output.setCurrentCharFormat(commandFormat);
   output.setText(
       QStringLiteral("%1 %2").arg(process.program()).arg(process.arguments().join(' ')));
   process.start();
}

main 函数显示窗口。 moc 文件包含在最后,因为我们没有单独的 main.h 头文件:

int main(int argc, char *argv[]) {
   QApplication a(argc, argv);
   MainWindow w;
   w.show();
   return a.exec();
}
#include "main.moc"

示例到此结束。

【讨论】:

  • 感谢您所做的一切。让它运行但是......即使“HHBD.py”文件在正确的位置,仍然得到错误“python:无法打开文件'HHBD.py':[Errno 2]没有这样的文件或目录”。有什么建议吗?
  • 解决了!我的目标路径中有一个空格,这就是它不起作用的原因。我抱怨 HHBD.py 文件仍然很奇怪,但可能是它不检查外部脚本的参数。再次感谢 Kuba Ober 和 Scheff
猜你喜欢
  • 2019-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 2023-02-02
相关资源
最近更新 更多