有没有办法确保QSplashScreen在屏幕上重新粉刷?

时间:2018-04-27 09:37:06

标签: c++ qt splash-screen qsplashscreen

我有一个问题,在Linux上使用Xorg(Ubuntu 14.04)和Qt 5.5.1 QSplashScreen在我到达事件循环之前不会被绘制。即使我多次调用QApplication::processEvents(),它仍然没有被绘制,即使在1000次调用之后,虽然窗口已经在屏幕上,保留了应用程序启动之前的原始像素,因此实际上是不可见的< SUP> * 。从this answer开始,我想到了使用 timed 循环调用QApplication::processEvents(),就像这里一样:

#include <QThread>
#include <QApplication>
#include <QSplashScreen>

int main(int argc, char** argv)
{
    QApplication a(argc,argv);

    QSplashScreen splash;
    splash.show();
    splash.showMessage("Loading...");
    // The hack to try to ensure that splash screen is repainted
    for(int i=0;i<30;++i)
    {
        QThread::usleep(1e3);
        a.processEvents();
    }

    QThread::usleep(5e6); // simulate slow loading process
    splash.showMessage("Finished");

    return a.exec();
}

以上代码主动休眠30毫秒,试图进行QSplashScreen重绘。这适用于我,但我不确定它是否会一直有效,例如在繁忙/缓慢的CPU或任何其他条件下(根据经验发现30次迭代的神奇值)。

另一种非常具有侵入性的方式是在另一个线程中进行所有必要的加载,只是为了确保主线程中的QSplashScreen确实有一个活动的消息队列。由于需要大大重做主程序,这看起来不太好解决。

那么,有没有办法确保QSplashScreen已经重新绘制,以便它的窗口不包含垃圾,然后继续进行长时间的阻塞加载过程?

* 当我在启动画面后面移动一个窗口时发现了这个

2 个答案:

答案 0 :(得分:0)

