【茶余饭后002】命令行单行指令式python海龟画笔练习工具 主要用turtle库写的 有python就能运行

命令行单行指令式python海龟画笔练习工具 也可以用鼠标

效果展示:

本代码供练习海龟画笔的命令 和综合复习python语法使用,如有疑问请留言
在这里插入图片描述

在这里插入图片描述

代码展示:

话不多说 代码伺候

'''运行后直接在 控制台 输入代码指令 即可控制画笔 使用鼠标左中右键也阔以哦
首次读代码的时候 如果看到函数 简单看一眼就可以跳过 要找到函数的调用点再读
运行以后由于还要在控制台输入命令  所以可以适当把编辑器缩小 留下终端区即可
代码若是改坏了 可去这里下载原始版本 https://blog.csdn.net/weixin_43521165/article/details/108807063
'''

import turtle
import threading
t = turtle.Pen()  # 画笔一 用于画图
t2 = turtle.Pen()  # 画笔二 用于常用海龟命令的打印
t3 = turtle.Pen()  # 画笔三 用于在需要时给画布上的位置做标记
t2.hideturtle()
t3.hideturtle()
# 基础设置
width = 1000  # width变量存储画布的宽
height = 700  # height变量存储画布的高
draw_name = '花华哗画'  # draw_name变量存储画布的标题
t.screen.setup(width, height)  # 设置画布的宽高
t.screen.title(draw_name)  # 设置画布窗口的标题
# t.screen.tracer(0)
is_order_detail_show = False
old_file=None  # 之前老的代码文件名
# 用字典嵌套的格式存储常用海龟画笔的命令
normal_order = {
    '请点击你要查看的指令集或功能\n1画笔移动': {'1画笔移动类:\n撤销':'undo()','前进': 'forward()', '后退': 'backward()', '左转': 'left()', '右转': 'right()', '文字书写': 'write("内容",font=("字体(可以空着)",大小)))',
                                '移动到某点': 'goto(x,y)', '抬笔': 'penup()', '落笔': 'pendown()', '隐藏画笔': 'hideturtle()','画笔位置': 'position()',
                                '显示画笔': 'showturtle()', '画笔速度': 'speed() # 0是最快的 1~10逐渐变快', '画笔颜色': 'color()  # 颜色单词记得是引号包裹的字符串', '画笔粗细': 'pensize()', '画圆': 'circle(r:半径)'},
                        '2形状和颜色': {'2形状和颜色类\n画圆弧': 'circle(r:半径,L:弧度,p:内接多边形)', '画笔形状': 'shape()',
                                '打印所有画笔形状': 'screen().getshapes()', '画笔盖章': 'stamp()', '清除盖章': 'clearstamp()',
                                '颜色填充': 'fillcolor()', '开始填充前命令': 'begin_fill()','结束填充命令': 'end_fill()'},
                        '3窗口操作': {'3窗口操作类\n窗口标题': 'screen.title()', '背景颜色': 'screen.bgcolor()', '窗口大小': 'screen.setup(宽,高)',
                                     '背景图片': 'screen.bgpic() 需要png 或 gif为后缀名的RGBA模式图片', '获取窗口宽度': 'screen.window_width()', '获取窗口高度': 'screen.window_height()',
                                '重新设置海归画笔坐标系': 'screen.setworldcoordindates(x1,y1,x2,y2)'},
                        '4键盘鼠标事件': {
                                '4键盘鼠标事件类\n监听鼠标点击': 'screen.onclick(fun) fun代表某函数名,该函数会接收传过来的鼠标点击的坐标x,y',
                                '监听键盘按下': 'screen.onkey(fun,key),此键盘监听需要配合screen.listen()命令一起使用',
                                '监听鼠标对画笔的拖动': 'ondrag(t.goto,1) 1是左键 2是滚轮 3是右键 ',
                                '弹出获取用户输入的弹框':'turtle.textinput("标题","说明内容") # 有返回值 取消为None'},
                        '5临摹模式': ' ',
                        '6继续上次绘制': '',
                        '7直接开始绘制吧': '',
                        '8结束绘制':''
}

