# grafikfenster.py # Auf der Grundlage der wxPython Demo # kommentierte Version # Version 2023 import wx import colorsys from grafikfenster import * # use buffered drawing USE_BUFFER = True ## ggf ('wxMSW' in wx.PlatformInfo) or ('wxGTK' in wx.PlatformInfo) import os ## eingefuegt fuer : def opj(path): ## eingefuegt aus Demo Main """Convert paths to the platform-specific separator""" st = os.path.join(*tuple(path.split('/'))) # HACK: on Linux, a leading / gets lost... if path.startswith('/'): st = '/' + st return st ### --------------------------------------------------- class Zeichenflaeche(wx.Panel): """Die Klasse stellt eine Zeichenflaeche fuer Vektorgrafik bereit. Sie wendet das Singleton-Muster an, also kein Konstruktoraufruf, sondern Methode GibZeichenflaeche() ----------------------------------- -> Mausereignisse [wxEvent] / \\ [wxCommandEvent] [wxMouseEvent] Es gibt folgende moegliche Maus-Ereignisse: EVT_LEFT_DOWN eingebaut EVT_LEFT_UP eingebaut EVT_LEFT_DCLICK eingebaut EVT_MIDDLE_DOWN EVT_MIDDLE_UP EVT_MIDDLE_DCLICK EVT_RIGHT_DOWN eingebaut EVT_RIGHT_UP eingebaut EVT_RIGHT_DCLICK EVT_MOTION eingebaut EVT_ENTER_WINDOW EVT_LEAVE_WINDOW EVT_MOUSEWHEEL EVT_MOUSE_EVENTS """ __zeichenflaeche=None @staticmethod def GibZeichenflaeche(parent=None): if Zeichenflaeche.__zeichenflaeche == None: if parent==None: return None else: Zeichenflaeche.__zeichenflaeche=Zeichenflaeche(parent) return Zeichenflaeche.__zeichenflaeche def __init__(self, parent): """Konstruktor !nicht direkt aufrufen!""" wx.Panel.__init__(self, parent, -1) self.Bind(wx.EVT_PAINT, self.OnPaint) if USE_BUFFER: self.Bind(wx.EVT_SIZE, self.OnSize) self.objekte = [] self.gc = None self.__image=None self.__bgImage,self.__bgFaktor=None,1 self.__bgColor='WHITE' self.__controller=None # neu ## Mauserreignisse self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDoubleClick) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) self.Bind(wx.EVT_MOTION, self.OnMotion) ## Modell bekannt machen def SetzeController(self,controller): # neu self.__controller=controller def SetBGImage(self, name, bgFaktor=1): '''setzt ein Hintergrundbild''' self.__bgImage,self.__bgFaktor=name, bgFaktor def SetBGColor(self, colorName): '''setzt die Hintergrundfarbe''' self.__bgColor=colorName def GetBGColor(self): '''gibt die Hintergrundfarbe''' return self.__bgColor ## Ereignisbehandlung def OnLeftDown(self, event): x,y = event.GetX(),event.GetY() self.__controller.LinksKlick(x,y) ##event.Skip() def Angeklickt(self, figuren, x, y): '''prüft, ob die übergebene Figur angeklickt wurde''' pos=wx.Point2D(x,y) for figur in figuren: if figur.GetBox().Contains(pos): return True return False def OnLeftUp(self, event): x,y = event.GetX(),event.GetY() self.__controller.MausLosgelassen(x,y) ##event.Skip() def OnLeftDoubleClick(self, event): x,y = event.GetX(),event.GetY() self.__controller.DoppelKlickLinks(x,y) ##event.Skip() def OnRightDown(self, event): x,y = event.GetX(),event.GetY() self.__controller.RechtsKlick(x,y) ##event.Skip() def OnRightUp(self, event): x,y = event.GetX(),event.GetY() self.__controller.MausLosgelassen(x,y) ##event.Skip() def OnMotion(self, event): x,y = event.GetX(),event.GetY() if event.LeftIsDown(): self.__controller.GezogenAuf(x,y) else: self.__controller.BewegtAuf(x,y) ##event.Skip() ## Ende Mausereignisse def OnSize(self, evt): """Ereignismethode, die beim Aendern der Groesse des fensters aufgerufen wird.""" self.InitBuffer() evt.Skip() def OnPaint(self, evt): """Ereignismethode fuer das Zeichnen Die erste Anweisung ist im Unterschied zur Demo notwendig, um unter Windows beim ersten Zeichnen den Puffer richtig zu initialisieren""" self.InitBuffer() if USE_BUFFER: dc = wx.BufferedPaintDC(self, self._buffer) else: dc = wx.PaintDC(self) self.gc = self.MakeGC(dc) self.Draw(self.gc) def InitBuffer(self): """Initialisierungsmethode fuer den Zeichenpuffer""" sz = self.GetClientSize() sz.width = max(1, sz.width) sz.height = max(1, sz.height) self._buffer = wx.Bitmap(sz.width, sz.height, 32) dc = wx.MemoryDC(self._buffer) dc.SetBackground(wx.Brush("WHITE")) dc.Clear() self.gc = self.MakeGC(dc) self.Draw(self.gc) def MakeGC(self, dc): """erzeugt den 'Graphics-Context'""" try: gc = wx.GraphicsContext.Create(dc) except NotImplementedError: dc.DrawText("This build of wxPython does not support the wx.GraphicsContext " "family of classes.", 25, 25) return None self.gc = gc return gc def Draw(self, gc): """Die eigentliche Zeichenmethode wxPython transformiert den Zeichenpfad. Wenn man mehrfach Zeichnen will, muss man daher vor einer Transformation den Zustand sichern und anschliessend wiederherstellen.""" font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetWeight(wx.BOLD) gc.SetFont(font, wx.BLACK) gc.PushState() # save current translation/scale/other state if self.__bgImage!=None: ## eingefuegt aus Demo: # Draw a bitmap with an alpha channel on start of drawing bmp = wx.Bitmap(opj(self.__bgImage)) bsz = bmp.GetSize() gc.DrawBitmap(bmp, 0, 0, ## xPos,yPos bsz.width*self.__bgFaktor, # breite ggf anpassen bsz.height*self.__bgFaktor) # hoehe ggf anpassen gc.PopState() else: # anderenfalls weissen/schwarzen Hintergrund zeichnen back = gc.CreatePath() back.AddRectangle(0, 0, self.GetClientSize().width, self.GetClientSize().height) gc.SetBrush(wx.Brush(self.__bgColor)) gc.FillPath(back) # nur Fuellung gc.PopState() # restore saved state # Daten holen figuren=[] if self.__controller==None: return figuren=self.__controller.GibFiguren() for figur in figuren: ## Neumodellierung 03'21: Moebel aus mehreren Figuren ## nur sichtbare Figuren werden dargestellt gc.PushState() path = gc.CreatePath() path.AddPath(figur[0]) f=self._HoleFarbe(figur[1]) if f==None: f=wx.BLACK if f==self.__bgColor: if f==wx.BLACK: f=wx.WHITE else: f=wx.BLACK gc.SetPen(wx.Pen(f)) ff=None if figur[2]!=None: ff=self._HoleFarbe(figur[2]) if ff!=None: # zulaessiger Farbname if self.__bgColor==wx.BLACK: if ff==wx.WHITE: ff=wx.BLACK gc.SetBrush(wx.Brush(ff)) gc.DrawPath(path) else: gc.StrokePath(path) gc.PopState() def _HoleFarbe(self, farbe): if type(farbe)==tuple: if len(farbe)==3: rot,gruen,blau=farbe f=wx.Colour(rot,gruen,blau) elif len(farbe)==4: rot,gruen,blau,alpha=farbe f=wx.Colour(rot,gruen,blau,alpha) else: return None else: f=wx.TheColourDatabase.Find(farbe) if f[0]==-1: return None return f def Zeichne(self, objekt): """Erweiterungen für das Raumplanerprojekt: Ruft nacheinander das Zeichnen fuer alle Objekte auf.""" if objekt in self.objekte: self.objekte.remove(objekt) self.objekte.append(objekt) self.Refresh() def Entferne(self, objekt): """Entfernt ein Objekt aus der Darstellung.""" if objekt in self.objekte: self.objekte.remove(objekt) self.Refresh() def GibGC(self): """Gibt den aktuellen Graphics-Context zurueck.""" if self.gc==None: self.InitBuffer() return self.gc def GibPfad(self): '''Gibt einen Zeichenpfad zurueck.''' if self.gc==None: self.InitBuffer() return self.gc.CreatePath() def BildExport(self, dateiname): '''exportiert im png- oder jpg-Format''' self.InitBuffer() typ=dateiname.partition('.')[2].upper() if not(typ in ('PNG','JPG')): return 'Dateityp png oder jpg' self.__image=self._buffer.ConvertToImage() bitmap_type={'PNG':wx.BITMAP_TYPE_PNG, 'JPG':wx.BITMAP_TYPE_JPEG}[typ] try: with open(dateiname, 'wb') as bild_datei: self.__image.SaveFile(dateiname,bitmap_type) return 'OK' except IOError as ioerr: return 'Dateifehler: ' + str(ioerr) ### --------------------------------------------------- from wx.py.shell import ShellFrame from wx.py.filling import FillingFrame ### --------------------------------------------------- class GrafikFenster(wx.Frame): """ Die Klasse GrafikFenster erzeugt einen Frame. """ def __init__(self, parent, title, pos=(20, 50), size=(600, 640)): wx.Frame.__init__(self, parent, -1, title, pos, size) if wx.PlatformInfo[1]=='wxMSW': self.SetSize((size[0],size[1]+20)) self.panel = Zeichenflaeche.GibZeichenflaeche(self) self.shellFrame = ShellFrame(parent=self, pos=wx.Point(450,100)) self.fillingFrame = FillingFrame(parent=self, pos=wx.Point(40,500)) def ZeigeShellFrame(self, zeigen=True): """Zeigt die interaktive Shell an.""" self.shellFrame.Show(zeigen) def ZeigeFillingFrame(self, zeigen=True): """Zeigt den Monitor an.""" self.fillingFrame.Show(zeigen)