Custom Widget stylesheet is not set with property - qt

I have a custom widget using in my project and I am trying to change background color and label color with the click event. background color changes but label color does not change... Hope someone can help to figure out where I am wrong...
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "myframe.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
MyFrame *frame = new MyFrame(this);
ui->gridLayout_2->addWidget(frame);
setStyleSheet("MyFrame {background-color: yellow;}"
"MyFrame QLabel{color: black; font: bold 12px;}"
"MyFrame[pressed=\"true\"] {background-color:red;}"
"MyFrame[pressed=\"true\"] QLabel {color:white;}"
);
}
MainWindow::~MainWindow()
{
delete ui;
}
My custom widget //header file
#ifndef MYFRAME_H
#define MYFRAME_H
#include <QFrame>
#include <QGridLayout>
#include <QLabel>
#include <QDebug>
class MyFrame : public QFrame
{
Q_OBJECT
Q_PROPERTY(bool pressed READ pressed WRITE setPressed)
QGridLayout *layout;
QLabel *name;
public:
explicit MyFrame(QWidget *parent = 0);
signals:
public slots:
bool pressed();
void setPressed(bool);
protected:
bool mPressed;
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
};
#endif // MYFRAME_H
//source file
#include "myframe.h"
MyFrame::MyFrame(QWidget *parent) :
QFrame(parent)
{
mPressed = false;
layout = new QGridLayout(this);
setLayout(layout);
name = new QLabel("button");
layout->addWidget(name);
}
bool MyFrame::pressed()
{
qDebug() << Q_FUNC_INFO << mPressed;
return mPressed;
}
void MyFrame::setPressed(bool status)
{
qDebug() << Q_FUNC_INFO << status;
mPressed = status;
}
void MyFrame::mousePressEvent(QMouseEvent *event)
{
qDebug() << Q_FUNC_INFO << "mouse pressed";
setPressed(true);
style()->unpolish(this);
style()->polish(this);
}
void MyFrame::mouseReleaseEvent(QMouseEvent *event)
{
qDebug() << Q_FUNC_INFO << "mouse released";
setPressed(false);
style()->unpolish(this);
style()->polish(this);
}
The frame background color changes but label color doesn't change on click

Thanks .. Resolved the issue..
I have made a trial n error ;)
style()->unpolish(this);
style()->unpolish(name);
style()->polish(this);
style()->polish(name);
doing this ie, unpolish and polish child also worked

It seems that this properties is not dynamical. It means that stylesheet should not change color when you click button. One way to solve this is to set new stylesheet every clicking. In this case every clicking new stylesheet will be parsed and will be applied with correct colors.
Use one color for true and another color for false.
"MyFrame[pressed=\"true\"] {background-color:red;}"
"MyFrame[pressed=\"false\"] {background-color:yellow;}"
Currently I can't find something in documentation to prove my words, but if I'm wrong in something, please point me in comment.
https://bugreports.qt-project.org/browse/QTBUG-21762
If stylesheet contains large information, then you can polish widget again(call polish)

Related

Qt QTreeView indexBelow does not work

