#!/usr/bin/python3
# ===================================================================
# my basic plot object
#
# Things to do/fix/add:
# - add tickmarks
# - cleanup offset origin (Ignore plot points with negative XY
# coordinate. They are outside the plot area.)
# - set a maximum XY axis values (not all the way to the edge)
# (ignore data points not in the plot area)
# - scale user data to fit the plot (scale user data range to
# plot data range)
#
# Note: This code was for practice. For the real world use one
# of the existing plot packages. They are much more capable
# and sophisticated.
# ===================================================================
import graphics as gr
# -------------------------------------------------------------------
# ---- plot class (accumulated plot functionality)
# -------------------------------------------------------------------
class MyPlot:
'''my basic plot object - graphics window, axes, ...'''
def __init__(self,
# ---- graphics window width and height
win_width=801,
win_height=801,
# ---- graphics window title
win_title='My Plot',
# ---- graphics window background color
win_bcolor='white',
# ---- origin location in graphics window
offset_origin = False,
x_origin=100,
y_origin=100,
# ---- draw axes?
draw_axes=True,
# ---- draw axes labels?
draw_axis_labels = False,
x_axis_label = "X AXIS",
y_axis_label = "Y AXIS"):
self.wtitle = win_title
self.wwidth = win_width
self.wheight = win_height
self.wbcolor = win_bcolor
self.offsetorigin = offset_origin
self.xorigin = x_origin
self.yorigin = y_origin
self.drawaxes = draw_axes
self.drawaxislabels = draw_axis_labels
self.xaxislabel = x_axis_label
self.yaxislabel = y_axis_label
if not self.offsetorigin:
self.xorigin = int(win_width/2)
self.yorigin = int(win_height/2)
self.axislables = False
self.win = gr.GraphWin(win_title,self.wwidth,self.wheight)
self.win.setBackground(win_bcolor)
if draw_axes:
self.draw_xy_axes()
# ---------------------------------------------------------------
# ---- vertical string (display helper function)
# ---------------------------------------------------------------
def vertical_string(self,string):
vstring = ''
l = len(string)
i = 0
while i < l:
vstring = vstring + string[i]
i += 1
if i >= l: break
vstring = vstring + '\n'
return vstring
# ---------------------------------------------------------------
# ---- true or false (display helper function)
# ---------------------------------------------------------------
def true_false(self,tf):
if tf: return 'True'
return 'False'
# ---------------------------------------------------------------
# ---- display internal state
# ---------------------------------------------------------------
def internal_state(self):
print('----------------------------------------------------')
print(f'win title = {self.wtitle}')
print(f'win width = {self.wwidth:<12} (pixels)')
print(f'win height = {self.wheight:<12} (pixels)')
print(f'win bcolor = {self.wbcolor}')
tf = self.true_false(self.offsetorigin)
print(f'offset origin = {tf:<12} (flag)')
print(f'x origin = {self.xorigin:<12} (win coords)')
print(f'y origin = {self.yorigin:<12} (win coords)')
tf = self.true_false(self.drawaxes)
print(f'draw axes = {tf:<12} (flag)')
tf = self.true_false(self.drawaxislabels)
print(f'draw axis labels = {tf:<12} (flag)')
print(f'x axis label = "{self.xaxislabel}"')
print(f'y axis label = "{self.yaxislabel}"')
print('----------------------------------------------------')
return
# ---------------------------------------------------------------
# ---- get graphics window object
# ---------------------------------------------------------------
def get_win(self):
return self.win
# ---------------------------------------------------------------
# ---- draw X,Y axes (also labels?)
# ---------------------------------------------------------------
def draw_xy_axes(self,linewidth=1,linecolor="black"):
go = [] # graphics object list
# ---- draw axes
if self.drawaxes:
if self.offsetorigin:
xstart = self.xorigin - 1
xend = self.wwidth - 1
xaxis_y = self.y_coord_for_x_axis()
ystart = self.wheight - self.yorigin - 1
yend = 0
yaxis_x = self.x_coord_for_y_axis()
else:
xstart = 0
xend = self.wwidth - 1
xaxis_y = self.y_coord_for_x_axis()
ystart = self.wheight - 1
yend = 0
yaxis_x = self.x_coord_for_y_axis()
##print('---------------------------------------')
##print(f'xstart = {xstart}')
##print(f'xend = {xend}')
##print(f'xaxis y = {xaxis_y}')
##print(f'ystart = {ystart}')
##print(f'yend = {yend}')
##print(f'yaxis x = {yaxis_x}')
##print('---------------------------------------')
# ---- X axis
xl = gr.Line(gr.Point(xstart,xaxis_y),
gr.Point(xend,xaxis_y))
xl.setWidth(linewidth)
xl.setFill(linecolor)
xl.draw(self.win)
go.append(xl)
# ---- Y axis
yl = gr.Line(gr.Point(yaxis_x,ystart),
gr.Point(yaxis_x,yend))
yl.setWidth(linewidth)
yl.setFill(linecolor)
yl.draw(self.win)
go.append(yl)
# ---- draw axis labels (only for offset plots)
if self.drawaxislabels and self.offsetorigin:
# X axis lable
xaxis = gr.Text(gr.Point(self.xorigin,self.yorigin+30),
self.xaxislabel)
xaxis.setSize(24)
xaxis.setStyle("bold")
xaxis.draw(self.win)
go.append(xaxis)
# ---- Y axis label
vstring = self.vertical_string(self.yaxislabel)
yaxis = gr.Text(gr.Point(self.yorigin,self.xorigin-30),
vstring)
yaxis.setSize(24)
yaxis.setStyle("bold")
yaxis.draw(self.win)
go.append(yaxis)
return go
# ---------------------------------------------------------------
# ---- draw a point (small circle) using window coordinates
# ---------------------------------------------------------------
def draw_point(self,wx,wy,color='red',size=4):
p = gr.Circle(gr.Point(wx,wy),size)
p.setFill(color)
p.setOutline('black')
p.draw(self.win)
return p
# ---------------------------------------------------------------
# ---- given XY Cartesian coordinates
# ---- return their window XY coordinates
# ---------------------------------------------------------------
def window_xy(self,x,y):
wx = round(self.xorigin + x)
wy = round(self.wheight -self.yorigin - y)
return (wx,wy)
# ---------------------------------------------------------------
# ---- Y coordinate for X axis
# ---------------------------------------------------------------
def y_coord_for_x_axis(self):
return self.wheight - self.yorigin - 1
# ---------------------------------------------------------------
# ---- X coordinate for y axis
# ---------------------------------------------------------------
def x_coord_for_y_axis(self):
if self.offsetorigin:
return self.xorigin - 1
else:
return self.wheight - self.yorigin - 1
# ---------------------------------------------------------------
# ---- pause - wait for mouse click
# ---------------------------------------------------------------
def wait_for_mouse_click(self):
click = self.win.getMouse()
return (click.getX(),click.getY())
# -------------------------------------------------------------------
# ---- main
# -------------------------------------------------------------------
if __name__ == '__main__':
import math
# ---------------------------------------------------------------
# ---- plot a point (main support function)
# ---------------------------------------------------------------
def plot_a_point(x,y):
wx,wy = plt.window_xy(x,y)
p = plt.draw_point(wx,wy)
return p
# ---------------------------------------------------------------
# ---- plot a sine curve (main support function)
# ---------------------------------------------------------------
def plot_sine_curve():
go = [] # graphics objects
x = 0
for deg in range(0,361,30):
rad = math.radians(deg)
y = math.sin(rad) * 100
wx,wy = plt.window_xy(x,y)
p = plt.draw_point(wx,wy,color='green',size=8)
go.append(p)
x += 30
return go
# ---- offset plot
#plt = MyPlot(offset_origin=True,x_origin=100,y_origin=200)
# ---- center (Cartesian) plot
plt = MyPlot()
plt.internal_state()
plot_a_point(200,200)
plot_a_point(-200,200)
plot_a_point(-200,-200)
plot_a_point(200,-200)
plot_sine_curve()
plt.wait_for_mouse_click()
plt.get_win().close()