Python像素检测脚本报expected 4 got 3值解包错误排查
问题说明
运行如下基于PIL、keyboard、threading开发的自动按键脚本时,在color1, color2, color3, color4 = get_img(play_num)行抛出错误:
ValueError: not enough values to unpack (expected 4, got 3)
脚本设计逻辑为:arrow系列函数检测对应坐标像素是否为白色,符合条件则触发对应按键;get_img函数负责抓取屏幕指定坐标的像素值返回。预期get_img返回4个像素值,但实际返回值数量不匹配。
问题原代码:
from PIL import ImageGrab import keyboard import threading threads = [] play_num = input('are you playing as player (1) or (2)> ') def get_img(player_num): image = ImageGrab.grab() if player_num == '1': b1 = image.getpixel((246, 144)) b2 = image.getpixel((426, 151)) b3 = image.getpixel((591, 136)) b4 = image.getpixel((761, 151)) return b1 and b2 and b3 and b4 elif player_num == '2': b1 = image.getpixel((1161, 151)) b2 = image.getpixel((1325, 164)) b3 = image.getpixel((1489, 128)) b4 = image.getpixel((1671, 149)) return b1 and b2 and b3 and b4 else: quit() def arrow_one(color_index): if not color_index[0] == 255: pass else: keyboard.press('a') keyboard.release('a') def arrow_two(color_index): if not color_index[0] == 255: pass else: keyboard.press('s') keyboard.release('s') def arrow_tree(color_index): if not color_index[0] == 255: pass else: keyboard.press('d') keyboard.release('d') def arrow_four(color_index): if not color_index[0] == 255: pass else: keyboard.press('f') keyboard.release('f') def stop_code(): if keyboard.is_pressed('a' and 'c'): print('gg') quit() if __name__ == '__main__': while True: if keyboard.is_pressed('space'): while True: color1, color2, color3, color4 = get_img(play_num) for _ in range(1): stop_check = threading.Thread(target=stop_code()) step1 = threading.Thread(target=arrow_one(color1)) step2 = threading.Thread(target=arrow_two(color2)) step3 = threading.Thread(target=arrow_tree(color3)) step4 = threading.Thread(target=arrow_four(color4)) step1.start() step2.start() step3.start() step4.start() stop_check.start() threads.append(step1 and step2 and step3 and step4 and stop_check) for thread in threads: thread.join() else: pass
错误原因
核心返回值逻辑错误
get_img中使用return b1 and b2 and b3 and b4的写法完全不符合返回多值的要求:Python中and是逻辑运算符,执行逻辑与判断时会从左到右校验每个操作数的布尔值,遇到第一个布尔值为假的操作数就直接返回,所有操作数都为真则返回最后一个操作数,不会将多个变量打包为元组。
PIL默认抓取的截图为RGB模式,getpixel返回长度为3的(R,G,B)元组,只要前3个像素不是全黑(布尔值为真),最终返回的就是b4这一个长度为3的元组,自然无法解包为4个独立变量。其余隐藏功能bug
除了触发报错的核心问题,代码中还有3处会导致功能完全不符合预期的写法:
- 退出检测逻辑错误:
keyboard.is_pressed('a' and 'c')中,字符串'a' and 'c'的运算结果为'c',实际仅检测c键是否按下,无法实现a+c同时按下退出的效果 - 多线程调用错误:初始化
Thread对象时,target=stop_code()、target=arrow_one(color1)这类写法会在主线程直接执行对应函数,将函数返回值作为target参数传入,完全没有实现多线程异步执行的效果 - 线程收集逻辑错误:
threads.append(step1 and step2 and step3 and step4 and stop_check)同样是逻辑运算,最终只会将最后一个线程对象加入列表,其余线程无法被正确join回收
修复方案
针对上述问题逐点修正:
- 将
get_img两个分支的返回语句改为return b1, b2, b3, b4,以元组形式返回4个像素值 - 修正退出按键检测逻辑,改为分别检测两个按键的按下状态再做逻辑与判断
- 修正线程初始化写法,target参数传入函数对象(不加括号),函数参数通过
args参数以元组形式传入 - 修正线程列表收集逻辑,将所有线程对象统一加入列表,每次循环结束后清空列表避免重复join
修复后可正常运行的完整代码:
from PIL import ImageGrab import keyboard import threading threads = [] play_num = input('are you playing as player (1) or (2)> ') def get_img(player_num): image = ImageGrab.grab() if player_num == '1': b1 = image.getpixel((246, 144)) b2 = image.getpixel((426, 151)) b3 = image.getpixel((591, 136)) b4 = image.getpixel((761, 151)) return b1, b2, b3, b4 elif player_num == '2': b1 = image.getpixel((1161, 151)) b2 = image.getpixel((1325, 164)) b3 = image.getpixel((1489, 128)) b4 = image.getpixel((1671, 149)) return b1, b2, b3, b4 else: quit() def arrow_one(color_index): if color_index[0] == 255: keyboard.press('a') keyboard.release('a') def arrow_two(color_index): if color_index[0] == 255: keyboard.press('s') keyboard.release('s') def arrow_tree(color_index): if color_index[0] == 255: keyboard.press('d') keyboard.release('d') def arrow_four(color_index): if color_index[0] == 255: keyboard.press('f') keyboard.release('f') def stop_code(): if keyboard.is_pressed('a') and keyboard.is_pressed('c'): print('gg') quit() if __name__ == '__main__': while True: if keyboard.is_pressed('space'): while True: color1, color2, color3, color4 = get_img(play_num) stop_check = threading.Thread(target=stop_code) step1 = threading.Thread(target=arrow_one, args=(color1,)) step2 = threading.Thread(target=arrow_two, args=(color2,)) step3 = threading.Thread(target=arrow_tree, args=(color3,)) step4 = threading.Thread(target=arrow_four, args=(color4,)) step1.start() step2.start() step3.start() step4.start() stop_check.start() threads.extend([step1, step2, step3, step4, stop_check]) for thread in threads: thread.join() threads.clear() else: pass
内容的提问来源于stack exchange,提问作者BurningSoul202
相关产品推荐
相关产品推荐

