DrawLine

Plattformunabhängige GUIs mit wxWidgets.
tomate
User
Beiträge: 48
Registriert: Sonntag 5. August 2007, 12:07

Donnerstag 30. August 2007, 17:17

Moin!
Warum macht es keinen Unterschied ob ich

Code: Alles auswählen

    def DrawLine(self, event):
        dc = wx.ClientDC(self)
	dc.DrawLine(100, 200, 500, 200)
	dc.DrawLine(250, 200, 250, 300)
Warum sieht die Ausgabe ungefähr so aus:

______________________
|
|

Sollte dc.DrawLine(250, 200, 250, 300) nicht einen Strich erzeugen
der von y-wert 200 zu y-wert 300 geht und somit nach oben zeigt?
alan
User
Beiträge: 81
Registriert: Dienstag 10. April 2007, 11:30

Donnerstag 30. August 2007, 18:06

Hallo!

Poste doch deinen Code doch mal so, dass man das auch selber ausprobieren kann, ohne lange herumzubasteln. :-)

Aber eigentlich müsste die Ausgabe ungefähr so aussehen

Code: Alles auswählen

            |
            |
            |
            |
------------------------------------
(Edit: Im kartesischen Koordinatensystem :shock:)
Zuletzt geändert von alan am Donnerstag 30. August 2007, 18:27, insgesamt 1-mal geändert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 30. August 2007, 18:21

Hallo!

Das Koordinatensystem in wxPython beginnt links oben und setzt sich nach rechts unten fort.

Code: Alles auswählen

_____________
|+ (0,0)
| + (1,1)
|    + (4,2)
|          + (10,3)
|
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
tomate
User
Beiträge: 48
Registriert: Sonntag 5. August 2007, 12:07

Donnerstag 30. August 2007, 18:38

Danke!
Bzw gibt es irgendeinen Trick wie ich Koordinaten aus dem kartesischen Koordinatensystem wxPython geeignet machen kann?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 30. August 2007, 18:49

Ich weiß jetzt auch nicht, warum ich das jetzt geschrieben habe: :K

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import wx

wx.SetDefaultPyEncoding("iso-8859-15")


class MyFrame(wx.Frame):
    
    def __init__(
        self, parent = None, title = "Example", size = wx.Size(550, 420)
    ):
        wx.Frame.__init__(self, parent, -1, title, size = size)
        
        panel = wx.Panel(self)
        panel.Bind(wx.EVT_PAINT, self.on_panel_paint)
        self.panel = panel
    
    
    def on_panel_paint(self, event = None):
        dc = wx.PaintDC(self.panel)
        
        # Hintergrund gelb ausmalen
        dc.SetBackground(wx.Brush("white", wx.SOLID))
        dc.Clear()
        
        # GraphicsContext erstellen (damit sehen die Bilder besser aus)
        gc = wx.GraphicsContext_Create(dc)
        assert isinstance(gc, wx.GraphicsContext) # =Helfer für Wing IDE (kann man weg lassen)
        
        # Blauen Stift zuweisen
        pen = wx.Pen(colour = "blue", width = 2, style = wx.SOLID)
        gc.SetPen(pen)
        
        # Kreis zeichnen
        gc.DrawEllipse(x = 50, y = 50, w = 80, h = 80)
        
        # Grünen Stift zuweisen
        pen = wx.Pen("green", 2, wx.SOLID)
        gc.SetPen(pen)
        
        # Zwei horizontale Linien zeichnen
        gc.DrawLines(((50, 80), (130, 80)))
        gc.DrawLines(((50, 100), (130, 100)))
        
        # Blauen Stift zuweisen
        pen = wx.Pen("blue", 2, wx.SOLID)
        gc.SetPen(pen)
        
        # Zwei vertikale Linien zeichnen
        gc.DrawLines(((80, 50), (80, 130)))
        gc.DrawLines(((100, 50), (100, 130)))
        
        # Rotes Kreuz zeichnen
        pen = wx.Pen("red", 8, wx.SOLID)
        gc.SetPen(pen)
        brush = wx.Brush("red")
        gc.SetBrush(brush)
        gc.DrawLines(
            (
                (150, 200), (150, 150), (180, 150), (180, 200), 
                (230, 200), (230, 230), (180, 230), (180, 280), 
                (150, 280), (150, 230), (100, 230), (100, 200), 
                (150, 200)
            )
        )


