当前位置:   article > 正文

7个惊艳众人的 Python 实用项目!【附源码】

python项目开发案例

今天分享7个学妹看见都惊呆的 Python 小项目!【附源码】

建议收藏

界面应用

1、计算器

1. 案例介绍

本例利用 Python 开发一个可以进行简单的四则运算的图形化计算器,会用到 Tkinter 图形组件进行开发。主要知识点:Python Tkinter 界面编程;计算器逻辑运算实现。本例难度为初级,适合具有 Python 基础和 Tkinter 组件编程知识的用户学习。

2. 设计原理

要制作一个计算器,首先需要知道它由哪些部分组成。示意如下图所示。

22c9ab73e86152abcafc6b4ee51ea3e0.png

从结构上来说,一个简单的图形界面,需要由界面组件、组件的事件监听器(响应各类事件的逻辑)和具体的事件处理逻辑组成。界面实现的主要工作是创建各个界面组件对象,对其进行初始化,以及控制各组件之间的层次关系和布局。

3. 示例效果

98a446fb70b311bf0b89ca0959bf779a.gif

4. 示例源码

  1. import tkinter
  2. import math
  3. import tkinter.messagebox
  4. class Calculator(object):
  5.     # 界面布局方法
  6.     def __init__(self):
  7.         # 创建主界面,并且保存到成员属性中
  8.         self.root = tkinter.Tk()
  9.         self.root.minsize(280450)
  10.         self.root.maxsize(280470)
  11.         self.root.title('计算器')
  12.         # 设置显式面板的变量
  13.         self.result = tkinter.StringVar()
  14.         self.result.set(0)
  15.         # 设置一个全局变量  运算数字和f符号的列表
  16.         self.lists = []
  17.         # 添加一个用于判断是否按下运算符号的标志
  18.         self.ispresssign = False
  19.         # 界面布局
  20.         self.menus()
  21.         self.layout()
  22.         self.root.mainloop()
  23.     # 计算器菜单界面摆放
  24.     def menus(self):
  25.         # 添加菜单
  26.         # 创建总菜单
  27.         allmenu = tkinter.Menu(self.root)
  28.         # 添加子菜单
  29.         filemenu = tkinter.Menu(allmenu, tearoff=0)
  30.         # 添加选项卡
  31.         filemenu.add_command(
  32.             label='标准型(T)            Alt+1', command=self.myfunc)
  33.         filemenu.add_command(
  34.             label='科学型(S)            Alt+2', command=self.myfunc)
  35.         filemenu.add_command(
  36.             label='程序员(P)            Alt+3', command=self.myfunc)
  37.         filemenu.add_command(label='统计信息(A)        Alt+4', command=self.myfunc)
  38.         # 添加分割线
  39.         filemenu.add_separator()
  40.         # 添加选项卡
  41.         filemenu.add_command(label='历史记录(Y)      Ctrl+H', command=self.myfunc)
  42.         filemenu.add_command(label='数字分组(I)', command=self.myfunc)
  43.         # 添加分割线
  44.         filemenu.add_separator()
  45.         # 添加选项卡
  46.         filemenu.add_command(
  47.             label='基本(B)             Ctrl+F4', command=self.myfunc)
  48.         filemenu.add_command(label='单位转换(U)      Ctrl+U', command=self.myfunc)
  49.         filemenu.add_command(label='日期计算(D)      Ctrl+E', command=self.myfunc)
  50.         menu1 = tkinter.Menu(filemenu, tearoff=0)
  51.         menu1.add_command(label='抵押(M)', command=self.myfunc)
  52.         menu1.add_command(label='汽车租赁(V)', command=self.myfunc)
  53.         menu1.add_command(label='油耗(mpg)(F)', command=self.myfunc)
  54.         menu1.add_command(label='油耗(l/100km)(U)', command=self.myfunc)
  55.         filemenu.add_cascade(label='工作表(W)', menu=menu1)
  56.         allmenu.add_cascade(label='查看(V)', menu=filemenu)
  57.         # 添加子菜单2
  58.         editmenu = tkinter.Menu(allmenu, tearoff=0)
  59.         # 添加选项卡
  60.         editmenu.add_command(label='复制(C)         Ctrl+C', command=self.myfunc)
  61.         editmenu.add_command(label='粘贴(V)         Ctrl+V', command=self.myfunc)
  62.         # 添加分割线
  63.         editmenu.add_separator()
  64.         # 添加选项卡
  65.         menu2 = tkinter.Menu(filemenu, tearoff=0)
  66.         menu2.add_command(label='复制历史记录(I)', command=self.myfunc)
  67.         menu2.add_command(
  68.             label='编辑(E)                      F2', command=self.myfunc)
  69.         menu2.add_command(label='取消编辑(N)            Esc', command=self.myfunc)
  70.         menu2.add_command(label='清除(L)    Ctrl+Shift+D', command=self.myfunc)
  71.         editmenu.add_cascade(label='历史记录(H)', menu=menu2)
  72.         allmenu.add_cascade(label='编辑(E)', menu=editmenu)
  73.         # 添加子菜单3
  74.         helpmenu = tkinter.Menu(allmenu, tearoff=0)
  75.         # 添加选项卡
  76.         helpmenu.add_command(label='查看帮助(V)       F1', command=self.myfunc)
  77.         # 添加分割线
  78.         helpmenu.add_separator()
  79.         # 添加选项卡
  80.         helpmenu.add_command(label='关于计算器(A)', command=self.myfunc)
  81.         allmenu.add_cascade(label='帮助(H)', menu=helpmenu)
  82.         self.root.config(menu=allmenu)
  83.     # 计算器主界面摆放
  84.     def layout(self):
  85.         # 显示屏
  86.         result = tkinter.StringVar()
  87.         result.set(0)
  88.         show_label = tkinter.Label(self.root, bd=3, bg='white', font=(
  89.             '宋体'30), anchor='e', textvariable=self.result)
  90.         show_label.place(x=5, y=20, width=270, height=70)
  91.         # 功能按钮MC
  92.         button_mc = tkinter.Button(self.root, text='MC', command=self.wait)
  93.         button_mc.place(x=5, y=95, width=50, height=50)
  94.         # 功能按钮MR
  95.         button_mr = tkinter.Button(self.root, text='MR', command=self.wait)
  96.         button_mr.place(x=60, y=95, width=50, height=50)
  97.         # 功能按钮MS
  98.         button_ms = tkinter.Button(self.root, text='MS', command=self.wait)
  99.         button_ms.place(x=115, y=95, width=50, height=50)
  100.         # 功能按钮M+
  101.         button_mjia = tkinter.Button(self.root, text='M+', command=self.wait)
  102.         button_mjia.place(x=170, y=95, width=50, height=50)
  103.         # 功能按钮M-
  104.         button_mjian = tkinter.Button(self.root, text='M-', command=self.wait)
  105.         button_mjian.place(x=225, y=95, width=50, height=50)
  106.         # 功能按钮←
  107.         button_zuo = tkinter.Button(self.root, text='←', command=self.dele_one)
  108.         button_zuo.place(x=5, y=150, width=50, height=50)
  109.         # 功能按钮CE
  110.         button_ce = tkinter.Button(
  111.             self.root, text='CE', command=lambda: self.result.set(0))
  112.         button_ce.place(x=60, y=150, width=50, height=50)
  113.         # 功能按钮C
  114.         button_c = tkinter.Button(self.root, text='C', command=self.sweeppress)
  115.         button_c.place(x=115, y=150, width=50, height=50)
  116.         # 功能按钮±
  117.         button_zf = tkinter.Button(self.root, text='±', command=self.zf)
  118.         button_zf.place(x=170, y=150, width=50, height=50)
  119.         # 功能按钮√
  120.         button_kpf = tkinter.Button(self.root, text='√', command=self.kpf)
  121.         button_kpf.place(x=225, y=150, width=50, height=50)
  122.         # 数字按钮7
  123.         button_7 = tkinter.Button(
  124.             self.root, text='7', command=lambda: self.pressnum('7'))
  125.         button_7.place(x=5, y=205, width=50, height=50)
  126.         # 数字按钮8
  127.         button_8 = tkinter.Button(
  128.             self.root, text='8', command=lambda: self.pressnum('8'))
  129.         button_8.place(x=60, y=205, width=50, height=50)
  130.         # 数字按钮9
  131.         button_9 = tkinter.Button(
  132.             self.root, text='9', command=lambda: self.pressnum('9'))
  133.         button_9.place(x=115, y=205, width=50, height=50)
  134.         # 功能按钮/
  135.         button_division = tkinter.Button(
  136.             self.root, text='/', command=lambda: self.presscalculate('/'))
  137.         button_division.place(x=170, y=205, width=50, height=50)
  138.         # 功能按钮%
  139.         button_remainder = tkinter.Button(
  140.             self.root, text='//', command=lambda: self.presscalculate('//'))
  141.         button_remainder.place(x=225, y=205, width=50, height=50)
  142.         # 数字按钮4
  143.         button_4 = tkinter.Button(
  144.             self.root, text='4', command=lambda: self.pressnum('4'))
  145.         button_4.place(x=5, y=260, width=50, height=50)
  146.         # 数字按钮5
  147.         button_5 = tkinter.Button(
  148.             self.root, text='5', command=lambda: self.pressnum('5'))
  149.         button_5.place(x=60, y=260, width=50, height=50)
  150.         # 数字按钮6
  151.         button_6 = tkinter.Button(
  152.             self.root, text='6', command=lambda: self.pressnum('6'))
  153.         button_6.place(x=115, y=260, width=50, height=50)
  154.         # 功能按钮*
  155.         button_multiplication = tkinter.Button(
  156.             self.root, text='*', command=lambda: self.presscalculate('*'))
  157.         button_multiplication.place(x=170, y=260, width=50, height=50)
  158.         # 功能按钮1/x
  159.         button_reciprocal = tkinter.Button(
  160.             self.root, text='1/x', command=self.ds)
  161.         button_reciprocal.place(x=225, y=260, width=50, height=50)
  162.         # 数字按钮1
  163.         button_1 = tkinter.Button(
  164.             self.root, text='1', command=lambda: self.pressnum('1'))
  165.         button_1.place(x=5, y=315, width=50, height=50)
  166.         # 数字按钮2
  167.         button_2 = tkinter.Button(
  168.             self.root, text='2', command=lambda: self.pressnum('2'))
  169.         button_2.place(x=60, y=315, width=50, height=50)
  170.         # 数字按钮3
  171.         button_3 = tkinter.Button(
  172.             self.root, text='3', command=lambda: self.pressnum('3'))
  173.         button_3.place(x=115, y=315, width=50, height=50)
  174.         # 功能按钮-
  175.         button_subtraction = tkinter.Button(
  176.             self.root, text='-', command=lambda: self.presscalculate('-'))
  177.         button_subtraction.place(x=170, y=315, width=50, height=50)
  178.         # 功能按钮=
  179.         button_equal = tkinter.Button(
  180.             self.root, text='=', command=lambda: self.pressequal())
  181.         button_equal.place(x=225, y=315, width=50, height=105)
  182.         # 数字按钮0
  183.         button_0 = tkinter.Button(
  184.             self.root, text='0', command=lambda: self.pressnum('0'))
  185.         button_0.place(x=5, y=370, width=105, height=50)
  186.         # 功能按钮.
  187.         button_point = tkinter.Button(
  188.             self.root, text='.', command=lambda: self.pressnum('.'))
  189.         button_point.place(x=115, y=370, width=50, height=50)
  190.         # 功能按钮+
  191.         button_plus = tkinter.Button(
  192.             self.root, text='+', command=lambda: self.presscalculate('+'))
  193.         button_plus.place(x=170, y=370, width=50, height=50)
  194.     # 计算器菜单功能
  195.     def myfunc(self):
  196.         tkinter.messagebox.showinfo('''预留接口,学成之后,你是不是有冲动添加该功能.')
  197.     # 数字方法
  198.     def pressnum(self, num):
  199.         # 全局化变量
  200.         # 判断是否按下了运算符号
  201.         if self.ispresssign == False:
  202.             pass
  203.         else:
  204.             self.result.set(0)
  205.             # 重置运算符号的状态
  206.             self.ispresssign = False
  207.         if num == '.':
  208.             num = '0.'
  209.         # 获取面板中的原有数字
  210.         oldnum = self.result.get()
  211.         # 判断界面数字是否为0
  212.         if oldnum == '0':
  213.             self.result.set(num)
  214.         else:
  215.             # 连接上新按下的数字
  216.             newnum = oldnum + num
  217.             # 将按下的数字写到面板中
  218.             self.result.set(newnum)
  219.     # 运算函数
  220.     def presscalculate(self, sign):
  221.         # 保存已经按下的数字和运算符号
  222.         # 获取界面数字
  223.         num = self.result.get()
  224.         self.lists.append(num)
  225.         # 保存按下的操作符号
  226.         self.lists.append(sign)
  227.         # 设置运算符号为按下状态
  228.         self.ispresssign = True
  229.     # 获取运算结果
  230.     def pressequal(self):
  231.         # 获取所有的列表中的内容(之前的数字和操作)
  232.         # 获取当前界面上的数字
  233.         curnum = self.result.get()
  234.         # 将当前界面的数字存入列表
  235.         self.lists.append(curnum)
  236.         # 将列表转化为字符串
  237.         calculatestr = ''.join(self.lists)
  238.         # 使用eval执行字符串中的运算即可
  239.         endnum = eval(calculatestr)
  240.         # 将运算结果显示在界面中
  241.         self.result.set(str(endnum)[:10])
  242.         if self.lists != 0:
  243.             self.ispresssign = True
  244.         # 清空运算列表
  245.         self.lists.clear()
  246.     # 暂未开发说明
  247.     def wait(self):
  248.         tkinter.messagebox.showinfo('''更新中......')
  249.     # ←按键功能
  250.     def dele_one(self):
  251.         if self.result.get() == '' or self.result.get() == '0':
  252.             self.result.set('0')
  253.             return
  254.         else:
  255.             num = len(self.result.get())
  256.             if num > 1:
  257.                 strnum = self.result.get()
  258.                 strnum = strnum[0:num - 1]
  259.                 self.result.set(strnum)
  260.             else:
  261.                 self.result.set('0')
  262.     # ±按键功能
  263.     def zf(self):
  264.         strnum = self.result.get()
  265.         if strnum[0] == '-':
  266.             self.result.set(strnum[1:])
  267.         elif strnum[0] != '-' and strnum != '0':
  268.             self.result.set('-' + strnum)
  269.     # 1/x按键功能
  270.     def ds(self):
  271.         dsnum = 1 / int(self.result.get())
  272.         self.result.set(str(dsnum)[:10])
  273.         if self.lists != 0:
  274.             self.ispresssign = True
  275.         # 清空运算列表
  276.         self.lists.clear()
  277.     # C按键功能
  278.     def sweeppress(self):
  279.         self.lists.clear()
  280.         self.result.set(0)
  281.     # √按键功能
  282.     def kpf(self):
  283.         strnum = float(self.result.get())
  284.         endnum = math.sqrt(strnum)
  285.         if str(endnum)[-1] == '0':
  286.             self.result.set(str(endnum)[:-2])
  287.         else:
  288.             self.result.set(str(endnum)[:10])
  289.         if self.lists != 0:
  290.             self.ispresssign = True
  291.         # 清空运算列表
  292.         self.lists.clear()
  293. # 实例化对象
  294. my_calculator = Calculator()

2、记事本

1. 案例介绍

tkinter 是 Python下面向 tk 的图形界面接口库,可以方便地进行图形界面设计和交互操作编程。tkinter 的优点是简单易用、与 Python 的结合度好。tkinter 在 Python 3.x 下默认集成,不需要额外的安装操作;不足之处为缺少合适的可视化界面设计工具,需要通过代码来完成窗口设计和元素布局。本例采用的 Python 版本为 3.8,如果想在 python 2.x下使用 tkinter,请先进行安装。需要注意的是,不同 Python 版本下的 tkinter 使用方式可能略有不同,建议采用 Python3.x 版本。本例难度为中级,适合具有 Python 基础和 Tkinter 组件编程知识的用户学习。

2. 示例效果

a9bc1f29c0ac39b5577d2bc616f4ea89.gif

3. 示例源码

  1. from tkinter import *
  2. from tkinter.filedialog import *
  3. from tkinter.messagebox import *
  4. import os
  5. filename = ""
  6. def author():
  7.     showinfo(title="作者", message="Python")
  8. def power():
  9.     showinfo(title="版权信息", message="课堂练习")
  10. def mynew():
  11.     global top, filename, textPad
  12.     top.title("未命名文件")
  13.     filename = None
  14.     textPad.delete(1.0, END)
  15. def myopen():
  16.     global filename
  17.     filename = askopenfilename(defaultextension=".txt")
  18.     if filename == "":
  19.         filename = None
  20.     else:
  21.         top.title("记事本" + os.path.basename(filename))
  22.         textPad.delete(1.0, END)
  23.         f = open(filename, 'r')
  24.         textPad.insert(1.0, f.read())
  25.         f.close()
  26. def mysave():
  27.     global filename
  28.     try:
  29.         f = open(filename, 'w')
  30.         msg = textPad.get(1.0'end')
  31.         f.write(msg)
  32.         f.close()
  33.     except:
  34.         mysaveas()
  35. def mysaveas():
  36.     global filename
  37.     f = asksaveasfilename(initialfile="未命名.txt", defaultextension=".txt")
  38.     filename = f
  39.     fh = open(f, 'w')
  40.     msg = textPad.get(1.0, END)
  41.     fh.write(msg)
  42.     fh.close()
  43.     top.title("记事本 " + os.path.basename(f))
  44. def cut():
  45.     global textPad
  46.     textPad.event_generate("<<Cut>>")
  47. def copy():
  48.     global textPad
  49.     textPad.event_generate("<<Copy>>")
  50. def paste():
  51.     global textPad
  52.     textPad.event_generate("<<Paste>>")
  53. def undo():
  54.     global textPad
  55.     textPad.event_generate("<<Undo>>")
  56. def redo():
  57.     global textPad
  58.     textPad.event_generate("<<Redo>>")
  59. def select_all():
  60.     global textPad
  61.     # textPad.event_generate("<<Cut>>")
  62.     textPad.tag_add("sel""1.0""end")
  63. def find():
  64.     t = Toplevel(top)
  65.     t.title("查找")
  66.     t.geometry("260x60+200+250")
  67.     t.transient(top)
  68.     Label(t, text="查找:").grid(row=0, column=0, sticky="e")
  69.     v = StringVar()
  70.     e = Entry(t, width=20, textvariable=v)
  71.     e.grid(row=0, column=1, padx=2, pady=2, sticky="we")
  72.     e.focus_set()
  73.     c = IntVar()
  74.     Checkbutton(t, text="不区分大小写", variable=c).grid(row=1, column=1, sticky='e')
  75.     Button(t, text="查找所有", command=lambda: search(v.get(), c.get(),
  76.                                                   textPad, t, e)).grid(row=0, column=2, sticky="e" + "w", padx=2,
  77.                                                                        pady=2)
  78.     def close_search():
  79.         textPad.tag_remove("match""1.0", END)
  80.         t.destroy()
  81.     t.protocol("WM_DELETE_WINDOW", close_search)
  82. def mypopup(event):
  83.     # global editmenu
  84.     editmenu.tk_popup(event.x_root, event.y_root)
  85. def search(needle, cssnstv, textPad, t, e):
  86.     textPad.tag_remove("match""1.0", END)
  87.     count = 0
  88.     if needle:
  89.         pos = "1.0"
  90.         while True:
  91.             pos = textPad.search(needle, pos, nocase=cssnstv, stopindex=END)
  92.             if not pos:
  93.                 break
  94.             lastpos = pos + str(len(needle))
  95.             textPad.tag_add("match", pos, lastpos)
  96.             count += 1
  97.             pos = lastpos
  98.         textPad.tag_config('match', fg='yellow', bg="green")
  99.         e.focus_set()
  100.         t.title(str(count) + "个被匹配")
  101. top = Tk()
  102. top.title("记事本")
  103. top.geometry("600x400+100+50")
  104. menubar = Menu(top)
  105. # 文件功能
  106. filemenu = Menu(top)
  107. filemenu.add_command(label="新建", accelerator="Ctrl+N", command=mynew)
  108. filemenu.add_command(label="打开", accelerator="Ctrl+O", command=myopen)
  109. filemenu.add_command(label="保存", accelerator="Ctrl+S", command=mysave)
  110. filemenu.add_command(label="另存为", accelerator="Ctrl+shift+s", command=mysaveas)
  111. menubar.add_cascade(label="文件", menu=filemenu)
  112. # 编辑功能
  113. editmenu = Menu(top)
  114. editmenu.add_command(label="撤销", accelerator="Ctrl+Z", command=undo)
  115. editmenu.add_command(label="重做", accelerator="Ctrl+Y", command=redo)
  116. editmenu.add_separator()
  117. editmenu.add_command(label="剪切", accelerator="Ctrl+X", command=cut)
  118. editmenu.add_command(label="复制", accelerator="Ctrl+C", command=copy)
  119. editmenu.add_command(label="粘贴", accelerator="Ctrl+V", command=paste)
  120. editmenu.add_separator()
  121. editmenu.add_command(label="查找", accelerator="Ctrl+F", command=find)
  122. editmenu.add_command(label="全选", accelerator="Ctrl+A", command=select_all)
  123. menubar.add_cascade(label="编辑", menu=editmenu)
  124. # 关于 功能
  125. aboutmenu = Menu(top)
  126. aboutmenu.add_command(label="作者", command=author)
  127. aboutmenu.add_command(label="版权", command=power)
  128. menubar.add_cascade(label="关于", menu=aboutmenu)
  129. top['menu'] = menubar
  130. # shortcutbar = Frame(top, height=25, bg='light sea green')
  131. # shortcutbar.pack(expand=NO, fill=X)
  132. # Inlabe = Label(top, width=2, bg='antique white')
  133. # Inlabe.pack(side=LEFT, anchor='nw', fill=Y)
  134. textPad = Text(top, undo=True)
  135. textPad.pack(expand=YES, fill=BOTH)
  136. scroll = Scrollbar(textPad)
  137. textPad.config(yscrollcommand=scroll.set)
  138. scroll.config(command=textPad.yview)
  139. scroll.pack(side=RIGHT, fill=Y)
  140. # 热键绑定
  141. textPad.bind("<Control-N>", mynew)
  142. textPad.bind("<Control-n>", mynew)
  143. textPad.bind("<Control-O>", myopen)
  144. textPad.bind("<Control-o>", myopen)
  145. textPad.bind("<Control-S>", mysave)
  146. textPad.bind("<Control-s>", mysave)
  147. textPad.bind("<Control-A>", select_all)
  148. textPad.bind("<Control-a>", select_all)
  149. textPad.bind("<Control-F>", find)
  150. textPad.bind("<Control-f>", find)
  151. textPad.bind("<Button-3>", mypopup)
  152. top.mainloop()

3、登录和注册

1. 案例介绍

本例设计一个用户登录和注册模块,使用 Tkinter 框架构建界面,主要用到画布、文本框、按钮等组件。涉及知识点:Python Tkinter 界面编程、pickle 数据存储。本例实现了基本的用户登录和注册互动界面,并提供用户信息存储和验证。pickle 是 python 语言的一个标准模块,安装 python 后已包含 pickle 库,不需要单独再安装。pickle 模块实现了基本的数据序列化和反序列化。通过 pickle 模块的序列化操作能够将程序中运行的对象信息保存到文件中去,永久存储;通过 pickle 模块的反序列化操作,能够从文件中创建上一次程序保存的对象。本例难度为中级,适合具有 Python 基础和 Tkinter 组件编程知识的用户学习。

2. 示例效果

55e4623f7f61e8243d54fae8ccfa5954.png 2a3e3cf65a09d5fcdd96223edebd86c9.png

3. 示例源码

  1. import tkinter as tk
  2. import pickle
  3. import tkinter.messagebox
  4. from PIL import Image, ImageTk
  5. # 设置窗口---最开始的母体窗口
  6. window = tk.Tk()  # 建立一个窗口
  7. window.title('欢迎登录')
  8. window.geometry('450x300')  # 窗口大小为300x200
  9. # 画布
  10. canvas = tk.Canvas(window, height=200, width=900)
  11. # 加载图片
  12. im = Image.open("images/01.png")
  13. image_file = ImageTk.PhotoImage(im)
  14. # image_file = tk.PhotoImage(file='images/01.gif')
  15. image = canvas.create_image(10040, anchor='nw', image=image_file)
  16. canvas.pack(side='top')
  17. # 两个文字标签,用户名和密码两个部分
  18. tk.Label(window, text='用户名').place(x=100, y=150)
  19. tk.Label(window, text='密  码').place(x=100, y=190)
  20. var_usr_name = tk.StringVar()  # 讲文本框的内容,定义为字符串类型
  21. var_usr_name.set('amoxiang@163.com')  # 设置默认值
  22. var_usr_pwd = tk.StringVar()
  23. # 第一个输入框-用来输入用户名的。
  24. # textvariable 获取文本框的内容
  25. entry_usr_name = tk.Entry(window, textvariable=var_usr_name)
  26. entry_usr_name.place(x=160, y=150)
  27. # 第二个输入框-用来输入密码的。
  28. entry_usr_pwd = tk.Entry(window, textvariable=var_usr_pwd, show='*')
  29. entry_usr_pwd.place(x=160, y=190)
  30. def usr_login():
  31.     usr_name = var_usr_name.get()
  32.     usr_pwd = var_usr_pwd.get()
  33.     try:
  34.         with open('usrs_info.pickle''rb') as usr_file:
  35.             usrs_info = pickle.load(usr_file)
  36.     except FileNotFoundError:
  37.         with open('usrs_info.pickle''wb') as usr_file:
  38.             usrs_info = {'admin''admin'}
  39.             pickle.dump(usrs_info, usr_file)
  40.     if usr_name in usrs_info:
  41.         if usr_pwd == usrs_info[usr_name]:
  42.             tk.messagebox.showinfo(
  43.                 title='欢迎光临', message=usr_name + ':请进入个人首页,查看最新资讯')
  44.         else:
  45.             tk.messagebox.showinfo(message='错误提示:密码不对,请重试')
  46.     else:
  47.         is_sign_up = tk.messagebox.askyesno('提示''你还没有注册,请先注册')
  48.         print(is_sign_up)
  49.         if is_sign_up:
  50.             usr_sign_up()
  51. # 注册按钮
  52. def usr_sign_up():
  53.     def sign_to_Mofan_Python():
  54.         np = new_pwd.get()
  55.         npf = new_pwd_confirm.get()
  56.         nn = new_name.get()
  57.         # 上面是获取数据,下面是查看一下是否重复注册过
  58.         with open('usrs_info.pickle''rb') as usr_file:
  59.             exist_usr_info = pickle.load(usr_file)
  60.             if np != npf:
  61.                 tk.messagebox.showerror('错误提示''密码和确认密码必须一样')
  62.             elif nn in exist_usr_info:
  63.                 tk.messagebox.showerror('错误提示''用户名早就注册了!')
  64.             else:
  65.                 exist_usr_info[nn] = np
  66.                 with open('usrs_info.pickle''wb') as usr_file:
  67.                     pickle.dump(exist_usr_info, usr_file)
  68.                 tk.messagebox.showinfo('欢迎''你已经成功注册了')
  69.                 window_sign_up.destroy()
  70.     # 点击注册之后,会弹出这个窗口界面。
  71.     window_sign_up = tk.Toplevel(window)
  72.     window_sign_up.title('欢迎注册')
  73.     window_sign_up.geometry('360x200')  # 中间是x,而不是*号
  74.     # 用户名框--这里输入用户名框。
  75.     new_name = tk.StringVar()
  76.     new_name.set('amoxiang@163.com')  # 设置的是默认值
  77.     tk.Label(window_sign_up, text='用户名').place(x=10, y=10)
  78.     entry_new_name = tk.Entry(window_sign_up, textvariable=new_name)
  79.     entry_new_name.place(x=100, y=10)
  80.     # 新密码框--这里输入注册时候的密码
  81.     new_pwd = tk.StringVar()
  82.     tk.Label(window_sign_up, text='密  码').place(x=10, y=50)
  83.     entry_usr_pwd = tk.Entry(window_sign_up, textvariable=new_pwd, show='*')
  84.     entry_usr_pwd.place(x=100, y=50)
  85.     # 密码确认框
  86.     new_pwd_confirm = tk.StringVar()
  87.     tk.Label(window_sign_up, text='确认密码').place(x=10, y=90)
  88.     entry_usr_pwd_confirm = tk.Entry(
  89.         window_sign_up, textvariable=new_pwd_confirm, show='*')
  90.     entry_usr_pwd_confirm.place(x=100, y=90)
  91.     btn_confirm_sign_up = tk.Button(
  92.         window_sign_up, text=' 注  册 ', command=sign_to_Mofan_Python)
  93.     btn_confirm_sign_up.place(x=120, y=130)
  94. # 创建注册和登录按钮
  95. btn_login = tk.Button(window, text=' 登  录 ', command=usr_login)
  96. btn_login.place(x=150, y=230)  # 用place来处理按钮的位置信息。
  97. btn_sign_up = tk.Button(window, text=' 注  册 ', command=usr_sign_up)
  98. btn_sign_up.place(x=250, y=230)
  99. window.mainloop()

游戏开发

1、2048

1. 游戏简介

2048 是一款比较流行的数字游戏。游戏规则:每次可按上、下、左、右方向键滑动数字,每滑动一次,所有数字都会往滑动方向靠拢,同时在空白位置随机出现一个数字,相同数字在靠拢时会相加。不断叠加最终拼出 2048 这个数字算成功。2048 最早于 2014年3月20日发行。原版 2048 首先在 GitHub 上发布,原作者是 Gabriele Cirulli,后被移植到各个平台。本例难度为初级,适合具有 Python 基础和 Pygame 编程知识的用户学习。

2. 设计原理

这个游戏的本质是二维列表,就以 4*4 的二位列表来分析关键的逻辑以及实现。二维列表如下图:

e1f40555b3236a03c00a3ca55e988036.png

所有的操作都是对这个二维列表的数据的操作。分为上下左右四个方向。先说向左的方向(如图)。

7c921c2fc78269fe3024f224cd273c6b.png

向左操作的结果如下图;当向左的方向是,所有的数据沿着水平方向向左跑。

3734eb8c80e3ce26debd54abfeddadf1.png

水平说明操作的是二维列表的一行,而垂直操作的则是二位列表的一列。这样就可以将二维列表的操作变成遍历后对一维列表的操作。向左说明数据的优先考虑的位置是从左开始的。这样就确定了一维列表的遍历开始的位置。上面第 2 个图共四行,每一个行都能得到一个列表。

  1. list1:[0,0,2,0]
  2. list2:[0,4,2,0]
  3. list3:[0,0,4,4]
  4. list4:[2,0,2,0]

这样一来向左的方向就变成。从上到下获得每一行的列表,方向向左。参数(row,left)。

5cfb914682e6cf37057314a6b7567714.png

其他的三个方向在开始的时候记住是怎样获得以为列表的,等操作完才放回去这样就能实现了。

**3. 示例效果  **

bee615320e757c3638598462f3f36d55.png

4. 示例源码

  1. import random
  2. import sys
  3. import pygame
  4. from pygame.locals import *
  5. PIXEL = 150
  6. SCORE_PIXEL = 100
  7. SIZE = 4
  8. # 地图的类
  9. class Map:
  10.     def __init__(self, size):
  11.         self.size = size
  12.         self.score = 0
  13.         self.map = [[0 for i in range(size)] for i in range(size)]
  14.         self.add()
  15.         self.add()
  16.     # 新增24,有1/4概率产生4
  17.     def add(self):
  18.         while True:
  19.             p = random.randint(0, self.size * self.size - 1)
  20.             if self.map[int(p / self.size)][int(p % self.size)] == 0:
  21.                 x = random.randint(03) > 0 and 2 or 4
  22.                 self.map[int(p / self.size)][int(p % self.size)] = x
  23.                 self.score += x
  24.                 break
  25.     # 地图向左靠拢,其他方向的靠拢可以通过适当旋转实现,返回地图是否更新
  26.     def adjust(self):
  27.         changed = False
  28.         for a in self.map:
  29.             b = []
  30.             last = 0
  31.             for v in a:
  32.                 if v != 0:
  33.                     if v == last:
  34.                         b.append(b.pop() << 1)
  35.                         last = 0
  36.                     else:
  37.                         b.append(v)
  38.                         last = v
  39.             b += [0] * (self.size - len(b))
  40.             for i in range(self.size):
  41.                 if a[i] != b[i]:
  42.                     changed = True
  43.             a[:] = b
  44.         return changed
  45.     # 逆时针旋转地图90
  46.     def rotate90(self):
  47.         self.map = [[self.map[c][r]
  48.                      for c in range(self.size)] for r in reversed(range(self.size))]
  49.     # 判断游戏结束
  50.     def over(self):
  51.         for r in range(self.size):
  52.             for c in range(self.size):
  53.                 if self.map[r][c] == 0:
  54.                     return False
  55.         for r in range(self.size):
  56.             for c in range(self.size - 1):
  57.                 if self.map[r][c] == self.map[r][c + 1]:
  58.                     return False
  59.         for r in range(self.size - 1):
  60.             for c in range(self.size):
  61.                 if self.map[r][c] == self.map[r + 1][c]:
  62.                     return False
  63.         return True
  64.     def moveUp(self):
  65.         self.rotate90()
  66.         if self.adjust():
  67.             self.add()
  68.         self.rotate90()
  69.         self.rotate90()
  70.         self.rotate90()
  71.     def moveRight(self):
  72.         self.rotate90()
  73.         self.rotate90()
  74.         if self.adjust():
  75.             self.add()
  76.         self.rotate90()
  77.         self.rotate90()
  78.     def moveDown(self):
  79.         self.rotate90()
  80.         self.rotate90()
  81.         self.rotate90()
  82.         if self.adjust():
  83.             self.add()
  84.         self.rotate90()
  85.     def moveLeft(self):
  86.         if self.adjust():
  87.             self.add()
  88. # 更新屏幕
  89. def show(map):
  90.     for i in range(SIZE):
  91.         for j in range(SIZE):
  92.             # 背景颜色块
  93.             screen.blit(map.map[i][j] == 0 and block[(i + j) % 2]
  94.                         or block[2 + (i + j) % 2], (PIXEL * j, PIXEL * i))
  95.             # 数值显示
  96.             if map.map[i][j] != 0:
  97.                 map_text = map_font.render(
  98.                     str(map.map[i][j]), True, (10690205))
  99.                 text_rect = map_text.get_rect()
  100.                 text_rect.center = (PIXEL * j + PIXEL / 2,
  101.                                     PIXEL * i + PIXEL / 2)
  102.                 screen.blit(map_text, text_rect)
  103.     # 分数显示
  104.     screen.blit(score_block, (0, PIXEL * SIZE))
  105.     score_text = score_font.render((map.over(
  106.     ) and "Game over with score " or "Score: ") + str(map.score), True, (10690205))
  107.     score_rect = score_text.get_rect()
  108.     score_rect.center = (PIXEL * SIZE / 2, PIXEL * SIZE + SCORE_PIXEL / 2)
  109.     screen.blit(score_text, score_rect)
  110.     pygame.display.update()
  111. map = Map(SIZE)
  112. pygame.init()
  113. screen = pygame.display.set_mode((PIXEL * SIZE, PIXEL * SIZE + SCORE_PIXEL))
  114. pygame.display.set_caption("2048")
  115. block = [pygame.Surface((PIXEL, PIXEL)) for i in range(4)]
  116. # 设置颜色
  117. block[0].fill((152251152))
  118. block[1].fill((240255255))
  119. block[2].fill((0255127))
  120. block[3].fill((225255255))
  121. score_block = pygame.Surface((PIXEL * SIZE, SCORE_PIXEL))
  122. score_block.fill((245245245))
  123. # 设置字体
  124. map_font = pygame.font.Font(None, int(PIXEL * 2 / 3))
  125. score_font = pygame.font.Font(None, int(SCORE_PIXEL * 2 / 3))
  126. clock = pygame.time.Clock()
  127. show(map)
  128. while not map.over():
  129.     # 12为实验参数
  130.     clock.tick(12)
  131.     for event in pygame.event.get():
  132.         if event.type == QUIT:
  133.             sys.exit()
  134.     # 接收玩家操作
  135.     pressed_keys = pygame.key.get_pressed()
  136.     if pressed_keys[K_w] or pressed_keys[K_UP]:
  137.         map.moveUp()
  138.     elif pressed_keys[K_s] or pressed_keys[K_DOWN]:
  139.         map.moveDown()
  140.     elif pressed_keys[K_a] or pressed_keys[K_LEFT]:
  141.         map.moveLeft()
  142.     elif pressed_keys[K_d] or pressed_keys[K_RIGHT]:
  143.         map.moveRight()
  144.     show(map)
  145. # 游戏结束
  146. pygame.time.delay(3000)

2、贪吃蛇

1. 案例介绍

贪吃蛇是一款经典的益智游戏,简单又耐玩。该游戏通过控制蛇头方向吃蛋,从而使得蛇变得越来越长。通过上下左右方向键控制蛇的方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能碰墙,不能咬到自己的身体,更不能咬自己的尾巴,等到了一定的分数,就能过关,然后继续玩下一关。本例难度为中级,适合具有 Python 基础和 Pygame 编程知识的用户学习。

2. 设计要点

游戏是基于 PyGame 框架制作的,程序核心逻辑如下:游戏界面分辨率是 640*480,蛇和食物都是由 1 个或多个 20*20 像素的正方形块儿(为了方便,下文用点表示 20*20 像素的正方形块儿) 组成,这样共有 32*24 个点,使用 pygame.draw.rect 来绘制每一个点;初始化时蛇的长度是 3,食物是 1 个点,蛇初始的移动的方向是右,用一个数组代表蛇,数组的每个元素是蛇每个点的坐标,因此数组的第一个坐标是蛇尾,最后一个坐标是蛇头;游戏开始后,根据蛇的当前移动方向,将蛇运动方向的前方的那个点 append 到蛇数组的末位,再把蛇尾去掉,蛇的坐标数组就相当于往前挪了一位;如果蛇吃到了食物,即蛇头的坐标等于食物的坐标,那么在第 2 点中蛇尾就不用去掉,就产生了蛇长度增加的效果;食物被吃掉后,随机在空的位置(不能与蛇的身体重合) 再生成一个;通过 PyGame 的 event 监控按键,改变蛇的方向,例如当蛇向右时,下一次改变方向只能向上或者向下;当蛇撞上自身或墙壁,游戏结束,蛇头装上自身,那么蛇坐标数组里就有和舌头坐标重复的数据,撞上墙壁则是蛇头坐标超过了边界,都很好判断;其他细节:做了个开始的欢迎界面;食物的颜色随机生成;吃到实物的时候有声音提示等。

3. 示例效果

e8bb2eaaa051cd3b77f12b3e4636abf6.png

4. 示例源码

  1. import pygame
  2. from os import path
  3. from sys import exit
  4. from time import sleep
  5. from random import choice
  6. from itertools import product
  7. from pygame.locals import QUIT, KEYDOWN
  8. def direction_check(moving_direction, change_direction):
  9.     directions = [['up''down'], ['left''right']]
  10.     if moving_direction in directions[0] and change_direction in directions[1]:
  11.         return change_direction
  12.     elif moving_direction in directions[1] and change_direction in directions[0]:
  13.         return change_direction
  14.     return moving_direction
  15. class Snake:
  16.     colors = list(product([064128192255], repeat=3))[1:-1]
  17.     def __init__(self):
  18.         self.map = {(x, y): 0 for x in range(32for y in range(24)}
  19.         self.body = [[100100], [120100], [140100]]
  20.         self.head = [140100]
  21.         self.food = []
  22.         self.food_color = []
  23.         self.moving_direction = 'right'
  24.         self.speed = 4
  25.         self.generate_food()
  26.         self.game_started = False
  27.     def check_game_status(self):
  28.         if self.body.count(self.head) > 1:
  29.             return True
  30.         if self.head[0] < 0 or self.head[0] > 620 or self.head[1] < 0 or self.head[1] > 460:
  31.             return True
  32.         return False
  33.     def move_head(self):
  34.         moves = {
  35.             'right': (200),
  36.             'up': (0-20),
  37.             'down': (020),
  38.             'left': (-200)
  39.         }
  40.         step = moves[self.moving_direction]
  41.         self.head[0] += step[0]
  42.         self.head[1] += step[1]
  43.     def generate_food(self):
  44.         self.speed = len(
  45.             self.body) // 16 if len(self.body) // 16 > 4 else self.speed
  46.         for seg in self.body:
  47.             x, y = seg
  48.             self.map[x // 20, y // 20] = 1
  49.         empty_pos = [pos for pos in self.map.keys() if not self.map[pos]]
  50.         result = choice(empty_pos)
  51.         self.food_color = list(choice(self.colors))
  52.         self.food = [result[0] * 20, result[1] * 20]
  53. def main():
  54.     key_direction_dict = {
  55.         119'up',  # W
  56.         115'down',  # S
  57.         97'left',  # A
  58.         100'right',  # D
  59.         273'up',  # UP
  60.         274'down',  # DOWN
  61.         276'left',  # LEFT
  62.         275'right',  # RIGHT
  63.     }
  64.     fps_clock = pygame.time.Clock()
  65.     pygame.init()
  66.     pygame.mixer.init()
  67.     snake = Snake()
  68.     sound = False
  69.     if path.exists('eat.wav'):
  70.         sound_wav = pygame.mixer.Sound("eat.wav")
  71.         sound = True
  72.     title_font = pygame.font.SysFont('simsunnsimsun'32)
  73.     welcome_words = title_font.render(
  74.         '贪吃蛇', True, (000), (255255255))
  75.     tips_font = pygame.font.SysFont('simsunnsimsun'20)
  76.     start_game_words = tips_font.render(
  77.         '点击开始', True, (000), (255255255))
  78.     close_game_words = tips_font.render(
  79.         '按ESC退出', True, (000), (255255255))
  80.     gameover_words = title_font.render(
  81.         '游戏结束', True, (2059292), (255255255))
  82.     win_words = title_font.render(
  83.         '蛇很长了,你赢了!', True, (00205), (255255255))
  84.     screen = pygame.display.set_mode((640480), 032)
  85.     pygame.display.set_caption('贪吃蛇')
  86.     new_direction = snake.moving_direction
  87.     while 1:
  88.         for event in pygame.event.get():
  89.             if event.type == QUIT:
  90.                 exit()
  91.             elif event.type == KEYDOWN:
  92.                 if event.key == 27:
  93.                     exit()
  94.                 if snake.game_started and event.key in key_direction_dict:
  95.                     direction = key_direction_dict[event.key]
  96.                     new_direction = direction_check(
  97.                         snake.moving_direction, direction)
  98.             elif (not snake.game_started) and event.type == pygame.MOUSEBUTTONDOWN:
  99.                 x, y = pygame.mouse.get_pos()
  100.                 if 213 <= x <= 422 and 304 <= y <= 342:
  101.                     snake.game_started = True
  102.         screen.fill((255255255))
  103.         if snake.game_started:
  104.             snake.moving_direction = new_direction  # 在这里赋值,而不是在event事件的循环中赋值,避免按键太快
  105.             snake.move_head()
  106.             snake.body.append(snake.head[:])
  107.             if snake.head == snake.food:
  108.                 if sound:
  109.                     sound_wav.play()
  110.                 snake.generate_food()
  111.             else:
  112.                 snake.body.pop(0)
  113.             for seg in snake.body:
  114.                 pygame.draw.rect(screen, [000], [
  115.                     seg[0], seg[1], 2020], 0)
  116.             pygame.draw.rect(screen, snake.food_color, [
  117.                 snake.food[0], snake.food[1], 2020], 0)
  118.             if snake.check_game_status():
  119.                 screen.blit(gameover_words, (241310))
  120.                 pygame.display.update()
  121.                 snake = Snake()
  122.                 new_direction = snake.moving_direction
  123.                 sleep(3)
  124.             elif len(snake.body) == 512:
  125.                 screen.blit(win_words, (33210))
  126.                 pygame.display.update()
  127.                 snake = Snake()
  128.                 new_direction = snake.moving_direction
  129.                 sleep(3)
  130.         else:
  131.             screen.blit(welcome_words, (240150))
  132.             screen.blit(start_game_words, (246310))
  133.             screen.blit(close_game_words, (246350))
  134.         pygame.display.update()
  135.         fps_clock.tick(snake.speed)
  136. if __name__ == '__main__':
  137.     main()

3、俄罗斯方块

1. 案例介绍

俄罗斯方块是由 4 个小方块组成不同形状的板块,随机从屏幕上方落下,按方向键调整板块的位置和方向,在底部拼出完整的一行或几行。这些完整的横条会消失,给新落下来的板块腾出空间,并获得分数奖励。没有被消除掉的方块不断堆积,一旦堆到顶端,便告输,游戏结束。本例难度为高级,适合具有 Python 进阶和 Pygame 编程技巧的用户学习。

2. 设计要点

边框――由 15*25 个空格组成,方块就落在这里面。盒子――组成方块的其中小方块,是组成方块的基本单元。方块――从边框顶掉下的东西,游戏者可以翻转和改变位置。每个方块由 4 个盒子组成。形状――不同类型的方块。这里形状的名字被叫做 T, S, Z ,J, L, I , O。如下图所示:
34b1cebfdbfb8b7685d12b9b2cd2717a.png1bcbdd91d3f114f9fefd3b203b586adf.png

模版――用一个列表存放形状被翻转后的所有可能样式。全部存放在变量里,变量名字如 S or J。着陆――当一个方块到达边框的底部或接触到在其他的盒子话,就说这个方块着陆了。那样的话,另一个方块就会开始下落。

3. 示例效果

954f93938580b7f4627c26800b6db83a.gif

4. 示例源码

  1. import pygame
  2. import random
  3. import os
  4. pygame.init()
  5. GRID_WIDTH = 20
  6. GRID_NUM_WIDTH = 15
  7. GRID_NUM_HEIGHT = 25
  8. WIDTH, HEIGHT = GRID_WIDTH * GRID_NUM_WIDTH, GRID_WIDTH * GRID_NUM_HEIGHT
  9. SIDE_WIDTH = 200
  10. SCREEN_WIDTH = WIDTH + SIDE_WIDTH
  11. WHITE = (0xff0xff0xff)
  12. BLACK = (000)
  13. LINE_COLOR = (0x330x330x33)
  14. CUBE_COLORS = [
  15.     (0xcc0x990x99), (0xff0xff0x99), (0x660x660x99),
  16.     (0x990x000x66), (0xff0xcc0x00), (0xcc0x000x33),
  17.     (0xff0x000x33), (0x000x660x99), (0xff0xff0x33),
  18.     (0x990x000x33), (0xcc0xff0x66), (0xff0x990x00)
  19. ]
  20. screen = pygame.display.set_mode((SCREEN_WIDTH, HEIGHT))
  21. pygame.display.set_caption("俄罗斯方块")
  22. clock = pygame.time.Clock()
  23. FPS = 30
  24. score = 0
  25. level = 1
  26. screen_color_matrix = [[None] * GRID_NUM_WIDTH for i in range(GRID_NUM_HEIGHT)]
  27. # 设置游戏的根目录为当前文件夹
  28. base_folder = os.path.dirname(__file__)
  29. def show_text(surf, text, size, x, y, color=WHITE):
  30.     font_name = os.path.join(base_folder, 'font/font.ttc')
  31.     font = pygame.font.Font(font_name, size)
  32.     text_surface = font.render(text, True, color)
  33.     text_rect = text_surface.get_rect()
  34.     text_rect.midtop = (x, y)
  35.     surf.blit(text_surface, text_rect)
  36. class CubeShape(object):
  37.     SHAPES = ['I''J''L''O''S''T''Z']
  38.     I = [[(0-1), (00), (01), (02)],
  39.          [(-10), (00), (10), (20)]]
  40.     J = [[(-20), (-10), (00), (0-1)],
  41.          [(-10), (00), (01), (02)],
  42.          [(01), (00), (10), (20)],
  43.          [(0-2), (0-1), (00), (10)]]
  44.     L = [[(-20), (-10), (00), (01)],
  45.          [(10), (00), (01), (02)],
  46.          [(0-1), (00), (10), (20)],
  47.          [(0-2), (0-1), (00), (-10)]]
  48.     O = [[(00), (01), (10), (11)]]
  49.     S = [[(-10), (00), (01), (11)],
  50.          [(1-1), (10), (00), (01)]]
  51.     T = [[(0-1), (00), (01), (-10)],
  52.          [(-10), (00), (10), (01)],
  53.          [(0-1), (00), (01), (10)],
  54.          [(-10), (00), (10), (0-1)]]
  55.     Z = [[(0-1), (00), (10), (11)],
  56.          [(-10), (00), (0-1), (1-1)]]
  57.     SHAPES_WITH_DIR = {
  58.         'I': I, 'J': J, 'L': L, 'O': O, 'S': S, 'T': T, 'Z': Z
  59.     }
  60.     def __init__(self):
  61.         self.shape = self.SHAPES[random.randint(0len(self.SHAPES) - 1)]
  62.         # 骨牌所在的行列
  63.         self.center = (2, GRID_NUM_WIDTH // 2)
  64.         self.dir = random.randint(0len(self.SHAPES_WITH_DIR[self.shape]) - 1)
  65.         self.color = CUBE_COLORS[random.randint(0len(CUBE_COLORS) - 1)]
  66.     def get_all_gridpos(self, center=None):
  67.         curr_shape = self.SHAPES_WITH_DIR[self.shape][self.dir]
  68.         if center is None:
  69.             center = [self.center[0], self.center[1]]
  70.         return [(cube[0] + center[0], cube[1] + center[1])
  71.                 for cube in curr_shape]
  72.     def conflict(self, center):
  73.         for cube in self.get_all_gridpos(center):
  74.             # 超出屏幕之外,说明不合法
  75.             if cube[0] < 0 or cube[1] < 0 or cube[0] >= GRID_NUM_HEIGHT or \
  76.                     cube[1] >= GRID_NUM_WIDTH:
  77.                 return True
  78.             # 不为None,说明之前已经有小方块存在了,也不合法
  79.             if screen_color_matrix[cube[0]][cube[1]] is not None:
  80.                 return True
  81.         return False
  82.     def rotate(self):
  83.         new_dir = self.dir + 1
  84.         new_dir %= len(self.SHAPES_WITH_DIR[self.shape])
  85.         old_dir = self.dir
  86.         self.dir = new_dir
  87.         if self.conflict(self.center):
  88.             self.dir = old_dir
  89.             return False
  90.     def down(self):
  91.         # import pdb; pdb.set_trace()
  92.         center = (self.center[0] + 1, self.center[1])
  93.         if self.conflict(center):
  94.             return False
  95.         self.center = center
  96.         return True
  97.     def left(self):
  98.         center = (self.center[0], self.center[1] - 1)
  99.         if self.conflict(center):
  100.             return False
  101.         self.center = center
  102.         return True
  103.     def right(self):
  104.         center = (self.center[0], self.center[1] + 1)
  105.         if self.conflict(center):
  106.             return False
  107.         self.center = center
  108.         return True
  109.     def draw(self):
  110.         for cube in self.get_all_gridpos():
  111.             pygame.draw.rect(screen, self.color,
  112.                              (cube[1] * GRID_WIDTH, cube[0] * GRID_WIDTH,
  113.                               GRID_WIDTH, GRID_WIDTH))
  114.             pygame.draw.rect(screen, WHITE,
  115.                              (cube[1] * GRID_WIDTH, cube[0] * GRID_WIDTH,
  116.                               GRID_WIDTH, GRID_WIDTH),
  117.                              1)
  118. def draw_grids():
  119.     for i in range(GRID_NUM_WIDTH):
  120.         pygame.draw.line(screen, LINE_COLOR,
  121.                          (i * GRID_WIDTH, 0), (i * GRID_WIDTH, HEIGHT))
  122.     for i in range(GRID_NUM_HEIGHT):
  123.         pygame.draw.line(screen, LINE_COLOR,
  124.                          (0, i * GRID_WIDTH), (WIDTH, i * GRID_WIDTH))
  125.     pygame.draw.line(screen, WHITE,
  126.                      (GRID_WIDTH * GRID_NUM_WIDTH, 0),
  127.                      (GRID_WIDTH * GRID_NUM_WIDTH, GRID_WIDTH * GRID_NUM_HEIGHT))
  128. def draw_matrix():
  129.     for i, row in zip(range(GRID_NUM_HEIGHT), screen_color_matrix):
  130.         for j, color in zip(range(GRID_NUM_WIDTH), row):
  131.             if color is not None:
  132.                 pygame.draw.rect(screen, color,
  133.                                  (j * GRID_WIDTH, i * GRID_WIDTH,
  134.                                   GRID_WIDTH, GRID_WIDTH))
  135.                 pygame.draw.rect(screen, WHITE,
  136.                                  (j * GRID_WIDTH, i * GRID_WIDTH,
  137.                                   GRID_WIDTH, GRID_WIDTH), 2)
  138. def draw_score():
  139.     show_text(screen, u'得分:{}'.format(score), 20, WIDTH + SIDE_WIDTH // 2, 100)
  140. def remove_full_line():
  141.     global screen_color_matrix
  142.     global score
  143.     global level
  144.     new_matrix = [[None] * GRID_NUM_WIDTH for i in range(GRID_NUM_HEIGHT)]
  145.     index = GRID_NUM_HEIGHT - 1
  146.     n_full_line = 0
  147.     for i in range(GRID_NUM_HEIGHT - 1-1-1):
  148.         is_full = True
  149.         for j in range(GRID_NUM_WIDTH):
  150.             if screen_color_matrix[i][j] is None:
  151.                 is_full = False
  152.                 continue
  153.         if not is_full:
  154.             new_matrix[index] = screen_color_matrix[i]
  155.             index -= 1
  156.         else:
  157.             n_full_line += 1
  158.     score += n_full_line
  159.     level = score // 20 + 1
  160.     screen_color_matrix = new_matrix
  161. def show_welcome(screen):
  162.     show_text(screen, u'俄罗斯方块'30, WIDTH / 2, HEIGHT / 2)
  163.     show_text(screen, u'按任意键开始游戏'20, WIDTH / 2, HEIGHT / 2 + 50)
  164. running = True
  165. gameover = True
  166. counter = 0
  167. live_cube = None
  168. while running:
  169.     clock.tick(FPS)
  170.     for event in pygame.event.get():
  171.         if event.type == pygame.QUIT:
  172.             running = False
  173.         elif event.type == pygame.KEYDOWN:
  174.             if gameover:
  175.                 gameover = False
  176.                 live_cube = CubeShape()
  177.                 break
  178.             if event.key == pygame.K_LEFT:
  179.                 live_cube.left()
  180.             elif event.key == pygame.K_RIGHT:
  181.                 live_cube.right()
  182.             elif event.key == pygame.K_DOWN:
  183.                 live_cube.down()
  184.             elif event.key == pygame.K_UP:
  185.                 live_cube.rotate()
  186.             elif event.key == pygame.K_SPACE:
  187.                 while live_cube.down() == True:
  188.                     pass
  189.             remove_full_line()
  190.     # level 是为了方便游戏的难度,level 越高 FPS // level 的值越小
  191.     # 这样屏幕刷新的就越快,难度就越大
  192.     if gameover is False and counter % (FPS // level) == 0:
  193.         # down 表示下移骨牌,返回False表示下移不成功,可能超过了屏幕或者和之前固定的
  194.         # 小方块冲突了
  195.         if live_cube.down() == False:
  196.             for cube in live_cube.get_all_gridpos():
  197.                 screen_color_matrix[cube[0]][cube[1]] = live_cube.color
  198.             live_cube = CubeShape()
  199.             if live_cube.conflict(live_cube.center):
  200.                 gameover = True
  201.                 score = 0
  202.                 live_cube = None
  203.                 screen_color_matrix = [[None] * GRID_NUM_WIDTH for i in range(GRID_NUM_HEIGHT)]
  204.         # 消除满行
  205.         remove_full_line()
  206.     counter += 1
  207.     # 更新屏幕
  208.     screen.fill(BLACK)
  209.     draw_grids()
  210.     draw_matrix()
  211.     draw_score()
  212.     if live_cube is not None:
  213.         live_cube.draw()
  214.     if gameover:
  215.         show_welcome(screen)
  216.     pygame.display.update()

4、连连看

1. 案例介绍

连连看是一款曾经非常流行的小游戏。

游戏规则:

  1. 点击选中两个相同的方块。

  2. 两个选中的方块之间连接线的折点不超过两个(接线由X轴和Y轴的平行线组成)。

  3. 每找出一对,它们就会自动消失。

  4. 连线不能从尚未消失的图案上经过。

  5. 把所有的图案全部消除即可获得胜利。

2. 设计思路

  1. 生成成对的图片元素。

  2. 将图片元素打乱排布。

  3. 定义什么才算 相连(两张图片的连线不多于3跟直线,或者说转角不超过2个)。

  4. 实现 相连 判断算法。

  5. 消除图片元素并判断是否消除完毕。

3. 示例效果

b98dec78aa6be80e2d688be378e54416.gif

4. 示例源码

  1. from tkinter import *
  2. from tkinter.messagebox import *
  3. from threading import Timer
  4. import time
  5. import random
  6. class Point:
  7.     # 点类
  8.     def __init__(self, x, y):
  9.         self.x = x
  10.         self.y = y
  11. # --------------------------------------
  12. '''
  13. 判断选中的两个方块是否可以消除
  14. '''
  15. def IsLink(p1, p2):
  16.     if lineCheck(p1, p2):
  17.         return True
  18.     if OneCornerLink(p1, p2):  # 一个转弯(折点)的联通方式
  19.         return True
  20.     if TwoCornerLink(p1, p2):  # 两个转弯(折点)的联通方式
  21.         return True
  22.     return False
  23. # ---------------------------
  24. def IsSame(p1, p2):
  25.     if map[p1.x][p1.y] == map[p2.x][p2.y]:
  26.         print("clicked at IsSame")
  27.         return True
  28.     return False
  29. def callback(event):  # 鼠标左键事件代码
  30.     global Select_first, p1, p2
  31.     global firstSelectRectId, SecondSelectRectId
  32.     # print ("clicked at", event.x, event.y,turn)
  33.     x = (event.x) // 40  # 换算棋盘坐标
  34.     y = (event.y) // 40
  35.     print("clicked at", x, y)
  36.     if map[x][y] == " ":
  37.         showinfo(title="提示", message="此处无方块")
  38.     else:
  39.         if Select_first == False:
  40.             p1 = Point(x, y)
  41.             # 画选定(x1,y1)处的框线
  42.             firstSelectRectId = cv.create_rectangle(x * 40, y * 40, x * 40 + 40, y * 40 + 40, width=2, outline="blue")
  43.             Select_first = True
  44.         else:
  45.             p2 = Point(x, y)
  46.             # 判断第二次点击的方块是否已被第一次点击选取,如果是则返回。
  47.             if (p1.x == p2.x) and (p1.y == p2.y):
  48.                 return
  49.             # 画选定(x2,y2)处的框线
  50.             print('第二次点击的方块', x, y)
  51.             # SecondSelectRectId=cv.create_rectangle(100,20,x*40+40,y*40+40,width=2,outline="yellow")
  52.             SecondSelectRectId = cv.create_rectangle(x * 40, y * 40, x * 40 + 40, y * 40 + 40, width=2,
  53.                                                      outline="yellow")
  54.             print('第二次点击的方块', SecondSelectRectId)
  55.             cv.pack()
  56.             # 判断是否连通
  57.             if IsSame(p1, p2) and IsLink(p1, p2):
  58.                 print('连通', x, y)
  59.                 Select_first = False
  60.                 # 画选中方块之间连接线
  61.                 drawLinkLine(p1, p2)
  62.                 # clearTwoBlock()
  63.                 # time.sleep(0.6)
  64.                 # clearFlag=True
  65.                 t = Timer(timer_interval, delayrun)  # 定时函数
  66.                 t.start()
  67.             else:  # 重新选定第一个方块
  68.                 # 清除第一个选定框线
  69.                 cv.delete(firstSelectRectId)
  70.                 cv.delete(SecondSelectRectId)
  71.                 # print('清除第一个选定框线')
  72.                 # firstSelectRectId=SecondSelectRectId
  73.                 # p1=Point(x,y)           #设置重新选定第一个方块的坐标
  74.                 Select_first = False
  75. timer_interval = 0.3  # 0.3
  76. # --------------------------------------
  77. def delayrun():
  78.     clearTwoBlock()  # 清除连线及方块
  79. def clearTwoBlock():  # 清除连线及方块
  80.     # 延时0.1
  81.     # time.sleep(0.1)
  82.     # 清除第一个选定框线
  83.     cv.delete(firstSelectRectId)
  84.     # 清除第2个选定框线
  85.     cv.delete(SecondSelectRectId)
  86.     # 清空记录方块的值
  87.     map[p1.x][p1.y] = " "
  88.     cv.delete(image_map[p1.x][p1.y])
  89.     map[p2.x][p2.y] = " "
  90.     cv.delete(image_map[p2.x][p2.y])
  91.     Select_first = False
  92.     undrawConnectLine()  # 清除选中方块之间连接线
  93. def drawQiPan():  # 画棋盘
  94.     for i in range(015):
  95.         cv.create_line(2020 + 40 * i, 58020 + 40 * i, width=2)
  96.     for i in range(015):
  97.         cv.create_line(20 + 40 * i, 2020 + 40 * i, 580, width=2)
  98.     cv.pack()
  99. def print_map():  # 输出map地图
  100.     global image_map
  101.     for x in range(0, Width):  # 0--14
  102.         for y in range(0, Height):  # 0--14
  103.             if (map[x][y] != ' '):
  104.                 img1 = imgs[int(map[x][y])]
  105.                 id = cv.create_image((x * 40 + 20, y * 40 + 20), image=img1)
  106.                 image_map[x][y] = id
  107.     cv.pack()
  108.     for y in range(0, Height):  # 0--14
  109.         for x in range(0, Width):  # 0--14
  110.             print(map[x][y], end=' ')
  111.         print(",", y)
  112. '''
  113. * 同行同列情况消除方法 原理:如果两个相同的被消除元素之间的 空格数
  114. spaceCount等于他们的(行/列差-1)则 两者可以联通消除
  115. * x代表列,y代表行
  116. * param p1 第一个保存上次选中点坐标的点对象
  117. * param p2 第二个保存上次选中点坐标的点对象
  118. '''
  119. # 直接连通
  120. def lineCheck(p1, p2):
  121.     absDistance = 0
  122.     spaceCount = 0
  123.     if (p1.x == p2.x or p1.y == p2.y):  # 同行同列的情况吗?
  124.         print("同行同列的情况------")
  125.         # 同列的情况
  126.         if (p1.x == p2.x and p1.y != p2.y):
  127.             print("同列的情况")
  128.             # 绝对距离(中间隔着的空格数)
  129.             absDistance = abs(p1.y - p2.y) - 1
  130.             # 正负值
  131.             if p1.y - p2.y > 0:
  132.                 zf = -1
  133.             else:
  134.                 zf = 1
  135.             for i in range(1, absDistance + 1):
  136.                 if (map[p1.x][p1.y + i * zf] == " "):
  137.                     # 空格数加1
  138.                     spaceCount += 1
  139.                 else:
  140.                     break;  # 遇到阻碍就不用再探测了
  141.         # 同行的情况
  142.         elif (p1.y == p2.y and p1.x != p2.x):
  143.             print(" 同行的情况")
  144.             absDistance = abs(p1.x - p2.x) - 1
  145.             # 正负值
  146.             if p1.x - p2.x > 0:
  147.                 zf = -1
  148.             else:
  149.                 zf = 1
  150.             for i in range(1, absDistance + 1):
  151.                 if (map[p1.x + i * zf][p1.y] == " "):
  152.                     # 空格数加1
  153.                     spaceCount += 1
  154.                 else:
  155.                     break;  # 遇到阻碍就不用再探测了
  156.         if (spaceCount == absDistance):
  157.             # 可联通
  158.             print(absDistance, spaceCount)
  159.             print("行/列可直接联通")
  160.             return True
  161.         else:
  162.             print("行/列不能消除!")
  163.             return False
  164.     else:
  165.         # 不是同行同列的情况所以直接返回false
  166.         return False;
  167.     # --------------------------------------
  168. # 第二种,直角连通
  169. '''
  170. 直角连接,即X,Y坐标都不同的,可以用这个方法尝试连接
  171.  param first:选中的第一个点
  172.  param second:选中的第二个点
  173. '''
  174. def OneCornerLink(p1, p2):
  175.     # 第一个直角检查点,如果这里为空则赋予相同值供检查
  176.     checkP = Point(p1.x, p2.y)
  177.     # 第二个直角检查点,如果这里为空则赋予相同值供检查
  178.     checkP2 = Point(p2.x, p1.y);
  179.     # 第一个直角点检测
  180.     if (map[checkP.x][checkP.y] == " "):
  181.         if (lineCheck(p1, checkP) and lineCheck(checkP, p2)):
  182.             linePointStack.append(checkP)
  183.             print("直角消除ok", checkP.x, checkP.y)
  184.             return True
  185.     # 第二个直角点检测
  186.     if (map[checkP2.x][checkP2.y] == " "):
  187.         if (lineCheck(p1, checkP2) and lineCheck(checkP2, p2)):
  188.             linePointStack.append(checkP2)
  189.             print("直角消除ok", checkP2.x, checkP2.y)
  190.             return True
  191.     print("不能直角消除")
  192.     return False;
  193. # -----------------------------------------
  194. '''
  195. #第三种,双直角连通
  196. 双直角联通判定可分两步走:
  197. 1. 在p1点周围4个方向寻找空格checkP
  198. 2. 调用OneCornerLink(checkP, p2)
  199. 3. 即遍历 p1 4 个方向的空格,使之成为 checkP,然后调用 OneCornerLink(checkP, 
  200. p2)判定是否为真,如果为真则可以双直角连同,否则当所有的空格都遍历完而没有找
  201. 到一个checkP使OneCornerLink(checkP, p2)为真,则两点不能连同
  202. 具体代码:
  203. 双直角连接方法
  204. @param p1 第一个点
  205. @param p2 第二个点
  206. '''
  207. def TwoCornerLink(p1, p2):
  208.     checkP = Point(p1.x, p1.y)
  209.     # 四向探测开始
  210.     for i in range(04):
  211.         checkP.x = p1.x
  212.         checkP.y = p1.y
  213.         # 向下
  214.         if (i == 3):
  215.             checkP.y += 1
  216.             while ((checkP.y < Height) and map[checkP.x][checkP.y] == " "):
  217.                 linePointStack.append(checkP)
  218.                 if (OneCornerLink(checkP, p2)):
  219.                     print("下探测OK")
  220.                     return True
  221.                 else:
  222.                     linePointStack.pop()
  223.                 checkP.y += 1
  224.             print("ssss", checkP.y, Height - 1)
  225.             # 补充两个折点都在游戏区域底侧外部
  226.             if checkP.y == Height:  # 出了底部,则仅需判断p2能否也达到底部边界
  227.                 z = Point(p2.x, Height - 1)  # 底部边界点
  228.                 if lineCheck(z, p2):  # 两个折点在区域外部的底侧
  229.                     linePointStack.append(Point(p1.x, Height))
  230.                     linePointStack.append(Point(p2.x, Height))
  231.                     print("下探测到游戏区域外部OK")
  232.                     return True
  233.         # 向右
  234.         elif (i == 2):
  235.             checkP.x += 1
  236.             while ((checkP.x < Width) and map[checkP.x][checkP.y] == " "):
  237.                 linePointStack.append(checkP)
  238.                 if (OneCornerLink(checkP, p2)):
  239.                     print("右探测OK")
  240.                     return True
  241.                 else:
  242.                     linePointStack.pop()
  243.                 checkP.x += 1
  244.             # 补充两个折点都在游戏区域右侧外部
  245.             if checkP.x == Width:  # 出了右侧,则仅需判断p2能否也达到右部边界
  246.                 z = Point(Width - 1, p2.y)  # 右部边界点
  247.                 if lineCheck(z, p2):  # 两个折点在区域外部的底侧
  248.                     linePointStack.append(Point(Width, p1.y))
  249.                     linePointStack.append(Point(Width, p2.y))
  250.                     print("右探测到游戏区域外部OK")
  251.                     return True
  252.         # 向左
  253.         elif (i == 1):
  254.             checkP.x -= 1
  255.             while ((checkP.x >= 0) and map[checkP.x][checkP.y] == " "):
  256.                 linePointStack.append(checkP)
  257.                 if (OneCornerLink(checkP, p2)):
  258.                     print("左探测OK")
  259.                     return True
  260.                 else:
  261.                     linePointStack.pop()
  262.                 checkP.x -= 1
  263.         # 向上
  264.         elif (i == 0):
  265.             checkP.y -= 1
  266.             while ((checkP.y >= 0) and map[checkP.x][checkP.y] == " "):
  267.                 linePointStack.append(checkP)
  268.                 if (OneCornerLink(checkP, p2)):
  269.                     print("上探测OK")
  270.                     return True
  271.                 else:
  272.                     linePointStack.pop()
  273.                 checkP.y -= 1
  274.     # 四个方向都寻完都没找到适合的checkP点
  275.     print("两直角连接没找到适合的checkP点")
  276.     return False;
  277. # ---------------------------
  278. # 画连接线
  279. def drawLinkLine(p1, p2):
  280.     if (len(linePointStack) == 0):
  281.         Line_id.append(drawLine(p1, p2))
  282.     else:
  283.         print(linePointStack, len(linePointStack))
  284.     if (len(linePointStack) == 1):
  285.         z = linePointStack.pop()
  286.         print("一折连通点z", z.x, z.y)
  287.         Line_id.append(drawLine(p1, z))
  288.         Line_id.append(drawLine(p2, z))
  289.     if (len(linePointStack) == 2):
  290.         z1 = linePointStack.pop()
  291.         print("2折连通点z1", z1.x, z1.y)
  292.         Line_id.append(drawLine(p2, z1))
  293.         z2 = linePointStack.pop()
  294.         print("2折连通点z2", z2.x, z2.y)
  295.         Line_id.append(drawLine(z1, z2))
  296.         Line_id.append(drawLine(p1, z2))
  297. # 删除连接线
  298. def undrawConnectLine():
  299.     while len(Line_id) > 0:
  300.         idpop = Line_id.pop()
  301.         cv.delete(idpop)
  302. def drawLine(p1, p2):
  303.     print("drawLine p1,p2", p1.x, p1.y, p2.x, p2.y)
  304.     # cv.create_line( 40+2040+20,200,200,width=5,fill='red')
  305.     id = cv.create_line(p1.x * 40 + 20, p1.y * 40 + 20, p2.x * 40 + 20, p2.y * 40 + 20, width=5, fill='red')
  306.     # cv.pack()
  307.     return id
  308. # --------------------------------------
  309. def create_map():  # 产生map地图
  310.     global map
  311.     # 生成随机地图
  312.     # 将所有匹配成对的动物物种放进一个临时的地图中
  313.     tmpMap = []
  314.     m = (Width) * (Height) // 10
  315.     print('m=', m)
  316.     for x in range(0, m):
  317.         for i in range(010):  # 每种方块有10
  318.             tmpMap.append(x)
  319.     random.shuffle(tmpMap)
  320.     for x in range(0, Width):  # 0--14
  321.         for y in range(0, Height):  # 0--14
  322.             map[x][y] = tmpMap[x * Height + y]
  323. # --------------------------------------
  324. def find2Block(event):  # 自动查找
  325.     global firstSelectRectId, SecondSelectRectId
  326.     m_nRoW = Height
  327.     m_nCol = Width
  328.     bFound = False;
  329.     # 第一个方块从地图的0位置开始
  330.     for i in range(0, m_nRoW * m_nCol):
  331.         # 找到则跳出循环
  332.         if (bFound):
  333.             break
  334.         # 算出对应的虚拟行列位置
  335.         x1 = i % m_nCol
  336.         y1 = i // m_nCol
  337.         p1 = Point(x1, y1)
  338.         # 无图案的方块跳过
  339.         if (map[x1][y1] == ' '):
  340.             continue
  341.         # 第二个方块从前一个方块的后面开始
  342.         for j in range(i + 1, m_nRoW * m_nCol):
  343.             # 算出对应的虚拟行列位置
  344.             x2 = j % m_nCol
  345.             y2 = j // m_nCol
  346.             p2 = Point(x2, y2)
  347.             # 第二个方块不为空 且与第一个方块的动物相同
  348.             if (map[x2][y2] != ' ' and IsSame(p1, p2)):
  349.                 # 判断是否可以连通
  350.                 if (IsLink(p1, p2)):
  351.                     bFound = True
  352.                     break
  353.     # 找到后自动消除
  354.     if (bFound):  # p1(x1,y1)与p2(x2,y2)连通
  355.         print('找到后', p1.x, p1.y, p2.x, p2.y)
  356.         # 画选定(x1,y1)处的框线
  357.         firstSelectRectId = cv.create_rectangle(x1 * 40, y1 * 40, x1 * 40 + 40, y1 * 40 + 40, width=2, outline="red")
  358.         # 画选定(x2,y2)处的框线
  359.         secondSelectRectId = cv.create_rectangle(x2 * 40, y2 * 40, x2 * 40 + 40, y2 * 40 + 40, width=2, outline="red")
  360.         # t=Timer(timer_interval,delayrun)#定时函数
  361.         # t.start()
  362.     return bFound
  363. # 游戏主逻辑
  364. root = Tk()
  365. root.title("Python连连看 ")
  366. imgs = [PhotoImage(file='images\\bar_0' + str(i) + '.gif'for i in range(010)]  # 所有图标图案
  367. Select_first = False  # 是否已经选中第一块
  368. firstSelectRectId = -1  # 被选中第一块地图对象
  369. SecondSelectRectId = -1  # 被选中第二块地图对象
  370. clearFlag = False
  371. linePointStack = []
  372. Line_id = []
  373. Height = 10
  374. Width = 10
  375. map = [[" " for y in range(Height)] for x in range(Width)]
  376. image_map = [[" " for y in range(Height)] for x in range(Width)]
  377. cv = Canvas(root, bg='green', width=440, height=440)
  378. # drawQiPan( )
  379. cv.bind("<Button-1>", callback)  # 鼠标左键事件
  380. cv.bind("<Button-3>", find2Block)  # 鼠标右键事件
  381. cv.pack()
  382. create_map()  # 产生map地图
  383. print_map()  # 打印map地图
  384. root.mainloop()

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本文链接:

https://blog.csdn.net/xw1680/article/details/116201057

推荐阅读    点击标题可跳转

Python之父推荐!《Python 3网络爬虫开发实战》第二版来了!文末送签名版!

分享3个好用到爆的Python模块,点赞收藏

用Python可视化分析全球火山分布,发现了这些有趣的现象

【原创干货】6000字、22个案例详解Pandas数据分析/预处理时的实用技巧,超简单

db81f69f39567b1f0db032b4a8709d87.gif

点击这里,阅读更多

Python

文章!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/73760
推荐阅读
相关标签
  

闽ICP备14008679号