【问题标题】:Signal-Slot makes a mess信号槽搞得一团糟
【发布时间】:2011-09-02 17:38:19
【问题描述】:

我正在尝试使用 Qt 创建一个图像保存应用程序。现在是存根

class ImageSaver:public QObject
{
    int index;
    QWebPage * main_Page;
    QNetworkAccessManager * manager;
    QNetworkReply * reply;
    QString file_Name;
    QSet<QString> image_Addresses;
    QString web_Address;
    Q_OBJECT
signals:
    void image_Saved();
public slots:
    void request_Image();
    void on_Finished(bool status);
    void got_Reply(QNetworkReply * reply);
public:
    ImageSaver();
    void start();
};

ImageSaver::ImageSaver()
{
    index = 0;

    manager = new QNetworkAccessManager;

    reply = NULL;

    connect(main_Page,SIGNAL(loadFinished(bool)),this,SLOT(on_Finished(bool)));

    connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(got_Reply(QNetworkReply*)));

    connect(this,SIGNAL(image_Saved()),this,SLOT(request_Image()));
}

void ImageSaver::start()
{
    //loads the url 
    // In the end of the loading it will emit load_Finished(bool)
    // So that signal will execute on_Finished(bool)
}

void ImageSaver::request_Image()
{
    QString temp_Address = *(image_Addresses.begin()+index);

   //makes a request to the server to give the image "temp_Address"
   //When the server gives the reply signal finished(QNetworkReply*) will be emitted
   // this in turn will call the got_Reply(QNetworkReply*)
}

void ImageSaver::on_Finished(bool status)
{
       //collects all the images's url addresses, and pushes them in the list 
        //"image_Addresses"
        //Then emits image_Saved();
        //This signal will wake up the function request_Image()
}

void ImageSaver::got_Reply(QNetworkReply * reply)
{
    //Image is extracted from the reply and got saved in the same name as in the page
    //index got increased;
    //emits the signal image_Saved();
    //This signal will activate the function request_Image()
}

int main(int argc,char * argv[])
{
    QApplication app(argc,argv);
    ImageSaver a;
    a.start();
    return app.exec();
}

#include "main.moc"

简而言之,第一次调用是“开始”。那调用“on_Finished”,直到这个没有问题。因此,所有图像文件的地址都被推送到列表中。接下来是对 image[i] 的一一请求,并保存回复图像。这种事情反复发生。在这里只有我遇到问题。此操作中出现崩溃,尤其是在保存图像时。

我的假设是“信号槽”不像函数调用,你或多或少像线程,但在同一个函数(指针)上运行。因此,当一个信号请求painter,它已经在渲染某些东西时,就会出现崩溃。

谁能说出崩溃背后的事实以及如何在不崩溃的情况下保存所有图像?

编辑: 你好,这是完整的代码。运行这个,连续点击消息框

    #include <QApplication>
#include <QDir>
#include <QImage>
#include <QObject>
#include <QMessageBox>
#include <QPainter>
#include <QPixmap>
#include <QSet>
#include <QTimer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QtWebKit/QWebElement>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebPage>
#include <QUrl>

class ImageSaver:public QObject
{
    int index;
    QWebPage * main_Page;
    QNetworkAccessManager * manager;
    QNetworkReply * reply;
    QString file_Name;
    QSet<QString> image_Addresses;
    QString web_Address;
    Q_OBJECT
signals:
    void image_Saved();
public slots:
    void request_Image();
    void on_Finished(bool status);
    void got_Reply(QNetworkReply * reply);
public:
    ImageSaver();
    void start();
protected:
    //void show_Frame(QWebFrame * frame);
};

ImageSaver::ImageSaver()
{
    index = 0;

    this->main_Page = new QWebPage;

    manager = new QNetworkAccessManager;

    reply = NULL;

    connect(main_Page,SIGNAL(loadFinished(bool)),this,SLOT(on_Finished(bool)));

    connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(got_Reply(QNetworkReply*)));

    connect(this,SIGNAL(image_Saved()),this,SLOT(request_Image()));
}

void ImageSaver::start()
{
    web_Address = "yahoo.com";

    QDir dir;

    dir.mkdir(web_Address);

    QUrl url = QUrl::fromUserInput(web_Address);

    main_Page->mainFrame()->load(url);
}

void ImageSaver::request_Image()
{
    QString temp_Address = *(image_Addresses.begin()+index);

        int a = temp_Address.lastIndexOf("/");
        file_Name = temp_Address.mid(a+1);

        //Without the below message box, the program closes shortly
        //This message box is slowing down that effect
        QMessageBox hh;
        hh.setText(file_Name);
        hh.exec();
        QNetworkRequest request= QNetworkRequest(QUrl(temp_Address));
        request.setRawHeader("img","src");
        manager->get(request);
}

