绘图程序结构简单,逻辑也不复杂,例如在工具栏 (tool_frame) 中选择画线 (draw_line), 在选项栏(top_frame) 设置,然后在画布 (canvas_frame) 中进行绘制即可。其他如画方画园等,无论是操作还是实现,都基本类同。
1. 效果图:
2. 代码:
import os
import sys
import tkinter as tk
from tkinter import colorchooser, messagebox, filedialog
# 为引用 utils,在 site-packages 目录下新建 mypath.pth 文件,
# 添加所需导入模块的目录路径, 如 ‘x01.lab/py/’ 所在路径。
import utils
from paint.core import CanvasFrame, R, ToolFrame, TopFrame
sys.path.append(utils.R.CurrentDir)
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title('x01.paint')
utils.R.win_center(self)
self.menu = tk.Menu(self)
utils.R.generate_menus(self,['file', 'edit', 'help'], sepindies=(2, 4))
self.configure(menu=self.menu)
self.top_frame = TopFrame(self)
self.top_frame.configure(height=25)
self.top_frame.pack(side='top', fill='x', pady=2)
self.tool_frame = ToolFrame(self)
self.tool_frame.configure(width=80, relief='raised')
self.tool_frame.pack(side='left', fill='y', pady=3)
self.canvas_frame = CanvasFrame(self)
self.canvas_frame.pack(side='right', fill='both', expand='yes')
self.tool_frame.tool_click(0)
def file_1_new(self):
self.canvas_frame.canvas.delete(tk.ALL)
self.canvas_frame.canvas.config(bg='white')
def file_2_save(self):
filename = filedialog.asksaveasfilename(master=self, title='Save',
filetypes=[('postscript file', '*.ps'), ('All Files', '*.*')])
if not filename: return
self.canvas_frame.canvas.postscript(file=filename, colormode='color')
messagebox.showinfo(title=self.title, message=filename + ' Save success!')
def file_3_quit(self):
self.destroy()
def edit_1_undo(self):
items = list(self.canvas_frame.canvas.find('all'))
try:
last_item = items.pop()
except IndexError:
return
self.canvas_frame.canvas.delete(last_item)
def edit_2_zoom_in(self):
self.canvas_frame.canvas.scale('all', 0,0,1.2,1.2)
self.canvas_frame.canvas.config(scrollregion=self.canvas_frame.canvas.bbox(tk.ALL))
def edit_3_zoom_out(self):
self.canvas_frame.canvas.scale('all', 0,0,0.8,0.8)
self.canvas_frame.canvas.config(scrollregion=self.canvas_frame.canvas.bbox(tk.ALL))
def help_1_about(self):
messagebox.showinfo('x01.paint', '绘图程序,版权属于x01(黄雄)所有。')
if __name__ == "__main__":
win = MainWindow()
win.mainloop()
import cmath
import math
import os
import sys
import tkinter as tk
from tkinter import colorchooser, ttk
import utils
class R:
Functions = (
"draw_line", "draw_oval", "draw_rectangle", "draw_arc",
"draw_triangle", "draw_star", "draw_irregular_line", "draw_super_shape",
"draw_text", "delete_item", "fill_item", "duplicate_item",
"move_to_top", "drag_item", "enlarge_item_size", "reduce_item_size"
)
SuperShapes = {
"shape A": (1.5, 1.5, 5, 2, 7, 7),
"shape B": (1.5, 1.5, 3, 5, 18, 18),
"shape C": (1.4, 1.4, 4, 2, 4, 13),
"shape D": (1.6, 1.6, 7, 3, 4, 17),
"shape E": (1.9, 1.9, 7, 3, 6, 6),
"shape F": (4, 4, 19, 9, 14, 11),
"shape G": (12, 12, 1, 15, 20, 3),
"shape H": (1.5, 1.5, 8, 1, 1, 8),
"shape I": (1.2, 1.2, 8, 1, 5, 8),
"shape J": (8, 8, 3, 6, 6, 6),
"shape K": (8, 8, 2, 1, 1, 1),
"shape L": (1.1, 1.1, 16, 0.5, 0.5, 16)
}
class CanvasFrame(tk.Frame):
current_item = None
fill = 'red'
outline = 'red'
width = 2.0
number_of_spokes = 5
arrow = None
dash = None
x1,y1,x2,y2 = 0,0,0,0
selected_super_shape = 'shape A'
def __init__(self, master):
super().__init__(master)
self.master = master
self.init_canvas()
self.bind_events()
def bind_events(self):
self.canvas.bind('<Button-1>', self.mouse_left_pressed)
self.canvas.bind('<Button1-Motion>', self.mouse_pressed_motion)
self.canvas.bind('<Motion>', self.mouse_unpressed_motion)
def mouse_left_pressed(self, e=None):
self.x1 = self.x2 = e.x
self.y1 = self.y2 = e.y
self.execute_selected_method()
def mouse_pressed_motion(self, e=None):
self.x2 = e.x
self.y2 = e.y
self.canvas.delete(self.current_item)
self.execute_selected_method()
def mouse_unpressed_motion(self, e=None):
self.master.tool_frame.current_coordinate_label.config(text='x:{}\ny:{}'.format(e.x,e.y))
def init_canvas(self):
self.canvas = tk.Canvas(self, background='white', width=500, height=500, scrollregion=(0,0,800,800))
x_scroll = tk.Scrollbar(self, orient='horizontal')
x_scroll.pack(side='bottom', fill='x')
x_scroll.config(command=self.canvas.xview)
y_scroll = tk.Scrollbar(self, orient='vertical')
y_scroll.pack(side='right', fill='y')
y_scroll.config(command=self.canvas.yview)
self.canvas.config(xscrollcommand=x_scroll.set, yscrollcommand=y_scroll.set)
self.canvas.pack(side='right', fill='both', expand='yes')
def execute_selected_method(self):
self.current_item = None
func = getattr(self, self.master.tool_frame.selected_function, self.function_not_defined)
func()
def function_not_defined(self): pass
def draw_line(self):
self.current_item = self.canvas.create_line(self.x1, self.y1, self.x2, self.y2, fill=self.fill,
width=self.width, arrow=self.arrow, dash=self.dash)
def draw_oval(self):
self.current_item = self.canvas.create_oval(self.x1, self.y1, self.x2, self.y2, outline=self.outline,
fill=self.fill, width=self.width)
def draw_rectangle(self):
self.current_item = self.canvas.create_rectangle(self.x1, self.y1, self.x2, self.y2, outline=self.outline,
fill=self.fill, width=self.width)
def draw_arc(self):
self.current_item = self.canvas.create_arc(self.x1,self.y1,self.x2, self.y2, outline=self.outline,
fill=self.fill, width=self.width)
def draw_triangle(self):
dx = self.x2 - self.x1
dy = self.y2 - self.y1
z = complex(dx,dy)
radius, angle0 = cmath.polar(z)
edges = 3
points = list()
for edge in range(edges):
angle = angle0 + edge * (2*math.pi)/edges
points.append(self.x1 + radius * math.cos(angle))
points.append(self.y1 + radius * math.sin(angle))
self.current_item = self.canvas.create_polygon(points, outline=self.outline,
fill=self.fill, width=self.width)
def draw_star(self):
dx = self.x2 - self.x1
dy = self.y2 - self.y1
z = complex(dx,dy)
radius_out, angle0 = cmath.polar(z)
radius_in = radius_out / 2
points = []
for edge in range(self.number_of_spokes):
angle = angle0 + edge * (2*math.pi) / self.number_of_spokes
points.append(self.x1 + radius_out * math.cos(angle))
points.append(self.y1 + radius_out * math.sin(angle))
angle += math.pi / self.number_of_spokes
points.append(self.x1 + radius_in * math.cos(angle))
points.append(self.y1 + radius_in * math.sin(angle))
self.current_item = self.canvas.create_polygon(points, outline=self.outline,
fill=self.fill, width=self.width)
def draw_irregular_line(self):
self.current_item = self.canvas.create_line(self.x1,self.y1,self.x2,self.y2,
fill=self.fill, width=self.width)
self.canvas.bind('<B1-Motion>', self.update_irregular_line)
def update_irregular_line(self, e=None):
self.x1, self.y1 = self.x2, self.y2
self.x2,self.y2 = e.x, e.y
self.draw_irregular_line()
def draw_super_shape(self):
points = self.get_shape_points(self.selected_super_shape)
self.current_item = self.canvas.create_polygon(points, fill=self.fill,
outline=self.outline, width=self.width)
def get_shape_points(self, key):
a,b,m,n1,n2,n3 = R.SuperShapes[key]
points = []
for i in self.float_range(0, 2*math.pi, 0.01):
raux = (abs(1 / a * abs(math.cos(m*i/4))) ** n2
+ abs(1 / b * abs(math.sin(m*i/4))) ** n3)
r = abs(raux) ** (-1/n1)
x = self.x2 + r * math.cos(i)
y = self.y2 + r * math.sin(i)
points.extend((x,y))
return points
def float_range(self, start, end, step):
while start < end:
yield start
start += step
def draw_text(self):
text = self.master.top_frame.text_entry.get()
fontsize = self.master.top_frame.fontsize_spinbox.get()
self.current_item = self.canvas.create_text(self.x2,self.y2, text=text,
font=('', fontsize), fill=self.fill)
def delete_item(self):
self.current_item = None
self.canvas.delete('current')
def fill_item(self):
try:
self.canvas.itemconfig('current', fill=self.fill, outline=self.outline)
except TclError:
self.canvas.itemconfig('current', fill=self.fill)
def duplicate_item(self):
try:
function_name = 'create_' + self.canvas.type('current')
except TypeError:
return
coordinates = tuple(map(lambda i: i+10, self.canvas.coords('current')))
configs = self.get_configs()
self.function_wrapper(function_name, coordinates, configs)
def get_configs(self):
configs = {}
for k,v in self.canvas.itemconfig('current').items():
if v[-1] and v[-1] not in ['0', '0.0', '0,0', 'current']:
configs[k] = v[-1]
return configs
def function_wrapper(self, function_name, *arg, **kwargs):
func = getattr(self.canvas, function_name)
func(*arg, **kwargs)
def move_to_top(self):
self.current_item = None
self.canvas.tag_raise('current')
def drag_item(self):
self.canvas.move('current', self.x2-self.x1, self.y2-self.y1)
self.canvas.bind('<B1-Motion>', self.drag_update)
def drag_update(self, e=None):
self.x1, self.y1 = self.x2, self.y2
self.x2, self.y2 = e.x, e.y
self.drag_item()
def enlarge_item_size(self):
self.current_item = None
if self.canvas.find_withtag('current'):
self.canvas.scale('current', self.x2, self.y2, 1.2, 1.2)
self.canvas.config(scrollregion=self.canvas.bbox(tk.ALL))
def reduce_item_size(self):
self.current_item = None
if self.canvas.find_withtag('current'):
self.canvas.scale('current', self.x2, self.y2, 0.8, 0.8)
self.canvas.config(scrollregion=self.canvas.bbox(tk.ALL))
class ToolFrame(tk.Frame):
def __init__(self, master):
super().__init__(master=master)
self.master = master
self.selected_function = R.Functions[0]
self.foreground = 'red'
self.background = 'white'
self.create_tool_buttons()
self.create_color_palette()
self.create_current_coordinate_label()
def create_current_coordinate_label(self):
self.current_coordinate_label = tk.Label(self, text='x:0\ny:0')
self.current_coordinate_label.grid(row=11,column=1, columnspan=2,padx=1, sticky='w')
def create_color_palette(self):
self.color_palette = tk.Canvas(self, height=55, width=55)
self.color_palette.grid(row=10,column=1,columnspan=2, pady=5, padx=5)
self.background_rect = self.color_palette.create_rectangle(15,15,40,40,
outline=self.background, fill=self.background)
self.foreground_rect = self.color_palette.create_rectangle(1,1,33,33,
outline=self.foreground, fill=self.foreground)
self.bind_color_palette()
def bind_color_palette(self):
self.color_palette.tag_bind(self.background_rect, '<Button-1>', self.set_background_color)
self.color_palette.tag_bind(self.foreground_rect, '<Button-1>', self.set_foreground_color)
def set_foreground_color(self, e=None):
self.foreground = colorchooser.askcolor(title='select foreground color')[-1]
self.color_palette.itemconfig(self.foreground_rect, outline=self.foreground, fill=self.foreground)
def set_background_color(self, e=None):
self.background = colorchooser.askcolor(title='select background color')[-1]
self.color_palette.itemconfig(self.background_rect, outline=self.background, fill=self.background)
def create_tool_buttons(self):
for i, name in enumerate(R.Functions):
icon = tk.PhotoImage(file=os.path.join(utils.R.CurrentDir, 'paint/icons/'+name+'.gif'))
self.button = tk.Button(self, image=icon, command=lambda i=i: self.tool_click(i))
self.button.grid(row=i//2,column=1+i%2, sticky='nsew')
self.button.image=icon
def tool_click(self, i):
self.selected_function = R.Functions[i]
self.remove_top_function()
self.add_top_function()
self.master.canvas_frame.bind_events()
def remove_top_function(self):
for child in self.master.top_frame.winfo_children():
child.destroy()
def add_top_function(self):
name = self.selected_function.replace('_', ' ').capitalize() + ':'
tk.Label(self.master.top_frame, text=name).pack(side='left', padx=5)
icon = tk.PhotoImage(file=os.path.join(utils.R.CurrentDir, 'paint/icons/'+self.selected_function+'.gif'))
label = tk.Label(self.master.top_frame, image=icon)
label.image = icon
label.pack(side='left')
self.master.top_frame.create_options(self.selected_function)
class TopFrame(tk.Frame):
def __init__(self, master):
super().__init__(master=master)
def create_options(self, selected_function):
name = '{}_options'.format(selected_function)
func = getattr(self, name, self.function_not_defined)
func()
def function_not_defined(self): pass
def draw_line_options(self):
self.create_fill_combobox()
self.create_width_combobox()
self.create_arrow_combobox()
self.create_dash_combobox()
def draw_oval_options(self):
self.create_fill_combobox()
self.create_outline_combobox()
self.create_width_combobox()
def draw_rectangle_options(self):
self.create_fill_combobox()
self.create_outline_combobox()
self.create_width_combobox()
def draw_arc_options(self):
self.create_fill_combobox()
self.create_outline_combobox()
self.create_width_combobox()
def draw_triangle_options(self):
self.create_fill_combobox()
self.create_outline_combobox()
self.create_width_combobox()
def draw_star_options(self):
self.create_spokes_combobox()
self.create_fill_combobox()
self.create_outline_combobox()
self.create_width_combobox()
def draw_irregular_line_options(self):
self.create_fill_combobox()
self.create_width_combobox()
def draw_super_shape_options(self):
self.create_shapes_combobox()
self.create_fill_combobox()
self.create_outline_combobox()
self.create_width_combobox()
def draw_text_options(self):
self.create_text_entry()
self.create_fontsize_spinbox()
self.create_fill_combobox()
def fill_item_options(self):
self.create_fill_combobox()
self.create_outline_combobox()
def create_fill_combobox(self):
tk.Label(self,text='Fill:').pack(side='left', padx=5)
self.fill_combobox = ttk.Combobox(self, state='readonly', width=5)
self.fill_combobox.pack(side='left')
self.fill_combobox['values'] = ('none', 'fg', 'bg', 'black', 'white')
self.fill_combobox.bind('<<ComboboxSelected>>', self.set_fill)
self.fill_combobox.set(self.master.canvas_frame.fill)
def create_width_combobox(self):
tk.Label(self, text='Width:').pack(side='left', padx=5)
self.width_combobox = ttk.Combobox(self, state='readonly', width=3)
self.width_combobox.pack(side='left')
self.width_combobox['values'] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
self.width_combobox.bind('<<ComboboxSelected>>', self.set_width)
self.width_combobox.set(1)
def create_arrow_combobox(self):
tk.Label(self, text='Arrow:').pack(side='left', padx=5)
self.arrow_combobox = ttk.Combobox(self, state='readonly', width=5)
self.arrow_combobox.pack(side='left')
self.arrow_combobox['values'] = ('none', 'first', 'last', 'both')
self.arrow_combobox.bind('<<ComboboxSelected>>', self.set_arrow)
self.arrow_combobox.set('none')
def create_dash_combobox(self):
tk.Label(self, text='Dash:').pack(side='left', padx=5)
self.dash_combobox = ttk.Combobox(self, state='readonly', width=5)
self.dash_combobox.pack(side='left')
self.dash_combobox['values'] = ('none', 'small', 'mediun', 'large')
self.dash_combobox.bind('<<ComboboxSelected>>', self.set_dash)
self.dash_combobox.set('none')
def create_outline_combobox(self):
tk.Label(self, text='Outline:').pack(side='left', padx=5)
self.outline_combobox = ttk.Combobox(self, state='readonly', width=5)
self.outline_combobox.pack(side='left')
self.outline_combobox['values'] = ('none', 'fg', 'bg', 'black', 'white')
self.outline_combobox.bind('<<ComboboxSelected>>', self.set_outline)
self.outline_combobox.set(self.master.canvas_frame.outline)
def create_spokes_combobox(self):
tk.Label(self,text='Spokes:').pack(side='left', padx=5)
self.spokes_combobox = ttk.Combobox(self, state='readonly', width=5)
self.spokes_combobox.pack(side='left')
self.spokes_combobox['values'] = tuple(i for i in range(5,50))
self.spokes_combobox.bind('<<ComboboxSelected>>', self.set_spokes)
self.spokes_combobox.set(5)
def create_shapes_combobox(self):
tk.Label(self,text='Shapes:').pack(side='left', padx=5)
self.shapes_combobox = ttk.Combobox(self, state='readonly', width=8)
self.shapes_combobox.pack(side='left')
self.shapes_combobox['values'] = sorted(tuple(i for i in R.SuperShapes.keys()))
self.shapes_combobox.bind('<<ComboboxSelected>>', self.set_shapes)
self.shapes_combobox.set(self.master.canvas_frame.selected_super_shape)
def create_text_entry(self):
tk.Label(self, text='Text:').pack(side='left', padx=5)
self.text_entry = tk.Entry(self, width=20)
self.text_entry.pack(side='left')
def create_fontsize_spinbox(self):
tk.Label(self, text='Font size:').pack(side='left', padx=5)
v = tk.IntVar()
v.set(12)
self.fontsize_spinbox = tk.Spinbox(self, from_=2,to=100,width=3, textvariable=v)
self.fontsize_spinbox.pack(side='left')
def set_width(self, e=None):
self.master.canvas_frame.width = float(self.width_combobox.get())
def set_fill(self, e=None):
clr = self.fill_combobox.get()
if clr == 'none':
self.master.canvas_frame.fill = ''
elif clr == 'fg':
self.master.canvas_frame.fill = self.master.tool_frame.foreground
elif clr == 'bg':
self.master.canvas_frame.fill = self.master.tool_frame.background
else :
self.master.canvas_frame.fill = clr
def set_arrow(self, e=None):
self.master.canvas_frame.arrow = self.arrow_combobox.get()
def set_dash(self, e=None):
dash = self.fill_combobox.get()
if dash == 'none':
self.master.canvas_frame.dash = None
elif dash == 'small':
self.master.canvas_frame.dash = 1
elif dash == 'medium':
self.master.canvas_frame.dash = 15
elif dash == 'large':
self.master.canvas_frame.dash = 100
def set_outline(self, e=None):
v = self.outline_combobox.get()
if v == 'none':
self.master.canvas_frame.outline = ''
elif v == 'fg':
self.master.canvas_frame.outline = self.master.tool_frame.foreground
elif v == 'bg':
self.master.canvas_frame.outline = self.master.tool_frame.background
else:
self.master.canvas_frame.outline = v
def set_spokes(self, e=None):
self.master.canvas_frame.number_of_spokes = int(self.spokes_combobox.get())
def set_shapes(self, e=None):
self.master.canvas_frame.selected_super_shape = self.shapes_combobox.get()
if __name__ == "__main__":
pass
3. 下载:
来源:oschina
链接:https://my.oschina.net/u/4276444/blog/4289788