Try   HackMD

Scratch and Python 2018 Spring - Python Lecture 2

Flow Control

  • Flow chart
  • Boolean Values
    • True
    • False
    • true and false are legal names for variables.
  • Comparison operators
    • ==
      • float is not accurate.
    • !=
    • <
    • <=
    • >
    • >=
    • Compare values of different types
      • string versus int
      • int versus float
  • Truthy and falsey values
    • Truthy values
      • Non-zero integers
      • Non-zero float
      • Non-empty string
    • Falsey values
      • 0
      • 0.0
      • ''
    • The are more truthy and falsey values.
  • Boolean operators
    • and
    • or
    • not
    • Precedence: not > and > or
    • Note: the results of the above operations might not be one of True and False. They may result in truthy value and falsey value.
  • Conditions: a Boolean expression
  • Block of code Colored structure
    • Selection
      • if
      • if-else
      • if-elif
      • if-elif-else
      • example
        ​​​​​​​​​​​​x = int(input("請輸入成績(0~100) : ")) ​​​​​​​​​​​​if x > 100 or x < 0: ​​​​​​​​​​​​ print('不要騙喔! OAO') ​​​​​​​​​​​​elif x >= 90: ​​​​​​​​​​​​ print('Nice!!') ​​​​​​​​​​​​elif x >= 60: ​​​​​​​​​​​​ print('Pass') ​​​​​​​​​​​​else: ​​​​​​​​​​​​ print('好課值得一修再修3修重修畢業後繼續修')
    • Iteration
      • while loop
        • Similar to if but go back to while for the next iteration after the loop body executed, not for the next statement.
      • break statement
        • Skip the following codes in the same block and end the loop.
      • Sample code 1
        ​​​​​​​​​​​​## Sample 1 ​​​​​​​​​​​​while True: ​​​​​​​​​​​​ x = input('助教帥不帥? (y/n)') ​​​​​​​​​​​​ if x != 'y': ​​​​​​​​​​​​ print('你確定?再給你一次機會') ​​​​​​​​​​​​ else: ​​​​​​​​​​​​ break ​​​​​​​​​​​​print('謝謝誇獎')
      • for loop
        • for item in bag: is to pull the next item out of the bag.
        • bag can be a string, a tuple, a list, and other iterables.
          • 'a','b','c' are items in the bag 'abc'.
          • x,y are items in the bag (x,y).
          • 1,2,987 are items in the bag [1,2,987].
          • 0,1,2, , n-1 are items in the bag range(n).
          • m,m+1,m+2, , n-1 are items in the bag range(m,n).
      • continue statement
        • Skip the following codes in the same block and start the next iteration.
      • Sample code 2
        ​​​​​​​​​​​​## Sample 2 ​​​​​​​​​​​​print('數質數 1 ~ 20') ​​​​​​​​​​​​prime = [2, 3, 5, 7, 11, 13, 17, 19] ​​​​​​​​​​​​for num in range(1, 21): ​​​​​​​​​​​​ is_prime = False ​​​​​​​​​​​​ for p in prime: ​​​​​​​​​​​​ if num == p: ​​​​​​​​​​​​ is_prime = True ​​​​​​​​​​​​ break ​​​​​​​​​​​​ if is_prime == False: ​​​​​​​​​​​​ continue ​​​​​​​​​​​​ print(num)
    • Function definition
      • def: define a custom function
      • return
        • return x: the value of the result is x
        • return: the value of the result is None
          • None 是一個 NoneType 型態的值,而 NoneType 只有 None 一種值,代表沒有。None是個 falsey value。
      • Sample code
        ​​​​​​​​​​​​#========Function========# ​​​​​​​​​​​​def findmax(arr): ​​​​​​​​​​​​ ans = arr[0] ​​​​​​​​​​​​ for tmp in arr: ​​​​​​​​​​​​ if ans < tmp: ​​​​​​​​​​​​ ans = tmp ​​​​​​​​​​​​ return ans ​​​​​​​​​​​​#========Function========# ​​​​​​​​​​​​array = [0, 2, 9, 3, 5, 6, 7] ​​​​​​​​​​​​print(findmax(array))
    • Exception handling
      • try-except
      • example
      ​​​​​​​​x = input("做除法, 請輸入2個數字,用空白隔開\n").split() ​​​​​​​​try: ​​​​​​​​ print(int(x[0]) / int(x[1])) ​​​​​​​​except: ​​​​​​​​ print('除數不得為0')
  • Python 3 language reference

Debugger

參考資料:Spyder Debugger Tutorial

作業四

  • Draw Rokumonsen
  • Draw Ichimonsen (一文銭)
  • Draw Sanjurokumonsen (三十六文銭)

pyautogui: open your eyes!

  • Sample code 1

    ​​​​import pyautogui, time ​​​​def report(): ​​​​ pos = pyautogui.position() ​​​​ color = pyautogui.screenshot().getpixel(pos) ​​​​ print('Mouse position:',pos,'Color:',color) ​​​​while True: ​​​​ report() ​​​​ time.sleep(0.1)
  • screenshot()

    • Pixel
      • screenshot().getpixel()
      • (R,G,B)
    • screenshot().im is iterable
      • Pixels are balls, and screenshot() is a bag containing balls.
    • screenshot().save('pic.png') 存檔到 pic.png
  • How to take a screen shot

    • Windows: print screen key
      • Hold alt, then press print screen
        • This gives you a picture of window on focus
      • Use Microsoft Paint to save the screenshot into png file
  • Sample code

    ​​​​import pyautogui, time ​​​​pyautogui.FAILSAFE = True ​​​​loc = pyautogui.locateOnScreen('box.png') # 偵測螢幕上是否有與 box.png 相同的圖片,若有 loc 將會被指派相同於圖片的地方的位置,若沒有 loc 會被指派為 None ​​​​ ​​​​while loc == None: # 如果 loc 是 None (剛剛螢幕上沒找到相同的圖片) / 如果有找到,會直接跳過這個迴圈 ​​​​ time.sleep(0.1) # 停 0.1 秒 ​​​​ loc = pyautogui.locateOnScreen('box.png') # 再找一次 ​​​​for loc_i in pyautogui.locateAllOnScreen('box.png'): ​​​​ print(loc_i) ​​​​ center = pyautogui.center(loc_i) ​​​​ pyautogui.click(center)
  • locateOnScreen(pic)

    • 在螢幕上尋找與 pic 相同的圖片,若找到一個,則回傳位置(4-element tuple (left,top,width,height))並停止尋找。
    • 若都沒有找到,則回傳 None
  • locateAllOnScreen(pic)

    • 在螢幕上尋找 全部pic 相同的圖片,吐出一個generator

作業五 Check all boxes!

Practice here

Issues on screenshot

  • screenshot is slow
  • locateOnScreen is very slow
  • Hard to recognize objects

Fast Screenshot

Save the following code as fastscreenshot.py at your desktop.

import time from PIL import Image from mss import mss # Fast screenshot # mss 3.0 ver # Usage just like pyautogui # Parameter: # region: optional, four-integer tuple (left, top, width, height) def screenshot(region=None, **kwargs): im = None monitors = None if region == None: region = kwargs.get('region') with mss() as sct: # Region to capture monitor = sct.monitors[1] if region != None: monitor['left'] = int(region[0]) monitor['top'] = int(region[1]) monitor['width'] = int(region[2]) monitor['height'] = int(region[3]) # Get pixels on image sct_img = sct.grab(monitor) im = Image.frombytes('RGBA', sct_img.size, bytes(sct_img.raw), 'raw', 'BGRA') im = im.convert('RGB') return im # Evaluate screenshot time in seconds def evaluate_screenshot_time(region=None): t = time.time() im = screenshot(region) return time.time() - t

Then, save the following as test.py at your desktop.

import time import pyautogui from pyautogui import screenshot # from fastscreenshot import screenshot def report_time(): t = time.time() left, top, width, height = 5, 66, 77, 8 screenshot(region=(left,top,width,height)) print(time.time()-t) for i in range(100): report_time()

If fastscreenshot.py does not work, use the following.

import time from PIL import Image from mss import mss # Fast screenshot # Usage just like pyautogui # Parameter: # region: optional, four-integer tuple (left, top, width, height) def screenshot(region=None,**kwargs): im = None monitors = None if region == None: region = kwargs.get('region') with mss() as sct: # Retrieve monitors informations: monitors = sct.enum_display_monitors() # Region to capture monitor = dict(monitors[1]) if region != None: monitor['left'] = int(region[0]) monitor['top'] = int(region[1]) monitor['width'] = (int(region[2]) // 80 + int(region[2] % 80 != 0)) * 80 monitor['height'] = int(region[3]) # Get pixels on image sct.get_pixels(monitor) im = Image.frombytes('RGB', (sct.width, sct.height), sct.image) if region != None: if monitor.get('width') != region[2]: im = im.crop((0, 0, region[2], region[3])) return im # Evaluate screenshot time in seconds def evaluate_screenshot_time(region=None): t = time.time() im = screenshot(region) return time.time() - t

