Skip to content

Latest commit

 

History

History
210 lines (175 loc) · 8.98 KB

File metadata and controls

210 lines (175 loc) · 8.98 KB

二十三、蚀刻绘图器

原文:http://inventwithpython.com/bigbookpython/project23.html

当您使用WASD键在屏幕上移动笔尖时,蚀刻绘图器通过描绘一条连续的线来形成一幅图片,就像蚀刻素描玩具一样。让你艺术的一面爆发出来,看看你能创造出什么形象!这个程序还可以让你把你的绘图保存到一个文本文件中,这样你以后就可以把它们打印出来。此外,你可以将其他图形的WASD运动复制粘贴到这个程序中,比如源代码第 6 到 14 行中的希尔伯特曲线分形的WASD命令。

运行示例

当你运行etchingdrawer.py时,输出将看起来像图 23-1 。

f23001

:在蚀刻绘图器程序中绘制的图

工作原理

与项目 17“骰子数学”一样,这个程序使用存储在名为canvas的变量中的字典来记录绘图的线条。关键字是(x, y)元组,值是表示要在屏幕上的 x,y 坐标处绘制的线形的字符串。附录 b 给出了可以在 Python 程序中使用的 Unicode 字符的完整列表。

第 126 行有一个对open()的函数调用,它传递了一个encoding='utf-8'参数。原因超出了本书的范围,但这是 Windows 将行字符写入文本文件所必需的。

"""Etching Drawer, by Al Sweigart email@protected
An art program that draws a continuous line around the screen using the
WASD keys. Inspired by Etch A Sketch toys.

For example, you can draw Hilbert Curve fractal with:
SDWDDSASDSAAWASSDSASSDWDSDWWAWDDDSASSDWDSDWWAWDWWASAAWDWAWDDSDW

Or an even larger Hilbert Curve fractal with:
DDSAASSDDWDDSDDWWAAWDDDDSDDWDDDDSAASDDSAAAAWAASSSDDWDDDDSAASDDSAAAAWA
ASAAAAWDDWWAASAAWAASSDDSAASSDDWDDDDSAASDDSAAAAWAASSDDSAASSDDWDDSDDWWA
AWDDDDDDSAASSDDWDDSDDWWAAWDDWWAASAAAAWDDWAAWDDDDSDDWDDSDDWDDDDSAASDDS
AAAAWAASSDDSAASSDDWDDSDDWWAAWDDDDDDSAASSDDWDDSDDWWAAWDDWWAASAAAAWDDWA
AWDDDDSDDWWAAWDDWWAASAAWAASSDDSAAAAWAASAAAAWDDWAAWDDDDSDDWWWAASAAAAWD
DWAAWDDDDSDDWDDDDSAASSDDWDDSDDWWAAWDD

This code is available at https://nostarch.com/big-book-small-python-programming
Tags: large, artistic"""

import shutil, sys

# Set up the constants for line characters:
UP_DOWN_CHAR         = chr(9474)  # Character 9474 is '│'
LEFT_RIGHT_CHAR      = chr(9472)  # Character 9472 is '─'
DOWN_RIGHT_CHAR      = chr(9484)  # Character 9484 is '┌'
DOWN_LEFT_CHAR       = chr(9488)  # Character 9488 is '┐'
UP_RIGHT_CHAR        = chr(9492)  # Character 9492 is '└'
UP_LEFT_CHAR         = chr(9496)  # Character 9496 is '┘'
UP_DOWN_RIGHT_CHAR   = chr(9500)  # Character 9500 is '├'
UP_DOWN_LEFT_CHAR    = chr(9508)  # Character 9508 is '┤'
DOWN_LEFT_RIGHT_CHAR = chr(9516)  # Character 9516 is '┬'
UP_LEFT_RIGHT_CHAR   = chr(9524)  # Character 9524 is '┴'
CROSS_CHAR           = chr(9532)  # Character 9532 is '┼'
# A list of chr() codes is at https://inventwithpython.com/chr

# Get the size of the terminal window:
CANVAS_WIDTH, CANVAS_HEIGHT = shutil.get_terminal_size()
# We can't print to the last column on Windows without it adding a
# newline automatically, so reduce the width by one:
CANVAS_WIDTH -= 1
# Leave room at the bottom few rows for the command info lines.
CANVAS_HEIGHT -= 5

"""The keys for canvas will be (x, y) integer tuples for the coordinate,
and the value is a set of letters W, A, S, D that tell what kind of line
should be drawn."""
canvas = {}
cursorX = 0
cursorY = 0