避免不可知的先验魔法超时的一种方法是等待一个确切的事件:paint事件。在X11上,似乎有一个延迟。要做到这一点,我们必须继承 if (rbDA.Checked == true) { dept = "DA"; dayTbl = "tblDayDA"; getDayData = "Select date,num_jobs_pending,jobs_called,total_jobs_overall,num_repairs_called,total_num_repairs,num_contracts_called,total_num_contracts FROM " + dayTbl + " WHERE date BETWEEN #" + dtbFrom.Value.ToShortDateString().Trim() + "# AND #" + dtpTo.Value.ToShortDateString().Trim() + "#"; } if (rbEL.Checked == true) { dept = "Elevator"; dayTbl = "tblDayEL"; getDayData = "Select date,num_jobs_pending,jobs_called,total_jobs_overall,num_repairs_called,total_num_repairs,num_contracts_called,total_num_contracts FROM " + dayTbl + " WHERE date BETWEEN #" + dtbFrom.Value.ToShortDateString().Trim() + "# AND #" + dtpTo.Value.ToShortDateString().Trim() + "#"; } else if (rbAC.Checked == true) { dept = "AC"; dayTbl = "tblDayAC"; getDayData = "Select date,num_jobs_pending,jobs_called,total_jobs_overall,num_repairs_called,total_num_repairs,num_contracts_called,total_num_contracts FROM " + dayTbl + " WHERE date BETWEEN #" + dtbFrom.Value.ToShortDateString().Trim() + "# AND #" + dtpTo.Value.ToShortDateString().Trim() + "#"; } else if (rbProj.Checked == true) { dept = "Projects"; dayTbl = "tblDayPROJ"; getDayData = "Select date,num_jobs_pending,jobs_called,total_jobs_overall,num_repairs_called,total_num_repairs,num_contracts_called,total_num_contracts FROM " + dayTbl + " WHERE date BETWEEN #" + dtbFrom.Value.ToShortDateString().Trim() + "# AND #" + dtpTo.Value.ToShortDateString().Trim() + "#"; } if (con.State == ConnectionState.Closed) { con.Open(); } try { DataSet ds1 = new DataSet(); DataTable dt1 = new DataTable(); ds1.Tables.Add(dt1); OleDbDataAdapter da = new OleDbDataAdapter(getDayData, con); da.Fill(dt1); dgvGetDates.SelectionMode = DataGridViewSelectionMode.FullRowSelect; dt1.NewRow(); // dt1.NewRow(); dgvGetDates.DataSource = dt1.DefaultView; string label = "Total"; int jobsCalled = 0; int totalNumJobs = 0; int totalJobsAllDepts = 0; int numRepairsCalled = 0; int totalRepairsToCall= 0; int numContractsCalled = 0; int numContractsToCall = 0; for (int i = 0; i < dgvGetDates.Rows.Count; i++) { jobsCalled += Convert.ToInt32(dgvGetDates.Rows[i].Cells["jobs_called"].Value); totalNumJobs = Convert.ToInt32(dgvGetDates.Rows[i].Cells["num_jobs_pending"].Value) + totalNumJobs; totalJobsAllDepts += Convert.ToInt32(dgvGetDates.Rows[i].Cells["total_jobs_overall"].Value); numRepairsCalled += Convert.ToInt32(dgvGetDates.Rows[i].Cells["num_repairs_called"].Value); totalRepairsToCall += Convert.ToInt32(dgvGetDates.Rows[i].Cells["total_num_repairs"].Value); numContractsCalled += Convert.ToInt32(dgvGetDates.Rows[i].Cells["num_contracts_called"].Value); numContractsToCall += Convert.ToInt32(dgvGetDates.Rows[i].Cells["total_num_contracts"].Value); } DataGridViewRow row = (DataGridViewRow)dgvGetDates.Rows[0].Clone(); string[] row1 = new string[] { jobsCalled.ToString(), totalNumJobs.ToString(), totalJobsAllDepts.ToString(), numRepairsCalled.ToString(), totalRepairsToCall.ToString(), numContractsCalled.ToString(), numContractsToCall.ToString() }; dgvGetDates.Rows.Add(row1); DGVPrinter printer = new DGVPrinter(); printer.Title = "CSR Calls Per Day Report - " + dept +" Department"; printer.SubTitle = " Date:" + dtbFrom.Value.ToShortDateString() + " - " + dtpTo.Value.ToShortDateString() + " Printed: " + DateTime.Now; printer.SubTitleFormatFlags = StringFormatFlags.LineLimit | StringFormatFlags.NoClip; printer.PageNumbers = true; printer.PageNumberInHeader = false; printer.PorportionalColumns = true; printer.HeaderCellAlignment = StringAlignment.Near; printer.FooterSpacing = 15; //printer.Footer = ""; printer.PrintPreviewDataGridView(dgvGetDates); 并覆盖QSplashScreen,如下所示:

QSplashScreen::paintEvent()

答案 1 :(得分:0)

解决方案非常简单:保持事件循环运行,直到重新绘制窗口。这应该没有任何旋转,即你不应该使用任何明确的超时。

#include <QtWidgets>

class EventSignaler : public QObject {
  Q_OBJECT
  QEvent::Type m_type;
protected:
  bool eventFilter(QObject *src, QEvent *ev) override {
    if (ev->type() == m_type)
      emit hasEvent(src);
    return false;
  }
public:
  EventSignaler(QEvent::Type type, QObject *object) :
    QObject(object), m_type(type) {
    object->installEventFilter(this);
  }
  Q_SIGNAL void hasEvent(QObject *);
};

int execUntilPainted(QWidget *widget) {
  EventSignaler painted{QEvent::paint, widget};
  QObject::connect(&painted, &EventSignaler::hasEvent, qApp, &QCoreApplication::quit);
  return qApp->exec();
}

int main(int argc, char **argv) {
  QApplication app{argc, argv};

  MySplashScreen splash;
  EventSignaler painted{QEvent::Paint, &splash};
  splash.show();
  splash.showMessage("Loading...");
  execUntilPainted(&splash);

  QThread::sleep(5); // simulate slow loading process
  splash.showMessage("Finished");

  return app.exec();
}

#include "main.moc"