Seite 1 von 1

Rückstände auf der Zeichenfläche - Umgang mit QPainter?

Verfasst: 15. Februar 2006 11:40
von MOAS
Hallo Qtler,

ich bin wieder mal dabei, mein kleines Icon-Programm weiter zu schreiben und habe da nun ein unschönes Zeichenproblem:
Zwei Icons werden durch eine gezeichnete Relation verbunden. Sobald ich nun eines der beiden Icons umherschiebe, hinterlässt die dazugehörige Relation Rückstände auf der Zeichenfläche, obwohl ich meiner Meinung nach den Painter korrekt setze.
Das Problem besteht meiner Meinung nach vielleicht darin, dass ich die areaPoints() nur einmal setze, die Paintergröße sich ja aber ständig ändert (je nach Abstand der beiden Icons zueinander). Wie gehe ich damit richtig um?

Hier der Quellcode (in Python):

Code: Alles auswählen

class RelationItem(QCanvasPolygonalItem):
    def __init__(self, pItem, cItem, canvas):
        QCanvasPolygonalItem.__init__(self, canvas)
        self.parentItem = pItem
        self.childItem   = cItem
        self.relRTTI     = 432506

    def itemMidX(self, thisItem):
        return (thisItem.x() + (thisItem.width()/2))

    def itemMidY(self, thisItem):
        return (thisItem.y() + (thisItem.image.height() / 2))

    def rtti(self):
        return self.relRTTI

    def areaPoints(self):
        res = QPointArray(4)
        point = []

        point.append(QPoint(self.itemMidX(self.parentItem), self.itemMidY(self.parentItem)))
        point.append(QPoint(self.itemMidX(self.childItem) , self.itemMidY(self.parentItem)))
        point.append(QPoint(self.itemMidX(self.childItem) , self.itemMidY(self.childItem)))
        point.append(QPoint(self.itemMidX(self.parentItem), self.itemMidY(self.childItem)))
        
        for counter in range(4):
            res.setPoint(counter, point[counter])
        
        return res

    def drawShape(self, p):
        self.move(self.itemMidX(self.parentItem), self.itemMidY(self.parentItem))
        if self.itemMidY(self.parentItem) > self.itemMidY(self.childItem):
            self.setY(self.itemMidY(self.childItem))
        if self.itemMidX(self.parentItem) > self.itemMidX(self.childItem):
            self.setX(self.itemMidX(self.childItem))

        p.setPen(QPen(Qt.gray, 1, Qt.SolidLine))

        p.setBackgroundMode(Qt.OpaqueMode)
        p.drawLine(self.itemMidX(self.parentItem), self.itemMidY(self.parentItem),
                    self.itemMidX(self.childItem), self.itemMidY(self.parentItem))
        p.drawLine(self.itemMidX(self.childItem), self.itemMidY(self.parentItem),
                    self.itemMidX(self.childItem), self.itemMidY(self.childItem))
Falls ein anderer Ausschnitt aus dem Code gewünscht wird, einfach Bescheid geben. Danke schonmal im Voraus.

MfG Heiko

Verfasst: 15. Februar 2006 20:42
von upsala
1. Warum verwendest du nicht QCanvasLine?

2. Die Canvas-Area muß 1 Pixel größer sein, als die Zeichenfläche

Verfasst: 16. Februar 2006 15:03
von MOAS
zu

2. Das habe ich bereits ausprobiert, es tritt aber dennoch der gleiche Fehler auf.

1. Ich wüsste ehrlich gesagt nicht wie. Die Relation besteht ja aus zwei Linien (horiziontal, vertikal), diese sollen jedoch zu einem Item zusammengefasst werden.
Daher dachte ich, müsste ich diese auf demselben Painter zeichnen. Außerdem soll die Position natürlich ständig aktualisiert werden, da die dazugehörigen Icons ja verschoben werden können.

Verfasst: 16. Februar 2006 19:42
von upsala
1. Dein Screenshot enthält 2x QCanvasLine 2x QCanvasSprite 2x QCanvasText
Was spricht dagegen diese Items auch zu verwenden?

2. QCanvas ist dafür ausgelegt schnell und optimiert zu zeichnen, dazu müssen aber die Areas auch richtig angegeben werden. Deine beiden Linien werden eigentlich durch 6 Punkte umgeben (nach meiner Rechnung) Du verwendest aber nur 4. Nimm lieber meine 1. Möglichkeit. Es ist einfacher, glaubs mir...

Verfasst: 17. Februar 2006 09:44
von MOAS
Du hast Recht, dass das Area optimal durch sechs Punkte gekennzeichnet werden müsste. Praktisch zwei sich überschneidende Rechtecke, in welche dann die Linien gezeichnet werden könnten. So haben die Linien noch "Spielraum" und müssten eigentlich korrekt dargestellt werden. Aber auch das habe ich bereits ausprobiert und es ruft den gleichen Fehler hervor. Wann und von wo aus wird areaPoints() eigentlich aufgerufen? Muss ich das manuell machen, wenn das Area sich ändert? In der Hilfe steht nur:
This function must be reimplemented by subclasses. It must return the points bounding (i.e. outside and not touching) the shape or drawing errors will occur.
Vielleicht müsste ich noch mit areaPointsAdvanced() arbeiten?

