Seite 1 von 1

[geloest] Static Methoden über versch. Thread aufrufen

Verfasst: 11. Januar 2009 17:53
von butze
hi!

ich möchte ein Logwindow erstellen, dass mit Hilfe von static Methoden beschrieben werden soll. Die Klasse Logwindow ist mit Hilfe eines Singletons implementiert, sodass ich einfach Logwin::info("infomessage"); aufrufen kann, ohne mich vorher um eine explizierte Instanziierung kümmern muss.

Im meinem Fall wird das Logwin zuerst von Thread A aufgerufen. Dort klappen alle Aufrufe.

Später ruft Thread B Logwin::debug("text") auf. In der Konsole erscheint folgendes:
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)

Anscheinend liegt das Problem darin, dass das textField im Thread A erzeugt wurde und später von Thread beschrieben wird.

Ich habe dann versucht innerhalb der Klasse mit Signal/Slots zu arbeiten, was allerdings durch die nötigen static SIGNALS nicht geklappt hat (debug(QString) müsste ein static emit aufrufen).

Kennt wer eine Lösung?

Code: Alles auswählen

#ifndef LOGWINDOW_H
#define LOGWINDOW_H

#include <QtGui>
#include <QTime>
#include <QColor>
#include <iostream>

class Logwindow : public QWidget
{
    Q_OBJECT


public:
    static void info(QString str);
    static void warning(QString str);
    static void error(QString str);


    /// appends new Line to Log output
    /// \param str Log text
    void appendText(QString str, QColor c);


private:
    QTextEdit *textField;

    static Logwindow* exemplar;
    Logwindow (QWidget*);

};

#endif

Code: Alles auswählen

#include "Logwindow.h"

Logwindow* Logwindow::exemplar = 0;

Logwindow::Logwindow(QWidget *parent=0)
{
     textField= new QTextEdit();
     textField->setReadOnly(TRUE);
     //layout krams
}

void Logwindow::error(QString str)
{
    if(exemplar == 0)
        exemplar = new Logwindow();
    exemplar->appendText("[ERROR] " + str, Qt::red);
}

void Logwindow::warning(QString str)
{
    if(exemplar == 0)
        exemplar = new Logwindow();
    exemplar->appendText("[WARNING] " + str, Qt::blue);
}

void Logwindow::info(QString str)
{
    if(exemplar == 0)
        exemplar = new Logwindow();
    exemplar->appendText("[INFO] " + str, Qt::black);
}

void Logwindow::appendText(QString str, QColor c)
{
    textField->setTextColor(c);
    textField->append(QTime::currentTime().toString("hh:mm:ss")+" "+str);
}

Verfasst: 11. Januar 2009 19:27
von upsala
Zum einen würde ich folgendes mal ändern:

Code: Alles auswählen

private:
 static Logwindow *m_exemplar; 
public:

static Logwindow *exemplar() { 
  if (!m_exemplar) {
   m_excemplar=new Logwindow();
  }
  return m_exemplar;
}

void Logwindow::error(QString str)  { 
  exemplar()->appendText("[ERROR] " + str, Qt::red); 
} 
Außer du schreibst gerne viel...

Dann schreibst du was von Threads: Du weist, daß sich GUI und Threads mit direkten Aufruf nicht vertragen. Bei dir geht nichts genaueres aus dem Code hervor.

Ich wüßte nicht, warum Signal/Slots nicht funktionieren sollten, und eine Methode debug ist bei dir im Code auch nicht sichbar, wie sollen wir sie dann beurteilen?

Verfasst: 14. Januar 2009 09:56
von butze
hi,

sorry, hab beim Ausdünnen die debug-Methode rausgenommen und trotzdem darauf referenziert.

Deine Vorschläge hab ich gleich mal eingebaut - sieht jetzt schon um einiges schöner aus - danke!

Nach einigen Stunden hab ich das jetzt scheinbar hinbekommen - aber schön ist was anderes, weil ich iwi umständlich mit Signals Slots hantiere. Mir kommt es ja sehr darauf an, dass die benutzenden Klassen -egal in welchem Thread sie auch laufen - nur einen Aufruf machen müssen.
Hast du da noch Verbesserungsvorschläge/ Kritikpunkte?

Gruß

Code: Alles auswählen

class Logwindow : public QWidget
{
    Q_OBJECT

public:
    static Logwindow* instance();
    static void info(QString str);
    static void warning(QString str);
    static void error(QString str);
    static void debug(QString str);

public slots:
    void writeToTextField(QString, QColor);

signals:
    void toTextField(QString, QColor);


private:
    Logwindow (QWidget*);

