'cout' im Fenster ausgeben



  • Hi, 🙂
    weiß jemand, ob es eine Funktion gibt, welche die Zeilen, die in einer Anwendung mit 'cout' ausgegeben werden, in einem Fenster (z.B. in einem ListBox oder in EditFeld) ausgibt?


    Anmelden zum Antworten
     


  • Ansi C++ kennt keine Listboxen und ähnliches, bietet dir aber einiges an Handlungsfreiheit, wohin du Stream-Ausgaben schicken willst - in Form von Stream-Buffern. Da mußt du nur einen Stream-Buffer schreiben, der dein Steuerelement als Ziel verwendet (schau dir die vorhandenen basic_streambuf-Kinder an, um auf den Geschmack zu kommen) - und cout mittels rdbuf() auf diesen umbiegen.





  • Noch_Student schrieb:

    Soll ich für den Stream-Buffer eine Klasse shreiben?

    Ja, genau darauf wollte ich hinaus.



  • Damit bin ich überfordert... 😞



  • ... hoffentlich nicht auch noch mit der englischen Sprache:

    http://www.williamwilling.com/blog/?p=74



  • Da die Aufnahme einer neuen Zeile in einem CListBox-Objekt nur ein einfacher AddString ist, ist so ein streambuf für eine CListBox eine einfache Sache - vorausgesetzt, man weis wie das überhaupt geht. Ich habe sowas vor Urzeiten mal gemacht.

    // File ListBoxStreambuf.h
    #pragma once
    // Bem.: erfordert ein #include <stdafx.h> (MFC) vor diesem include
    #include <streambuf>
    #include <iostream>
    #include <string>
    #include <vector>
    
    namespace listboxsb_detail
    {
        // --   hier werden die Zeichen-Typ-Konvertierungen erledigt
        template< typename E, typename Traits >
        int addString( CListBox& box, const std::basic_string< E, Traits >& line, E, std::basic_streambuf< E, Traits >* )
        {
            // keine Konvertierung E == TCHAR
            return box.AddString( line.c_str() );
        }
    
        template< typename Traits >
        int addString( CListBox& box, const std::basic_string< char, Traits >& line, wchar_t, std::streambuf* sb )
        {
            // Konvertierung von char -> wchar_t
            std::vector< wchar_t > wstr( line.size() + 1, wchar_t(0) );
            const std::ctype< wchar_t >& ctype_ = std::use_facet< std::ctype< wchar_t > >( sb->getloc() );
            const char* in = line.c_str();
            ctype_.widen( in, in + line.size(), &wstr[0] );
            return box.AddString( &wstr[0] );
        }
    }
    
    template< typename E, typename Traits = std::char_traits< E > >
    class basic_ListBoxStreambuf : public std::basic_streambuf< E, Traits >
    {
    public:
        explicit basic_ListBoxStreambuf( CListBox& box )
            : m_box( box )
            , m_line()
            , m_index( -1 )
        {}
    
        int ListBoxIndex() const { return m_index; }
    
    protected:
        virtual int_type overflow( int_type m = Traits::eof() )
        {
            if( Traits::eq_int_type( m, Traits::eof() ) )
                return Traits::not_eof( m );
            const E c = Traits::to_char_type( m ); 
            if( c == E('\n') )      // LF: Zeilenende
            {
                // --   neue Zeile anhängen
                m_index = listboxsb_detail::addString( m_box, m_line, TCHAR(), this );
                if( m_index < 0 )
                    return Traits::eof();   // Fehler Ausgabe ging schief
                m_line = std::basic_string< E, Traits >(); // m_line.clear();
            }
            else
                m_line.append( 1, c );  // Zeichen für später merken
            return m;
        }
    
    private:
        CListBox& m_box;
        std::basic_string< E, Traits > m_line;
        int m_index;    // current Index of the added line
    };
    
    template< typename E, typename Traits = std::char_traits< E > >
    class basic_ListBoxStream : public std::basic_ostream< E, Traits >
    {
        typedef std::basic_ostream< E, Traits > base_type;
    public:
        explicit basic_ListBoxStream( CListBox& box )
            : base_type( &m_sb )
            , m_sb( box )
        {}
    private:
        basic_ListBoxStreambuf< E, Traits > m_sb;
    };
    
    typedef basic_ListBoxStreambuf< char > ListBoxStreambuf;
    typedef basic_ListBoxStream< char > ListBoxStream;
    

    Damit man das bequem integrieren kann habe ich noch ein Helferlein zum Umschalten der streambufs gebastelt.

    // File IosSwitch.h
    #pragma once
    #include <ios>
    
    class IosSwitch  
    {
    public:
    	IosSwitch()
            : m_ios(0)
            , m_sbMerk(0)
        {}
    
    	IosSwitch( std::basic_ios< char >& ios, std::streambuf* sb )
            : m_ios( &ios )
            , m_sbMerk( ios.rdbuf( sb ) )
        {}
    
    	~IosSwitch()
        {
            release();
        }
    
        void set( std::basic_ios< char >& ios, std::streambuf* sb )
        {
            release();
    
            m_ios = &ios;
            m_sbMerk = ios.rdbuf( sb );
        }
    
    private:
        void release()
        {
            if( m_ios )
            {
                delete m_ios->rdbuf( m_sbMerk );
            }
        }
    
        // ---  Members
        std::basic_ios< char >* m_ios;
        std::streambuf*         m_sbMerk;
    
        // ---  nicht kopierbar
        IosSwitch( const IosSwitch& );
        IosSwitch& operator=( const IosSwitch& );
    };
    

    Jetzt brauchst Du nur noch in Deiner View- oder Dialogklasse einen Member von IosSwitch aufzunehmen:

    private:
        IosSwitch m_coutSwitch;  // erfordert include "IosSwitch.h"
    

    und bei der Initialisierung oder InitDialog auf den ListBoxStreambuf umschalten

    BOOL DeinDialog::OnInitDialog()
    {
        CDialog::OnInitDialog();
        // ... usw.
        m_coutSwitch.set( std::cout, new ListBoxStreambuf( m_listBox ) );
        return TRUE;
    }
    

    das erfordert natürlich den CListBox-Member 'm_listBox' und die includes "ListBoxStreambuf.h" und <iostream>. Anschließend landen alle Ausgaben auf cout in dieser ListBox.
    Noch ein Tipp: setze das Sort-Flag der ListBox auf FALSE.

    jencas schrieb:

    http://www.williamwilling.com/blog/?p=74

    Das beschreibt das Umschalten von cout auf eine Konsole, nicht in ein MFC-Element.

    Gruß
    Werner



  • Werner Salomon schrieb:

    jencas schrieb:

    http://www.williamwilling.com/blog/?p=74

    Das beschreibt das Umschalten von cout auf eine Konsole, nicht in ein MFC-Element.

    Wo er Recht hat, hat er Recht. Mea culpa, mea maxima culpa 😉



  • Hallo Werner Salomon, 🙂
    alles, du vorgeschlagen hast, funktioniert gut! Super Danke! 👍 👍
    Kannst du evtl. noch was empfehlen:
    alle meine vorherige "\t" werden jetzt als Quadrate angezeigt. Kann man das irgendwie vermeiden?
    Und noch eine Frage: die horizontale Bildlaufleiste erscheint nicht im ListBox, Einträge werden abgeschnitten.

    Übrigens kann man alles auf eine Konsole so ausgeben:

    AllocConsole();
    freopen("conout$", "w", stdout);
    

    Habe selber probiert.



  • Noch_Student schrieb:

    Kannst du evtl. noch was empfehlen:
    alle meine vorherige "\t" werden jetzt als Quadrate angezeigt. Kann man das irgendwie vermeiden?

    Ja - das ist ein nicht darstellbares Steuerzeichen. Das kann man aber auch bei den Resources der ListBox einstellen: 'Use Tabstops = TRUE'.

    Falls Du Sonderwünsche hast, kann man es auch selber implementieren. Füge dazu in der Klasse basic_ListBoxStreambuf ab der Zeile 59 folgenden oder einen ähnlichen Code ein:

    else if( c == E('\t') ) // TAB: Tabulator
            {
                const unsigned TABSIZE = 8; // oder ein anderer Wert, je nach dem wo der Tabulator hin soll
                m_line.append( TABSIZE - m_line.size() % TABSIZE , ' ' );
            }
    

    so richtig 'gut' sieht das aber erst aus, wenn Du eine nicht-proportionale Schrift (z.B. Courier) als Font für die ListBox wählst.

    Noch_Student schrieb:

    Und noch eine Frage: die horizontale Bildlaufleiste erscheint nicht im ListBox, Einträge werden abgeschnitten.

    In den Resources der ListBox 'Horizontal Scroll = TRUE' einstellen (funktioniert aber bei mir nicht!? 😕 ).
    @Edit: Ah - ein MFC-Problem siehe hier. Den dort vorgeschlagenen Aufruf könnte man auch schön in den ListBoxStreambuf aufnehmen.

    Gruß
    Werner



  • Wieder 👍 👍 👍 👍 👍 👍
    Danke!



  • Ist alles geklärt? Ich würde den Beitrag gern in die FAQ schieben. 🙂



  • estartu schrieb:

    Ist alles geklärt? Ich würde den Beitrag gern in die FAQ schieben. 🙂

    Oh - welche Ehre 🕶

    Bleibt vielleicht noch das Problem mit dem horizontal Scrolling in der Listbox. Ich habe mal etwas experimentiert und habe als Lösung folgende Funktion 'addString' geschrieben:

    struct DC
        {
            explicit DC( CWnd& wnd ) 
                : m_wnd( wnd ), m_dc( wnd.GetDC() ) {}
            ~DC()
            {
                m_wnd.ReleaseDC( m_dc );
            }
            CDC* operator->() { return m_dc; }
        private:
            CWnd& m_wnd;
            CDC* m_dc;
            DC( const DC& );        // Kopieren verhindern
            DC& operator=( const DC& );
        };
        // --   fügt den Text 'txt' der Listbox 'box' hinzu und passt ggf. das 'horizontal extend' an,
        //      so dass das horizontale Scrolling korrekt funktioniert.
        int addString( CListBox& box, const TCHAR* txt )
        {
            const int Rand = 4;     // rechter Rand in Pixeln
            const int ret = box.AddString( txt );
            DC dc( box );           // Wrapper sorgt für CWnd::ReleaseDC (s.o.)
            dc->SelectObject( box.GetFont() ); // ist notwendig, im Gegensatz zum Beispiel in der Hife zu CListBox::GetHorizontalExtent!
            const CSize sz = dc->GetTextExtent( txt );
            if( sz.cx + Rand > box.GetHorizontalExtent() )
            {
                box.SetHorizontalExtent( sz.cx + Rand );
            }
            return ret;
        }
    

    die ist im Listing des Files ListBoxStreambuf.h ab der Zeile 12 einzufügen. Die Aufrufe der Methode CListBox::AddString in den Zeilen 17 und 28 sind entsprechend anzupassen:

    return addString( box, line.c_str() ); // vorher Zeile 17
    

    und

    return addString( box, &wstr[0] ); // vorher Zeile 28
    

    sonst ist - denke ich - wohl alles klar.
    Dieser Thread über das Umleiten der Ausgabe von cout ist auch noch hilfreich.

    Gruß
    Werner


Anmelden zum Antworten