I have some custom QGraphicsItems in a QGraphicsView of a QGraphicsScene.
I wish to know if there is an Item in the view at given (x,y) coordinates.
To test purpose I use the following class as QGraphicsScene:
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (QGraphicsItem *item = itemAt(event->scenePos())) {
qDebug() << "You clicked on item" << item;
}
else {
qDebug() << "You didn't click on an item.";
}
}
};
In my application I have:
a class "Screen::Screen(QWidget *parent): QWidget(parent){...}" with inside an instance of my class "CustomScene : public QGraphicsScene {...}" described above and an instance of class QGraphicsView.
some instances of my class "Rect : public QGraphicsRectItem {...}", added to the QGraphicsView,s that draw some rectangles after some calculations.
When I launch the application and click on the drawn rectangles, I always have "You didn't click on an item." message.
Searching here in previous posts or searching on google I didn't find the reason why my code doesn't work. What am I doing wrong?
Edit 1: boundingRect() method returns correctly. I tried to add some QGraphicsRectItem, itemAt() method returns their information correctly.
The problem was that I didn't override the QPainterPath QGraphicsItem::shape () const [virtual] method.
Once done, itemAt() method started to works as expected.
Related
I need to make an element like this:
It's a combination of a line edit and a button with the same height and no space between them.
I have tried in Qt Designer but the height of them is not the same and there is always a small space between these 2 elements.
How can I solve this problem?
Maybe you could customize a QComboBox, something like this:
#include <QComboBox>
#include <QLineEdit>
class CustomBox : public QComboBox
{
Q_OBJECT
public:
CustomBox(QWidget * parent = nullptr) : QComboBox(parent)
{
setEditable(true);
QString ss =
"QComboBox::drop-down {border: none;}"
"QComboBox::down-arrow { image: url(/data/whatever.png); }"
"QComboBox::down-arrow:pressed { image: url(/data/whatever-pressed.png); }";
setStyleSheet(ss);
connect(lineEdit(), &QLineEdit::editingFinished, [this](){
QString t = lineEdit()->text();
clear();
lineEdit()->setText(t);
});
}
void setText(const QString & t) { lineEdit()->setText(t); }
QString text() const { return lineEdit()->text(); }
protected:
void showPopup() override
{
emit buttonClicked();
}
signals:
void buttonClicked();
};
The combo box is set as editable, so you can use an underlying QLineEdit, which is, more or less, under full control through the protected lineEdit member function. As you can see, I connected its editingFinished signal to a lambda to avoid adding items to the combo each time (this happens when the user press Enter in an editable combo box).
The styling is quite simple, given that you have a couple of icons for the button.
I exposed the line edit text getter/setter, and added a signal for the button click, which gets emitted from the showPopup protected method (called when the user press the button).
Just an idea.
If I use Qt Designer for my application's main window -- and the application contains many different widgets inside of it -- how do I further customize those widgets sepcifically? For example, I have a QTableView widget inside of my main application. Upon debugging the application, a ui_myapplication.h file gets created from the Qt Designer .ui form. Suppose I wanted to add some extra things to the widgets defined in that file. How would I if it's created at runtime?
Example:
In myApplication.cpp, I have this block of code:
void myApplication::mousePressEvent(QMouseEvent* event) {
if(event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
ui->tableView->setSortingEnabled(false);
}
}
}
This is similar to what I want, but not exactly. The idea is to have the QTableView widget named tableView (which is contained within the main application that I created in Qt Designer) to disable the table's sorting functionality when I hold Shift and left-click a column header. (the end goal is to make it so that Shift+clicking will disable column sorting for a short while so I can select all items in the column instead of having it sort the column).
The code above will only work if I Shift+Click the very bottom of the main application (in the space where there are no other widgets). That makes sense. But how do I make it so that doing a Shift+click inside the tableView widget will trigger the qDebug() << "shift modifier"; line?
I'd want something similar to this: (pseudocode):
void myApplication::mousePressEvent(QMouseEvent* event) {
if(target == ui->tableView->horizontalheader() && event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
ui->tableView->setSortingEnabled(false);
}
}
}
How can I do this?
EDIT: research had led me to believe I can do this with an event filter that could target a specific widget. Am I on the right track?
EDIT 2: Thanks to goug's answer below, I was able to accomplish what I needed by subclassing QTableView and then promoting my existing form's QTableView to the new class. See below:
mytableview.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include "mytableview.h"
#include <QTableView>
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget * parent = 0);
~MyTableView();
protected:
void mousePressEvent(QMouseEvent *event);
};
#endif // MYTABLEVIEW_H
mytableview.cpp
#include "mytableview.h"
#include <QDebug>
MyTableView::MyTableView(QWidget* parent)
{
}
MyTableView::~MyTableView()
{
}
void MyTableView::mousePressEvent(QMouseEvent* event) {
if(event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
setSortingEnabled(false);
}
}
}
There's a couple of different approaches you could use here. You could subclass QTableView, and then in Qt Designer, you place a QTableView as normal, but then promote it to your derived class. The generated code creates an instance of your class rather than QTableView. In Qt Designer, right-click on the table view and select from the Promote options. You'll have to enter your class details the first time. I'd be inclined to go this route especially if there's other custom behavior you want to implement on the table view.
Another option is to create a new class and install it as an event filter on your QTableView. Your new class then gets the events before they go to the QTableView and you can act accordingly on them. Look up installEventFilter in Qt Assistant, and that'll get you to the details of how to do this.
I was facing one issue with the Widget which is integrated in the QQuickPaintedItem class. When I have Widget integrated in the QQuickPaintedItem, QWidget::isVisible will return false. If I tried to set QWidget::setVisible(true) then it will open another window, which I do not want in my scenario.
Is there any way to get QWidget::isVisible return true so that my child widgets (In my actual scenario, we have 5 layer of parent child hierarchy) will also works fine when I say QWidget::show()?
I have created the scenario similar to it as below.
Header file:
class MyItem: public QQuickPaintedItem{
Q_OBJECT
public:
explicit MyItem(QQuickItem *parent = 0);
void paint(QPainter *painter);
~MyItem();
Q_INVOKABLE void initButton();
protected:
virtual void mousePressEvent( QMouseEvent* event );
private:
QPushButton* bp;
};
source file:
MyItem::MyItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
bp = new QPushButton("Hello");
}
MyItem::~MyItem()
{
delete bp;
}
void MyItem::paint(QPainter *painter){
bp->render(painter, QPoint(), QRegion(), QPushButton::DrawWindowBackground | QPushButton::DrawChildren);
}
void MyItem::mousePressEvent( QMouseEvent* event )
{
qDebug() << Q_FUNC_INFO << bp->isVisible();
}
Thanks for help in advance...!!!
I don't know why you want to do this.
Qt do not support to embed a QWidget into a Qt Quick Item in Qt5(Qt Quick 2).
In your code, QWidget is a seperate Window, and you Qt Quick item is in it's own Window.
If you want your Qt Quick item behavior like a Button, you should use Qt Quick's Button control or write one yourself.
If you really want to embed a QWidget into Qt Quick's control tree, you can use Qt Quick 1(Qt4.7/8) instead. Check out QGraphicsProxyWidget's document.
I am creating a simple gauge in Qt 4.7.4, and everything is working wonderfully. Except for the fact that, for the life of me, I cannot get the dial shape to paint over the text labels when it passes over them. It always paints it behind the label. I am just using a simple drawpolygon() method.
I'm thinking this has something to do about paint events? I am drawing everything inside a QFrame inside a MainWindow. I am using QFrame's paintEvent.
Edit:
The QLabels are created on start up with new QLabel(this). They are only created once, and never touched again ( Similar to manually adding them on the Ui with Designer). The drawpolygon() is in the QFrame's Paint event.
"myclass.h"
class gauge : public QFrame
{
Q_OBJECT
public:
explicit gauge(QWidget *parent = 0);
~gauge();
void setValues(int req, int Limit, bool extra=false);
private:
void drawDial();
protected:
void paintEvent(QPaintEvent *e);
};
"myclass.cpp"
void gauge::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
drawDial();
return;
}
void gauge::drawDial()
{
QPainter Needle(this);
Needle.save();
Needle.setRenderHint(Needle.Antialiasing, true); // Needle was Staggered looking, This will make it smooth
Needle.translate(centrePt); // Center of Widget
Needle.drawEllipse(QPoint(0,0),10,10);
Needle.restore();
Needle.end();
}
If the gauge widget and the QLabels are siblings, then you can move the gauge widget to the front by calling its raise() method.
If the QLabels are children of the gauge widget, on the other hand, then they will always display in front of it. In that case you can either reorganize your widget hierarchy so that they are siblings instead, or you can get rid of the QLabels and simply call drawText() from your paintEvent() method instead (after drawDial() returns)
I would like to have a drag-and-drop feature based on images. If I drag and drop an image I would like to know which image I picked out and moved with (a simple std::string will do to uniquely identify the object that that image is representing). My thought is to store my own object (QPixmapItem) in the QGraphicsScene:
#ifndef QPIXMAPITEM_H
#define QPIXMAPITEM_H
#include <QGraphicsPixmapItem>
#include <QPoint>
class QPixmapItem : public QGraphicsPixmapItem
{
public:
QPixmapItem(std::string path, std::string id, int x, int y);
std::string getIdentifier (){return this->identifier;}
QPoint getPosition () const{return this->position;}
private:
std::string identifier;
QPoint position;
};
#endif // QPIXMAPITEM_H
This is the method I use to add my objects to the scene:
void MainWindow::addPixmapItemToScene(std::string path, int x, int y)
{
// generate pixmap item & add it to the scene
QPixmapItem *item = new QPixmapItem(path, std::string("id123"), x, y);
ui->roomView->scene()->addItem(item);
// only update the affected area
ui->roomView->updateSceneRect(item->pixmap().rect());
}
Here is how I try to 'catch' the object at a QMouseEvent:
void MainWindow::mousePressEvent(QMouseEvent *event)
{
std::cout << "mouse pressed" << std::endl;
QGraphicsPixmapItem *currentItem = dynamic_cast<QGraphicsPixmapItem *>(childAt(event->pos()));
if (!currentItem) {
return;
}
std::cout << "item pressed" << std::endl;
}
The objects are being added to the scene but whenever I press them, the final line ("item pressed") never makes it onto the screen..
QGraphicsItem already support moving by way of drag-and-drop within a QGraphicsScene. All you need is set the QGraphicsItem::ItemIsMovable flag.
If you want to get notified when that happens, override QGraphicsItem::itemChange() in your custom QGraphicsItem.
childAt() won't work because it returns a QWidget, and QGraphicsPixMapItems are not QWidgets (therefore childAt() will never return a pointer to any kind of QGraphicsItem, and even if it somehow did, the dynamic_cast conversion would return NULL anyway).
In order to get a list of QGraphicsItems that intersect a specified point in the scene, call QGraphicsScene::items() on your scene object. Then iterate through the returned list to find out what QGraphicsPixMapItems (if any) are in it.