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

Alles rund um die Programmierung mit Qt
Antworten
MOAS
Beiträge: 8
Registriert: 3. Februar 2006 14:52

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

Beitrag 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
Dateianhänge
icons_01.JPG
icons_01.JPG (23.99 KiB) 4014 mal betrachtet
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

1. Warum verwendest du nicht QCanvasLine?

2. Die Canvas-Area muß 1 Pixel größer sein, als die Zeichenfläche
MOAS
Beiträge: 8
Registriert: 3. Februar 2006 14:52

Beitrag 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.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag 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...
MOAS
Beiträge: 8
Registriert: 3. Februar 2006 14:52

Beitrag 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.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag 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();
}

}
MOAS
Beiträge: 8
Registriert: 3. Februar 2006 14:52

Beitrag 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
Antworten