def main():
    """Testing"""
    app = wx.PySimpleApp()
    f = MyFrame()
    f.Center()
    f.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
tomate
User
Beiträge: 48
Registriert: Sonntag 5. August 2007, 12:07

Donnerstag 30. August 2007, 19:01

Danke
Zuletzt geändert von tomate am Donnerstag 30. August 2007, 19:09, insgesamt 1-mal geändert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 30. August 2007, 19:01

tomate hat geschrieben:Koordinaten aus dem kartesischen Koordinatensystem wxPython
Hallo tomate!

Z.B. so:

Code: Alles auswählen

>>> HEIGHT = 1000
>>> data = [[100, 100], [250, 100], [305, 500]]
>>> wxcoordinates = [ [HEIGHT - item[0], HEIGHT - item[1]] for item in data ]
>>> wxcoordinates
[[900, 900], [750, 900], [695, 500]]
>>>
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
tomate
User
Beiträge: 48
Registriert: Sonntag 5. August 2007, 12:07

Donnerstag 30. August 2007, 19:09

Besten Dank!
tomate
User
Beiträge: 48
Registriert: Sonntag 5. August 2007, 12:07

Donnerstag 30. August 2007, 21:23

Code: Alles auswählen

>>> HEIGHT = 1000
>>> data = [[100, 100], [250, 100], [305, 500]]
>>> wxcoordinates = [[item[0], HEIGHT - item[1]] for item in data ]
>>> wxcoordinates
[[100, 900], [250, 900], [305, 500]]
>>>
Ich glaube so ist es richtig.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Freitag 31. August 2007, 07:46

tomate hat geschrieben:

Code: Alles auswählen

>>> HEIGHT = 1000
>>> data = [[100, 100], [250, 100], [305, 500]]
>>> wxcoordinates = [ [item[0], HEIGHT - item[1]] for item in data ]
>>> wxcoordinates
[[100, 900], [250, 900], [305, 500]]
>>>
Ich glaube so ist es richtig.
Hallo tomate!

Natürlich! Ich wollte nur wissen, ob du mitdenkst. :mrgreen:

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
ennemoser
User
Beiträge: 7
Registriert: Freitag 31. August 2007, 18:48
Wohnort: Graz

Freitag 31. August 2007, 18:58

Hi,

so kann man die Koordinatenrichtung "wiederherstellen".
Man muss allerdings auch den Koordinaten-Ursprung verschieben.

Code: Alles auswählen

        # Drawing upside down (i.e. natural orientation)
        # With "True" x is positive and y is positive natural direction (i.e. y up)
        # Origin is still top left corner of window

        dc.SetAxisOrientation(True, True)

        # Original position is top left of window
        # Move it to bottom left of window

        self.width, self.height = self.GetClientSize()  # self  ist hier ein Frame
        dc.SetDeviceOrigin(0.0 , -self.height)

Andi
tomate
User
Beiträge: 48
Registriert: Sonntag 5. August 2007, 12:07

Montag 3. September 2007, 14:26

gerold hat geschrieben:

Code: Alles auswählen

    def on_panel_paint(self, event = None):
        dc = wx.PaintDC(self.panel)
Wie kann ich es hinkriegen, dass on_panel_paint erst aufgerufen wird, wenn ich einen Button in der Toolbar anklicke?
Es funktionert zwar, wenn meine Methode mit

Code: Alles auswählen

    def DrawGraph(self, event):
        dc = wx.ClientDC(self)
        dc.Clear()
...
beginnt. Aber so wie oben kriege ich es nicht hin :(
dc = wx.PaintDC(self.panel)
Gibt es vielleicht irgendwo ein Beispiel, wo ich mir das mal angucken könnte?

Edit by Gerold: FullQuote gekürzt
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 3. September 2007, 15:07

tomate hat geschrieben:Wie kann ich es hinkriegen, dass on_panel_paint erst aufgerufen wird, wenn ich einen Button in der Toolbar anklicke?
Hallo tomate!

``on_panel_paint`` wird im Beispiel beim Paint-Ereignis aufgerufen. Dieses Paint-Ereignis tritt immer dann auf, wenn z.B. das Objekt kurz verdeckt war oder aus dem sichtbaren Desktopbereich herausgezogen wurde und jetzt wieder sichtbar wird. NUR für dieses Paint-Ereignis wurde der wx.PaintDC geschaffen. Dieser lässt sich nur im Event-Handler des Paint-Ereignisses einsetzen.

Wenn du außerhalb dieses Event-Handlers etwas Zeichnen möchtest, dann kannst du den wx.PaintDC nicht verwenden. Der Nachteil ist, dass du dann nicht mitbekommst, wenn das Bild neu gezeichnet werden muss.

Du könntest mit Hilfe des wx.MemoryDC ein Bild zeichnen und in eine Bitmap (im RAM) speichern. Diese Bitmap könntest du dann im Paint-Event-Handler bei Bedarf auf die Oberfläche zeichnen lassen. Das ist ziemlich schnell und kann mit dem wx.BufferedPaintDC extrem beschleunigt werden. Das heißt aber auch, dass du auf eine Größenänderung der Bildoberfläche selbst reagieren musst. Das ist aber nicht so schwer. Reagiere einfach im Size-Event-Handler darauf und zeichne dort das Bitmap neu. Dieses wird dann automatisch wieder vom Paint-Event-Handler verwendet.

http://wiki.wxpython.org/Frequently_Ask ... ff9eaefbb9
Weitere Hilfe findest du im Buch "wxPython in Action".

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Montag 3. September 2007, 16:18

Hallo tomate!

So ähnlich vielleicht:
Bild

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import wx

wx.SetDefaultPyEncoding("iso-8859-15")


class MyDynamicImage(wx.Window):
    
    def __init__(self, parent, vertical_lines = False, horizontal_lines = False):
        wx.Window.__init__(self, parent)
        
        self.vertical_lines = vertical_lines
        self.horizontal_lines = horizontal_lines
        
        self._buffer_bmp = wx.EmptyBitmap(*self.GetSizeTuple())
        self._first_paint = True
        
        self.Bind(wx.EVT_PAINT, self.on_paint)
        self.Bind(wx.EVT_SIZE, self.on_size)
        
        self._size_timer = wx.Timer()
        self._size_timer.Bind(wx.EVT_TIMER, self.on_size_timer)
    
    
    def on_size_timer(self, event):
        self.refresh_bitmap()
        self.Refresh()
    
    
    def refresh_bitmap(self):
        self._buffer_bmp = wx.EmptyBitmap(*self.GetSizeTuple())
        memdc = wx.MemoryDC(self._buffer_bmp)
        memdc.SetBackground(wx.Brush("white"))
        memdc.Clear()
        
        w, h = self.GetSizeTuple()
        
        gd = wx.GraphicsContext_Create(memdc)
        
        if self.vertical_lines:
            gd.SetPen(wx.Pen("blue"))
            gd.DrawLines(((w / 3, 0), (w / 3, h)))
            gd.DrawLines(((w / 3 * 2, 0), (w / 3 * 2, h)))
        
        if self.horizontal_lines:
            gd.SetPen(wx.Pen("red"))
            gd.DrawLines(((0, h / 3), (w, h / 3)))
            gd.DrawLines(((0, h / 3 * 2), (w, h / 3 * 2)))
        
        memdc.SelectObject(wx.NullBitmap)
    
    
    def on_size(self, event):
        timer = self._size_timer
        
        timer.Stop()
        timer.Start(milliseconds = 100, oneShot = True)
    
    
    def on_paint(self, event):
        if self._first_paint:
            self._first_paint = False
            self.refresh_bitmap()
        dc = wx.BufferedPaintDC(self, self._buffer_bmp, style = wx.BUFFER_VIRTUAL_AREA)


class MyFrame(wx.Frame):
    
    def __init__(
        self, parent = None, title = "Example", size = wx.Size(550, 420)
    ):
        wx.Frame.__init__(self, parent, -1, title, size = size)
        
        panel = wx.Panel(self)
        
        vbox_main = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(vbox_main)
        
        my_image = MyDynamicImage(panel)
        vbox_main.Add(my_image, 1, wx.EXPAND | wx.ALL, 10)
        self.my_image = my_image
        
        hbox_buttons = wx.BoxSizer(wx.HORIZONTAL)
        vbox_main.Add(hbox_buttons, 0, wx.EXPAND | wx.ALL, 5)
        
        chb_horizontal = wx.CheckBox(panel, label = "Horizontale Linien")
        hbox_buttons.Add(chb_horizontal, 0, wx.ALL, 5)
        chb_horizontal.Bind(wx.EVT_CHECKBOX, self.on_horizontal_checkbox)
        self.chb_horizontal = chb_horizontal
        
        chb_vertical = wx.CheckBox(panel, label = "Vertikale Linien")
        hbox_buttons.Add(chb_vertical, 0, wx.ALL, 5)
        chb_vertical.Bind(wx.EVT_CHECKBOX, self.on_vertical_checkbox)
        self.chb_vertical = chb_vertical
    
    
    def on_horizontal_checkbox(self, event):
        my_image = self.my_image
        my_image.horizontal_lines = self.chb_horizontal.Get3StateValue() == wx.CHK_CHECKED
        my_image.refresh_bitmap()
        my_image.Refresh()

    
    def on_vertical_checkbox(self, event):
        my_image = self.my_image
        my_image.vertical_lines = self.chb_vertical.Get3StateValue() == wx.CHK_CHECKED
        my_image.refresh_bitmap()
        my_image.Refresh()


def main():
    """Testing"""
    app = wx.PySimpleApp()
    f = MyFrame()
    f.Center()
    f.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()
mfg
Gerold
:-)

