2013-05-11 14 views
9

Ho un'applicazione qt multithread. quando sto facendo alcuni processi in mainwindow.cpp, allo stesso tempo, voglio aggiornare mainwindow.ui da altro thread.Qt - aggiornamento della finestra principale con il secondo thread

ho mythread.h

#ifndef MYTHREAD_H 
#define MYTHREAD_H 
#include <QThread> 
#include "mainwindow.h" 

class mythread : public QThread 
{ 
    public: 
     void run(); 
     mythread(MainWindow* ana); 
    MainWindow* ana; 
private: 

}; 

#endif // MYTHREAD_H 

mythread.cpp

mythread::mythread(MainWindow* a) 
{ 
    cout << "thread created" << endl; 
     ana = a; 
} 

void mythread::run() 
{ 
    QPixmap i1 (":/notes/pic/4mdodiyez.jpg"); 
    QLabel *label = new QLabel(); 
    label->setPixmap(i1); 
    ana->ui->horizontalLayout_4->addWidget(label); 


} 

ma il problema è che, non riesco a raggiungere il ana->ui->horizontalLayout_4->addWidget(label);

come posso farlo?

+0

Se sei come me e hai 10 minuti fino alla scadenza, ecco una soluzione più aggressiva: aggiungi un pulsante fittizio nella finestra principale (larghezza e altezza 0), ogni volta che devi aggiornare l'interfaccia utente dall'operatore emettere un evento click() nel worker e sovrascrivere il gestore di clic per quel pulsante per eseguire gli aggiornamenti. – cristid9

risposta

12

ma il problema è che, non riesco a raggiungere il ana-> UI-> horizontalLayout_4-> addWidget (etichetta);

Inserire le modifiche dell'interfaccia utente in uno slot nella finestra principale e collegare un segnale di filo a quello slot, è probabile che funzioni. Penso che solo il thread principale abbia accesso all'interfaccia utente di Qt. Pertanto, se si desidera la funzionalità della GUI, deve essere presente e può essere segnalata solo da altri thread.

OK, ecco un semplice esempio. A proposito, il tuo scenario non ha davvero bisogno di estendere lo QThread - quindi è meglio non farlo, a meno che tu non debba farlo. Ecco perché in questo esempio, io uso un normale QThread con un lavoratore in base QObject invece, ma il concetto è lo stesso se si sottoclasse QThread:

L'interfaccia utente principale:

class MainUI : public QWidget 
{ 
    Q_OBJECT 

public: 
    explicit MainUI(QWidget *parent = 0): QWidget(parent) { 
     layout = new QHBoxLayout(this); 
     setLayout(layout); 
     QThread *thread = new QThread(this); 
     GUIUpdater *updater = new GUIUpdater(); 
     updater->moveToThread(thread); 
     connect(updater, SIGNAL(requestNewLabel(QString)), this, SLOT(createLabel(QString))); 
     connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater())); 

     updater->newLabel("h:/test.png"); 
    } 

public slots: 
    void createLabel(const QString &imgSource) { 
     QPixmap i1(imgSource); 
     QLabel *label = new QLabel(this); 
     label->setPixmap(i1); 
     layout->addWidget(label); 
    } 

private: 
    QHBoxLayout *layout; 
}; 

... e il oggetto operaio:

class GUIUpdater : public QObject { 
    Q_OBJECT 

public: 
    explicit GUIUpdater(QObject *parent = 0) : QObject(parent) {}  
    void newLabel(const QString &image) { emit requestNewLabel(image); } 

signals:  
    void requestNewLabel(const QString &); 
}; 

l'oggetto lavoratore viene creato e spostato in un altro filo, poi connessa allo slot che crea le etichette, allora il metodo newLabel viene richiamato, che è solo un involucro per emettere il requestNewLabel segnale e passare il percorso per l'immagine. Il segnale viene quindi passato dall'oggetto worker/thread allo slot dell'interfaccia utente principale insieme al parametro del percorso dell'immagine e una nuova etichetta viene aggiunta al layout.

Poiché l'oggetto di lavoro è stato creato senza genitore per poterlo spostare su un altro thread, colleghiamo anche il segnale thread-thread allo slot worker deleteLater().

+0

puoi dare un esempio per la connessione? non posso decidere elementi di connessione. – abby

+0

