Seite 1 von 2

DrawLine

Verfasst: Donnerstag 30. August 2007, 17:17
von tomate
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?

Verfasst: Donnerstag 30. August 2007, 18:06
von alan
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:)

Verfasst: Donnerstag 30. August 2007, 18:21
von gerold
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
:-)

Verfasst: Donnerstag 30. August 2007, 18:38
von tomate
Danke!
Bzw gibt es irgendeinen Trick wie ich Koordinaten aus dem kartesischen Koordinatensystem wxPython geeignet machen kann?

Verfasst: Donnerstag 30. August 2007, 18:49
von gerold
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
:-)

Verfasst: Donnerstag 30. August 2007, 19:01
von tomate
Danke

Verfasst: Donnerstag 30. August 2007, 19:01
von gerold
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
:-)

Verfasst: Donnerstag 30. August 2007, 19:09
von tomate
Besten Dank!

Verfasst: Donnerstag 30. August 2007, 21:23
von tomate

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.

Verfasst: Freitag 31. August 2007, 07:46
von gerold
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
:-)

Verfasst: Freitag 31. August 2007, 18:58
von ennemoser
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

Verfasst: Montag 3. September 2007, 14:26
von tomate
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

Verfasst: Montag 3. September 2007, 15:07
von gerold
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
:-)

Verfasst: Montag 3. September 2007, 16:18
von gerold
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

Verfasst: Sonntag 20. Januar 2008, 19:05
von Stranger
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

Verfasst: Montag 21. Januar 2008, 09:02
von nkoehring
tomate hat geschrieben:
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?
Hallo tomate!

Bin jetzt nicht sicher ob das weiterhilft, aber wenn du deine Paint-Methode erst beim dem Klicken des Buttons dem Event zuweist und danach ein Refresh() machst, wuerde das auch gehen. Ist es vielleicht was du willst?

Code: Alles auswählen

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.SetClientSize((640, 480))
        self.panel = wx.Panel(self)

        self.btn = wx.Button(self.panel, -1, "CLICK!",
            pos=(270, 220),
            size=(100, 20)
        )
        #btn2 = wx.Button(self, -1, "CLICK!", pos=(200, 200))
        self.btn.Bind(wx.EVT_BUTTON, self.on_button)

    def on_button(self, evt):
        self.panel.Bind(wx.EVT_PAINT, self.on_paint)
        self.return_bmp(forced_refresh=True)
        self.panel.Refresh()

    def on_paint(self, evt):
        wx.BufferedPaintDC(self.panel, self.return_bmp())

    def return_bmp(self, forced_refresh=False):
        if forced_refresh or not hasattr(self, "_buffer"):
            self._buffer = wx.EmptyBitmap(*self.GetClientSize())
            
            # hab mal mein Fieldset als Beispiel missbraucht
            MARGIN = 10
            TXT = "Hallo Welt!"
            
            dc = wx.MemoryDC()
            dc.SelectObject(self._buffer)
            dc.Clear()
            
            w, h = dc.GetSizeTuple()
            
            TXTW = dc.GetTextExtent(TXT)[0] + MARGIN
            TXTH = dc.GetTextExtent(TXT)[1]
            
            lines = (
                (MARGIN, MARGIN, 3*MARGIN, MARGIN),
                (3*MARGIN+TXTW, MARGIN, w-MARGIN, MARGIN),
                (MARGIN, MARGIN, MARGIN, h-MARGIN),
                (w-MARGIN, MARGIN, w-MARGIN, h-MARGIN),
                (MARGIN, h-MARGIN, w-MARGIN+1, h-MARGIN)
            )
            dc.DrawLineList(lines)
            dc.DrawText(TXT, 3*MARGIN+MARGIN/2, MARGIN-TXTH/2)
            
            dc.SelectObject(wx.NullBitmap)
            
        return self._buffer
        
        
if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = Frame(None)
    frame.Show()
    
    app.MainLoop()

Verfasst: Montag 21. Januar 2008, 13:18
von Stranger
Danke. Jetzt funktioniert es bei mir auch.

Kannst du mir vielleicht noch einen Tipp geben, wie ich das ganze mit mehreren Methoden zum Zeichnen umsetzen könnte?
Sollte ich dann für jede eine eigene "on_paint"-Methode erstellen oder geht das auch eleganter?

Verfasst: Montag 21. Januar 2008, 14:22
von nkoehring
Sei kreativ! Bei einem Refresh() kannst du die komplette Bitmap neuzeichnen lassen... du koenntest einfach ne Liste von Bitmaps durchgehen, indem du nen Index-Zaehler durchgehst, beim klicken.

Verfasst: Montag 21. Januar 2008, 15:40
von Stranger
Leider haut das alles noch nicht so hin wie ich es möchte.

Meine Idee war, dass ich eine Methode hab, die grundsätzlich für das Zeichnen verantwortlich ist, wie bei dir:

Code: Alles auswählen

    def return_bmp(self, forced_refresh=False):
        if forced_refresh or not hasattr(self, "_buffer"): 
        ...
Zusätzlich gibt es dann die

Code: Alles auswählen

    
    def on_paint(self, evt):
        wx.BufferedPaintDC(self.panel, self.return_bmp())
Meine Idee war jetzt, dass ich unterschiedliche Buttons hab. Also:

Code: Alles auswählen

    def on_button_x(self, evt):
        self.panel.Bind(wx.EVT_PAINT, self.on_paint)
        self.return_bmp(x, forced_refresh=True)
        self.panel.Refresh()

    def on_button_y(self, evt):
        self.panel.Bind(wx.EVT_PAINT, self.on_paint)
        self.return_bmp(y, forced_refresh=True)
        self.panel.Refresh()
Meine Idee war jetzt, dass ich der "return_bmp" Methode noch einen weiteren Parameter übergebe, der bestimmt was gezeichnet wird.
Ist es möglich, dass ich aus der return_bmp abhängig von dem Parameter eine bestimmte Methode aufrufen, die etwas zeichnet und es dann an die return_bmp zurückliefert? Oder ist das eine schlechte Idee?

Sollte dann ungefähr so aussehen:

Code: Alles auswählen

    def return_bmp(self, parameter, forced_refresh=False):
        if forced_refresh or not hasattr(self, "_buffer"):
            self._buffer = wx.EmptyBitmap(*self.GetClientSize())
           
           
            dc = wx.MemoryDC()
            dc.SelectObject(self._buffer)
            dc.Clear()
           
            if parameter == x:
                dc = DrawSomething(dc)
            elif parameter == y:
                dc. = DrawSomething2(dc)
          

           
            dc.SelectObject(wx.NullBitmap)
           
        return self._buffer 
Könnte das irgendwie so gehen?

Danke

Verfasst: Montag 21. Januar 2008, 17:25
von nkoehring
Ja so koennte man das machen. Probier es doch einfach mal aus ;)