samedi 4 septembre 2021

Why do too many loops makes the code slow?

Summary:

I Am programing a program that draws many shapes(Polygon, We have at least 3 points in each object if not more). Each polygon is an object. Right now am adding a feature so the user can select objects and move them ( With mouse selection like on the desktop selecting icons).

Using: Python 3.7, PyQt5, QPixmap

How I thought to add that feature: After drawing the objects, if the user starts selecting objects:

  1. First the code will take all objects that the user has drawn and loop them and see which of them are in the selection area and add them to the selected list.

  2. Second, If the user clicked on the selection area => Held those objects in the selected list and start moving them (Here it will loop each 1 pixel the user moves his mouse, Too many processes here)

The issue is: I have many loops in my code, And I don't think it is really effective way to do it like this. So am asking if there is any other option I can get the same results but with better code like design pattern or something, I don't know, Did I done it wrong?

The code:

def inside_selection(obj, begin_point, destination_point):
# Calculate the other two rectangle's point
p2 = Point(begin_point[0], destination_point[1], 0)
p4 = Point(destination_point[0], begin_point[1], 0)

rect_points = [begin_point, p2, destination_point, p4]
rect = shp.Polygon(rect_points)

if isinstance(obj, Point):
    point = obj
    result = rect.contains(shp.Point(point[0], point[1], 0))
    return result
else:
    points = obj.get_points() # each object has many points it is Polygon
    for point in points:
        result = rect.contains(shp.Point(point[0], point[1], 0))
        if result is True:
            return result

class HandTool:

def __init__(self):
    self.begin, self.destination = None, None   # two corner of the rectangle of selection
    self.held_objects = None    # the objects holding by the user to move them
    self.selected_objects = []  # store all objects inside the selection area
    self.selecting = False  # for drawing a selection area
    self.copy_points = None
    self.first_click = None

def mouse_click(self, event):
    point = Point(event.x(), event.y(), 0) # Cordinatins where user clicked

    # check if there are a selected objects
    #  And check if the clicked was on the selection area so the user want to move an objects
    if self.selected_objects and inside_selection(point, self.begin, self.destination):

        # ISSUE 1.0
        # make a copy so after the user moved them it won't change the original corrdinate
        self.held_objects = self.selected_objects.copy()

        # Loop 1
        for obj in self.held_objects:
                pass # some code here to make the original hidden while moving it like(Drag Drop)

        self.canvas.setMouseTracking(True)

    # else the click was outside the selection area so the user won't move any selected objects
    else:
        self.begin = point
        self.destination = self.begin
        self.selecting = True
        self.selected_objects = []

    self.canvas.update_pixmap()

def mouse_move(self, event):
    # if the user moving many object
    if self.held_objects is not None:
        for obj in self.held_objects:
            self.move_held_objects(e)
            self.canvas.update_pixmap()

def mouse_release(self, event):
    # After user draw a selection  and both begin and destination are not None, detemin which object(shape like points...etc)
    # is inside this selection
    if self.begin is not None and self.destination is not None:
        for obj in all_objects: # all objects in the widget
            if inside_selection(obj, self.begin, self.destination): # loop for all object and see if inside or not
                self.selected_objects.append(obj) # if True then it is inside the selection area so add it to list
                if obj.is_polygon():
                    # Make the selected objects visiable to user by changeing points's color to red
                    for point_id, _ in enumerate(obj.get_points(), 0):
                        self.zkpainter.draw_ellipse(obj.get_points()[point_id], 5, brush=QBrush(Qt.red))

    if self.held_objects:
        for obj in self.held_objects:
            pass # Some code to make the new changes of the objects on the view
        self.selected_objects = []

    self.canvas.update_pixmap()
    self.held_objects = None
    self.selecting = False


def move_held_objects(self, event):
    # Calculate how much the mouse moved since the user clicked and moving the objects
    diff_x, diff_y = event.x() - self.first_click[0], event.y() - self.first_click[1]
    for obj_id, obj in enumerate(self.held_objects):
        if obj.is_polygon():
            old_position = self.selected_objects[obj_id].get_points()
            for point_id, _ in enumerate(obj.get_points()):
                obj.get_points()[point_id][1] = old_position[point_id][1] + diff_y
                obj.get_points()[point_id][0] = old_position[point_id][0] + diff_x

Aucun commentaire:

Enregistrer un commentaire