'cout' im Fenster ausgeben



  • 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


Log in to reply