2013-03-05 74 views
0

我從上週開始因QPlainTextEdit更新而引發問題。我試圖用QPlainTextEdit從QMainWindow對話窗口中單獨創建。當我嘗試使用appendHtml信號(也嘗試使用appendText)時,問題就開始了,除非用鼠標標記,否則放置的文本是不可見的。重新繪製或更新程序崩潰中的原因或不可見的操作。如何強制Qt更新來自非主線程的GUI

簡化了QDialog的代碼與QPlainTextEdit頭:方法定義

namespace Ui { 
class LogWindow; 
} 

class LogWriter: public QDialog 
{ 
Q_OBJECT 

QMutex print_lock; 

public: 

class Log{ 

    Q_OBJECT 

    const static int MAX_SIZE = 100; 
    bool to_terminal; 
    QString color; 
    QMutex *print_lock; 
    QPlainTextEdit *text_place; 
    QVector< QPair<QString,time_t> > history; 
    LogWriter * obj; 

    public: 
    bool print; 

    Log(bool _print,QString _color,LogWriter *obj_ = NULL) 
    {print = _print; color = _color; obj = obj_;} 
    void setLock(QMutex *print_lock_){print_lock = print_lock_;} 
    void setTextField(QPlainTextEdit *_text) {text_place = _text;} 
    Log& operator<<(QString &a); 
    Log& operator<<(const char* a); 
}; 

static LogWriter* getInstance() 
{ 
    static LogWriter instance; // Guaranteed to be destroyed. 
           // Instantiated on first use. 
    return &instance; 
} 
~LogWriter(); 

Log LOW,MEDIUM,HIGH; 
Ui::LogWindow *ui; 

signals: 
void signalLogAppend(QString); 
}; 

簡化的代碼:

LogWriter::LogWriter(QWidget * parent): QDialog(parent) { 

ui = new Ui::LogWindow; 
ui->setupUi(this); 

LOW.setLock(&print_lock); 
MEDIUM.setLock(&print_lock); 
HIGH.setLock(&print_lock); 

connect(this,SIGNAL(signalLogAppend(QString)),ui->plainTextEdit, 
SLOT(appendHtml(QString)),Qt::DirectConnection); 

} 

LogWriter::Log& LogWriter::Log::operator<< (QString &s){ 
history.push_front(qMakePair(s,time(NULL))); 
if(history.size() > MAX_SIZE) history.pop_back(); 

if(print){ 
    //print_lock->lock(); 

    QString text = "<font color=\""; 
    text += color + "\">"; 
    text += s + "</font>"; 
    //cout << text.toStdString() << endl; 
    //text_place->appendHtml(text); 
    //text_place->repaint(); 
    emit (obj)->signalLogAppend(text); 
    //print_lock->unlock(); 

} 
return *this; 
} 

我有兩個獨立的用戶界面的文件(第一對主窗口中,第二日誌窗口)。 我必須在我的程序中使用日誌窗口(約10個線程),並且我對此問題進行了處理。我的問題是 - 是否有可能在不使用主線程的情況下強制GUI更新,如果沒有 - 我還有其他可能性。如果可能的話,我寧願避免重新構建我的所有代碼 - 這需要我一些時間來完成。現在登錄是超級容易 - 我ONY需要:

LogWindow *log = LogWindow::getInstance(); 
log->MEDIUM << "something"; 

隨着更多的信息,我添加QTCreator警告:

QObject::connect: Cannot queue arguments of type 'QTextBlock' 
    (Make sure 'QTextBlock' is registered using qRegisterMetaType().) 
    QObject::connect: Cannot queue arguments of type 'QTextCursor' 
    (Make sure 'QTextCursor' is registered using qRegisterMetaType().) 

回答

2

如果我正確理解你的代碼,你想從後臺線程登錄並使用直接連接將信號傳遞給GUI線程?這是行不通的,你必須通過默認連接發送信號,以便Qt可以發現它是一個跨線程信號並相應地通過線程(即通過前臺線程上的消息循環)傳遞它。

在Qt中,任何GUI交互都必須在Main/Foreground線程中發生,否則不好的事情會在您發現時發生。您當然可以從後臺線程發送信號來觸發GUI更新 - 我一直這樣做 - 但您需要確保您使用正確的連接。直接連接會導致直接函數調用,在這種情況下不會爲您工作。

在你的代碼中,問題是對connect()的調用 - 當你只使用默認設置時,你明確指定信號到插槽連接的連接模式。如果將連接顯式設置爲Qt::DirectConnection,則底層代碼將直接調用指定的插槽,這意味着您最終將調用信號的線程上下文中的插槽。你不希望這樣,因爲信號是在後臺線程中觸發的。

+0

是的 - 我試圖從後臺線程(甚至很多線程 - 按運算符<<)登錄,但我不使用直接調用(或者我錯了)我使用信號 - 就像在我附上的代碼中。 「通過默認連接發送信號」 - 這是什麼意思?這句話的其餘部分對我來說也很模糊。 – lagoru 2013-03-05 19:47:09

+1

他的意思是在你使用5個參數調用connect(....)的LogWritter類連接中。刪除第五個參數Qt:DirectConnection。直接連接僅適用於相同的線程連接。要麼刪除參數並讓Qt知道它需要是Qt:QueuedConnection,或者只是自己提及它。我發現最好是明確的交叉線程信號插槽 – Viv 2013-03-05 20:23:37

+0

OMG它的工作....我怎麼會錯過,四天的搜索,而我可以檢查什麼QT ::直接連接邊界是...謝謝非常多的幫助! – lagoru 2013-03-05 21:25:32

0

您不能將任意類型/類傳遞給信號和插槽。該列表是有限的,並不是所有的Qt類都在列表中。要將類型/類添加到可以傳遞給信號/插槽的列表中,必須爲該類調用qRegisterMetaType。我建議在你試圖傳遞到這樣的信號類的構造函數調用它:

MyClass::MyClass() : MyParentClass() 
{ 
    static int reg = qRegisterMetaType<MyClass>("MyClass"); 
} 

靜態INT確保登記只調用一次,所能使用的MyClass任何實例之前。