As far as I understood, indexBelow function is used for navigating to the next item on a tree in Qt's QTreeView. I want to write two functions - one to move to next item, one to move to previous item in a DirModel based TreeView. However, indexBelow does only work once.
Considering ui is the UI variable, the following is my function to move to the next item
void MainWindow::moveDown (void)
{
QModelIndex index_it = ui->treeView->indexBelow(ui->treeView->currentIndex());
qDebug() << index_it.row();
if (index_it.isValid())
{
qDebug() << "Valid";
ui->treeView->setCurrentIndex(index_it);
ui->treeView->selectionModel()->select(index_it, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
}
When a button is pressed, this will be triggered and the tree will be navigated using buttons. This however, works only once, and afterwards stops working. Note that I want to be able to move to next item even if there is an expanded child. Any pointers and help is greately appreciated.
Just tried your code and it work fine.
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QStandardItemModel>
#include <QFileSystemModel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
model->setReadOnly(false);
model->setSorting(QDir::DirsFirst |
QDir::IgnoreCase |
QDir::Name);
ui->treeView->setModel(model);
index = model->index("C:/");
// Set initial view of directory
// for the selected drive as expanded
ui->treeView->expand(index);
// Make it scroll to the selected
ui->treeView->scrollTo(index);
// Highlight the selected
ui->treeView->setCurrentIndex(ui->treeView->indexBelow(index));
// Resizing the column - first column
ui->treeView->resizeColumnToContents(0);
}
void MainWindow::on_pushButton_3_clicked()//<------Your function MoveDown
{
QModelIndex index_it = ui->treeView->indexBelow(ui->treeView
->currentIndex());
qDebug() << index_it.row();
if (index_it.isValid())
{
qDebug() << "Valid";
ui->treeView->setCurrentIndex(index_it);
ui->treeView->selectionModel()->select(index_it, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
}
And mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QModelIndex>
#include <QDirModel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
emitProgress(int per)
{
emit signalProgress(per);
}
void on_pushButton_clicked();
void on_pushButton_3_clicked();
signals:
private:
Ui::MainWindow *ui;
QModelIndex index;
QDirModel* model = new QDirModel(this);
};

Why does mousePressEvent to QGraphicsItem bugs out after opening a QDialog with exec or open?

I wrote a minimal working example of the problem and I believe it might be a Qt bug. But just in case I wanted to ask.
Here are My classes:
mydialog.h
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
class MyDialog : public QDialog
{
public:
MyDialog(QWidget *parent = 0);
};
mydialog.cpp
#include "mydialog.h"
MyDialog::MyDialog(QWidget *parent):QDialog(parent)
{
QLabel *label = new QLabel("Some random dialog",this);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(label);
this->setLayout(layout);
}
myitem.h
#include <QGraphicsTextItem>
#include <QPainter>
#include <QDebug>
#include "mydialog.h"
class MyItem : public QGraphicsItem
{
public:
MyItem();
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
QRectF boundingRect() const {return boundingBox;}
void setMyDialog(MyDialog *d){ dialog = d; }
private:
QRectF boundingBox;
MyDialog *dialog;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e);
};
myitem.cpp
MyItem::MyItem()
{
boundingBox = QRectF(0,0,200,100);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
painter->setBrush(QBrush(Qt::red));
painter->drawRect(boundingBox);
}
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *e){
//dialog->exec(); // BUG
//dialog->open(); // BUG
dialog->show(); // WORKS!
}
test.h
#include "myitem.h"
namespace Ui {
class Test;
}
class Test : public QMainWindow
{
Q_OBJECT
public:
explicit Test(QWidget *parent = 0);
~Test();
protected:
void resizeEvent(QResizeEvent *e);
private:
Ui::Test *ui;
MyDialog *diag;
};
And test.cpp
Test::Test(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Test)
{
ui->setupUi(this);
ui->graphicsView->setScene(new QGraphicsScene(this));
diag = new MyDialog(this);
}
void Test::resizeEvent(QResizeEvent *e){
ui->graphicsView->setSceneRect(0,0,ui->graphicsView->width(),ui->graphicsView->height());
ui->graphicsView->scene()->clear();
MyItem *item = new MyItem();
item->setMyDialog(diag);
ui->graphicsView->scene()->addItem(item);
}
Test::~Test()
{
delete ui;
}
So here is what happens (tested on Qt 5.7 and Qt 5.6). If the dialog is opened with either exec or open then, after it is closed ALL further mouse clicks ANYWHERE on the screen will open up the dialog again, making it impossible to interact with anything else drawn in there. This happens ONLY after it is opened for the first time. If i resize the screen, the item is recreated and I can click normally again. If I again click on the red box, then again all further clicks anywhere on the screen open up the dialog
However if the Dialog is opened by show, then it works as expected, only showing again if I click on the red rectangle.
Now the obvious problem is that exec make the dialog block execution until it is closed, but show doesn't. I can program around this using signals, but my question is why? and Is this a bug?
It seems that MyItem's reimplementation of mousePressEvent needs some behavior provided by default implementation. Here is the code, works fine in my machine:
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
dialog->exec(); // WORKS
//dialog->open(); // WORKS
//dialog->show(); // WORKS
QGraphicsItem::mousePressEvent(event);
}

Unexpected behaviour with QMouseMoveEvent and QKeyEvent modifiers

I am encountering unexpected behaviour in my code.
I have a QGraphicsView containing a QGraphicsScene. Now I want to detect the mouse wheel for zooming the view and mouse moving for moving items in the scene, the latter only while controll is pressed. Now I have two Problems:
MouseMoveEvent is called even when the mouse is not moved but only the mouse wheel.
Moving with and without controll pressed works fine but when I stop moving while controll is pressed and continue using the mouse wheel not only the mousemoveevent is called but also the controllmodifier is still active. What is the problem?
main.cpp
#include "ppi.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PPI w;
w.show();
return a.exec();
}
ppi.h
#ifndef PPI_H
#define PPI_H
#include <QtGui/QMainWindow>
#include <QGraphicsView>
#include <QDebug>
#include <QWheelEvent>
#include "ui_ppi.h"
#include "ppiView.h"
#include "ppiscene.h"
class PPI : public QMainWindow
{
Q_OBJECT
public:
PPI(QWidget *parent = 0, Qt::WFlags flags = 0);
~PPI();
int i;
private:
Ui::ppiClass ui;
PPIScene* ppiScene;
protected slots:
void onZoom(QWheelEvent *event);
void onMouseMoved(QGraphicsSceneMouseEvent *event);
};
#endif // PPI_H
ppi.cpp
#include "ppi.h"
PPI::PPI(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
ppiScene = new PPIScene(this);
connect(ppiScene, SIGNAL(mouseMoved(QGraphicsSceneMouseEvent*)), this, SLOT(onMouseMoved(QGraphicsSceneMouseEvent*)));
connect(ui.gVPPI, SIGNAL(zoom(QWheelEvent*)), this, SLOT(onZoom(QWheelEvent*)));
ppiScene->setSceneRect(0,0,1024,1024);
ui.gVPPI->setScene(ppiScene);
ui.gVPPI->setMouseTracking(true);
i = 0;
}
PPI::~PPI()
{
}
void PPI::onZoom(QWheelEvent *event)
{
if(event->delta() > 0)
ui.gVPPI->scale(1.01, 1.01);
else
ui.gVPPI->scale(1/1.01, 1/1.01);
}
void PPI::onMouseMoved(QGraphicsSceneMouseEvent *event)
{
i++;
qDebug() << "slot" << i << event->modifiers();
if(event->modifiers() & Qt::ControlModifier)
{
qDebug() << "ctrl pressed";
}
}
ppiview.h
#ifndef PPIVIEW_H
#define PPIVIEW_H
#include <QGraphicsView>
#include <QMouseEvent>
class PPIView : public QGraphicsView
{
Q_OBJECT
public:
PPIView(QWidget * parent = 0);
~PPIView();
private:
void wheelEvent(QWheelEvent *event);
signals:
void zoom(QWheelEvent *event);
};
#endif // PPIVIEW_H
ppiview.cpp
#include "ppiview.h"
PPIView::PPIView(QWidget * parent)
: QGraphicsView(parent)
{
}
PPIView::~PPIView()
{
}
void PPIView::wheelEvent(QWheelEvent *event)
{
emit zoom(event);
}
ppiscene.h
#ifndef PPISCENE_H
#define PPISCENE_H
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
class PPIScene : public QGraphicsScene
{
Q_OBJECT
public:
PPIScene(QObject *parent);
~PPIScene();
int i;
private:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
signals:
void mouseMoved(QGraphicsSceneMouseEvent *event);
};
#endif // PPISCENE_H
ppiscene.cpp
#include "ppiscene.h"
PPIScene::PPIScene(QObject *parent)
: QGraphicsScene(parent)
{
i = 0;
}
PPIScene::~PPIScene()
{
}
void PPIScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
i++;
qDebug() << "signal" << i << event->modifiers();
emit mouseMoved(event);
}
It is really strange. It looks like the Qt::KeyboardModifiers status, which QGraphicsSceneMouseEvent::modifiers returns, is only updated, when the mouse is physically moved. What is even more strange, is that in your code QGraphicsSceneMouseEvents of type QGraphicsSceneMouseMove are sent even when the mouse is not moved at all, but only the wheel is turned. Maybe the relative movement due to your scaling counts as movement, but not as movement, which updates the modifiers.
I was able to reproduce your problem: The status of the modifiers don't change unless the mouse is physically moved.
Fortunately there is an easy workaround. In
void PPI::onMouseMoved(QGraphicsSceneMouseEvent *event)
{
i++;
qDebug() << "slot" << i << event->modifiers();
if(event->modifiers() & Qt::ControlModifier)
{
qDebug() << "ctrl pressed";
}
}
replace:
if(event->modifiers() & Qt::ControlModifier)
with:
if(QApplication::queryKeyboardModifiers() & Qt::ControlModifier)
QApplication::queryKeyboardModifiers() is updated immediately when you press or release the control key.

