Search code examples
pythonpyautogui

Python - how do I extract a tuple from a list?


I have two co-ordinates stored in my variable points : [(100, 50)] I'm trying to move my mouse with pyautogui.moveTo(points) and I get the error:

pyautogui.PyAutoGUIException: The supplied sequence must have exactly 2 or exactly 4 elements (0 were received).

I assume this means I'm passing a single list object rather than the coordinates.

What does the expression [(100, 50)] mean and how I can I transform x and y into two elements.

The source code where I'm getting points from:

import cv2 as cv
import numpy as np

class Vision:

# properties
needle_img = None
needle_w = 0
needle_h = 0
method = None

# constructor
def __init__(self, needle_img_path, method=cv.TM_CCOEFF_NORMED):
    self.needle_img = cv.imread(needle_img_path, cv.IMREAD_UNCHANGED)

    # Save the dimensions of the needle image
    self.needle_w = self.needle_img.shape[1]
    self.needle_h = self.needle_img.shape[0]

    self.method = method

def find(self, haystack_img, threshold=0.5, debug_mode=None):
    # run the OpenCV algorithm
    result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
    # Get the all the positions from the match result that exceed our threshold
    locations = np.where(result >= threshold)
    locations = list(zip(*locations[::-1]))

    for loc in locations:
        rect = [int(loc[0]), int(loc[1]), self.needle_w, self.needle_h]
        # Add every box to the list twice in order to retain single (non-overlapping) boxes
        rectangles.append(rect)
        rectangles.append(rect)
    # Apply group rectangles
    rectangles, weights = cv.groupRectangles(rectangles, groupThreshold=1, eps=0.5)

    points = []
    if len(rectangles):

        line_color = (0, 255, 0)
        line_type = cv.LINE_4
        marker_color = (255, 0, 255)
        marker_type = cv.MARKER_CROSS

        # Loop over all the rectangles
        for (x, y, w, h) in rectangles:

            # Determine the center position
            center_x = x + int(w/2)
            center_y = y + int(h/2)
            # Save the points
            points.append((center_x, center_y))

            if debug_mode == 'rectangles':
                # Determine the box position
                top_left = (x, y)
                bottom_right = (x + w, y + h)
                # Draw the box
                cv.rectangle(haystack_img, top_left, bottom_right, color=line_color, 
                            lineType=line_type, thickness=2)
            elif debug_mode == 'points':
                # Draw the center point
                cv.drawMarker(haystack_img, (center_x, center_y), 
                            color=marker_color, markerType=marker_type, 
                            markerSize=40, thickness=2)

    if debug_mode:
        cv.imshow('Matches', haystack_img)

    return points

Solution

  • What does the expression [(100, 50)] mean and how I can I transform x and y into two elements.

    [...] creates a list containing whatever you put inside it. (100, 50) creates a tuple containing the integers 100 and 50. So you have a list that contains a tuple that contains two numbers.

    I assume this means I'm passing a single list object rather than the coordinates.

    You're right, kinda. The problem isn't that you're passing a single list object, you need to pass a single listor rather, sequence object. The problem is that that list object contains only one element: the tuple.

    You can check this by looking at the len of the list:

    >>> l = [(100, 50)]
    >>> len(l)
    1
    

    The way you intend to use it, pyautogui.moveTo(points) wants a sequence (a list or a tuple) that contains two elements. These elements are the coordinates of the point you want to move to.

    The tuple that is inside the list is this two-element sequence, so that's what you need to pass:

    pyautogui.moveTo(points[0])