Die beiden Icons bestehen nicht aus QCanvasSprite und meiner Meinung nach auch nicht aus QCanvasText. Oder ist der Text, der mittels der drawText()-Funktion eines Painters gezeichnet wird, auch ein QCanvasText?

Code: Alles auswählen

class IconItem(QCanvasRectangle):
    def __init__(self,ico, ico_text, canvas):
        QCanvasRectangle.__init__(self,canvas)

        self.imageRTTI=984376
        self.image = ico

        self.ico_txt = ico_text
        self.ico_txt_w = QCanvasText(ico_text, canvas).boundingRect().width() #hole Breite des später zu zeichnenden Textes
        self.ico_txt_h = QCanvasText(ico_text, canvas).boundingRect().height() #hole Höhe des später zu zeichnenden Textes
        
        self.setSize(self.image.width(), self.image.height()+self.ico_txt_h+2)
        self.pSize_w = self.image.width()
        if self.ico_txt_w > self.pSize_w:
            self.setSize(self.ico_txt_w, self.image.height()+self.ico_txt_h+2)
            self.pSize_w = self.ico_txt_w

        self.pixmap=QPixmap()
        self.pixmap.convertFromImage(self.image, Qt.OrderedAlphaDither);

    def rtti(self):
        return self.imageRTTI

    def hit(self,p):
        ix = p.x()-self.x()-(self.pSize_w - self.image.width()) / 2
        iy = p.y()-self.y()
        if not self.image.valid( ix , iy ):
            return False
        self.pixel = self.image.pixel( ix, iy )
        return  (qAlpha( self.pixel ) != 0)

    def drawShape(self,p):
        p.drawPixmap(self.x()+(self.pSize_w - self.image.width()) / 2, self.y(), self.pixmap)
        p.drawText(self.x()+(self.pSize_w - self.ico_txt_w) / 2, self.y()+self.image.height()+self.ico_txt_h, self.ico_txt)
Könntest du vielleicht bitte mal nen kurzen Code schreiben, der mir zeigt, wie ich in dem Falle QCanvasLine verwenden müsste? Ich stehe da momentan nämlich wohl total auf dem Schlauch. Kann auch C++ oder Pseudo-Code sein.

Verfasst: 17. Februar 2006 20:15
von upsala
areaPoints() wird vom QCanvasItem intern aufgerufen, wenn z.B. ein show() oder hide() aufgerufen wird. Da mußt du dich nicht darum kümmern. Deine Funktion muß nur die richtigen Werte zurückgeben.

areaPointsAdvanced() ist eigentlich für Animationen gedacht, ist meiner Meinung nach nicht so wichtig, und braucht in diesem Fall nicht beachtet werden

class Item {
QPoint p1;
QPoint p2;
QCanvasLine *line1;
QCanvasLine *line2;
QCanvasText *text1;
QCanvasText *text2;
QCanvasSprite *sprite1;
QCanvasSprite *sprite2;
QCanvasPixmapArray *array;

public:
Item(QCanvas *canvas, QPixmap &pix1, QPixmap &pix2)
{
line1=new QCanvasLine(canvas);
line1->setZ(-5);
line1->setPen(gray);
line2=new QCanvasLine(canvas);
line2->setZ(-5);
line2->setPen(gray);
text1=new QCanvasText("item1", canvas);
text1->setTextFlags(AlignHCenter|AlignTop);
text2=new QCanvasText("item2", canvas);
text2->setTextFlags(AlignHCenter|AlignTop);
array=new QCanvasPixmapArray();
array->setImage(0, new QCanvasPixmap(pix1, -QPoint(pix1.width(), pix1.height())/2));
array->setImage(1, new QCanvasPixmap(pix1, -QPoint(pix1.width(), pix1.height())/2));
sprite1=new QCanvasSprite(array, canvas);
sprite1->setFrame(0);
sprite2=new QCanvasSprite(array, canvas);
sprite2->setFrame(1);
}

void update()
{
sprite1->move(p1.x(), p1.y());
sprite2->move(p2.x(), p2.y());
line1->setPoints(p1.x(), p1.y(), p2.x(), p1.y());
line2->setPoints(p2.x(), p2.y(), p1.x(), p2.y());
text1->move(p1.x(), p1.y()+sprite1->image().height()/2);
text2->move(p2.x(), p2.y()+sprite2->image().height()/2);

line1->canvas()->update();
}

void move1(QPoint p);
p1=p;
update();
}

void move2(QPoint p);
p1=p;
update();
}

}

Verfasst: 20. Februar 2006 11:53
von MOAS
Die Lösung funktioniert, für den Fall, dass tatsächlich immer zwei Icons mit einer Relation verbunden sind.
Es soll jedoch so sein, dass beliebig viele Icons vorhanden sein können und diese ebenso beliebig mit Relationen verknüpft werden können. Das ganze ähnelt dann einem Graphen, dessen Knoten man verschieben kann. Da das aber eher ein programmiertechnisches Problem ist, gehört es wahrscheinlich nicht mehr hier rein.
Ich versuche das ganze jetzt mit QCanvasLine umzusetzen, sobald es dann für meine Aufgabe funktioniert, poste ich die Lösung gern auch nochmal.
Danke dir für deine Hilfe!

MfG Heiko