在 QDialog::exec() 期间使用 QThreads 执行 QWidget::grab()

Posted

技术标签:

【中文标题】在 QDialog::exec() 期间使用 QThreads 执行 QWidget::grab()【英文标题】:Using QThreads to execute QWidget::grab() during QDialog::exec() 【发布时间】:2017-03-02 20:13:34 【问题描述】:

我编写了一个 Qt 程序,当用户上传某个数据文件并按下按钮时,会生成一系列图。当按下按钮时,程序将计算一系列计算并绘制一些图形,并在函数调用QDialog::exec() 上将它们显示在屏幕上。我想要实现的是grab 使用QWidget::grab() 生成的绘图的屏幕截图。所以基本上QWidget::grab()QDialog::exec() 之后被调用。但是由于QDialog::exec() 一直运行直到用户关闭特定窗口,所以在关闭窗口后调用QWidget::grab() 不会产生预期的结果。

这是我的代码的一部分;

mainwindow.cpp

    graphWindow.setWindowState(Qt::WindowMaximized);
    graphWindow.exec(); 
    graphWindow.savePlots();

graphWindow.savePlots()

void GraphWindow::savePlots()
 

  QStringList plotNames;
  plotNames << "Income Statement (Plot A)" << "Income Statement (Plot B)" << "Balance Sheet (Plot A)"
            << "Balance Sheet (Plot B)" << "Cash Flow Plot" << "Holistic Stock Performance";

  for(int item = 0; item < ui->graphTab->count(); item++)
  
      ui->graphTab->setCurrentIndex(item);
      QWidget * currentWidget = ui->graphTab->widget(item);

      int height = currentWidget->height();
      int width = currentWidget->width();
      int x = currentWidget->x();
      int y = currentWidget->y();

      QRect grabRect(x,y,width,height);

      //destImagePath is a global variable
      QString imageFilePath = destImagePath;
      imageFilePath.append(plotNames.at(item));
      imageFilePath.append(".png");

      currentWidget->grab(grabRect).save(imageFilePath);
  

     //Reset to first tab after grab
     ui->graphTab->setCurrentIndex(0);
 

我正在考虑通过运行另一个QThread 来绕过QDialog::exec(),它在窗口仍在运行时执行GraphWindow::savePlots(),以便屏幕抓取产生所需的结果。

有没有更好的方法来做到这一点?我认为创建一个新的QThread 只是为了调用一个函数是非常低效的。即使不是,您能否解释一下如何在并发QThreads 中实现。

【问题讨论】:

从工作线程调用小部件方法是错误的:只要 GUI 设计为在 UI 线程上运行,它可能工作也可能不工作。您应该只调用 savePlots() 并执行对话框。只需在对话框退出之前执行此操作即可。 是在构造函数中调用它吗? (我知道它不会起作用,因为第一次创建对象时没有任何东西可以抓取)我如何从对话框中调用saveplots()?我知道你的意思,但对如何实施它不是很有信心。如果您能详细说明会非常有帮助吗? 为什么来自构造函数?图片什么时候准备好?你应该知道。我们看不到。 哦,我认为图像应该已经显示在窗口中,以便 grab() 工作。这就是我在saveplots() 之前调用exec() 的原因。我会试试你说的。谢谢。 【参考方案1】:

在您的情况下,实际上不需要使用 QThread 或使用 QtConcurrent::run,您可以改为使用对主线程上的插槽的调用排队

QTimer::singleShot(0, &graphWindow, &GraphWindow::savePlots);
graphWindow.exec();

在您的情况下的优势在于它将在主线程上运行,因此您不必为捕获而烦恼线程、同步等(即使 Qt 使它更容易)。

这种方法的唯一限制是它会在捕获期间有效地“冻结”您的应用程序。这通常应该是可以接受的,因为它会明显少于一秒。

【讨论】:

像魅力一样工作。非常感谢。我对您的代码做了一些小调整(在编辑中添加)。

以上是关于在 QDialog::exec() 期间使用 QThreads 执行 QWidget::grab()的主要内容,如果未能解决你的问题,请参考以下文章

阻止和隐藏 QDialog:exec_() 的替代方案?

在插槽中调用 QDialog::exec 会阻塞主事件循环吗?

Qt--对话框及其类型布局管理器

关于Qt的事件循环以及QEventLoop的简单使用(QEventLoop::quit()能够终止事件循环,事件循环是可以嵌套的)

QEventLoop

在构建期间使用 Qt 时出错:C2872:“字节”:不明确的符号 [重复]