wxWidgets Nicht alles wird auf wxPaintDC gezeichnet
-
Ich wollte mal versuchen Conways Game of Life (https://de.wikipedia.org/wiki/Conways_Spiel_des_Lebens) in C++ mit Hilfe von wxWigets zu implementieren. Nun bin ich gerade so weit , dass ich meine Zellen zufällig sage ob sie leben oder nicht.Dies funktioniert soweit auch.Aber sobald ich versuche die Zellen zu zeichnen , wird immer nur die erste Zeile gezeichnet,alle anderen nicht.Außerdem sind immer dieselben Zellen lebendig bzw. tot(Das dürfte ja auch nicht sein).
Hier noch mal ein Bild was rauskommt: http://img5.fotos-hochladen.net/uploads/bildschirmfotonxbdwgqou2.png
Hier mal mein Code:
main.h:
#include <wx/wx.h> class MyApp : public wxApp { public: virtual bool OnInit(); };
main.cpp:
#include "main.h" #include "frame.h" IMPLEMENT_APP(MyApp) bool MyApp::OnInit(){ Frame *frame = new Frame(wxT("Conways Game of Life C++ wxwidgets")); frame->Show(true); return true; }
frame.h:
#include <wx/wx.h> class Frame : public wxFrame { public: Frame(const wxString& title); void OnPaint(wxPaintEvent& event); }; class Cell { private: int x,y; bool life; int size; public: Cell(); Cell(int x_c,int y_c); void setLife(bool a); bool getLife(); void print(wxPaintDC* dc); }; class Simulation{ public: int width; int height; Simulation(wxPaintDC* dc); };
und die frame.cpp :
#include "frame.h" #include <stdlib.h> #include <time.h> #include <vector> #include <cstddef> #include <iostream> bool randBool(){ if(rand()%2==1) return false; else return true; } Frame::Frame(const wxString& title) : wxFrame(NULL,-1,title,wxDefaultPosition,wxSize(1000,1000)) { Connect(wxEVT_PAINT,wxPaintEventHandler(Frame::OnPaint)); srand(time(NULL)); Centre(); } Cell::Cell(){ size=5; } Cell::Cell(int x_c,int y_c){ x=x_c; y=y_c; size =5; } void Cell::setLife(bool a){ life=a; } bool Cell::getLife(){ return life; } void Cell::print(wxPaintDC* dc){ if(life){ wxCoord xc,yc; xc=x; yc=y; dc->DrawRectangle(xc*size,yc*size,size,size); } } Simulation::Simulation(wxPaintDC* dc) { width=200; height=200; Cell** cells = new Cell*[width]; for (int i = 0; i < width; i++) { cells[i] = new Cell[height]; } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { cells[x][y].setLife(randBool()); //std::cout<<cells[x][y].getLife()<<std::flush; cells[x][y].print(dc); } } /* for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { cells[x][y].print(dc); } } */ for (int j = 0; j < width; j++) { delete[] cells[j]; } delete[] cells; } void Frame::OnPaint(wxPaintEvent& event){ wxPaintDC dc(this); dc.SetBrush(*wxBLACK_BRUSH); wxSize sz = GetClientSize(); Simulation sim(&dc); }
Compliert wird beim mir, unter Linux (Ubuntu 16.4) mit:
g++ main.cpp main.h frame.cpp frame.h -o frame `wx-config --cflags --libs` -std=c++14
Schon mal vielen Dank
Lg Mofiche
-
Du rufst doch nirgendwo den Cell(int x_c,int y_c)-Konstruktor auf. Lösche mal den Standardkonstruktor Cell() und setze dann die passenden Parameter beim Aufruf.
Welche IDE verwendest du? Nutze am besten den Debugger...
-
Ja danke, jetzt funktioniert es.Is ja klar das ich die Zelle erst mal initialisieren muss.IDE hab ich keine ,ich nehm Sublime Text als Editor und compiliere über die Shell
-
Ohne Debugger wirst du aber beim Entwickeln ziemlich schnell frustriert sein (denn gerade das Arbeiten mit externen Frameworks und Libs erzeugt oftmals ungewolltes Verhalten, dem man schnell auf die Spur kommen will).
-
Auf deinen Ratschlag hin habe ich mir mal Code::Blocks herbeigezogen.
So jz gib es nen weiteres Problem:
Die Simulation is eigentlich fertig programmiert.Aber so bald ich sie starte wird mir ein weißer Bildschirm angezeigt und das Programm stürzt nach einer Weile ab.Ich habe mal aus Debug-Gründen anstatt einer while(true)-Schleife erst mal eine 10-Iterationen lange for-Schleife genutzt.Da ist es am Anfang weiß und danach , so als währen die anderen Iterationen durchgelaufen.Die einzelnen Änderungen hab ich aber nicht gesehen.Außerdem kommt es mir vor,dass die Zellen kleiner geworden sind.Kann es sein,dass die while-Schleife das Programm zum Absturz bringt?
Ach ja , nicht von ein paar ungenutzten Funktionen verwundern lassen.(Nur zum Test)
frame.h
#include <wx/wx.h> class Frame : public wxFrame { public: Frame(const wxString& title); void OnPaint(wxPaintEvent& event); }; class Cell { private: int x,y; bool life; int size; bool nextround; public: Cell(); Cell(int x_c,int y_c); void setLife(bool a); bool getLife(); void setNextRound(bool a); void NextRound(); void print(wxPaintDC* dc); }; class Simulation{ public: int width = 200; int height = 200; Cell** cells = new Cell*[width]; Simulation(); void draw(wxPaintDC* dc); void drawI(wxPaintDC* dc); void update(); ~Simulation(); };
frame.cpp
#include "frame.h" #include <stdlib.h> #include <stdio.h> #include <time.h> #include <vector> #include <cstddef> #include <iostream> #include <unistd.h> bool randBool(){ if(rand()%2==1) return false; else return true; } bool invert(bool a){ return !a; } Frame::Frame(const wxString& title) : wxFrame(NULL,-1,title,wxDefaultPosition,wxSize(1000,1000)) { Connect(wxEVT_PAINT,wxPaintEventHandler(Frame::OnPaint)); srand(time(NULL)); Centre(); } Cell::Cell(){ size=5; } Cell::Cell(int x_c,int y_c){ x=x_c; y=y_c; size =5; } void Cell::setLife(bool a){ life=a; } void Cell::NextRound(){ life=nextround; } void Cell::setNextRound(bool a){ nextround=a; } bool Cell::getLife(){ return life; } void Cell::print(wxPaintDC* dc){ wxCoord xc,yc; xc=x; yc=y; if(life){ dc->SetBrush(*wxBLACK_BRUSH); dc->DrawRectangle(xc*size,yc*size,size,size); }else{ dc->SetBrush(*wxWHITE_BRUSH); dc->DrawRectangle(xc*size,yc*size,size,size); } } Simulation::Simulation() { for (int i = 0; i < width; i++) { cells[i] = new Cell[height]; } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { cells[x][y]= Cell(x,y); cells[x][y].setLife(randBool()); } } } void Simulation::draw(wxPaintDC* dc){ for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { cells[x][y].setLife(randBool()); cells[x][y].print(dc); } } } void Simulation::drawI(wxPaintDC* dc){ for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { bool a = cells[x][y].getLife(); bool b = invert(a); cells[x][y].setLife(b); cells[x][y].print(dc); } } } void Simulation::update(){ for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int mx =x-1; if(mx<0) mx=width-1; int my =y-1; if(my<0) my=height-1; int gx = (x+1)%width; int gy =(y+1)%height; int alivecounter =0; if(cells[mx][my].getLife()) alivecounter++; if(cells[mx][y].getLife()) alivecounter++; if(cells[mx][gy].getLife()) alivecounter++; if(cells[x][my].getLife()) alivecounter++; if(cells[x][gy].getLife()) alivecounter++; if(cells[gx][my].getLife()) alivecounter++; if(cells[gx][y].getLife()) alivecounter++; if(cells[gx][gy].getLife()) alivecounter++; if(alivecounter<2||alivecounter>3) cells[x][y].setNextRound(false); else if(alivecounter == 2) cells[x][y].setNextRound(cells[x][y].getLife()); } } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { cells[x][y].NextRound(); } } } Simulation::~Simulation(){ for (int j = 0; j < width; j++) { delete[] cells[j]; } delete[] cells; } void Frame::OnPaint(wxPaintEvent& event){ wxPaintDC dc(this); dc.SetBrush(*wxBLACK_BRUSH); wxSize sz = GetClientSize(); Simulation sim; sim.draw(&dc); for(int i=0;i<10;i++){ sim.update(); sim.draw(&dc); } }
-
Puh, da fehlen noch ein paar Grundlagen...
Im OnPaint-Ereignis darfst du keine Logik ausführen (und erst recht nicht eine Endlosschleife bzw. Endlosrekursion) - dort wird nur gezeichnet, und danach dann wieder in die Message-Loop des Frameworks gesprungen, um andere Nachrichten (Maus, Tastatur, andere Elemente zeichnen, ...) zu verarbeiten.
Die Simulation mußt du von außerhalb starten (z.B. von der main-Funktion oder aber bei Button-Click etc.).
-
Ok danke.Ja stimmt bin noch relativ neu in der GUI Programmierung
-
Ich bin grad dabei das Programm abzuändern.Hast du zufällig eine Idee wie ich das wxPaintDC Object in die Frame-Klasse bekomme?Als Rückgabetyp der OnPaint() kann ich dc(das wxPaintDC Objekt) ja auch nicht nehmen.
-
Gib dem Frame einen Zeiger (oder noch besser eine Referenz) auf das Simulationsobjekt mit und rufe dann vom Frame::OnPaint die draw-Funktion der Simulation auf.
Außerhalb des OnPaint benötigt man normalerweise das PaintDC-Objekt nicht (außer so wie du es richtigerweise gemacht hast, um es an die draw-Funktionen der Unterobjekt weiterzuleiten).