Programme Python


Librairie graphique python, basée sur PIL (Python Imaging Librairy)
Fichier
Type: Programme python
File type: py (python)
Télécharger:  
Description
Librairie graphique python, basée sur PIL (Python Imaging Librairy)

# -*- coding: utf-8; -*-
################################################################################

"""
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
__version__="2.1"
__author__="Y. Morel"
__about__="Library for graphical functions and tools with pure python - written by "+__author__+", detailled infos on https://xymaths.fr/Libxy "
__date__="2017"

import platform, sys, os.path
r=platform.python_version()
if (int(r[0])==2): pyv=2;print("Using Libxy"+__version__+" withpython 2.x\n")
elif (int(r[0])==3): pyv=3;print("Using Libxy",__version__," with python 3.x\n")
else: pyv=3;print("Using Libxy ",__version__," with undefined version of python is used, assuming 3.x\n")

def InitGraph (Xmin=-10,Xmax=10,Ymin=-10,Ymax=10,**kwargs):
    global Width, Height
    global xmin, xmax, ymin, ymax
    global ptsz, lw, lc
    global palette, bitarray
    ptsz=kwargs.get('PointSize',1)
    lw=kwargs.get('LineWidth',1)
    lc=kwargs.get ('LineColor',"blue")
    #Width=int(kwargs.get('Width',500))
    #Height=int(kwargs.get('Height',500))
    Size=int(kwargs.get('Size',400))
    # Image carrée pour l'instant
    Width=Size;Height=Size;#Width=400;Height=400 
    bkgd=kwargs.get('background',"white")
    tmparray = [ 0 ] * Height
    bitarray = [ tmparray[:] for i in range( Width ) ]
    xmin,xmax,ymin,ymax=float(Xmin),float(Xmax),float(Ymin),float(Ymax)

    bkgdrgb=RGBColorByName(bkgd) # background color
    palette = []
    #palette.append( hash(bkgdrgb) )

    palette.append(hash(RGBColorByName("white")))
    palette.append(hash(RGBColorByName("black")))
    global blackplidx
    blackplidx=palette.index(hash(RGBColorByName("black")))
    #print("black index pl= ",blackplidx) 
    palette.append(hash(RGBColorByName("blue")))
    palette.append(hash(RGBColorByName("red")))
    palette.append(hash(RGBColorByName("green")))
    palette.append(hash(RGBColorByName("cyan")))
    palette.append(hash(RGBColorByName("magenta")))
    palette.append(hash(RGBColorByName("yellow")))
    palette.append(hash(RGBColorByName("purple")))
    palette.append(hash(RGBColorByName("gray")))
    palette.append(hash(RGBColorByName("brown")))
    #print("palette=",palette)



def shortToString(i):
  hi = (i & 0xff00) >> 8
  lo = i & 0x00ff
  if pyv==2:
      return chr(lo) + chr(hi)
  elif pyv==3:
      return bytes([lo]) + bytes([hi])

def longToString(i):
  hi = (int(i) & 0x7fff0000) >> 16
  lo = int(i) & 0x0000ffff
  return shortToString(lo) + shortToString(hi)

def long24ToString(i):
  if pyv==2:
      return chr(i & 0xff) + chr(i >> 8 & 0xff) + chr(i >> 16 & 0xff)
  elif pyv==3:
      #return bytes([i & 0xff]) + bytes([i >> 8 & 0xff]) + bytes([i >> 16 & 0xff])
      return bytes([i >> 16 & 0xff]) + bytes([i >> 8 & 0xff]) + bytes([i & 0xff])


def stringToLong(input_string, offset):
  return ord(input_string[offset+3]) << 24 | ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])

def stringToLong24(input_string, offset):
  return ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])

def hash(color): #color=(r,g,b)
    return ( ( int(color[0]) ) + 
             ( int(color[1]) <<  8 ) + 
             ( int(color[2]) << 16 ) )

def RGBColorByName(color):
    if color=="black" or color=="k":
        rgb=(   0,   0,   0 )
    elif color=="white" or color=="w":
        rgb=( 255,   255,   255 )
    elif color=="red" or color=="r":
        rgb=( 255,   0,   0 )
    elif color=="green" or color=="g":
        rgb=(   0, 255,   0 )
    elif color=="blue" or color=="b":
        rgb=(   0, 0,   255 )
    elif color=="indigo" or color=="i":
        rgb=(   75, 0,   130 )
    elif color=="orange" or color=="orange":
        rgb=(   255, 165,  0)
    elif color=="cyan":
        rgb=(   0, 255,   255 )
    elif color=="magenta" or color=="m":
        rgb=( 255,   0, 255 )
    elif color=="yellow" or color=="y":
        rgb=( 255, 255,   0 )
    elif color=="teal":
        rgb=(   0, 128, 128 )
    elif color=="purple" or color=="p":
        rgb=( 128,   0, 128 )
    elif color=="brown":
        rgb=( 150, 75,   0)
    elif color=="chocolate" or color=="c":
        rgb=( 210, 105,   30)
    elif color=="gray":
        rgb=( 128, 128, 128 )
    elif color=="pink":
        rgb=( 255, 20, 147 )
    elif color=="darkred":
        rgb=( 128,   0,   0 )
    elif color=="darkgreen":
        rgb=(   0, 128,   0 )
    elif color=="darkblue":
        rgb=(   0,   0, 128 )
    else:
        print("Unknown color: "+color)
        print("using defaut: black color")
        rgb=(   0,   0,   0 )        
    return rgb


def MakePalette( color ):
    if not(isinstance(color,str)):
        # If color is given via rgb=(r,g,b)
        n=len(color)
        if (n==3):
            rgbcolornum=hash(color)
        else:
            print("Color must either be called with a valid color name (e.g. black, blue, red, green, ...) or  be an array of RGB colors, with 3 parameters color=(r,g,b) where 0<=r,g,b<=256\nUsing black color instead...")
            rgbcolornum=hash(RGBColorByName("black"))
    else: 
        rgbcolor=RGBColorByName(color)
        rgbcolornum=hash(rgbcolor)

    try:
        plidx=palette.index( rgbcolornum )
    except ValueError:
        if len(palette) < 256 :
            palette.append( rgbcolornum )
            plidx=len(palette)-1
        else:
            plidx = blackplidx
    return plidx

def coord (X,Y):
    x=xmin+X*(xmax-xmin)*1.0/Width
    y=ymin+Y*(ymax-ymin)*1.0/Height
    #print("coord  |",Width,X,x)
    return x, y

def coordim (x,y):
    X=Width*(x-xmin)*1.0/(xmax-xmin)
    Y=Height*(y-ymin)*1.0/(ymax-ymin)
    return X, Y

def Point (*args,**kwargs):
    sz1=kwargs.get ('size',False)
    sz2=kwargs.get ('PointSize',False)
    if sz2: 
        sz=sz2
    elif sz1:
        sz=sz1
    else:
        sz=ptsz
    fill=kwargs.get ('fill',"red")
    if (len (args)==1):
        try: 
            A=(args [0][0],args [0][1])
        except TypeError:
            print("Trying to execute: Point"+str(args))
            print("Two coordinates are required to define a point")
            print(" Point(x,y)  or A=(x,y);Point(A)\n")
            exit()
    elif (len (args)==2):
        try: 
            tmp=len(args[0])
            A=(args [0][0],args [0][1])
            try:
                sz=int(args[1])
            except ValueError:
                fill=args[1]
        except TypeError: 
            A=(args[0],args[1])
    elif (len(args)==3):
        try: 
            tmp=len(args[0])
            A=(args [0][0],args [0][1])
            try:
                sz=int(args[1])
                fill=args[2]
            except ValueError:
                sz=args[2]
                fill=args[1]
        except TypeError:
            A=(args[0],args[1])
            try:
                sz=int(args[2])
            except ValueError:
                fill=args[2]
    elif (len(args)==4):
        A=(args[0],args[1])
        try:
            sz=int(args[2])
            fill=args[3]
        except ValueError:
            try:
                sz=int(args[3])
            except ValueError:
                print("Bad syntax used in Point(),", args[3]," not recognized")
            fill=args[2]
    plidx=MakePalette(fill)
    [X,Y]=coordim(A[0],A[1])
    X=int(X);Y=int(Y)
    if sz<=1:
        if ( 0 <= X < Width and 0 <= Y < Height ):
            bitarray[X][Y] = plidx
    else:
        bz=int(sz/2.0)
        if ( bz <= X < Width-bz and bz <= Y < Height-bz ):
            for i in range (-bz,bz):
                for j in range (-bz,bz):
                    bitarray[X+i][Y+j] = plidx

def Points (*args,**kwargs):
    fill=kwargs.get ('fill',"red")
    sz=int (kwargs.get ('size',ptsz))
    for i in range (len (args)):
        Point (args[i],size=sz,fill=fill)

def Line (A,B,**kwargs):
    fill=kwargs.get ('fill',lc)
    width=kwargs.get ('width',lw)
    xA=A[0];yA=A[1];xB=B[0];yB=B[1]
    if not(xB==xA):
        m=(yB-yA)*1.0/(xB-xA)
        if m<0.5:
            p=yA-m*xA
            for i in range(Width):
                xi=xA+i*1.0*(xB-xA)/Width;
                yi=m*xi+p
                #print(xi,yi,coordim(xi,yi))
                Point(xi,yi,size=width,fill=fill)
        else:
            m=(xB-xA)*1.0/(yB-yA)
            p=xA-m*yA
            for i in range(Height):
                yi=yA+i*1.0*(yB-yA)/Height;
                xi=m*yi+p
                Point(xi,yi,size=width,fill=fill)
    else: #if xB==xA
        for i in range(Height):
            yi=yA+i*1.0*(yB-yA)/Height;
            xi=xA
            Point(xi,yi,size=width,fill=fill)
        
def Lines (*args,**kwargs):
    fill=kwargs.get ('fill',lc)
    width=kwargs.get ('width',lw)
    for i in range (len (args)-1):
        Line (args[i],args[i+1],fill=fill,width=width)


def Polygon (*args,**kwargs):
    outline=kwargs.get ('outline',lc)
    fill=kwargs.get ('fill',None)
    width=kwargs.get ('width',lw)
    N=len (args)

    if (fill): 
        # Polygon's bounding box 
        tmparray = [ 0 ] * N
        for i in range(N): tmparray[i]=args[i][0]
        xpolmin=min(tmparray);xpolmax=max(tmparray)
        for i in range(N): tmparray[i]=args[i][1]
        ypolmin=min(tmparray);ypolmax=max(tmparray)
        M1=(xpolmin,ypolmin);
        M2=(xpolmax,ypolmin)
        M3=(xpolmax,ypolmax)
        M4=(xpolmin,ypolmax)
        #Lines(M1,M2,M3,M4,M1)
        #print("filling polygon in",fill)
        (Xpolmin,Ypolmin)=coordim(xpolmin,ypolmin)
        (Xpolmax,Ypolmax)=coordim(xpolmax,ypolmax)
        prec=.75*(xmax-xmin)/Width
        I=[0,0]
        for xx in range(int(Xpolmin),int(Xpolmax)):
            R=(xx,int(Ypolmax)+2);
            R=coord(*R);
            for yy in range(int(Ypolmin),int(Ypolmax)):
                M=coord(xx,yy)
                cpt=0;Vrt=0;brd=0
                for i in range(N): 
                    A=args[i];
                    if (i<N-1): B=args[i+1]
                    else: B=args[0]
                    # (RM): x=xx
                    if not(B[0]==A[0]): # then (RM) intersects (AB) in I
                        # (AB): y=mx+p
                        m=(B[1]-A[1])*1.0/(B[0]-A[0])
                        p=A[1]-m*A[0]
                        I[0]=R[0]
                        I[1]=m*R[0]+p
                        if (Norm(A,I)<prec or (Norm(B,I)<prec)): Vrt=1
                        if (Norm(M,I)<2.5*prec): brd=1
                    if ((I[0]-A[0])*(I[0]-B[0])<0 and (I[1]-R[1])*(I[1]-M[1])<0):
                        cpt+=1
                if (cpt%2) and not(Vrt) and not(brd): Point(M,size=4,fill=fill)
    if width>=1:
        for i in range (N-1):
            Line (args[i],args [i+1],width=width,fill=outline)
        Line (args [N-1],args [0],width=width,fill=outline)

def Norm (A,B):
    return ((B[0]-A[0])**2+(B[1]-A[1])**2)**0.5

def Axes (xtick=0,ytick=0):
    if xtick<=0: xtick=(xmax-xmin)/10
    for i in range(int (xmin/xtick),int(xmax/xtick)+1):
        x=i*xtick
        y=(ymax-ymin)/200
        Line ((x,y), (x,-y),fill="blue")
        istr=str(int(i*xtick*100)/100.0)
        #Text ((x,-2*y),istr,fill="blue")
    if ytick<=0: ytick= (ymax-ymin)/10
    for i in range(int (ymin/ytick),int(ymax/ytick)+1):
        y=i*ytick
        x=(xmax-xmin)/200
        Line ((x,y), (-x,y),fill="blue")
        istr=str(int(i*ytick*100)/100.0)
        #Text ((-4*x,y),istr,fill="blue")
    Line ((xmin,0), (xmax,0),fill="blue",width=2)
    Line ((0,ymin), (0,ymax),fill="blue",width=2)
    global XtickAxes, YtickAxes
    XtickAxes,YtickAxes=xtick,ytick

def Grid (Dx=0,Dy=0):
    if not Dx>0:
        try:
            Dx=XtickAxes
        except NameError:
            Dx=(xmax-xmin)/10
    if not Dy>0:
        try:
            Dy=YtickAxes
        except NameError:
            Dy=(ymax-ymin)/10 
    for i in range(int (xmin/Dx),int(xmax/Dx)+1):
        x=i*Dx
        for j in range (0,100):
            y=ymin+j*(ymax-ymin)/100
            Point ((x,y),size=1,fill="orange")
    for i in range(int (ymin/Dy),int(ymax/Dy)+1):
        y=i*Dy
        for j in range (0,100):
            x=xmin+j*(xmax-xmin)/100
            Point ((x,y),size=1,fill="orange")


def Circle (*args,**kwargs):
    R=kwargs.get('R',False)
    outline=kwargs.get ('outline',lc)
    fill=kwargs.get ('fill',None)
    width=kwargs.get ('width',lw)
    N=len (args)
    if len(args)==1 and not(R):
        print("R is missing for circle, using R=0 (plotting a single point)")
        O=(args [0][0],args [0][1])
    elif len(args)==2 and not(R):
        O=(args[0][0],args[0][1])
        R=args[1]
    elif len(args)==2 and not(R):
        O=(args [0][0],args [0][1])
        R=args[1]
    elif len(args)==3 and not(R):
        O=(args[0],args[1])
        R=args[2]
    elif len(args)==3 and R:
        O=(args[0],args[1])
        
    N=min([4*int(1.0*Width*R/max([xmax-xmin,ymax-ymin])),2*Width])
    if R>0:
        if (fill):
            for i in range(N):
                if i<N/4:
                    x=O[0]-R+2.0*i**2*R/N**2
                elif i<3*N/4: 
                    x=O[0]-R+2.0*(i-N/4)*R/(N/2)
                else:
                    x=O[0]+R-2.0*(N-i)**2*R/N**2
                y1=O[1]+(R**2-(x-O[0])**2)**0.5
                y2=O[1]-(R**2-(x-O[0])**2)**0.5
                for j in range(N):
                    Point(x,y1+j*(y2-y1)*1.0/N,size=width,fill=fill)
            Circle(O,R,width=width,outline=outline)    
        else:
            for i in range(N):
                if i<N/4:
                    x=O[0]-R+2.0*i**2*R/N**2
                elif i<3*N/4: 
                    x=O[0]-R+2.0*(i-N/4)*R/(N/2)
                else:
                    x=O[0]+R-2.0*(N-i)**2*R/N**2
                y1=O[1]+(R**2-(x-O[0])**2)**0.5
                y2=O[1]-(R**2-(x-O[0])**2)**0.5
                Point(x,y1,size=width,fill=outline)
                Point(x,y2,size=width,fill=outline)
    else:
        Point(A,fill=outline,size=width)
    
def Norm (A,B):
    return ((B[0]-A[0])**2+(B[1]-A[1])**2)**0.5

def Vector (A,B,fill="red",width=1,ArrowLength=0.5,ArrowWidth=0.3):
    #"cf. https://xymaths.fr/Informatique-Programmation/javascript/canvas-dessin-fleche.php"
    AB=Norm(A,B)
    xA=A[0];yA=A[1]
    xB=B[0];yB=B[1]
    xC=xB+ArrowLength*(xA-xB)/AB
    yC=yB+ArrowLength*(yA-yB)/AB
    xD=xC+ArrowWidth*(-(yB-yA))/AB
    yD=yC+ArrowWidth*((xB-xA))/AB
    xE=xC-ArrowWidth*(-(yB-yA))/AB
    yE=yC-ArrowWidth*((xB-xA))/AB
    Line (A,B,fill=fill,width=width)
    D=(xD,yD);E=(xE,yE)
    Lines (D,B,E,fill=fill,width=width)

def SaveGraph (filename="Picture"):

#def WriteBMP(filename,width,height):
    #wd = Width #int(width)
    #ht = Height #int(height) 

    #f = file( filename, "wb" )
    path=os.path.abspath(os.path.dirname(__file__))
    fullPicName=path+'/'+filename+'.bmp'
    #f = open( filename, "wb" )
    f = open( fullPicName , "wb" )
    
    line_padding = (4 - (Width % 4)) % 4
    
    # write bitmap header
    if pyv==2: 
        f.write("BM")
        #f.write( longToString( 54 + 256*4 + self.ht*self.wd ) )   # DWORD size in bytes of the file
    elif pyv==3: 
        f.write(bytes("BM",'ascii'))
        
    f.write( longToString( 54 + Height*(Width*3 + line_padding) ) )   # DWORD size in bytes of the file
    f.write( longToString( 0 ) )    # DWORD 0
    #f.write( longToString( 54 + 256*4 ) )    # DWORD offset to the data
    f.write( longToString( 54  ) )
    f.write( longToString( 40 ) )    # DWORD header size = 40
    f.write( longToString( Width ) )    # DWORD image width
    f.write( longToString( Height ) )    # DWORD image height
    f.write( shortToString( 1 ) )    # WORD planes = 1
    f.write( shortToString( 24 ) )    # WORD bits per pixel = 8
    f.write( longToString( 0 ) )    # DWORD compression = 0
    f.write( longToString( Height * (Width * 3 + line_padding) ) )    # DWORD sizeimage = size in bytes of the bitmap = width * height
    f.write( longToString( 0 ) )    # DWORD horiz pixels per meter (?)
    f.write( longToString( 0 ) )    # DWORD ver pixels per meter (?)
    f.write( longToString( 0 ) )    # DWORD number of colors used = 256
    f.write( longToString( 0 ) )    # DWORD number of "import colors = len( self.palette )

    # write pixels
    #bitarray.reverse()
    #for row in bitarray:
    #    for pixel in row:
    for X in range(Width):
        for Y in range(Height):
            c = palette[bitarray[Y][X]]
            f.write( long24ToString(c) )
        for i in range(line_padding):
            if pyv==2: 
                f.write( chr( 0 ))
            elif pyv==3:
                f.write( bytes([0]))
    
    # close file
    f.close()
    print("\nImage "+filename+".bmp saved in "+path+"\n")

if __name__ == '__main__':
    print("\nLibxy, version : "
    	  +str (__version__)+
    	"""
Usage: 
form Libxy import *
InitGraph()
#
#Graphical and python programming instructions 
#
SaveGraph()

See xymaths.fr/Libxy for detailled informations
  """)

Mots clé
python, Libxy, librairie graphique, TICE

Quelques devoirs


    Voir aussi: