You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
错误原因
  1. 核心返回值逻辑错误
    get_img中使用return b1 and b2 and b3 and b4的写法完全不符合返回多值的要求:Python中and是逻辑运算符,执行逻辑与判断时会从左到右校验每个操作数的布尔值,遇到第一个布尔值为假的操作数就直接返回,所有操作数都为真则返回最后一个操作数,不会将多个变量打包为元组。
    PIL默认抓取的截图为RGB模式,getpixel返回长度为3的(R,G,B)元组,只要前3个像素不是全黑(布尔值为真),最终返回的就是b4这一个长度为3的元组,自然无法解包为4个独立变量。

  2. 其余隐藏功能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回收
修复方案

针对上述问题逐点修正:

  1. 将get_img两个分支的返回语句改为return b1, b2, b3, b4,以元组形式返回4个像素值
  2. 修正退出按键检测逻辑,改为分别检测两个按键的按下状态再做逻辑与判断
  3. 修正线程初始化写法,target参数传入函数对象(不加括号),函数参数通过args参数以元组形式传入
  4. 修正线程列表收集逻辑,将所有线程对象统一加入列表,每次循环结束后清空列表避免重复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:36:28