void ImageSaver::on_Finished(bool status)
{
    if(status)
    {
        QMessageBox mm;
        mm.setText("Finished");
        mm.exec();

        QWebElementCollection temp_Collection= main_Page->mainFrame()->findAllElements("*");

        for(int i=0;i<temp_Collection.count();++i)
        {
            QWebElement temp_Element = temp_Collection[i];
            if(temp_Element.tagName().contains("img",Qt::CaseInsensitive) && temp_Element.attributeNames().contains("src",Qt::CaseInsensitive))
            {
                QString image_Web_Address = temp_Element.attribute("src");
                if(!image_Addresses.contains(image_Web_Address))
                    image_Addresses.insert(image_Web_Address);
            }
        }
        emit image_Saved();
        QMessageBox kk;
        kk.setText("Image is going to be saved");
        kk.exec();
    }

    else
    {
        QMessageBox mm;
        mm.setText("Not ready");
        mm.exec();
    }

    QMessageBox mm;
    mm.setText("Getting out of finished");
    mm.exec();
}

void ImageSaver::got_Reply(QNetworkReply * reply)
{
    QImage image;
    if(image.load(static_cast<QIODevice *>(reply),0))
        image.save(web_Address+QDir::separator()+file_Name,0);

    ++index;
    emit image_Saved();
}

/*
void ImageSaver::show_Frame(QWebFrame * temp_Frame)
{
    QImage image(temp_Frame->contentsSize(),QImage::Format_ARGB32_Premultiplied);
    image.fill(Qt::transparent);

    QPainter painter(&image);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.setRenderHint(QPainter::TextAntialiasing,true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform,true);

    temp_Frame->documentElement().render(&painter);

    painter.end();

    foreach(QWebFrame * temp_Frame0,temp_Frame->childFrames())
        show_Frame(temp_Frame0);
}
*/

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

    ImageSaver a;
    a.start();

    return app.exec();
}

#include "main.moc"

这是专业文件

QT += webkit network

SOURCES += \
    main.cpp

【问题讨论】:

  • 哪里崩溃了?有消息吗?您应该提供一个完整的最小示例来演示该行为。插槽就像一个函数调用,信号只是稍后(在事件循环期间)调用插槽的一种方式。为什么要将算法拆分为多个插槽并不明显。我会尝试将整个过程放在一个插槽中,看看你是否仍然崩溃。
  • 从您当前的示例来看,您正在使用默认方法连接信号和插槽。这是一个直接连接,这意味着您实际上只是在进行函数调用。这里没有多线程,除非你在我们看不到的地方做一些线程。 Tom K 引用了“排队连接”,但它看起来不像你正在做的事情。看到这个:doc.qt.nokia.com/latest/…
  • 如果崩溃,请提供回溯。您对信号/插槽的假设是错误的,它们是正常的方法调用,不涉及多线程。
  • @Tom 尝试编辑中的代码。如果您说如何将整个过程放在一个插槽中,我将非常感谢您。
  • 我认为您崩溃的原因如下。然而,像这样使用 msgboxes 调试代码是危险的:如果有一个网络操作挂起并且您打开一个消息框,则 msgbox 将在 exec 中打开一个本地事件循环。如果在对话框打开时网络操作返回,则被调用的插槽将更改应用程序的状态。关闭对话框后,将执行 exec() 之后的代码,并且由于介于两者之间的网络操作调用的插槽,在 exec() 之前可以做出的任何假设都不再有效。最好不要在这样的上下文中使用阻塞 exec() 调用。

标签: c++ qt logic


【解决方案1】:

您的代码崩溃,因为您读取的内容超出了 image_Addresses 集的边界。

void ImageSaver::request_Image()
{
    QString temp_Address = *(image_Addresses.begin()+index);
    ...

您在收到每张图片后都会增加索引,但在代码中没有任何检查索引是否仍小于 image_Addresses.size(),因此一旦取消引用 image_Addresses.begin()+index for index ==,它就会崩溃image_Addresses.size()。

【讨论】:

  • 牛眼。现在我摆脱了那些崩溃。但是,问题并不在于所有图像都已保存。
【解决方案2】:

这个问题可能有很多解决方案。我认为你应该看看The State Machine Framework。在简单的情况下,您可以使用布尔变量来检查是否可以继续。您还应该考虑在忙于处理图像时该怎么做。您可以排队请求或拒绝它们。您还可以实现线程化,以便新线程处理新请求。

附:对我来说,信号更像是事件而不是线程。

【讨论】:

    【解决方案3】:

    什么是错误,为什么最后有一个#include

    仅供参考,您可以使用 QImage 类代替,其中包括从 QIODevice* 保存和加载,例如 QNetworkReply。在 Qt 这样庞大的框架中,需要重新发明轮子的情况极为罕见。

    【讨论】:

    • #include "main.moc" 是当您的主 cpp 文件中有信号/插槽时的标准做法。
    • 更多的是关于它在哪里而不是它是什么。
    • @AJG85 我想我使用了 QImage 类,请参阅编辑。
    • 我的意思是你可以做一些类似于class ImageSaver : public QImage 的事情,然后在你的版本中使用包含你想要的额外位并覆盖你想要的任何不同的东西。
    猜你喜欢
    • 2016-11-06
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2016-07-17
    • 2014-05-25
    • 2011-06-18
    相关资源
    最近更新 更多