wxWidgets Tutorial Part II: Spiel mit mir



  • 1 Vorbemerkungen

    Nachdem ich im ersten Artikel die Grundlagen von wxWidgets behandelt habe, möchte ich in diesem Artikel die Grundlagen etwas vertiefen, und an einer kleinen Beispielanwendung zeigen, was man mit wxWidgets so realisieren kann. Diese Beispielanwendung wird ein Tic-Tac-Toe Spiel sein. Im Gegensatz zum ersten Artikel verwende ich nun jedoch eine neue wxWidgets-Version(2.8.0), welche aber kompatibel zu 2.6 ist, somit sollte sich die Beispielanwendung auch mit einer älteren wxWidgets-Version kompilieren lassen.

    1.1 wxWidgets 2.8.0

    Bevor ich mit dem eigentlichen Tutorial anfange, möchte ich kurz noch auf einige Änderungen und Neuerungen hinweisen, die wxWidgets 2.8.0 mit sich bringt. wxWidgets verfügt nun über eine eigene RichTextCtrl-Library, für die Ein- bzw. Ausgabe von formatiertem Text (Allerdings kann diese Library noch keine Standard rtf-Dateien lesen).
    Auch wurde eine Dockinglibrary in wxWidgets aufgenommen, mit wxAUI (Advanced User Interface) lassen sich Fenster erstellen, welche sich in der Anwendung dann verschieben und an/abdocken lassen. Alle Neuerungen finden sich in diesem Artikel.

    Ein kleiner Hinweis noch an alle MinGW-Benutzer, die bisher die Library mit Msys gebaut haben:
    Die mitgelieferten Builddateien scheinen fehlerhaft zu sein, ich konnte jedenfalls wxWidgets2.8.0 nicht mit Msys kompilieren. Alternativ geht es aber über die Windowskommandozeile, einfach in das Verzeichnis %wxDir%/build/msw wechseln, und mit "mingw32-make.exe -f makefile.gcc" den Buildprozess starten. Wer die Library als Releaseversion bauen will, muss in der config.gcc den Wert BUILD von debug auf release setzen.

    2 Implementierung eines eigenen Steuerelementes

    Beim Erstellen eines eigenen Steuerelementes ist vor allem das Eventhandling wichtig. Man muss in seinem Steuerelement bestimmte Events empfangen können, und auf diese dann entsprechend reagieren. Falls es schon ein Steuerelement gibt, welches einen Teil der Anforderungen abdeckt, empfiehlt es sich von diesem abzuleiten. Wenn dies nicht möglich ist, sollte man sein Steuerelement von wxPanel ableiten, oder falls man ein "scrollbares" Steuerelement will, von wxScrolledWindow.
    Bei der Beispielanwendung ist es so, dass wir ein Steuerelement brauchen, welches als Spielfeld, bzw. Spielstein fungiert.
    Dafür leiten wir von wxPanel ab, und richten für wxEVT_PAINT und wxEVT_LEFT_DOWN eigene Handler ein:

    2.1 Selber zeichnen

    Für den Spielstein ist es wichtig, anhand seines Stati ein X oder ein O zu zeichnen. Wenn man den wxPaintEvent überschreibt, muss man in der OnPaint-Methode einen wxPaintDC anlegen, damit wxWidgets weiß, dass das Fenster neugezeichnet wurde, ansonsten würde wxWidgets weiter Paintevents an das Fenster schicken.

    void FieldPanel::OnPaint(wxPaintEvent& event)
    {
    	wxPaintDC dc(this);
    	dc.DrawRectangle(2,2,GetSize().GetWidth()-2,GetSize().GetHeight()-2);
    	int abstand = 10;
    	if(status == GameLogic::X)
    	{
    		wxPen pen = *wxRED_PEN;
    		pen.SetStyle(wxSOLID);
    		pen.SetWidth(4);
    		dc.SetPen(pen);
    		dc.DrawLine(abstand,abstand,GetSize().GetWidth()-abstand,GetSize().GetHeight()-abstand);
    		dc.DrawLine(GetSize().GetWidth()-abstand,abstand,abstand,GetSize().GetHeight()-abstand);
    	}
    	else if(status == GameLogic::clown:
    	{
    		wxPen pen(wxColour(0,0,250),4);
    		dc.SetPen(pen);
    		int x = GetSize().GetWidth()/2;
    		int y = GetSize().GetHeight()/2;
    		if( x < y )
    			dc.DrawCircle(x,y,x - abstand);
    		else
    			dc.DrawCircle(x,y,y - abstand);
    	}
    }
    

    2.2 Mausevent

    Da nur der Linksklick als Mausevent registriert wurde, reagiert auch das Steuerelement nur auf diesen.
    In diesem Fall muss geprüft werden, ob der aktuelle Status noch den Anfangswert ist, und dann ein neuer Wert gesetzt werden:

    void FieldPanel::OnLeftClick(wxMouseEvent& event)
    {
    	if(status == GameLogic::EMPTY)
    	{
    		status = GameLogic::X;// Der Spieler hat den X Stein.
    		Refresh();// Panel aktualisieren
    	}
    }
    

    Jetzt stellt das Panel zwar den Zug dar, aber außerhalb von dieser Panelinstanz hat niemand etwas von diesen Event wahrgenommen. Wenn wir davon ausgehen, das jeder Spielstein einem Spielfeld liegt, so müsste dieses Spielfeld das Parent des Steuerelementes sein. Man könnte also mittels eines Casts den Parentzeiger auf die Spielfeldklasse casten, und dort dann eine Methode aufrufen. Für den aktuellen Fall würde dies gehen, solange bis das Control eine andere Parentklasse hätte.
    Es wäre also eleganter wenn das Steuerelement einen Event an sein Parentfenster senden könnte.

    2.3 Ein eigener Notifyevent

    Die Implementierung unseres eigenen Events sieht so aus:

    class TurnEvent : public wxNotifyEvent
    {
    	int status;
    	int field;
    public:
    	TurnEvent(int status, int field, wxEventType eventType = wxEVT_NULL, int id = 0);
    	TurnEvent(const TurnEvent& copy);
    	virtual ~TurnEvent();
    	virtual TurnEvent* Clone()const {return new TurnEvent(*this);};
    };
    

    Über die Variablen status und field wird übergeben welches Spielfeld den Event auslöst, und welcher Spielstein dort gesetzt werden soll.
    Damit wxWidgets die Eventklasse auch kennt, und den Event entsprechend weiterleiten kann, sind noch einige Makros nötig:

    // in der TurnEvent.hpp (nach der Klassendeklaration)
    
    // ein Typedef auf die entsprechenden EventHandler
    typedef void(wxEvtHandler::*TurnEventFunction)(TurnEvent&); 
    
    // wxWidgets den neuen Event mitteilen
    BEGIN_DECLARE_EVENT_TYPES()
      DECLARE_EVENT_TYPE(EVT_TURN_CHANGE, -1)
    END_DECLARE_EVENT_TYPES()
    
    // dieses #define sorgt dafür das man den Event über den Makro EventTable einbinden kann
    #define EVT_TURN_CHANGE_MACRO(id, fn) DECLARE_EVENT_TABLE_ENTRY(               \
            EVT_TURN_CHANGE, id, -1, (wxObjectEventFunction)               \
            (wxEventFunction)(TurnEventFunction) & fn,                             \
            (wxObject*) NULL),
    
    // dieses #define sorgt dafür das man den Event auch über Connect verbinden kann
    #define TurnEventHandler(func)                                                 \
            (wxObjectEventFunction)                                                \
            (wxEventFunction)wxStaticCastEvent(TurnEventFunction, &func)
    
    // in der TurnEvent.cpp muss nun 'nur' noch der Event Definiert werden:
    DEFINE_EVENT_TYPE(EVT_TURN_CHANGE)
    

    Nun ist der Event fertig implementiert, und kann nun von FieldPanel in der OnLeftClick Methode gesendet werden:

    void FieldPanel::OnLeftClick(wxMouseEvent& event)
    {
    	if(status == GameLogic::EMPTY)
    	{
    		status = GameLogic::X;
    		Refresh();// Panel aktualisieren
    		TurnEvent myevent(GameLogic::X,id,EVT_TURN_CHANGE);
    		GetParent()->AddPendingEvent(myevent);
    	}
    }
    

    ... und wird in der Klasse GamePanel empfangen:

    // im Konstruktor wird Connect aufgerufen
    Connect(EVT_TURN_CHANGE,TurnEventHandler(GamePanel::OnTurnChange));
    
    // Und in der GamePanel.cpp entsprechend die Event Methode definiert:
    void GamePanel::OnTurnChange(TurnEvent& event)
    {
    	wxMessageBox("TurnChange Event!");
    }
    

    Nun ist unser eigenes Steuerelement fertig.

    3 Das Programm

    Mit Fieldpanel verfügt die Anwendung nun über ein Steuerelement welches als Spielstein fungieren kann. Das Programm hat einen recht einfachen Aufbau, als Basisgerüst dient eine von wxFrame abgeleitete Klasse, welches ja schon aus dem ersten Teil des Tutorials bekannt ist. In unserer Anwendung ist die Klasse MyFrame aber eher ein Nebendarsteller, abgesehen von ihren Standardaufgaben (Exit, Infotext anzeigen, Menuhandling) werden ihr die restlichen Aufgaben von GamePanel abgenommen. Das ist ganz bewusst so gewählt, es bietet einige Vorteile, zum einen kann man später das Aussehen der Applikation leicht verändern, falls man z.B. ein MDI Tic Tac Toe Spiel umsetzen will, oder aber auch die Klasse MyFrame kopieren, und wo anders wieder als Basis für z.b. ein 4 Gewinnt Spiel nutzen.
    Die Klasse GamePanel kümmert sich nun um das eigentliche Spielgeschehen, und empfängt auch die Events unseres Steuerelementes FieldPanel:

    class GamePanel : public wxPanel
    {
    	GameLogic gamelogic;// die eigentliche Spiellogik wurde (teilweise) ausgelagert
    	std::vector<FieldPanel*> panels;// das Spielfeld
    	int freefields;// Hilfsvariable
    	wxStaticText* player1;// Damit es netter aussieht
    	wxStaticText* player2;// Damit es netter aussieht Vol2
    	int player1_won;// Counter zum zählen wer wann gewonnen hat
    	int player2_won;
    	wxString playername;
    protected:
    public:
    	GamePanel(wxWindow* parent, int id);
    	virtual ~GamePanel();
    	void OnTurnChange(TurnEvent& event);// Die Eventmethode
    	void Clear();
    };
    // GamePanel Constructor
    GamePanel::GamePanel(wxWindow* parent, int id): wxPanel(parent,id)
    {
    	wxFlexGridSizer* flexgridsizer= new wxFlexGridSizer(1,2,0,0);
    	flexgridsizer->AddGrowableRow(0);
    	flexgridsizer->AddGrowableCol(0);
    	wxGridSizer *gSizer1;
    	gSizer1 = new wxGridSizer(3, 3, 0, 0);// Spielfeldsizer
    	for(int i =0; i < 9; i++)// füllen des Spielfeldes mit Spielsteinen
    	{
    		panels.push_back(new FieldPanel(this,i));
    		gSizer1->Add(panels.back(), 0, wxEXPAND | wxALL, 5);
    	}
    	flexgridsizer->Add(gSizer1,0, wxEXPAND | wxALL, 5);
    	wxBoxSizer* box= new wxBoxSizer(wxVERTICAL);
    	playername = wxT("Player1");
    	player1_won =0;
    	player2_won=0;
    	// Zum Anzeigen wer wie oft gewonnen hat
    	player1 = new wxStaticText(this,-1,playername + wxString::Format(" wins %i games",player1_won));
    	box->Add(player1,0, wxALL, 5);
    	player2 = new wxStaticText(this,-1,wxString::Format("Computer wins %i games",player2_won));
    	box->Add(player2,0, wxALL, 5);
    	flexgridsizer->Add(box,0, wxALL, 5);
    	this->SetSizer(flexgridsizer);
    	this->SetAutoLayout(true);
    	this->Layout();
    	// Der Event für den nächsten Spielzug
    	Connect(EVT_TURN_CHANGE,TurnEventHandler(GamePanel::OnTurnChange));
    
    	freefields = 9;
    }
    

    Ich habe mich entschlossen die eigentliche Spiellogik nicht in diesem Tutorial zu behandeln, da sie im eigentlichen Sinne nichts mit wxWidgets zu tun hat, dennoch ist sie vollständig in der Beispielanwendung implementiert, den Link zum vollständigen Code der Anwendung befindet sich am Ende des Tutorials.

    Die Anwendung sieht nun so aus:

    4 Ein eigener Dialog

    Wie man auf dem obigen Bild und im GamePanel Konstruktor sieht, ist der Standardname für den Spieler "Player1". Als Programmierer kann man den Namen des Spielers nicht wissen, folglich sollte man dem Spieler eine Möglichkeit geben, diesen zu ändern, ebenfalls kann man in diesem Settingsdialog die Farben von X und O ändern:

    Die Klasse SettingsDlg ist von wxDialog abgeleitet, und hat neben einem Konstruktor jeweils Handler für die X und O Buttons:

    class SettingsDlg : public wxDialog
    {
    	wxColour xcolor;
    	wxColour ocolor;
    	wxButton* btn_xcolor;
    	wxButton* btn_ocolor;
    	// Diese Panel dienen der Darstellung der Farbe
    	wxPanel* xpanel;
    	wxPanel* opanel;
    	wxTextCtrl* txt_name;
    protected:
    public:
    	SettingsDlg(wxString playername, wxWindow* parent, int id);
    	virtual ~SettingsDlg();
    	void OnXColor(wxCommandEvent& event);
    	void OnOColor(wxCommandEvent& event);
    };
    

    Der Konstruktor:

    SettingsDlg::SettingsDlg(wxString playername, wxWindow* parent, int id):
    	wxDialog(parent,wxT("Application Settings"),id), xcolor(FieldPanel::xcolor), ocolor(FieldPanel::ocolor)
    {
    	wxBoxSizer* mainbox = new wxBoxSizer(wxVERTICAL);
    
    	// xcolor Settings
    	wxStaticBoxSizer* xcolorbox = new wxStaticBoxSizer(wxHORIZONTAL,this,wxT("X Colorsettings"));
    
    	xpanel = new wxPanel(this,-1);
    	xpanel->SetBackgroundColour(xcolor);
    	xcolorbox->Add(xpanel,0,wxALL|wxEXPAND,5);
    	btn_xcolor = new wxButton(this,wxNewId(),wxT("Change Color"));
    	xcolorbox->Add(btn_xcolor,0,wxALL,5);
    
    	mainbox->Add(xcolorbox,0,wxALL|wxEXPAND,5);
    
    	// ocolor Settings
    	wxStaticBoxSizer* ocolorbox = new wxStaticBoxSizer(wxHORIZONTAL,this,wxT("O Colorsettings"));
    
    	opanel = new wxPanel(this,-1);
    	opanel->SetBackgroundColour(ocolor);
    	ocolorbox->Add(opanel,0,wxALL|wxEXPAND,5);
    	btn_ocolor = new wxButton(this,wxNewId(),wxT("Change Color"));
    	ocolorbox->Add(btn_ocolor,0,wxALL,5);
    
    	mainbox->Add(ocolorbox,0,wxALL|wxEXPAND,5);
    
    	// name Settings
    	wxBoxSizer* namebox = new wxBoxSizer(wxHORIZONTAL);
    
    	namebox->Add(new wxStaticText(this,-1,wxT("Playername: ")),0,wxALL,5);
    	txt_name = new wxTextCtrl(this,wxNewId(),playername);
    	namebox->Add(txt_name,0,wxALL,5);
    
    	mainbox->Add(namebox,0,wxALL,5);
    	// Ok und Cancel Buttons hinzufügen
    	mainbox->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL),0,wxALL,5);
    	SetSizer(mainbox);
    	SetAutoLayout(true);
    	Layout();
    	Fit();
    
    	// Eventhandler
    	Connect(btn_xcolor->GetId(),wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(SettingsDlg::OnXColor));
    	Connect(btn_ocolor->GetId(),wxEVT_COMMAND_BUTTON_CLICKED,wxCommandEventHandler(SettingsDlg::OnOColor));
    }
    

    4.1 wxWidgets Standarddialoge

    wxWidgets bietet für verschiedene Aufgaben Standarddialoge an (z.B. Datei- oder Verzeichnisauswahl, Passwortabfragen, Textabfragen etc.), welche dann den jeweiligen Standarddialogen auf der jeweiligen Plattform entsprechen. Als Beispiel für Standarddialoge verwende ich aber nun wxColourDialog, da wir ja dem Benutzer ermöglichen wollen für das X und O eigene Farben zu vergeben:

    // direkter Aufruf des Standarddialoges
    void SettingsDlg::OnXColor(wxCommandEvent& event)
    {
    	wxColourData data;
    	data.SetChooseFull(true);
    	// der ColourDialog bietet 16 "Standardfarben" an.
    	for (int i = 0; i < 16; i++)
    	{
    		wxColour colour(i*16, i*16, i*16);
    		data.SetCustomColour(i, colour);
    	}
    	// Die vorausgewählte Farbe, Standard ist Schwarz
    	data.SetColour(xcolor);
    	wxColourDialog dialog(this,&data);
    	if (dialog.ShowModal() == wxID_OK)
    	{
    		wxColourData retData = dialog.GetColourData();
    		xcolor = retData.GetColour();
    		xpanel->SetBackgroundColour(xcolor);
    		xpanel->Refresh();
    	}
    }
    // wxWidgets bietet auch vorgefertigte Funktionen, mit denen man die Standarddialoge aufrufen kann:
    void SettingsDlg::OnOColor(wxCommandEvent& event)
    {
    	wxColour col = wxGetColourFromUser(this, ocolor);
    	// mit col.Ok() wird der Returnwert des Dialoges überprüft
    	if(col.Ok())
    	{
    		ocolor = col;
    		opanel->SetBackgroundColour(ocolor);
    		opanel->Refresh();
    	}
    }
    

    In der Klasse GamePanel wird der Dialog aufgerufen:

    void GamePanel::ShowSettings()
    {
    	SettingsDlg dlg(playername,this,-1);
    	if(dlg.ShowModal() == wxID_OK)
    	{
    		FieldPanel::xcolor = dlg.Getxcolor();
    		FieldPanel::ocolor = dlg.Getocolor();
    		playername = dlg.Getplayername();
    		Refresh();
    	}
    }
    

    5. Dateien speichern und laden

    Jetzt fehlt nur noch dass die Anwendung die Einstellungen auch speichern kann. Man kann problemlos unter wxWidgets die STL nutzen, und mittels <fstream> Dateien schreiben und lesen. wxWidgets bietet jedoch auch eigene Streamklassen an, welche auch Support für Zipstreams oder Sockets bieten.
    Für die Beispielanwendung jedoch reicht ein simpler wxFileStream aus:

    // Die Einstellungen in einer Datei speichern
    void GamePanel::SaveSettings()
    {
    	wxFileOutputStream file("settings");
    	if(file.Ok())
    	{
    		wxTextOutputStream out(file);
    		out << FieldPanel::xcolor.Red() << ' ' << FieldPanel::xcolor.Green() << ' '<< FieldPanel::xcolor.Blue() << endl;
    
    		out << FieldPanel::ocolor.Red() << ' '<< FieldPanel::ocolor.Green() << ' '<< FieldPanel::ocolor.Blue() << endl;
    
    		out << playername;
    	}
    }
    
    // Die Einstellungen aus einer Datei laden
    void GamePanel::LoadSettings()
    {
    	wxFileInputStream file("settings");
    	if(file.Ok())
    	{
    		wxTextInputStream in(file);
    		int r,g,b;
    		in >> r >> g >> b;
    		FieldPanel::xcolor.Set(r,g,b);
    		in >> r >> g >> b;
    		FieldPanel::ocolor.Set(r,g,b);
    		playername = in.ReadLine();
    	}
    }
    

    Den Code der Beispielanwendung findet sich hier.

    Weitere Links zum Thema wxWidgest:
    http://www.wxwidgets.org
    Die offizielle wxWidgets Klassenliste (englisch)
    Das offizielle wxWidgets Forum (englisch)



  • Gute Einführung in das Zeichnen mit wxWidgets. 🙂 👍

    Ist es mit wxWidgets eigentlich irgendwie möglich auf ähnliche Zeichenfunktionen zuzugreifen wie sie beispielsweise GDI+ bietet? Damit meine ich zum Beispiel das Antialiasing von Linien oder Alphablending von Bitmaps.



  • Ich mein es gäbe mittlerweile dafür Klassen, aber ich finds jetzt gerade nicht in der Doku. Gesehen hab ich das aber mal.
    Evtl. wirst du auch auf wxCode fündig. wxCairo gibts glaub ich da auch noch.



  • Hi 🙂

    Erstmal muss ich auch sagen: Gute Einführung 🙂

    Aber was mich interessiert:

    Heißt es tatsächlich:
    "Player X wins 4 games"

    Vielleicht ist mein Sprachgefühl ja wieder kaputt jetzt sogar im Englischen 😃

    Aber ich würde schreiben: Player X won 4 games

    Was ist nun richtig?

    Danke im Voraus

    Gruß Patrick



  • Ich würde sagen beides 😉
    Das eine ist halt die Vergangenheitsform, wobei ich noch nicht so lange gespielt habe, das sie sinnvoll wäre 😉



  • [klugscheiß]Ich würde eher sagen "Player has won 4 games" da er die Spiele zwar schon gewonnen hat, aber derzeit noch spielt.[/klugscheiß]

    Zum Tutorial selbst: Gut struktiert und auch der Code ist gut lesbar, auch für diejenigen die wxWidgets nicht kenne. 👍

    lg, bloodycross



  • This post is deleted!


  • Klingt nach einem Linkerfehler. Sind die Dateien auch korrekt im Projekt eingetragen?



  • This post is deleted!


  • Also ich hab gerade das Zipfile im Original runter geladen, und das kompiliert hier ohne Probleme.



  • This post is deleted!


  • Ich verstehe grade nicht so ganz wie ich das Programm compilieren soll (also den runtergeladenen Beispielcode) steh da total auf dem Schlauch.

    Ich benutzte Linux (Ubuntu Intrepid) und den g++ compiler.
    tree in meinem tictactoe ordner gibt:
    .
    |-- Game
    | |-- FieldPanel.cpp
    | |-- FieldPanel.hpp
    | |-- GameLogic.cpp
    | |-- GameLogic.hpp
    | |-- GamePanel.cpp
    | |-- GamePanel.hpp
    | |-- SettingsDlg.cpp
    | \-\- SettingsDlg.hpp |\-\- TurnEvent.cpp |\-\- TurnEvent.hpp |\-\- app.cpp |\-\- app.h |\-\- main.cpp |\-\- main.h |\-\- settings |\-\- tictactoe.cbp |\-\- tictactoe.depend |\-\- tictactoe.layout-- wx_pch.h

    Sind also alle Dateien da.
    Compilieren tue ich mit:
    g++ main.cpp `wx-config --libs` `wx-config --cxxflags` -o prog

    aber er sagt er könne die GamePanel.hpp nicht finden, obwohl sie doch da ist??
    was ist falsch?



  • Vielleicht müssen sich alle Datein im selben Ordner befinden?



  • Also entweder ist ein Fehler im Programm selbst was ich mir allerdings bei einem Tutorial nicht vorstellen möchte oder es liegt an dem folgenden Fehler...

    Codeblocks couldnt parse wxs file because wxsmith is disabled...

    Nach diesem Fehler hab ich mich jetzt schon durch etliche Foren gekämpft aber nichts gefunden. Es wäre schön wenn von euch vll einer Rat wüsste achja die Fehler post ich auch mal

    ||=== TICTACTOE, Debug ===|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp||In member function 'void Game::FieldPanel::OnPaint(wxPaintEvent&)':|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|21|error: 'wxPaintDC' was not declared in this scope|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|21|error: expected ';' before 'dc'|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|22|error: 'dc' was not declared in this scope|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|26|error: variable 'wxPen pen' has initializer but incomplete type|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|35|error: variable 'wxPen pen' has initializer but incomplete type|
    ||=== Build finished: 5 errors, 0 warnings ===|

    Muss dazu sagen das ich noch nicht allzuviel Erfahrung mit wxwidgets habe da wir das Thema gerade begonnen haben. Aber auf diesen Fehler weiss unserer Programmierlehrer auch keinen Rat.

    Mit freundlichen Grüßen
    Tollmer



  • Konnte es auch problemlos kompilieren. Sieht gut aus! 🙂



  • Tollmer schrieb:

    Also entweder ist ein Fehler im Programm selbst was ich mir allerdings bei einem Tutorial nicht vorstellen möchte oder es liegt an dem folgenden Fehler...

    Codeblocks couldnt parse wxs file because wxsmith is disabled...

    Nach diesem Fehler hab ich mich jetzt schon durch etliche Foren gekämpft aber nichts gefunden. Es wäre schön wenn von euch vll einer Rat wüsste achja die Fehler post ich auch mal

    ||=== TICTACTOE, Debug ===|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp||In member function 'void Game::FieldPanel::OnPaint(wxPaintEvent&)':|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|21|error: 'wxPaintDC' was not declared in this scope|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|21|error: expected ';' before 'dc'|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|22|error: 'dc' was not declared in this scope|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|26|error: variable 'wxPen pen' has initializer but incomplete type|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|35|error: variable 'wxPen pen' has initializer but incomplete type|
    ||=== Build finished: 5 errors, 0 warnings ===|

    Muss dazu sagen das ich noch nicht allzuviel Erfahrung mit wxwidgets habe da wir das Thema gerade begonnen haben. Aber auf diesen Fehler weiss unserer Programmierlehrer auch keinen Rat.

    Also das sieht nach einem Problem mit den Pfaden aus.
    Überprüfe doch bitte ob deine Pfade zu wx korrekt sind.



  • Heyho leute also mit dem Pfaden müsste das ja eig stimmen weil einfache Proejekte funktionieren ja auch.Das Testprojekt von wxSmith funktioniert übrigens auch.Das ist iwie komisch^^

    aber dennohc einmal die Pfade die ich eingestellt habe:

    Settings->Compiler und Debuggersettings->Linker Settings->C:\Program Files\CodeBlocks\wxWidgets2.8\lib\gcc_dll

    Settings->Compiler und Debuggersettings->Search Directories->C:\Program Files\CodeBlocks\wxWidgets2.8\lib\gcc_dll

    Settings->Compiler und Debuggersettings->Toolchain Executables->Additional Paths->C:\Program Files\CodeBlocks\wxWidgets2.8\lib\gcc_dll

    Mit freundlichen Grüßen
    Tollmer



  • Und das Include Verzeichnis?



  • Also entweder ist ein Fehler im Programm selbst was ich mir allerdings bei einem Tutorial nicht vorstellen möchte oder es liegt an dem folgenden Fehler...

    Codeblocks couldnt parse wxs file because wxsmith is disabled...

    Nach diesem Fehler hab ich mich jetzt schon durch etliche Foren gekämpft aber nichts gefunden. Es wäre schön wenn von euch vll einer Rat wüsste achja die Fehler post ich auch mal

    ||=== TICTACTOE, Debug ===|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp||In member function 'void Game::FieldPanel::OnPaint(wxPaintEvent&)':|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|21|error: 'wxPaintDC' was not declared in this scope|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|21|error: expected ';' before 'dc'|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|22|error: 'dc' was not declared in this scope|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|26|error: variable 'wxPen pen' has initializer but incomplete type|
    C:\Users\Marcel\Desktop\Schule\Programmieren\WxWidgets\tictactoe\Game\FieldPanel.cpp|35|error: variable 'wxPen pen' has initializer but incomplete type|
    ||=== Build finished: 5 errors, 0 warnings ===|

    Muss dazu sagen das ich noch nicht allzuviel Erfahrung mit wxwidgets habe da wir das Thema gerade begonnen haben. Aber auf diesen Fehler weiss unserer Programmierlehrer auch keinen Rat.

    Mit freundlichen Grüßen
    Tollmer

    Hab mit wxDevC++ die gleiche Fehlermeldungen gehabt.
    Folgende Nachtrag in FieldPanel.cpp hat das problem gelöscht.

    .....
    #include "../TurnEvent.hpp"
    #include <wx/wx.h>
    namespace Game
    .......

    Gruß
    Jordan



  • Ich hab n kleines Problem
    Ih kompiliere zwar mit der 2.8er , aber mein minGW wirft folgendes raus

    ld.exe||cannot find -lwx_msw_core-2.6|

    Da ich aber keine Ahnung hab, warum er die 2.6er benutzen will , obwohl ich 2.8 eingestellt hab ist mir das fragwürdig.

    Herzliche Grüße
    nt0r

    PS: C++0x weg hilft vorallem bei "strdup" einiges 😉

    EDIT: Hat sich... mehr oder weniger... erledigt. Hab einfach neues Projekt mit 2.8 gemacht und alles reingezogen.



  • GreenDelta schrieb:

    Ich verstehe grade nicht so ganz wie ich das Programm compilieren soll (also den runtergeladenen Beispielcode) steh da total auf dem Schlauch.

    Ich benutzte Linux (Ubuntu Intrepid) und den g++ compiler.
    tree in meinem tictactoe ordner gibt:
    .
    |-- Game
    | |-- FieldPanel.cpp
    | |-- FieldPanel.hpp
    | |-- GameLogic.cpp
    | |-- GameLogic.hpp
    | |-- GamePanel.cpp
    | |-- GamePanel.hpp
    | |-- SettingsDlg.cpp
    | \-\- SettingsDlg.hpp |\-\- TurnEvent.cpp |\-\- TurnEvent.hpp |\-\- app.cpp |\-\- app.h |\-\- main.cpp |\-\- main.h |\-\- settings |\-\- tictactoe.cbp |\-\- tictactoe.depend |\-\- tictactoe.layout-- wx_pch.h

    Sind also alle Dateien da.
    Compilieren tue ich mit:
    g++ main.cpp `wx-config --libs` `wx-config --cxxflags` -o prog

    aber er sagt er könne die GamePanel.hpp nicht finden, obwohl sie doch da ist??
    was ist falsch?

    Nur damit andere bescheid wissen die das gleiche Problem mit dem g++ haben: der g++ benötigt ALLE datein des Projektes. das heist jede Datei die mit cpp endet und zum Projekt gehört muss als Quelle angegeben werden. Da das ein sehr langer Befehl wäre gibt es ja auch makefiles....


Log in to reply