How to render QQuickItem children? - c++

Suppose I have this class:
class CustomRow : public QQuickPaintedItem {
Q_OBJECT
CustomRow(QQuickItem* parent) {...}
void paint(QPainter* painter) {
/* Custom painting of children here */
}
}
How could I position and paint the children in a cusom way? For instance, if I had a percentLeft property, how could I make it so that two children are rendered so that the left takes up percentLeft of the space there? Suppose it was in QML like this:
CustomRow {
percentLeft: 80
Text {
text: "Hi"
}
Text {
text: "Bye"
}
}
How would I make it so that the text that says "Hi" took up 80% of the width of the CustomRow item? Do I just need to position the children and they take care of it themselves?

Do I just need to position the children and they take care of it
themselves?
Nailed it. Items are responsible for their own painting. You need not concern yourself with that, only with any custom painting that you do.
You can use property bindings to position the children inside the parent item.

Related

Overlay multiple widgets

I have one QWidget which contains a multiple sliders. All sliders resized to main QWidget size. As result all sliders share same draw rectangle. For sliders I overload paintEvent method, so it draw only required stuff. Here is an example code:
class MySlider : public QSlider
{
void paintEvent(QPaintEvent *event) {
...
}
}
class MyWidget : public QWidget
{
MyWidget() : QWidget() {
slider1 = new MySlider(this);
slider2 = new MySlider(this);
slider1->resize(rect().width(), rect().height());
slider2->resize(rect().width(), rect().height());
}
MySlider * slider1;
MySlider * slider2;
}
adsf
Groove is not seen with this solution (because we don't call QSlider::paintEvent), but it still exist. For this widget it is possible to use only the last created slider (slider2). The rest are visible, but they are not available.
Is it possible to overlay multiple widgets on each other and still be able to access all of them with mouse event?
Overlapping widgets isn't a good idea, expect only one is visible at the same time. What is the purpose for that overlapping?
You can set QWidget::setAttribute(Qt::WA_TransparentForMouseEvents) to not generate any mouse events for that particular widget so that only one slider will get that events. Then you are able to redirect that messages to your other sliders.

QToolbar force expand on too many QActions

Hi all is there any way to automatically expand a QToolbar if there is too many QActions in?
Using Qt version 5.4.1 C++11
Ive tried :ui->mainToolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred)
But this only expands it horizontally. I need it to expand vertically like the Expand button does.
Always expanding a toolbar vertically is not possible as far as I know (never seen it). A solution would be to add multiple toolbars. This way, you can arrange them one under the other.
What you can try is to add a custom widget to the toolbar that grows horizontally. This was proposed here by using a QScrollArea... Not sure whether this is exactly what you want, but it may work good enough.
This is how you can make a function to expand/retract a QToolbar. Firstly using a Forloop get all the child widgets from the QToolbar. You can use a Bool to lock to only get the first Widget which is the Expanding button/Action.
bool get_first_action{true};
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(get_first_action)
{
get_first_action = false;
// This is the expanding action!
m_action_expand = widget;
}
}
Or you can do this which is probably a bit safer.
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(widget->objectName() == "qt_toolbar_ext_button")
{
// This is the expanding action!
m_action_expand = widget;
}
}
Once you have the sneaky expanding action assign it to a member varible
// Make sure to initialize this in the constructor!
// m_action_expand = new QWidget(this // parent)
QWidget* m_action_expand;
Now create a handy function with a good name;
void MainWindow::forceToolbarExpand()
{
// Grab the position of the expanding action/widget
QPointF pos(m_action_expand->pos());
// Create a fake/dummy event that replicates the mouse button press
QMouseEvent event_press(QEvent::MouseButtonPress, pos, Qt::LeftButton,0, 0);
// Create a fake/dummy event that replicates the mouse button release
QMouseEvent event_release(QEvent::MouseButtonRelease, pos, Qt::LeftButton,0, 0);
// These two events together will fire the QAction::Toggled signal.
// Make sure to send the events!
QApplication::sendEvent(m_action_expand, &event_press);
QApplication::sendEvent(m_action_expand, &event_release);
}
And there we have it your QToolbar, if it can be expanded/retracted now will when you call this function. I'm not too sure if you can directly Moc/fake the toggled event but you can try it. I know this method works so yeah.

Paint over top of label, not behind it in Qt

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)

How do I get the size of an area of a QVBoxLayout?

This is the structure I have. I want to find the total size of label_2, or the QVBoxLayout, as it's displayed. When I use verticalLayout_2->width(), I always get 100 and verticalLayout_2->height() always returns 30. It's set to expanding, so I thought it would fill the area, which is 385x379, according to Qt Creator.
Doing label_2-width() and label_2-height() also results in 100x30, regardless of the window size and the area I thought it would expand to.
There is nothing much visually going on after your widget's constructor has been run. The setupUi call happens in the constructor. The real work happens once the event loop gets going.
Your real problem is that you should not be checking the size at an arbitrary point in time. You should be checking it each time it changes. To do this, you need your own layout. All it takes is to derive from an existing layout, and reimplement setGeometry. This method is called each time the parent widget or parent layout resizes the given layout. That is the only correct approach, and it doesn't require any hacks to accomplish.
For example, the following class could be used to signal when there's a new geometry:
class SigBoxLayout : public QBoxLayout {
Q_OBJECT
protected:
void setGeometry(const QRect & r) Q_DECL_OVERRIDE {
if (r != geometry()) emit hasNewGeometry(r);
QBoxLayout::setGeometry(r);
}
public:
SigBoxLayout(QBoxLayout::Direction dir, QWidget * parent = 0) :
QBoxLayout(dir, parent) {}
Q_SIGNAL void hasNewGeometry(const QRect & r);
};

QGraphicsView missing text on some draws

I've tried drawing a rectangle with text inside in a QGraphicsView. I get the text from the currently selected item in a QTreeWidget. The scene seems to sporadically show the text, sometimes it will, sometimes it won't.
void MainWindow::on_treewidget_itemSelectionChanged()
{
drawSectionFromProperties(ui->treewidget->currentItem());
}
void MainWindow::drawSectionFromProperties(QTreeWidgetItem *section)
{
ui->graphicsview->setScene(new QGraphicsScene());
ui->graphicsview->scene()->addRect(0,0,200,300,QPen(QColor(0,0,0)),QBrush(QColor(255,250,129)));
QFont objectTitle;
ui->graphicsview->scene()->addSimpleText(section->text(0),objectTitle);
}
Hmm, looks like you are creating a new scene on each item selection?
This isn't a very nice way to go :)
Better do the following:
Create an 'QGraphicsScene* m_scene;' and 'QGraphicsSimpleTextItem* m_textItem' data members in your MainWindow class private section.
In MainWindow::drawSectionFromProperties() do something like:
MainWindow::MainWindow(QWidget* parent, ...)
: m_scene(0), m_textItem(0)
{
...
}
// leave your on_treewidget_itemSelectionChanged as is
void MainWindow::drawSectionFromProperties(QTreeWidgetItem *section)
{
// setup things only ONE time on the first call
if(!m_scene)
{
m_scene = new QGraphicsScene();
ui->graphicsview->setScene(m_scene);
m_textItem = ui->graphicsview->scene()->addSimpleText(QString());
}
// here only change text of existing item
m_textItem->setText(section->text(0));
}
This way you won't be creating new scene on every item selection. Actually you need ONE scene and ONE item in it, no need to create them over and over and over again and stack one onto another like you currently do.
Cheers :)