    void appendText(QString str, QColor c);

    static Logwindow* m_instance;
    QMutex *m_textFieldMtx;
};

Code: Alles auswählen

#include "Logwindow.h"

Logwindow* Logwindow::m_instance = 0; // initial no object created

Logwindow::Logwindow(QWidget *parent=0)
{
   //Layout stuff

    connect(this, SIGNAL(toTextField(QString, QColor)), this, SLOT(writeToTextField(QString, QColor)));
    
    m_textFieldMtx = new QMutex();
}

Logwindow* Logwindow::instance()
{
    if (!m_instance)
    {
        m_instance=new Logwindow();
    }
    return m_instance;
}

void Logwindow::error(QString str)
{
    instance()->appendText("[ERROR] " + str, Qt::red);
}

void Logwindow::warning(QString str)
{
    instance()->appendText("[WARNING] " + str, Qt::blue);
}

void Logwindow::info(QString str)
{
    instance()->appendText("[INFO] " + str, Qt::black);
}

void Logwindow::debug(QString str)
{
    instance()->appendText("[DEBUG] " + str, Qt::gray);
}


void Logwindow::appendText(QString str, QColor c)
{
    emit toTextField(str, c);
}

void Logwindow::writeToTextField(QString str, QColor c)
{
    m_textFieldMtx->lock();
    textField->setTextColor(c);
    textField->append(QTime::currentTime().toString("hh:mm:ss")+" "+str);
    m_textFieldMtx->unlock();
}


Verfasst: 14. Januar 2009 12:48
von franzf
Wenn du mehr mit deinen Logs anfangen willst (z.B. QMessageBox, falls ein critical error auftritt, Log bei Programmbeendigung in ein File schreiben) wäre vllt. eine eigene Non-Gui-Klasse sinnvoll.
Dann kannst du ein LogWindow erstellen, dem du die Logger::instance() im Konstruktor übergibst und auf die log-Signals reagierst, etc.
Bei mir sieht das z.B. so aus:
Logger.h:

Code: Alles auswählen

#ifndef LOGGER_H
#define LOGGER_H

#include <QObject>
#include <QList>
#include <QDateTime>

struct Log{
    typedef enum{
        Info,
        Warning,
        Error
    } Type;
    QDateTime timeStamp;
    Type type;
    QString title;
    QString message;
};

class Logger : public QObject
{
    Q_OBJECT
    public:
        void log(Log log);

        static void info( const QString& title, const QString& message );
        static void warning( const QString& title, const QString& message );
        static void error( const QString& title, const QString& message );

        const QList<Log>& logs() const {return m_logs;}

        static Logger* instance();
        static void destroy();

    Q_SIGNALS:
        void newLog(Log log);

    protected:
        Logger(QObject* parent=0);
        QList<Log> m_logs;

        static Logger* s_instance;
};

static Logger*
logger()
{
    return Logger::instance();
}

#endif  // LOGGER_H
Lobber.cpp:

Code: Alles auswählen

#include "Logger.h"

Logger* Logger::s_instance=0;

Logger::Logger( QObject* parent)
: QObject(parent)
{}

Logger*
Logger::instance()
{
    if( !Logger::s_instance ) Logger::s_instance = new Logger;
    return Logger::s_instance;
}

void
Logger::destroy()
{
    if( Logger::s_instance ) {
        delete Logger::s_instance;
        Logger::s_instance=NULL;
    }
}

void
Logger::log(Log log)
{
    m_logs.append(log);
    emit newLog(log);
}

void
Logger::info( const QString& title, const QString& message )
{
    Log log_;
    log_.type = Log::Info;
    log_.timeStamp = QDateTime::currentDateTime();
    log_.title = title;
    log_.message = message;

    Logger::instance()->log( log_ );
}

void
Logger::warning( const QString& title, const QString& message )
{
    Log log_;
    log_.type = Log::Warning;
    log_.timeStamp = QDateTime::currentDateTime();
    log_.title = title;
    log_.message = message;

    Logger::instance()->log( log_ );
}

void
Logger::error( const QString& title, const QString& message )
{
    Log log_;
    log_.type = Log::Error;
    log_.timeStamp = QDateTime::currentDateTime();
    log_.title = title;
    log_.message = message;

    Logger::instance()->log( log_ );
}
Hat den Vortiel, dass du unabhängig von einer grafischen Darstellung den Typ des Logs ermitteln kannst, ebenso den Zeitpunkt des Auftretens.

Grüße
Franz

Verfasst: 15. Januar 2009 20:27
von butze
cool!

das ist genau das, was ich brauche!

Vielen Dank - die Logscopes sind super!


Gruß