def getCanvasString(canvasData, cx, cy):
   """Returns a multiline string of the line drawn in canvasData."""
   canvasStr = ''

   """canvasData is a dictionary with (x, y) tuple keys and values that
   are sets of 'W', 'A', 'S', and/or 'D' strings to show which
   directions the lines are drawn at each xy point."""
   for rowNum in range(CANVAS_HEIGHT):
       for columnNum in range(CANVAS_WIDTH):
           if columnNum == cx and rowNum == cy:
               canvasStr += '#'
               continue

           # Add the line character for this point to canvasStr.
           cell = canvasData.get((columnNum, rowNum))
           if cell in (set(['W', 'S']), set(['W']), set(['S'])):
               canvasStr += UP_DOWN_CHAR
           elif cell in (set(['A', 'D']), set(['A']), set(['D'])):
               canvasStr += LEFT_RIGHT_CHAR
           elif cell == set(['S', 'D']):
               canvasStr += DOWN_RIGHT_CHAR
           elif cell == set(['A', 'S']):
               canvasStr += DOWN_LEFT_CHAR
           elif cell == set(['W', 'D']):
               canvasStr += UP_RIGHT_CHAR
           elif cell == set(['W', 'A']):
               canvasStr += UP_LEFT_CHAR
           elif cell == set(['W', 'S', 'D']):
               canvasStr += UP_DOWN_RIGHT_CHAR
           elif cell == set(['W', 'S', 'A']):
               canvasStr += UP_DOWN_LEFT_CHAR
           elif cell == set(['A', 'S', 'D']):
               canvasStr += DOWN_LEFT_RIGHT_CHAR
           elif cell == set(['W', 'A', 'D']):
               canvasStr += UP_LEFT_RIGHT_CHAR
           elif cell == set(['W', 'A', 'S', 'D']):
               canvasStr += CROSS_CHAR
           elif cell == None:
               canvasStr += ' '
       canvasStr += '\n'  # Add a newline at the end of each row.
   return canvasStr


moves = []
while True:  # Main program loop.
   # Draw the lines based on the data in canvas:
   print(getCanvasString(canvas, cursorX, cursorY))

   print('WASD keys to move, H for help, C to clear, '
        + 'F to save, or QUIT.')
    response = input('> ').upper()

    if response == 'QUIT':
        print('Thanks for playing!')
        sys.exit()  # Quit the program.
    elif response == 'H':
        print('Enter W, A, S, and D characters to move the cursor and')
        print('draw a line behind it as it moves. For example, ddd')
        print('draws a line going right and sssdddwwwaaa draws a box.')
        print()
        print('You can save your drawing to a text file by entering F.')
        input('Press Enter to return to the program...')
        continue
    elif response == 'C':
        canvas = {}  # Erase the canvas data.
        moves.append('C')  # Record this move.
    elif response == 'F':
        # Save the canvas string to a text file:
        try:
            print('Enter filename to save to:')
            filename = input('> ')

            # Make sure the filename ends with .txt:
            if not filename.endswith('.txt'):
                filename += '.txt'
            with open(filename, 'w', encoding='utf-8') as file:
                file.write(''.join(moves) + '\n')
                file.write(getCanvasString(canvas, None, None))
        except:
            print('ERROR: Could not save file.')

    for command in response:
        if command not in ('W', 'A', 'S', 'D'):
            continue  # Ignore this letter and continue to the next one.
        moves.append(command)  # Record this move.

        # The first line we add needs to form a full line:
        if canvas == {}:
            if command in ('W', 'S'):
                # Make the first line a horizontal one:
                canvas[(cursorX, cursorY)] = set(['W', 'S'])
            elif command in ('A', 'D'):
                # Make the first line a vertical one:
                canvas[(cursorX, cursorY)] = set(['A', 'D'])

        # Update x and y:
        if command == 'W' and cursorY > 0:
            canvas[(cursorX, cursorY)].add(command)
            cursorY = cursorY - 1
        elif command == 'S' and cursorY < CANVAS_HEIGHT - 1:
            canvas[(cursorX, cursorY)].add(command)
            cursorY = cursorY + 1
        elif command == 'A' and cursorX > 0:
            canvas[(cursorX, cursorY)].add(command)
            cursorX = cursorX - 1
        elif command == 'D' and cursorX < CANVAS_WIDTH - 1:
            canvas[(cursorX, cursorY)].add(command)
            cursorX = cursorX + 1
        else:
            # If the cursor doesn't move because it would have moved off
            # the edge of the canvas, then don't change the set at
            # canvas[(cursorX, cursorY)].
            continue

        # If there's no set for (cursorX, cursorY), add an empty set:
        if (cursorX, cursorY) not in canvas:
            canvas[(cursorX, cursorY)] = set()

        # Add the direction string to this xy point's set:
        if command == 'W':
            canvas[(cursorX, cursorY)].add('S')
        elif command == 'S':
            canvas[(cursorX, cursorY)].add('W')
        elif command == 'A':
            canvas[(cursorX, cursorY)].add('D')
        elif command == 'D':
            canvas[(cursorX, cursorY)].add('A') 

探索程序

试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。

  1. 如果把 101 行的response = input('> ').upper()改成response = input('> ')会怎么样?
  2. 如果把第 61 行的canvasStr += '#'改成canvasStr += '@'会怎么样?
  3. 如果把第 89 行的canvasStr += ' '改成canvasStr += '.'会怎么样?
  4. 如果把 94 行的moves = []改成moves = list('SDWDDSASDSAAWASSDSAS')会怎么样?