Zusätzliche Suchworte: zeichnen Bitmap MemoryDC PaintDC Size Event verzögern OnSize
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Stranger
User
Beiträge: 4
Registriert: Sonntag 20. Januar 2008, 18:34

Sonntag 20. Januar 2008, 19:05

Hi!
Ich bin neu hier und habe mich heute etwas eingelesen. Ich beschäftige mich seit kurzer Zeit mit Python und bin gerade dabei, ein kleines Programm mit wxPython zu erstellen. Das Zeichnen klappt wunderbar. Ich hab allerdings das Problem, dass beim Öffnen eines Dialogs das gezeichnete Bild teilweise verschwindet.
Deswegen hab ich mir die Beispiele von Gerold und einige anderen Seiten angeguckt. Das Buch wxPython in Action hab ich leider noch nicht.

Ich nutze auch eine Methode zum Zeichnen, die wx.ClientDC verwendet.

Code: Alles auswählen

    def Draw(self, event):
        dc = wx.ClientDC(self.splitter.GetWindow1())
        dc.Clear()
        dc.DrawEllipse(x, y, 26, 26)

Ich würde das Zeichnen jetzt gerne so umsetzen, dass die Zeichnung beim Öffnen von Dialogen nicht mehr verschwindet.
Dazu erzeuge ich ein leeres Bitmap:

Code: Alles auswählen

self._buffer_bmp = wx.EmptyBitmap(*self.GetSizeTuple()) 
Dann noch die Methode on_paint, die dafür da ist, mein Bild aus dem Buffer zu zeichnen?

Code: Alles auswählen

    def on_paint(self, event):
        dc = wx.BufferedPaintDC(self, self._buffer_bmp, style = wx.BUFFER_VIRTUAL_AREA) 
Leider sind alle meine Versuche die "Draw"-Methode anzupassen gescheitert. Sehe ich es richtig, dass ich anstatt wx.ClientDC wx.MemoryDC(self._buffer_bmp) benutzen muss?
Aber wie wird das Bild dann gezeichnet? Wann und wie wird on_paint aufgerufen? Wie zeichne ich das Bild in mein Splitter-Fenster?

Ich hoffe, dass ich mich einigermaßen verständlich ausdrücken konnte und mir jemand helfen kann.

Danke
Maik
Antworten