Type

  • All values have their own types.
  • Use type(v) to check the type of value v.
  • All defined name (identifier) have their values.
  • Use type(name) to check the type of name.

Function

  • def your_function():
  • return
    • The function is terminated either by return or by the end of the block.
    • None: if there is no return or just return nothing
  • def your_function_with_parameter(parameter)
    • Argument
      ​​​​​​​​def square(x): ​​​​​​​​ return x**2 ​​​​​​​​ ​​​​​​​​def square_root(x): ​​​​​​​​ return x**0.5
  • def your_function_with_parameters(parameter1, parameter2)
    • More arguments
      ​​​​​​​​def distance(x1,y1,x2,y2): ​​​​​​​​ delta_x = x1-x2 ​​​​​​​​ delta_y = y1-y2 ​​​​​​​​ dx2 = square(delta_x) ​​​​​​​​ dy2 = square(delta_y) ​​​​​​​​ return square_root(dx2+dy2)
  • def your_function_with_parameters(*args)
    • Variable length list of arguments can be passed by args which is a tuple.
    • More flexible
      ​​​​​​​​def square_var_len(*x): ​​​​​​​​ print(type(x)) ​​​​​​​​ print(len(x)) ​​​​​​​​ ret = tuple() ​​​​​​​​ for v in x: ​​​​​​​​ ret = ret + (v**2,) ​​​​​​​​ return ret
    • Access data via index i: args[i]
  • def your_function_with_parameters(**kwargs)
    • Keyword arguments
    • print(some_str, end='')
    • print(some_str_1, some_str_2, sep=',')
    • Access data via key string
      • kwargs['end']
      • kwargs['sep']
    • ​​​​​​​​def square_kw(x,**kwargs): ​​​​​​​​ if kwargs.get('test'): ​​​​​​​​ return 'test' ​​​​​​​​ return x**2
  • Please do not overwrite the arguments in your function now. We will discuss what will happen if you do so later.
  • Scope
    • Sample code
      ​​​​​​​​a_global_var = 'A global variable' ​​​​​​​​def f(arg,*args,**kwargs): ​​​​​​​​ print(arg) ​​​​​​​​ print('args:',args) ​​​​​​​​ print('kwargs:',kwargs) ​​​​​​​​ local = 'A local variable' ​​​​​​​​ print('local:',local) ​​​​​​​​ print('a_global_var:',a_global_var) ​​​​​​​​f('hi','hi','ni',hihi='hihihi')
    • Variables are defined by assignment statements
    • Global
      • Defined in global scope
      • global
        • global some_var: some_var in this function is the global some_var
        • You must do this if you're going to write global variables
    • Local
      • Defined in local scope
    • Principles
      • Local variables cannot be used globally.
      • A local scope cannot access local variables in other scopes.
      • A local variable and a global variable may have the same name, but only local variable can be accessed.
      • You may still read the global variable correctly if no local variable is using the same name.

Task - Debug it!

MZ wants to write a program which will report the color under the mouse pointer until a special color (139,0,0) is touched. But the following does not work, please fix it.

import pyautogui, time color = None def report(): loc = pyautogui.position() color = pyautogui.screenshot().getpixel(loc) print("position is:",loc,"color is",color) while True: report() time.sleep(0.5) if color == (139, 0, 0): break

There are several approaches to do this. Each of the following hints will leads to an answer.

  • Return value might be useful.
  • global

Generator

  • Defined by a def block containing yield
  • Override return
  • Sample code
    ​​​​​def squares(n): ​​​​​ for i in range(n+1): ​​​​​ yield i**2 ​​​​​print('squares is',type(squares)) ​​​​​print('squares() is',type(squares(5))) ​​​​​for i in squares(5): ​​​​​ print(i)
  • Sample code 2
    ​​​​​def f(): ​​​​​ yield 1 ​​​​​ yield 2 ​​​​​ yield 3 ​​​​​ return 5 ​​​​​for i in f(): ​​​​​ print(i)
  • Sample code 3
    ​​​​​def primes(n): ​​​​​ if n < 2: ​​​​​ return ​​​​​ n_is_prime = True ​​​​​ for i in primes(n-1): ​​​​​ yield i ​​​​​ if n % i == 0: ​​​​​ n_is_prime = False ​​​​​ if n_is_prime: ​​​​​ yield n ​​​​​for i in primes(100): ​​​​​ print(i)