# 显示海归画笔的常用命令
def show_normal_order(x=None):
    global is_order_detail_show
    if x != None :  # 如果有参数传进来 则按照参数指定的具体指令集进行显示
        if is_order_detail_show == False:
            hide_normal_order()
            is_order_detail_show = True
            t2.color('red')
            count = 0
            for i in normal_order[x]:
                t2.penup()
                t2.goto(-t.screen.window_width()//2+10,
                        t.screen.window_height()//3-count*35)
                t2.write(i+':'+normal_order[x][i], font=('', 20))
                t2.pendown()
                count += 1
            t2.penup()
            t2.goto(-t.screen.window_width()//2+10,
                    t.screen.window_height()//3-count*35)
            t2.color('blue')
            t2.write('点击画布右上角1/8区域,可再次显示指令集', font=('', 20))
    else:  # 如果没有参数传进来 则显示指令集的外层分类
        is_order_detail_show = False
        t2.color('blue')
        count = 0
        for i in normal_order:
            t2.penup()
            t2.goto(-t.screen.window_width()//2+10, t.screen.window_height()//3-count*45)
            t2.write(i, font = ('', 30))
            t2.pendown()
            count += 1
        t2.penup()
        t2.goto(-t.screen.window_width()//2+10,
                t.screen.window_height()//3-(count+5)*45)
        t2.color('pink')
        t2.write('''使用技巧:
点击画布右下角1/4区域,可隐藏指令集,点击右上角1/8再次显示
鼠标左键可以拖曳画笔(请慢一点拖动) 右键可以无痕移动位置 中键可以撤销操作\n
在终端区输入命令时 请都带上画笔名字t 例如 t.color('red')
在终端区使用键盘的上↑下↓方向键可以切换之前输入过的命令
必须在终端区输入break才可以完整结束绘制 然后生成记录绘制过程的python代码''', font=('', 20))
# 隐藏海归画笔的命令
def hide_normal_order():
    global is_order_detail_show
    is_order_detail_show = True
    t2.clear()

def choose_order(x,y):
    if x < -200 and is_order_detail_show == False:
        if  0 < y - t.screen.window_height()//3 < 35:
            show_normal_order('请点击你要查看的指令集或功能\n1画笔移动')
        elif -45 < y - t.screen.window_height()//3 < 0:
            show_normal_order('2形状和颜色')
        elif -90 < y - t.screen.window_height()//3 < -45:
            show_normal_order('3窗口操作')
        elif -135 < y - t.screen.window_height()//3 < 90:
            show_normal_order('4键盘鼠标事件')
        elif -180 < y - t.screen.window_height()//3 < -135:
            import tkinter
            from tkinter import filedialog
            new_bg = filedialog.askopenfilename(
                title='请选择png格式的图片', initialfile='*.png')
            try:
                t.screen.bgpic(new_bg)
            except:
                print('\n\n路径有误 要求输入图片文件的路径必须是正确的 并且包括图片的后缀名一起 例如 code.png')
                print('(下面光标闪烁区 依旧可以写命令行代码 并按回车执行:)')

        elif -225 < y - t.screen.window_height()//3 < -180:
            import tkinter
            from tkinter import filedialog
            global old_file
            global old_file_orders
            old_file = filedialog.askopenfilename(
                title='请选择之前的绘制文件', initialfile='*.py')
            if old_file != '' :
                with open(old_file, 'r', encoding='utf8') as f:
                    old_file_orders = f.readlines()
                for index, i in enumerate(old_file_orders):
                    if i.strip() == 'turtle.done()':
                        old_file_orders[index]=''
                    else:
                        exec(i)
            

        elif -270 < y - t.screen.window_height()//3 < -225:
            hide_normal_order()
            new_name=turtle.textinput('给画起个新名字吧','有名字 请输入,取消 则将会用默认名字')
            global draw_name  # 使得之前函数外定义的draw_name全局变量 可以被函数内的代码修改
            if new_name != '' and new_name != None:  # 如果用户重新定义了画布名则直接设置 否则不处理
                draw_name = new_name
                t.screen.title(draw_name)
        elif -315 < y - t.screen.window_height()//3 < -270:
            hide_normal_order()
            ready_draw('break')

    elif x > 0 and y < 0:
        hide_normal_order()
    elif x > t.screen.window_height()//4 and y > t.screen.window_height()//4:  # 画布右上角1/8区域
        hide_normal_order()
        show_normal_order()

# 定义鼠标滚轮点击的作用 为撤销上次操作
def go_back(x,y):
    t.undo()
    try :
        order_list.pop()
    except:
        pass
    # order_list.append('t.undo()')
# 定义鼠标左键可以拖动画笔
def drag_to(x,y):
    order_list.append(f't.goto({x},{y})')
    t.goto(x, y)
# 鼠标右键可以移动画笔
def go_somewhere(x,y):
    t.penup()
    t.goto(x, y)
    print(f'画笔跳动到新位置({x},{y})')
    t.pendown()
    #传入列表时: extend() 可以把两个列表的数据融合到一个中去  而append()则会把一个列表添加到另一个列表 成为子母列表
    order_list.extend(['t.penup()', f't.goto({x},{y})', 't.pendown()'])

'''  这也时一种注释的写法 叫做三引号法 首位三个单引号或三个双引号都可以 一致就行
自定义一个画弧线的方法 radian_line 供终端区随时调用 但不一定用的上 细练的话会用上
angle:旋转角度,distance:画笔单次前进距离,n:画笔总共重复旋转和前进几次,x:画笔粗细,**argv:接收画笔颜色参数 例如color='red'
'''
def radian_line(angle, distance, n, x=1, **argv):
    t.pensize(x)  # 设置画笔粗细
    color = argv.get('color')  # 从传进来的键值对形式的参数取color键对应的值 如果没传则get结果为None
    if color != None:  # 如果有颜色参数传进来 则对画笔颜色进行相应设置
        t.color(color) 
    for i in range(n):
        t.right(angle)  # 向右转angle度 若要向左转传入负数即可
        t.forward(distance)  # 向前走dis的距离  若要向后走传入负数即可


#  对画笔进行指令控制和执行
order_list = []
import time
time_use=int(time.time())
def ready_draw(end=None):
    global time_use
    keep_going = True
    while keep_going == True:
        print('----按回车键可以执行输入的命令,输入break可以终止创作,点击画布右上角可以查看指令集----')
        if end != 'break':
            order = input('++++例如输入t.goto(10,20),请输入您对画笔的t命令 :')
        else:
            order = end
        try:
            if order != 'break':
                t_last_pos = t.position()
                # eval 函数可以将字符串形式的代码转化为python可执行的代码 但不能处理赋值语句(即带等号的字符)
                # eval(order)
                # 若要是想连赋值语句也一并执行 则可以用更牛掰的exec 替换一下eval就行 如下行 这内置函数简直帅呆了
                exec(order)
                t3.penup()
                t3.goto(t.position())  # 画笔t3的位置随时跟随t1 主要是为了单独做标记用 写代码时名字要用t3
                t3.pendown()
                print('====您刚才输入的指令为:', order, '========\n')
                order_list.append(order)
            else:
                keep_going = False
            import time
            # 两次输入时间差>60秒则调用一次命令保存功能 避免突然断电等意外而丢失已输入过的命令 如果没有意外发生则该文件自动删除
            if (int(time.time()) - time_use) > 60:
                    with open('画笔命令临时记录表.json', 'w', encoding='utf8') as f:
                        f.write(str(order_list))  # 真有意外发生 以后需要自己动手从文件还原并拼接代码 届时考验你的时候就到了
                    time_use = int(time.time())
        except Exception as e:
            print(e)
            print(f'!!!!!!刚才输入的-> {order} <-语法有误,请检查后重新输入--!!!!!!!!!!\n')
    else :
        if len(order_list) != 0:
            print('\n本次绘制所有的未撤销指令为:\n {}'.format(order_list), '\n以上是所有输入的未撤销的指令')
            import time
            import os
            dir_name = '画图成果文件夹'
            if not os.path.exists(dir_name):
                os.mkdir(dir_name)
            if not old_file:
                x = int(time.time())
                with open(f'{dir_name}{os.sep}{draw_name}{x}.py', 'w', encoding='utf8') as f:
                    f.write('#'+time.asctime())
                    f.write(
                        f"# 画笔绘制记录本\nimport turtle   \nt = turtle.Pen()  # 画笔一 用于画图\n# 基础设置\nt.screen.setup({width}, {height})  # 设置画布的宽高\nt.screen.title('{draw_name}')  # 设置画布窗口的标题\n")
                    for i in order_list:
                        f.write(i+'\n')
                    f.write('t.penup()\nt.home()\nt.pendown()\nt.hideturtle()\nturtle.done()')
                print(f'本次所有指令已经存入 --{dir_name}{os.sep}{draw_name}{x}.py文档 --之后可打开查看')
                try:
                    os.remove('画笔命令临时记录表.json')
                except:
                    pass
            else:
                with open(old_file, 'w', encoding='utf8') as f2:
                    f2.write(''.join(old_file_orders))
                    for j in order_list:
                        f2.write(j+'\n')
                    f2.write('\nt.penup()\nt.home()\nt.pendown()\nt.hideturtle()\nturtle.done()')
                print(f'本次所有指令已经新加入 --{old_file}文档 --之后可打开查看')
                try :
                    os.remove('画笔命令临时记录表.json')
                except:
                    pass
        else:
            print('再见咯~')
show_normal_order()  # 调用函数让上面常用命令展示

t.screen.onclick(choose_order, 1)  # 监听鼠标左键的点击事件 1是左键 2是滚轮 3 是右键
t.screen.onclick(go_back, 2)  # 监听鼠标滚轮的点击 点击后可以撤销之前的操作
t.screen.onclick(go_somewhere, 3)  # 监听鼠标右键的点击 点击移动画笔到指定位置
t.ondrag(drag_to)  # 监听鼠标右键的拖动 当鼠标拖动画笔箭头进行移动的时候 画笔会跟随鼠标移动

#  用一个新开的线程去负责控制台指令的收集和执行 避免海龟画布卡机  可以理解1个线程就是1台机器
new_thread = threading.Thread(target=ready_draw)  # target = 后面跟的是函数的名字 代表用这个线程启动某函数
new_thread.setDaemon(True)  # 线程守护模式  主线程结束 则所有子线程也结束  本程序中 海龟画板就是子线程
new_thread.start()  # 启动该线程

turtle.done()



素描图片的生成 需要pillow库

from PIL import Image, ImageOps, ImageFilter
import matplotlib.pyplot as plt
img = Image.open("微素描.jpg")
width, height = img.size
#创建画布
_, axes = plt.subplots(1, 5, figsize=(30, 30))
#原图
axes[0].imshow(img)
axes[0].set_title("original")
#转灰度图
img_gray = img.convert("L")
plt.rcParams["image.cmap"] = "gray"
axes[1].imshow(img_gray)
axes[1].set_title("gray")
#反色
img_invert = ImageOps.invert(img_gray)
axes[2].imshow(img_invert)
axes[2].set_title("inverse")

#高斯模糊
img_gaussian = img_invert.filter(ImageFilter.GaussianBlur(5))
axes[3].imshow(img_gaussian)
axes[3].set_title("gaussian")

#颜色减淡
for x in range(width):
    for y in range(height):
        pos = (x, y)
        A = img_gray.getpixel(pos)
        B = img_gaussian.getpixel(pos)
        img_gray.putpixel(pos, min(int(A+A*B/(255-B)), 255))
axes[4].imshow(img_gray)
axes[4].set_title("sketch")
img_gray.save("素描素材.png")

plt.show()

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值