@abby - Ho aggiunto un rapido esempio su come ottenere ciò di cui hai bisogno. – dtech

+0

Ho provato questo, ma invece di caricare un'immagine ho appena messo alcuni calcoli in newLabelText() (ho dovuto modificare newLabel(), requestNewLabel()) per vedere se questo risolve effettivamente un problema comune di congelamento dell'interfaccia utente quando si usano i thread quando si esegue lavoro di sfondo (come l'autore suggerisce che lui/lei vuole fare). E in effetti fa congelare il tutto per un po '(ho messo un controllo numerico LCD e un pulsante che uso per contare e visualizzare il risultato sul display LCD solo per verificare se effettivamente si blocca). Il che significa anche che se cariciamo un'immagine molto grande, questo potrebbe portare a bloccare l'interfaccia utente. – rbaleksandar

3

Prima di tutto, "you're doing it wrong". Normalmente vuoi creare una classe derivata da un oggetto QObject e spostare quella classe in un nuovo oggetto thread invece di derivare la tua classe da un Qthread

Ora per ottenere le specifiche della tua domanda, non sei in grado di modificare direttamente gli elementi dell'interfaccia utente del tuo thread GUI principale da un thread separato. Devi connect a signal dal tuo secondo thread a un slot nella tua discussione principale. Puoi passare tutti i dati che ti servono attraverso questa connessione segnale/slot ma non sei in grado di modificare direttamente l'elemento ui (che in tutta onestà probabilmente non vuoi se hai intenzione di mantenere separata la frontend della tua app dal backend). segnale di cassa Qt e scanalatura documentation per un intero lotto più informazioni

+0

c'è un modo per aggiornare mainwindow.ui dal thread di lavoro? Perché ne ho davvero bisogno. – abby

+0

si aggiornerebbe l'elemento ui nello slot che era collegato al segnale dal thread diverso – g19fanatic

+0

Ah quel famigerato post del blog "Stai sbagliando". Sono fortemente in disaccordo con lui. Vedi http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html –

1

come posso farlo?

Hai già le risposte a ciò che dovresti fare, ma non un perché, quindi aggiungerò un perché.

Il motivo per cui non si modificano gli elementi della GUI da un altro thread è perché gli elementi della GUI in genere non sono thread-safe. Ciò significa che se il thread dell'interfaccia utente principale e il thread di lavoro aggiornano l'interfaccia utente, non puoi essere certo dell'ordine di ciò che è accaduto quando.

Per la lettura dei dati, in genere ciò può talvolta essere soddisfacente (ad esempio, il controllo di una condizione) ma in genere non si desidera che questo sia il caso. Per la scrittura di dati, questa è quasi sempre la fonte di bug molto, molto stressanti che si verificano "a caso".

Un'altra risposta ha sottolineato i buoni principi di progettazione: non solo limita la logica della GUI a un thread e fa scattare segnali per risolvere i problemi relativi alle condizioni della gara, ma ti costringe anche a compartimentare il codice in modo appropriato. La logica di presentazione (il bit di visualizzazione) e la logica di elaborazione dei dati possono essere separate in modo pulito, il che rende molto più semplice mantenere i due.

In questa fase si potrebbe pensare: diamine, questa attività di thread è farrrrrrr troppo lavoro! Lo eviterò. Per capire perché questa è una cattiva idea, implementa un programma di copia di file in un singolo thread con una semplice barra di avanzamento che ti dice fino a che punto è lungo la copia. Eseguilo su un file di grandi dimensioni. Su Windows, dopo un po ', l'applicazione diventerà "bianca" (o su XP penso che diventi grigia) e "non risponderà". Questo è molto letteralmente ciò che sta accadendo.

Le applicazioni GUI per lo più lavorano sulla variazione dei messaggi di elaborazione e dispacciamento "one big loop". Windows, ad esempio, misura il tempo di risposta a quei messaggi. Se un messaggio impiega troppo tempo per ottenere una risposta, Windows decide che è morto e prende il sopravvento. This is documented in GetMessage().

Così mentre può sembrare un bel po 'di lavoro, Signals/Slots (un modello basato sugli eventi) è fondamentalmente la strada da percorrere - un altro modo di pensare a questo è che è totalmente accettabile per i tuoi thread generare "eventi" anche per l'interfaccia utente, come aggiornamenti di avanzamento e simili.

Problemi correlati