简体   繁体   中英

Python - how to compare all items within list

As python starter, trying to get help from smart people when encountered the problem. And that is now:

I got to compare items (Qt scene items) from one list among each other, and make separate groups of items which collides mutually.

Please help me with code :

class MainWin(QMainWindow):
    def __init__(self):
        super(MainWin, self).__init__()
        self.Win()
        self.L = self.buildAllWalls()
        items = self.scene.items()
        allGroups = groupItemsFromList(None, items)
        self.paintGroups(allGroups)
        print len(allGroups)

    def paintGroups(self, groups):
        for g in groups :
            color = QColor(0, 0, 0)
            # RANDOM COLOR
            namcol = "#%s" % "".join([hex(randrange(0, 255))[2:] for i in range(3)])
            color.setNamedColor(namcol)
            while color.isValid() == False :   #   ERROR CHECK
                namcol = "#%s" % "".join([hex(randrange(0, 255))[2:] for i in range(3)])
                color.setNamedColor(namcol)
            pen = QPen(color, 14, Qt.SolidLine)
            for w in g :
                w.setPen(pen)

    def Win(self):
        self.scene = QGraphicsScene()
        self.sView = QGraphicsView(self.scene)
        self.sView.setRenderHint(QPainter.Antialiasing)
        self.sView.setAlignment( Qt.AlignLeft | Qt.AlignTop )

        self.setCentralWidget(self.sView)
        self.setGeometry(20, 380, 400, 300)
        self.show()

    def buildAllWalls(self):
        data = self.wallCoordinates()
        for p in range(len(data)) :
            ptA = QPointF(data[p][0], data[p][1])
            ptB = QPointF(data[p][2], data[p][3])
            self.wall(ptA, ptB)

    def wall(self, ptA, ptB):
        pen = QPen(QColor(100, 100, 100), 14, Qt.SolidLine)
        currL = self.scene.addLine(QLineF(ptA.x(), ptA.y(), ptB.x(), ptB.y()))
        currL.setPen(pen)
        return currL

    #[50,75,325,75],
    def wallCoordinates(self):
        data = [[50,100,150,100],[175,200,125,200],[175,275,125,275],[175,275,175,200],
            [150,150,150,100],[175,100,225,100],[250,100,325,100],[350,125,175,125],
            [50,125,125,125],[125,175,125,125],[150,150,175,150],[175,150,175,200],
            [50,150,100,150],[100,150,100,200],[100,200,125,200],[50,175,75,175],
            [75,225,75,175],[75,225,125,225],[125,275,125,225]]
        return data

def main():

    app = QApplication(sys.argv)
    ex = MainWin()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Here is how I would write this:

def groupItemsFromList(self, itemList):
    tmp = itemList[:]
    allGroups = []
    while tmp:
        it = tmp.pop(0)  
        currentGroup = [it]
        # loop from back to front so we can remove items safely
        for i in range(len(tmp)-1, -1, -1):
            if it.collidesWithItem(tmp[i]):
                currentGroup.append(tmp.pop(i))
        allGroups.append(currentGroup)
    return allGroups

For example:

class Test(object):
    def __init__(self, key):
        self.key = key
    def collidesWithItem(self, other):
        return isinstance(other, self.__class__) and self.key == other.key
    def __repr__(self):
        return '{0}({1})'.format(self.__class__.__name__, self.key)

example = [Test(1), Test(2), Test(1), Test(1), Test(3), Test(2), Test(3), Test(4)]
print groupItemsFromList(None, example)

Output:

[[Test(1), Test(1), Test(1)], [Test(2), Test(2)], [Test(3), Test(3)], [Test(4)]]

This makes the assumption that all items that collide with an item will also collide with each other.

edit: Sounds like the assumption was not valid, try the following (untested):

def groupItemsFromList(self, itemList):
    tmp = itemList[:]
    allGroups = []
    while tmp:
        it = tmp.pop(0)  
        currentGroup = [it]
        i = len(tmp) - 1
        while i >= 0:
            if any(x.collidesWithItem(tmp[i]) for x in currentGroup):
                currentGroup.append(tmp.pop(i))
                i = len(tmp) - 1
            else:
                i -= 1
        allGroups.append(currentGroup)
    return allGroups

It looks like you could do this:

 
 
 
 
  
  
  def groupItemsFromList(self, itemList): """ Make a list of lists, where each list is composed of the items (excepting itself, of course) that an item collides with. """ return [ [item for item in itemList[:i] + itemList[i:] if item.collidesWithItem(x)] for i, x in enumerate(itemList) ]
 
 
  

itemList[:i] + itemList[i:] is python idiom for "I want all elements of the original list except the i'th item."


Later: I see. You want something more like this:

def groupItemsFromList(self, itemList):
    def collision_indexes(i, target):
        return [i] + [j for j, item in enumerate(itemList[i + 1:], start=i + 1) if item.collidesWithItem(target)]

    processed = set()
    results = []
    for i, target in enumerate(itemList):
        if i not in processed:
            indexes = collision_indexes(i, target)
            processed.update(indexes)
            results.append([itemList[j] for j in indexes])
    return results

The only advantage here is that this is side-effect-free code. There is no mutation to the original data but only functions applied to the data and changes made to new, temporary data structures.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM