Seite 1 von 1

QMouseEvent und QScrollArea

Verfasst: 20. November 2010 17:46
von Mathao
Hallo zusammen,

ich habe vor zwei Tagen angefangen mich wieder etwas intensiver mit Qt auseinander zu setzen. Aus diesem Grund habe ich mir ein paar kleine Übungsaufgaben ausgedacht und an einer von Ihnen hänge ich. Vielleicht kann mir hier jemand weiterhelfen?

Ich habe ein Klasse namens ImageFrame, in der ich versuche ein Bild anzuzeigen. Wenn das Bild größer als das Frame ansich ist, entsteht eine QScrollArea.

Was ich nun versuche ist mir die Mausposition beim mousePressEvent auszugeben. Das funktioniert eigentlich wunderbar. Was mir aber aufgefallen ist, dass egal wie weit ich innerhalb der QScrollArea scrolle, ich nur die Positionen innerhalb des imageFrames erhalte. Sprich das Scrollen wird nicht in Betracht gezogen, so dass sich die maximale Position des Mauszeigers aus der Größe des imageFrames ergibt und nicht aus der Größe des Bildes, Hoffe das ist mehr oder weniger verständlich. Hier mal ein Auszug aus dem Code:

Code: Alles auswählen

#include "ImageFrame.h"

ImageFrame::ImageFrame()
{
    createWidget();
}

ImageFrame::ImageFrame(QImage* inputImage)
{
    createWidget();
    setImage(inputImage);
}

void ImageFrame::setImage(QImage* inputImage)
{
    frameImage = inputImage;
    imageLabel->setPixmap(QPixmap::fromImage(*frameImage));
    imageLabel->adjustSize();
}

void ImageFrame::mousePressEvent(QMouseEvent* event){
origin = (event->pos());
std::cout<<origin.x()<<std::endl;
std::cout<<origin.y()<<std::endl;

}

QImage* ImageFrame::getImage()
{
    if (!frameImage->isNull()) {
        return frameImage;
    } else {
        frameImage = new QImage();
        return frameImage;
    }

}

void ImageFrame::createWidget()
{
    frameImage = new QImage();

    imageLabel = new QLabel();
    imageLabel->setBackgroundRole(QPalette::Dark);
    imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);

    frameScrollArea = new QScrollArea();
    frameScrollArea->setBackgroundRole(QPalette::Dark);
    frameScrollArea->setWidget(imageLabel);

    imageLabel->adjustSize();

    frameLayout = new QVBoxLayout();
    frameLayout->addWidget(frameScrollArea);

    this->setLayout(frameLayout);
}
Und die imageFrame.h:

Code: Alles auswählen

#ifndef IMAGEFRAME_H
#define IMAGEFRAME_H

#include <QFrame>
#include <QWidget>
#include <QtGui>
#include <iostream>

class ImageFrame : public QFrame
{
    Q_OBJECT;

public:
    ImageFrame();
    ImageFrame(QImage* inputImage);
    QPoint origin;
    void mousePressEvent(QMouseEvent *event);

    void setImage(QImage* inputImage);
    QImage* getImage();

private:
    void createWidget();

    QImage* frameImage;
    QVBoxLayout* frameLayout;
    QScrollArea* frameScrollArea;
    QLabel* imageLabel;

};

#endif // IMAGEFRAME_H
In der MainWindow.cpp wird eigentlich nur noch die setImage Funktion aufgerufen, wenn der Button open im Menü geklickt wird. Ich habs eigentlich mit diversen mapTo Funktionen versucht zu lösen, was aber nicht wirklich funktioniert hat.

Ich danke schon mal jedem, der sich die Sache etwas näher anschaut. Sonstigen Code kann ich gerne nachliefern :)

Verfasst: 21. November 2010 17:47
von Mathao
Hallo zusammen,

ich habe mittlerweile mit Hilfe der Suchfunktion beispielsweise folgende Lösung aus diesem Thread http://www.qtforum.de/forum/viewtopic.p ... mouseevent versucht, leider funktioniert das auch nicht wirklich. Vielleicht kann mir jemand wenigstens einen Denkanstoß geben oder ist das Problem so trivial? Soll ich es etwas genauer erläutern?

Verfasst: 21. November 2010 18:44
von franzf
Du könntest z.B. einen eventFilter in imageLabel installieren, und da das MousePressed-Event abfangen - die Koordinaten stimmen dann garantiert. Schau dir mal QScrollArea (und deren Basisklasse) genauer an, in dem Scrollbereich liegt nämlich noch ein Widget und DAS interessiert dich :)

Btw:

Code: Alles auswählen

void ImageFrame::setImage(QImage* inputImage)
{
    frameImage = inputImage;  // 1)
    imageLabel->setPixmap(QPixmap::fromImage(*frameImage));  // 2)
    imageLabel->adjustSize();
}
1) Das erzeugt im günstigsten Falle ein MemoryLeak, wenns dumm läuft undefined behaviour. Ersteres, weil du dein vorheriges frameImage nicht zerstörst, nach der Zuweisung ist das alte Objekt nicht mehr erreichbar. Letzteres, wenn inputImage nach dem Funktionsaufruf nicht mehr ezistiert, frameImage also auf nicht korrektes mehr zeigt. Spätere Zugriffe auf das QImage greifen auf nicht mehr korrekt initialisierten Speicher zu (wenn du Pech hast, und das ist wahrscheinlich).
2) In deinem geposteten Code speicherst du zwar einen Zeiger auf ein QImage (siehe 1)), verwenden tust du den aber nie wieder, einzig in dieser Zeile 2) wird das Image dem Label zugewiesen. Wenn du nicht vor hast, dein image später nochmal zu bearbeiten, speicher es erst gar nicht. Und lass den Umweg über QImage.
Eine mögliche bereinigte Version:

Code: Alles auswählen

void ImageFrame::setImage(QPixmap const& pix)
{
    imageLabel->setPixmap(pix);
    imageLabel->adjustSize();
}

QPixmap ImageFrame::getImage()
{
   return imageLabel->pixmap();
}
Und google auch mal nach "C++ const correctness". Dein getter hat ein const verdient!

Insgesamt sind die meisten Zeiger nicht notwendig. Hör auf in Java zu denken, leg Objekte direkt an, ohne Umweg über Zeiger und new. In C++ kostet new mehr Zeit, Zeiger dereferenzieren auch, und wenns nicht nötig ist handelt man sich nur Probleme ein wenn man nicht aufpasst (siehe 1).

Es passt in deinem Code noch mehr nicht (Layout, fehlender Destruktor, etc), aber das wäre zu viel für diesen kleinen Post. Beschäftige dich am besten erstmal intensiver mit C++, bevor du in die komplexe Welt der GUI-Toolkits einsteigst.

Verfasst: 21. November 2010 21:19
von Mathao
Hi,

ok habe es mit dem EventFilter versucht und es funktioniert. Ja der Code wurde nicht wirklich vollständig gepostet, mit den Bildern passiert noch etwas, Dekonstruktor ist eigentlich auch vorhanden und Layout wird auch verwendet. Wollte es so kurz wie es geht halten, sry dafür, nächstes mal poste ich alles...

Vielen Dank für deine Hilfe franzf

Grüße,
Mathaos

Verfasst: 22. November 2010 10:36
von franzf
Mathao hat geschrieben:Wollte es so kurz wie es geht halten, sry dafür, nächstes mal poste ich alles...
Nein, bitte nicht alles posten! Dein Codeausschnitt war schon gut so, um zu sehen wie du dein Problem angehst. Meine kleine Kritik ging ja komplett an deinem Problem (event->pos()) vorbei!
Wenn du mit dem QImage arbeitest ist das in Ordnung zu speichern. ABER! Speicher eine Kopie, und nicht einen Zeiger auf ein QImage außerhalb der Klasse.
Also:

Code: Alles auswählen

class ImageFrame : public QFrame {
  QImage image_;
public:
  void setImage( QImage const& image) {
    image_ = image;
    // usw.
  }
  QImage getImage() const {
    return image_;
  }
};
Jedes ImageFrame-Objekt hat somit seine eigene Kopie eines Bildes, Verantwortlichkeiten und Besitz sind klar gesetzt.

Verfasst: 22. November 2010 20:33
von Mathao
Alles klar,

Danke für die Tipps :)