MFC SDI | Per GDI in Bitmap zeichnen?
-
Hallo Leute,
ich habe ein Problem mit meiner MFC Anwendung, bzw. weiß nicht, wie das Problem angehen soll.
Ich habe mich bereits mit dem GDI der MFC beschäftigt, habe aber noch keine Lösung gefunden.
Folgendes soll erreicht werden: Ein Roboter sendet in einen Endlos-Thread über WLAN seine aktuelle Position (x- und y-Koordinate, sowie Ausrichtung in Grad) im Raum. Eine einfache SDI-Anwendung (ohne Doc/View Modell) soll nun aus den Koordinaten eine Map erstellen. Dazu soll die Wegstrecke, sowie Hindernisse über die Infrarot-Sensoren des Roboters eingezeichnet werden.
Ich hatte mir das so gedacht: Ich erstelle eine Bitmap, in der ich dann die nötigen Pixel über eine Umrechnungskonstante aus den Koordinaten setze. Diese Bitmap wollte ich dann in der OnPaint in meine Anwendung (ChildView) zeichnen und dann ständig neu zeichnen lassen.
Nun zu meinen Fragen:
• Ich denke mal ich brauche ein CBitmap Objekt, richtig?
• Kann ich in dieses Objekt genauso einfach zeichnen, wie in einen DC (MoveTo,LineTo,SetPixel, etc.)? Wenn ja wie?
Dieses Bitmap muss ich dann nun noch in meiner OnPaint Funktion abbilden lassen und die Anwendung dazu bringen, neu zu zeichnen (mit Invalidate() z.B.)?Ich hoffe wirklich auf eure Hilfe, da es sich hier nicht um ein Freizeit Projekt handelt, sondern die Programmierung der Anwendung ein Teil meiner Abiturprüfung darstellt (Besondere Lernleistung), deren Abgabe schon am 26. März ist. Bitte helft mir.
Danke im Voraus. Gruß,
andre0605
-
CImage bietet in Verbindung mit CDC genau dass was du willst.
Hier habe ich schon mal ein Beispiel dazu gezeigt:
http://www.c-plusplus.net/forum/viewtopic-var-p-is-1847125.html#Du könntest eventuell auch mal die Suchfunktion bemühen
-
Entschuldigung.
Der richtige Link ist:
http://www.c-plusplus.net/forum/viewtopic-var-p-is-1847125.html#1847125
-
Hallo Bernhard,
vielen Dank für deine Antwort. Das sieht schon mal vielversprechend aus. Ich werde versuchen, das morgen mal in mein Programm zu intigrieren und gebe dann Rückmeldung. Für heute ist, nach 6 Std. Programmier- und Schreibarbeit, keine Konzentration mehr verfügbar :D.
Du könntest eventuell auch mal die Suchfunktion bemühen
Ja du hast Recht, das hätte ich finden müssen. Bin wahrscheinlich mit den falschen Suchbegriffen an die Sache gegangen.
Danke nochmal.
Gruß, André
-
Hallo,
mit Bernhards Methode funktioniert es. Danke nochmal. Ich hab allerdings noch ein paar Fragen zu der Methode:
Die Größe meines Fensters soll dynamisch sein. Von daher muss ich nach Erstellung des CChildView-Bereiches, die Größe dieses Bereiches ermitteln und außerdem bei einer Größenänderung des CImage anpassen.
Wie und an welcher Stelle mache ich das am besten?
Die Anpassung bei Änderung wahrscheinlich in der OnSize()-Funktion.
Ich bestimme die Fenstergröße bisher so:CRect Rectangle; GetClientRect(Rectangle); int x=Rectangle.Width(); int y=Rectangle.Height();
Gibts da nicht eine leichtere Methode?
Vielen Dank für Eure Mithilfe!
Gruß, andre0605
-
in OnSize() bekommst du doch schon die neue größe in cx und cy geliefert.
-
Wenn du während der Größenänderung dein Bild mitändern willst, verwende zu OnSize zusätzlich OnSizing.
Gelegentlich kommt es zu unschönen "Rändern" im Bild wenn man ein anderes Fenster über deine Anzeige hinwegbewegt.
Desswegen solltest du in OnEraseBkgnd auch die Anzeige malen.Also:
BOOL CMyDlg::OnEraseBkgnd(CDC* pDC) { // Überprüfen ob die CDC verwendet werden kann if(pDC == NULL) { return FALSE; } // Überprüfen ob die CImageklasse bereit zum Malen ist // Wenn cDisplayImg nicht beim Start des Programms initialisiert wird, den Hintergrund in einer else-Bedingung "reinigen" if(!cDisplayImg.IsNull()) { // Das Bild/die Linien auf den Bildbereich klatschen cDisplayImg.Draw(pDC->GetSafeHdc(),0,0); } // Damit verhindert man das Flickern beim Neuzeichnen return TRUE; }
Wenn du nicht umbedingt immer ein gestochen scharfes Bild haben mußt, kannst du auch anstatt das Bild bei einer Größenänderng immer neu zu generieren einfach beim Kopieren des Bildes die Größe des Bildes auf den Anzeigebereich "zurechtzoomen".
Dazu verwendest du am Bessten anstatt cDisplayImg.Draw:
// Damit wird dass Bild schöner, braucht aber ein wenig mehr Rechnerleistung dc.SetStretchBltMode(HALFTONE); // Das Bild/die Linien auf den Bildbereich klatschen und dabei die Größe anpassen StretchBlt(dc.GetSafeHdc(), StartpunktX, StartpunktY, Anzeigebreite, Anzeigehöhe, cDisplayImg.GetDC(), 0, 0, cDisplayImg.GetWidth(), cDisplayImg.GetHeight(), SRCCOPY);
Wenn das Bild nicht verzerrt werden soll, mußt du StartpunktX, StartpunktY, Anzeigebreite und Anzeigehöhe so anpassen, dass das Seitenverhältnis geich mit dem von cDisplayImg ist. (Dabei das Löschen des Hintergrundes nicht vergessen.)
-
Danke für die ausführlichen Antworten
. Ich programmiere das gerade und melde mich dann wenn es geht.
Vielen Dank für die kompetente Hilfe in diesem Forum! Echt super!
Gruß, André
-
Mir ist gerade ein anderes Problem aufgefallen:
void CChildView::OnSize(UINT nType, int cx, int cy) { CWnd ::OnSize(nType, cx, cy); cMapImage.Destroy(); cMapImage.Create(cx,cy,24); }
Ich kann das CImage Objekt nicht in der OnSize erstellen, da es dann ja bei jeder Größenänderung gelöscht und neu erstellt wird. Ich brauche allerdings den aktuellen Stand zum neuzeichnen. In welcher Funktion erstelle ich am besten das Image-Objekt und zeichne es weiß? Es müsste ja eine Funktion sein, die nur einmal zu Beginn aufgerufen wird. OnCreate und PreCreateWindow gehen nicht, da dort die CChildView Größe noch nicht ermittelt werden kann.
Jetzt stehe ich etwas auf dem Schlauch
-
na ich würde mal sagen wenn ein neues Bild erstellt werden soll, wie in jedem anderen Programm wenn Neu (New) im Nenü aufgerufen wird? Oder halt wenn Laden (load) im Menü aufgerufen wird, dann erstellen und das bild rein laden.
Wenn die größe des Bildes verändert wird, was natürlich nichts mit der Anzeige zu tun hat, dann wirst du das bild von einem CImage in das andere Kopieren müssen außer CImage hat eine Methode zur Größenanderung.
-
Wieder mal etwas,auf das ich selber kommen müsste. Entschuldigt, wenn meine Fragen etwas doof rüberkommen, aber ich arbeite noch nicht so lange mit MFC.
Ich habe es jetzt so gelöst, dass ich mir in meiner CChildView eine Funktion initView() geschrieben habe, in der ich das CImage erstelle. Diese Funktion rufe ich dann nach dem Verbinden mit dem Roboter auf.Das mit der Größenänderung und OnSize() versuche ich dann morgen. Danke soweit!
-
Ich habe schon wieder eine Frage, die ich alleine nicht lösen kann:
Ich habe jetzt das CImage Objekt in meiner initView belegt:if (!cMapImage.IsNull()) cMapImage.Destroy(); CRect rect; GetClientRect(rect); cMapImage.Create(rect.Width(),rect.Height(),32); CDC* dc = CDC::FromHandle(cMapImage.GetDC());// DC zum malen holen. CBrush cBrush;// Pinsel deffinieren cBrush.CreateSolidBrush(COLORREF(0xFFFFFF));// Die Farbe Weiß wählen dc->SelectObject(&cBrush); dc->Rectangle(0,0,cMapImage.GetWidth(),cMapImage.GetHeight()); this->Invalidate(); this->UpdateWindow(); cMapImage.ReleaseDC(); return 0;
Nun möchte ich gerne die Mitte des DC Koordinatensystems in die Mitte des CImage legen. Dazu hab ich bereits folgendes versucht:
dc->SetViewportExt(cMapImage.GetWidth(),cMapImage.GetHeight()); dc->SetViewportOrg(cMapImage.GetWidth()/2,cMapImage.GetHeight()/2);
Die SetViewport Funkionen beziehen sich allerdings immer auf den CChildView Bereich, was dann zu Folge hat, dass mein CImage geviertelt wird.
(Siehe Bild).
Wie kann ich nun den Mittelpunkt setzen? Oder geht das mit dieser Methode gar nicht?
Viele Grüße, André
-
Tut mir leid dass ich jetzt erst schreibe.
Aber gestern konnte ich nicht mehr schreiben.Ich würde an deiner Stelle mit einer fixen Größe von CImage arbeiten.
Dass erleichtert die Umrechnung der Daten und man muß das Bild nicht immer wieder neu zeichnen.
Außerdem ist dann nicht dass Problem dass beim Verkleinern des Bildes Daten verliert.Passe einfach die Größe des Bildes mit StretchBlt an die Fenstergröße an.
Ein kleines Beispiel:
//OnDraw/OnPaint bzw. OnEraseBkgnd if (!cMapImage.IsNull()) { // Damit wird dass Bild schöner, braucht aber ein wenig mehr Rechnerleistung dc.SetStretchBltMode(HALFTONE); // Das Bild/die Linien auf den Bildbereich klatschen und dabei die Größe anpassen StretchBlt(dc.GetSafeHdc(), 0, 0, Anzeigebreite, Anzeigehöhe, cMapImage.GetDC(), 0, 0, cMapImage.GetWidth(), cMapImage.GetHeight(), SRCCOPY); cMapImage.ReleaseDC(); }
void CChildView::OnSize(UINT nType, int cx, int cy) { CWnd ::OnSize(nType, cx, cy); Anzeigebreite = cx; Anzeigehöhe = cy; Invalidate(); UpdateWindow(); }
// Diese Funktion wird nur beim Starten des Programms -> Ein Ausführen in OnSize,... ist nicht nötig void CChildView::InitScreen() { if (!cMapImage.IsNull()) { cMapImage.Destroy(); } cMapImage.Create(500,500,24); CDC* dc = CDC::FromHandle(cMapImage.GetDC());// DC zum malen holen. CBrush cBrush;// Pinsel deffinieren cBrush.CreateSolidBrush(COLORREF(0x0000FF));// Die Farbe rot wählen CBrush* cOldBrush = dc->SelectObject(&cBrush); dc->Rectangle(0,0,cMapImage.GetWidth(),cMapImage.GetHeight()); // Aufräumen nicht vergessen dc->SelectObject(cOldBrush) }