Qt ListBox Problem



  • Scorcher24 schrieb:

    Warum beisst sich das? Ich kann jederzeit ein Singleton in ein Programm integrieren, ich wüsste jetzt nicht warum sich das ausgerechnet bei UI-Programmen als schlecht darstellen sollte... hat QT keine globale LogKlasse oder sowas?

    Prinzipiell ist ein Singleton kein Problem. Aber wenn ich meine Widget-Klasse als Singleton konzipiere, dann schon 😉
    Ein Widget-Objekt repräsentiert genau ein Element im Fenster. Ich kann dieses eine Objekt nicht an meherern Stellen anzeigen. Wenn ich in Qt ein Widget in ein anderes Layout lege, wird es automatisch aus dem alten entfernt. Wenn LoggerWidget ein Singleton ist, heißt dies auch dass das Widget in vollem Umfang als Singleton nutzbar ist, und das ist es leider nur mit Problemen.

    Ein weiteres Problem ist das rekursive Löschen von child-QObjects. Lege ich mein Singleton-Logger-Widget als child in ein Fenster, welches evtl. nur temporär existiert (z.B. ein Dialog über das Menü "Zeige Programm-Log"), wird das Programm beim nächsten "Logger::instance()->LogText()" SegFaulten 😉

    Mach ich nur "Logger::LogText()" static, brauch ich auch ein einziges statisches "ui"-Member. Und das geht noch schiefer, wenn ich mehrere Logger-Objekte erstellen will...

    Man stellt sich mehr Beine, als man davon profitiert. Auch vom Programm-Design her ist es besser, das eigentliche Loggen von der Loganzeige zu trennen.

    friend hilft aber auch nur bedingt, wenn er auf ein existierendes Objekt zugreifen wil. Ich würde einfach das Log als Referenz übergeben, fertig.

    Es kann doch sein, dass das ui (also eine Instanz der vom Designer+uic generierten Klasse) ein private-Member ist, und er einfach eine andere Klasse (z.B. MainWindow) hat, von der aus er direkt das ui->textEdit oder so verändern will. Dann hilft friend sehr wohl 🙂

    Nur ist meine Glaskugel gerade kaputt und den Hellsehkurs hab ich auch nicht bestanden - wir brauchen eindeutige, endgültige Infos vom OP 🙂



  • Ich dachte immer dass QT dem MVC-Konzept folgt? Sollte also der eigentlich logger nicht nur die Daten sammeln und die Darstellung ( das Widget ) in einer anderen Klasse erfolgen?

    Und wenn ich nur Zugriff auf textEdit will, dann reicht auch nen getter... da brauch ich nicht gleich friend :D.
    rya.



  • Scorcher24 schrieb:

    Ich dachte immer dass QT dem MVC-Konzept folgt? Sollte also der eigentlich logger nicht nur die Daten sammeln und die Darstellung ( das Widget ) in einer anderen Klasse erfolgen?

    Es gibt keinen Logger in Qt, deshalb gibt es da auch kein MVC 😛 Der schreibt sich doch selber nen Logger, deshalb liegt die Aufgabe des Designs auch bei ihm.
    Nur weil man Qt programmiert ist nicht alles was man macht automatisch MVC 😉

    Und wenn ich nur Zugriff auf textEdit will, dann reicht auch nen getter... da brauch ich nicht gleich friend :D.

    Klar, wenn man für alle den LineEdit als public exponieren will. Wenn es wirklich nur diese eine Klasse ist, hat man (meiner Meinung nach) bessere Kapselung, wenn man wirklich nur diese eine Klasse, die man ja selber schreibt, an die privaten Sachen ranlässt (ich weiß, kann man wieder weiterstreiten).
    Ciao (Yeah, Italien strauchelt, Frankreich streitelt, die WM kommt in Fahrt :D)



  • Ok für alle die nocht nicht so verstanden haben wie ich es meinte.

    Also ich habe eine Gui mit dem class name Farmcrawler. Dann habe ich noch eine Headerdatein wo meine eigene Klasse MeineClass drin ist. Weiterhin habe ich eine Funktion die in einem Extra Thread ausgeführt werden soll Funktion

    void StartIt()
    {
    MeineClass data;
    int Status;
    do
    {
       Status = Check_Status();
       if(Status == 1)
       {
           data.SetState(1);
           LogText("Aktion ist Fertig führe 2ten Schritt aus");
       }
       else
       {
           data.SetState(0);
           LogText("Muss noch ein wenig warten");
       }
    
    }
    while(1==1)
    }
    

    (Code frei erfunden war lediglich ein beispiel)

    So nun ist LogText eine Funktion der Gui Klasse (Farmcrawler). Wenn ich ein objekt Farmcrawler abc;
    anlegen würde wird ja nicht viel passieren, Da ich dann ja die GUI 2mal aufrufen würde. Nun ist meine frage wie kann ich aus der obrigen endlossschleife auf die Funktion LogText aus der Gui classe zugreifen.

    void Farmcrawler::LogText(const QString logtext)
    {
        ui->textEdit->insertPlainText(logtext);
        ui->textEdit->insertPlainText("\n");
    }
    

    Hoffe ihr versteht jetzt mein Problem



  • Wie ich bereits schrieb:
    Übergib an die jeweiligen Klassen eine Referenz auf die GUI-Klasse FarmCrawler oder trenne das Problem und arbeite mit Events. Sprich, du hast eine LogKlasse welche als Singleton implementiert ist und jedesmal wenn die eine Nachricht erhält, muss deine GUIKlasse drauf reagieren.
    Eine dieser Methoden würde ich wählen.
    rya.



  • Du musst in dem Fall (Arbeitender Thread vs. Mainthread (Gui-Thread)) sowieso andere Wege gehen, da du Gui-Zeichnen nur aus dem Gui-Thread heraus anstoßen kannst (textEdit->append() ist so eine Funktion). Am besten sendet deine Logger-Klasse (die selber kein Widget ist!) nur Signale aus, die in deiner Gui aufgefangen werden und einen Eintrag in einem separaten Logger-Widget (QTextEdit, QListView,...) einfügt. Wurde auch so schon empfohlen.
    Du musst aber die Logger-Klasse mit Mutex synchronisieren, da mehrere Threads gleichzeitig darauf zugreifen können und das Objekt verändern.



  • Mh wenn ich

    class Myclass : public Farmcrawler
    

    Dann bekomme ich einen Runtime Error bekommen



  • Kann mir jemand mal Bitte einen kleinen Beispielcode geben ich stehe auf dem Schlauch :S ich bin in Klassen noch nicht so gut :S



  • Dann solltest du das nachholen. Es hat für dich keinen Wert, wenn wir uns Mühe geben und ein funktionierendes Beispiel posten würden. C++-Grundlagen sind Pflicht, und dazu gehört auch das Wissen, wie Klassen funktionieren und wie man sie verwendet. Wie lernst du denn C++? Buch, Tutorial, Online-Kurs, Schule? Lass Anfangs einfach Gui (und auch Threads...) außen vor, das ist zu viel und bringt nochmal zusätzliche Spezialitäten (Events, Zeichnen, Layouts, Synchronisation, ...) die ein wirklich solides Wissen um C++ voraussetzen.



  • Ahh das ist ja nicht das Problem. Ich weiß wie Threads funktionieren und auch wie klassen. Jedoch habe ich bei der GUI ein Problem weil ich ja normal ein Objekt aus einer klasse erzeuge. Aber wie soll ich das machen wenn ich auf ein Vorhandenes zugreifen will?? Oder kann man Objekte weitergeben??

    Ich habe es mir selber beigebracht und dannach noch ergenzend ein Buch ( C++ a-z) gekauft. Ich bekomme soweit alles hin inc grundfunktionen von cpp. Danke für eure Hilfe :s ich habe ja alles soweit fertig das letzte element was fehlt ist halt die GUI. Ich hatte es schonmal mit der WINApI gemacht aber jedoch ist das zuviel code und man kann nicht so tolle sachen damit erstellen. Ich würde micht sehr doll über ein Beispiel freuen!



  • 1. Beispiel: Singleton

    #include <iostream>
    #include <string>
    
    class LoggerSingleton
    {
    private:
        LoggerSingleton() {}
        LoggerSingleton(const LoggerSingleton&) {}   
    
    public:
       void LogText(const std::string& text)
       {
          std::cout << "LogMessage : " << text << std::endl;
       }
    
       static LoggerSingleton& getInstance()
       {
          static LoggerSingleton OnlyOne;
          return OnlyOne;
       }
    };
    
    class foobar
    {
    public:
       foobar() 
       {
            LoggerSingleton::getInstance().LogText("Objekt erstellt");
       }
    };
    
    int main()
    {
      foobar foo;
    };
    

    Das hier ist das Beispiel wie man das über ein Singeton lösen kann. Ein Singleton ist eine Klasse, die nicht mehrfach instanziert werden kann. Man greift über einen statischen Getter immer auf das selbe Objekt zu. Natürlich muss das über einen Mutex auch synchronisieren. Aber Du weisst ja wie das geht :).

    2. Beispiel: Weitergabe des Loggers

    #include <iostream>
    #include <string>
    
    class Logger
    {
    public:
       void LogText(const std::string& text)
       {
          std::cout << "LogMessage : " << text << std::endl;
       }   
    };
    
    class foobar
    {
    public:
       foobar(Logger& log) 
        : m_log(log)
       {
            m_log.LogText("Objekt erstellt");
       }
    
    private:
       Logger& m_log;   
    };
    
    int main()
    {
       Logger log;
       foobar foo(log);
    };
    

    Hier wird eine Instanz der Klasse Logger erstellt und über den Konstruktor an foobar als Referenz weitergegeben. Eigentlich easy zu verstehen. foobar greift über die Referenz auf die Instanz der Klasse zu die in der Main erstellt wurde. Ergo landets im richtigen Logbuch.
    Mit QTEvents kann ich Dir leider keines liefern da ich ein anderes GUI Toolkit verwende, aber ich denke die Beispiele reichen um das zum laufen zu bringen.
    HTH
    rya.



  • Ok ich habe mich für die Singleton Variante entschieden. Doch wenn ich es aufrufe passiert nichts.



  • Liegt warscheinlich dadran das ich kein 2tes Objekt der Gui klasse erzeugen kann da es ja dann auf ein anderes objekt zugreift als das was ich möchte



  • hamburger schrieb:

    Liegt warscheinlich dadran das ich kein 2tes Objekt der Gui klasse erzeugen kann da es ja dann auf ein anderes objekt zugreift als das was ich möchte

    Ohne Code kann man nix sagen. Wenn du das Singleton-Beispiel von Scorcher übernommen hast, hast du nicht genau hingeschaut. Üblicherweise versteckt man bei einem Singleton CTOR, Copy-CTOR, OP=, um garantiert immer nur genau ein Opjekt zu haben - wie es im Beispiel steht.

    Nur mal als Frage: Du machst jetzt dein Widget als Singleton? Bist du dir darüber im Klaren, was das im weiteren Verlauf bedeutet? (Widget-Placement, Objekt-Zerstörung, Zugriffe von anderen Threads aus, SEGFAULT, usw)?



  • Ich hab jetzt mal kurz was geschrieben. Eventuelle Fehler darfst du behalten 😉

    Logger.hpp

    #ifndef LOGGER_CLASS_H
    #define LOGGER_CLASS_H
    
    #include <QObject>
    #include <QReadWriteLock>
    
    struct Log {
        enum Scope {
            Info,
            Warning,
            Error
        };
        Scope scope;
        QString message;
        Log(Scope s=Log::Info, const QString& m=QString())
         : scope(s), message(m)
        {}
    };
    
    class Logger : public QObject
    {
        Q_OBJECT
    
        QReadWriteLock lock_;
        QList<Log> logs_;
        Logger();
        void log(const Log& log);
    
        // disable these
        Logger(const Logger&);
        Logger& operator=(const Logger&);
        void setParent(QObject*);
    public:
        static Logger& instance();
    
        static void info( const QString& message);
        static void warning( const QString& message );
        static void error( const QString& message );
        const QList<Log>& logs() const;
    
    signals:
        void newLog(const Log& log);
    };
    
    #endif
    

    Logger.cpp

    #include <QReadLocker>
    #include <QWriteLocker>
    #include <QMetaType>
    #include "Logger.hpp"
    
    Logger::Logger()
    {
        qRegisterMetaType<Log>("Log");
    }
    
    Logger& Logger::instance() {
        static Logger logger;
        return logger;
    }
    
    void Logger::log( const Log& log )
    {
        QWriteLocker locker(&lock_);
        logs_.push_back(log);
        emit newLog(log);
    }
    
    const QList<Log>& Logger::logs() const {
        QReadLocker locker(const_cast<QReadWriteLock*>(&lock_));
        return logs_;
    }
    
    void Logger::info( const QString& message ) {
        Logger::instance().log( Log(Log::Info, message) );
    }
    
    void Logger::warning( const QString& message ) {
        Logger::instance().log( Log(Log::Warning, message) );
    }
    
    void Logger::error( const QString& message ) {
        Logger::instance().log( Log(Log::Error, message) );
    }
    

    Win.hpp

    #ifndef WIN_CLASS_H
    #define WIN_CLASS_H
    
    #include <QWidget>
    #include <QTextBrowser>
    #include <QVBoxLayout>
    #include <QHBoxLayout>
    #include <QApplication>
    #include <QPushButton>
    
    #include "Logger.hpp"
    
    class Win : public QWidget
    {
        Q_OBJECT
        QTextEdit* logWindow;
    public:
        Win() {
            logWindow = new QTextBrowser;
            connect(&Logger::instance(), SIGNAL(newLog(const Log&)), SLOT(onNewLog(const Log&)));
    
            QPushButton* infoLog = new QPushButton("Info");
            QPushButton* warnLog = new QPushButton("Warning");
            QPushButton* errorLog = new QPushButton("Error");
            QPushButton* quit = new QPushButton("Quit");
    
            connect(infoLog, SIGNAL(clicked()), SLOT(onInfo()));
            connect(warnLog, SIGNAL(clicked()), SLOT(onWarn()));
            connect(errorLog, SIGNAL(clicked()), SLOT(onError()));
            connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
    
            QHBoxLayout* logLayout = new QHBoxLayout;
            logLayout->addWidget(infoLog);
            logLayout->addWidget(warnLog);
            logLayout->addWidget(errorLog);
            QVBoxLayout* ml = new QVBoxLayout(this);
            ml->addWidget(logWindow);
            ml->addLayout(logLayout);
            ml->addWidget(quit);
        }
    
    public slots:
        void onNewLog(const Log& log) {
            logWindow->append(log.message);
        }
        void onInfo() {
            Logger::info("Button Info");
        }
        void onWarn() {
            Logger::warning("Button Warning");
        }
        void onError() {
            Logger::error("Button Error");
        }
    };
    
    #endif
    

    main.cpp

    #include <QApplication>
    #include <QThread>
    #include <QtCore>
    #include <QtTest>
    
    #include <ctime>
    #include <cstdlib>
    
    #include "Win.hpp"
    #include "Logger.hpp"
    
    void threadLogTest() {
        std::srand(std::time(0));
        while(true) {
            QTest::qSleep(rand()%1000);
            Logger::info("Neues Log");
        }
    }
    
    int main( int argc, char** argv ) {
        QApplication app(argc, argv);
        Win win;
        win.show();
        QtConcurrent::run(&threadLogTest);
        return app.exec();
    }
    

    loggertest.pro

    TEMPLATE = app
    TARGET = 
    DEPENDPATH += .
    INCLUDEPATH += .
    
    QT += testlib
    
    # Input
    HEADERS += Logger.hpp Win.hpp
    SOURCES += Logger.cpp main.cpp
    

    Dass du siehst, wie ich das mit separater Logger-Klasse ohne Gui gemeint hab.



  • Danke das du dir so viel mühe gibst. Hier ist nochmal mein komplettes Packet ( Nur GUI ohne andere Funktionen)

    Hoffe du könntest mir dabei helfen würd dir wenn du PayPal hast auch n bischen was dafür geben. Danke!

    Ps: Der Download link: http://www.farmcrawler.kilu.de/Cppbot/dl/FcMb.rar



  • Du willst also nur ein scheiss Cheattool basteln oder wie muss man das sehen? Die Seite kommt mir mehr als verdächtig vor.
    rya.



  • Es ist nicht nur ein scheiß Cheat Tool so wie du es nennst... Es ändert nichts am ablauf des Spiels. Und der Sever ist nur mein Download sever.



  • l'abra d'or schrieb:

    eine ziemlich coole Logger Grundklasse

    Danke dafür, sowas habe ich gesucht. Ich habe noch

    Logger.hpp

    #include <QtCore/QDebug>
    
    struct Log { 
    // snip coolen code
    };
    QDebug operator<<(QDebug dbg, const Log &l);
    
    class Logger : public QObject {
     // snip coolen code
        ~Logger();
     // snip coolen code
    public:
     // snip coolen code
    };
    

    Logger.cpp

    QDebug operator<<(QDebug dbg, const Log &l)
    {
        if (l.scope == Log::Info)
            dbg.nospace() << QString("(Info)    ").append(l.message);
        else if (l.scope == Log::Warning)
            dbg.nospace() << QString("(Warning) ").append(l.message);
        else if (l.scope == Log::Error)
            dbg.nospace() << QString("(Error)   ").append(l.message);
    
         return dbg.space();
    }
    
    Logger::~Logger()
    {
            if (! logs().isEmpty() )
            qDebug() << logs(); // oder wahlweise QFile Ausgabe :)
    }
    

    dazugepackt 🙂


Anmelden zum Antworten