QQuickWidget grab image

I am saving an image of a QQuickWidget with several QML children but all I have is a blank image.
C++ side:
QQuickWidget* content..
content->setSource(QUrl("qml:/main.qml"));
QPixmap *pm = content->grab(QRect(QPoint(0,0),QSize(-1,-1));
pm->save("someFilename.png", 0, 100);
QML side:
Rectangle{ width: 5; height: 5; color: "yellow"; objectname: "rootobj"}
In the QML I wish to dynamically add children and be able to show them in the image. I have tried QQuickWindow grabWindow method with a connection to a slot and it works but it captures only the window visible area and I need to capture the whole QML.
I believe this is not rocket science just that I am not getting it somewhere. Thanks for your replies!
Addendum:
Ok, I do not think its the issue of before/after rendering since I can see all the qml children before I call the picture grabber. So sorry for not being precise.
c++ side:
QQuickWidget* content..
content->setSource(QUrl("qml:/main.qml"));
//do all my dynamic qml children adding
After I can visually see all my qml:
QPixmap *pm = content->grab(QRect(QPoint(0,0),QSize(-1,-1));
pm->save(....
Unless I am wrong, I dont think its rendering issue. Thank you!
Issue is like Mido said. You can solve it like follows.
Create a class Viewer:
viewer.h
class Viewer : public QQuickView{
Q_OBJECT
public:
explicit Viewer(QWindow *parent = 0);
Viewer(bool showBar);
virtual ~Viewer();
void setMainQmlFile(const QString file);
void addImportPath(const QString path);
public slots:
void beforeRendering();
void afterRendering()
}
Viewer.cpp
#include "viewer.h"
Viewer::Viewer(QWindow *parent)
: QQuickView(parent)
{
setWidth(800);
setHeight(480);
connect(this, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()));
connect(this, SIGNAL(afterRendering()), this, SLOT(afterRendering()));
}
void Viewer::setMainQmlFile(const QString file)
{
setSource(QUrl::fromLocalFile(file));
}
void Viewer::addImportPath(const QString path)
{
engine()->addImportPath(path);
}
void Viewer::beforeRendering()
{
//
}
void Viewer::afterRendering()
{
//grab window
QImage img = this->grabWindow();
img.save(path);
//or your code
}
main.cpp
Viewer *viewer = new Viewer;
//
///
//
viewer->setMainQmlFile(QStringLiteral("qml/main.qml"));
viewer->show();
I think your issue is that the capture screen is done before the rendering of the QML object.
In order to make it work you should connect the grab of the signal after rendering signal:
connect(this, SIGNAL(beforeRendering()), this, SLOT(sltBeforeRendering()));
connect(this, SIGNAL(afterRendering()), this, SLOT(sltAfterRendering()));
do the grab in sltAfterRendering slot.
To grab screen I use the grabWindow() function and I call it from QML.
It depends on the behaviour that you want from your software.
Try this:
grabber.h
#ifndef GRABBER_H
#define GRABBER_H
#include <QObject>
#include <QImage>
#include <QQuickView>
class Grabber : public QObject
{
Q_OBJECT
public:
explicit Grabber(QObject *parent = 0);
Grabber(QQuickView *view);
~Grabber();
Q_INVOKABLE void capture(QString const &path) const;
signals:
public slots:
private:
QQuickView* view_;
};
#endif // GRABBER_H
grabber.cpp
#include "grabber.h"
Grabber::Grabber(QObject *parent) :
QObject(parent)
{
}
Grabber::Grabber(QQuickView* view) :
view_(view)
{
}
Grabber::~Grabber()
{
if(view_ != NULL)
{
delete view_;
view_ = NULL;
}
}
void Grabber::capture(QString const &path) const
{
QImage img = view_->grabWindow();
img.save(path);
}
main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QQmlContext>
#include <QQmlEngine>
#include "grabber.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer *viewer = new QtQuick2ApplicationViewer;
Grabber * grab = new Grabber(viewer);
viewer->setHeight(480);
viewer->setWidth(800);
viewer->rootContext()->setContextProperty("grab", grab);
viewer->setMainQmlFile(QStringLiteral("qml/main.qml"));
viewer->showExpanded();
return app.exec();
}
Call it from QML with:
grab.capture(path + "imageName.png")

Get duration of QMediaPlaylist object

I create a player for audiobooks - when you open a folder with mp3 file, whole list of them is added to playlist and List View. And i have a Label, which suppose to show duration of the whole book. But player->duration returns only a duration of current track, and if i go through the loop and do playlist->next() every step, player->duration returns 0. I know about Phonon and file metadata, but i need to do this without using it.
I am attaching a source code of a working project, you can use. When the player changes the file, the duration is changed and printed out. To loop within files, there is a need to wait till the decoder completes reading the media file. See the code below and the comments.
This is mainwindow.cpp
#include "mainwindow.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
bool done =false;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
player = new QMediaPlayer(this);
playlist = new QMediaPlaylist(player);
playlist->setPlaybackMode(QMediaPlaylist::Sequential);
player->setPlaylist(playlist);
connect(player, &QMediaPlayer::durationChanged, this, &MainWindow::on_durationchanged);
//connect(player,&QMediaPlayer::)
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
playlist->addMedia(QUrl::fromLocalFile("Ar_today.mp3"));
playlist->addMedia(QUrl::fromLocalFile("Ar_sunday.mp3"));
playlist->setCurrentIndex(0); //set the first file
while (done == false) //wait till the duration is read
{
QApplication::processEvents();
}
done = false; playlist->setCurrentIndex(1); //change to the second file
while (done == false) //wait till the duration is read
{
QApplication::processEvents();
} //this way you can loop through files
player->setVolume(80);
player->play();
qDebug() << player->errorString();
}
void MainWindow::on_pushButton_2_clicked()
{
player->stop();
}
void MainWindow::on_durationchanged(qint64 duration)
{
done = true;
qDebug() << "duration = " << player->duration();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include <QDebug>
extern bool done;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_durationchanged(qint64 duration);
private:
Ui::MainWindow *ui;
QMediaPlayer* player;
QMediaPlaylist* playlist;
};
#endif // MAINWINDOW_H
In the form, create 2 buttons, one called pushbutton to play and the other is pushButton_2 to stop

Resources