Overlay multiple widgets - c++

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.

Related

Using Qt Undo Framework With QPainter

I am creating a drawing application with Qt and want to include undo and redo commands for whatever is drawn on a QImage. For this, I want to at least try and incorporate the Qt undo framework, since manually doing it will probably be above my skill level.
I have a MainWindow class where the main work is done and where I have the menu with undo and redo QActions.
The painting is done in a QWidget class called DrawingArea like this (PenShape is an enum of shapes):
void DrawingArea::drawLine(const QPoint &endPoint) {
QPainter painter(&image);
//some pen config code
switch (currentShape){
case PenShape::Polygon:
painter.drawPolygon(&endPoint, 5, Qt::OddEvenFill);
break;
case PenShape::Line:
painter.drawLine(lastPoint, endPoint);
break;
//other shapes and their draw methods..
}
update()
}
So as can be seen, the painter can draw in multiple shapes. I know I would have to somehow transfer this to a QUndoCommand class, but how? The drawline method is called only in DrawingArea's mouse event methods like this:
void DrawingArea::mousePressEvent(QMouseEvent *event){
if (event->button() == Qt::LeftButton) {
lastPoint = event->pos();
}
}
The QtUndoStack is located in MainWindow and the QActions are also there, but somehow I would have to connect the drawLine QtUndoCommand class and the mousePressEvent and mainWindow QAction with its connected slot method all together. This is what I don't know how to do.
Any help is appreciated!
Edit, here's the menu button actions:
m_undo = undoStack->createUndoAction(this, tr("&Undo"));
m_undo->setShortcuts(QKeySequence::Undo);
m_redo = undoStack->createRedoAction(this, tr("&Redo"));
m_redo->setShortcuts(QKeySequence::Redo);
I have no QUndoCommand classes because I'm unsure of how to create them yet.
In my MainWindow, I have a undoStack declared like this:
undoStack = new QUndoStack(this);

QDockWidgets merging incorrectly

I have a QDockWidget class and a QMainWindow:
// docker.hpp
class Docker : public QDockWidget
{
Q_OBJECT
public:
Docker(QString title, QWidget* parent = 0);
}
// docker.cpp
Docker::Docker(QString title, QWidget* parent): QDockWidget(title, parent)
{
QWidget* widget = new QWidget(this);
widget.setMinimumSize(200, 200);
setWidget(widget);
widget->setStyleSheet("border:5px solid gray;");
setAllowedAreas(Qt::AllDockWidgetAreas);
}
// mainwindow.hpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent);
private slots:
void createDockers();
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
{
setDockOptions(dockOptions() |
QMainWindow::AllowTabbedDocks |
QMainWindow::GroupedDragging);
// The following line of code does not change the situation.
// setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
// There are some other codes which connect a button to the void createDockers() method
}
void createDockers()
{
Docker* dock = new Docker("Docker", this);
dock->setFloating(true);
dock->show();
}
I am able to create two Dockers with clicks of the button mentioned above.
However, when I drag one QDockWidget onto the other, the border disappears and no tabs show up:
I am expecting the following to happen: (Achieved by spawning several QDockWidgets)
I am also noticing that one of the QDockWidgets did not vanish. Instead, it merged back to the MainWindow. This only happens if they are the "first two" QDockWidgets.
What caused this problem and how to solve it? I am trying to mimic this project.
I guess it's linked to the QMainWindow::GroupedDragging option. I'm pretty sure it should work well without it (I mean for the not showing tab issue). Do you have restrictions on dock position somewhere else? The documentation implies it could create issues: http://doc.qt.io/qt-5/qmainwindow.html#DockOption-enum
For the style issue, you may need to redefine it on tab event, because once tabbed, the widget may inherit the tab style instead of the dock widget style you defined (not certified at all ^^)
Last guess/thing you can try, is to start with the dock tabbed and not floating to see if you have any new bahaviour, it was what I was doing in a previous project and it was working pretty well.
Sorry but no other ideas for the moment.

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 place the QScrollBar on the content of the QScrollArea?

I want to make a scrollbar that fades in and out depending on usage. I subclassed QScrollBar and got the look that I want. The problem is that the scrollbar is placed next to the content. How do I instead make it go on top of the content?
I created a new QScrollbar which I connected to the original via signals and then used widget->setParent and then widget->setGeometry() to paint it on top
I quicker solution is to reparent the QScrollBars that the QScrollArea creates and add it to a new QLayout to position it how you want.
QScrollArea *scrollArea = new QScrollArea();
QScrollBar *scrollBar = scrollArea->horizontalScrollBar();
scrollBar->setParent(scrollArea);
scrollBar->setFixedHeight(20);//required for later
QVBoxLayout *scrollAreaLayout = new QVBoxLayout(scrollArea);
scrollAreaLayout->setContentsMargins(0, 0, 0, 10);//use whatever margins you want
scrollAreaLayout->addStretch(1);
scrollAreaLayout->addWidget(scrollBar);
This gets the basic functionality working, however the QScrollArea still adds space where the scrollbar would have been. To remove this, subclass QProxyStyle and override pixelMetric().
#include <QProxyStyle>
class StyleFixes : public QProxyStyle
{
public:
int pixelMetric(PixelMetric metric, const QStyleOption *option = Q_NULLPTR, const QWidget *widget = Q_NULLPTR) const override
{
if(metric == PM_ScrollBarExtent)
{
return 0;
}
return QProxyStyle::pixelMetric(metric, option, widget);
}
};
Then just apply it in main.cpp
QApplication::setStyle(new StyleFixes);
This will remove the arrows on the scrollbar however so you'll need to style it yourself.

QGraphicsView size in a GridLayout

I have found this: Getting the size of a QGraphicsView
But I can't figure out what does it mean to "move my initialization code to showEvent" and I can't comment on that answer.
I am want to resize a QPixmap so it could fit my QGraphicsView. I've placed my graphicsview in Designer and set GridLayout for my main window. In a MainWindow constructor I have written the following code:
ui->setupUi(this);
// Get GView size
g_sizeX = ui->mapView->width();
g_sizeY = ui->mapView->height();
// Init scene
scene = new QGraphicsScene(this);
// Init MAP pixmap and add it to scene
mapImage = new QPixmap(":/Map/europe.jpg");
QPixmap newmapImage = mapImage->scaled(g_sizeX, g_sizeY);
scene->addPixmap(newmapImage);
// Display scene in gview.
ui->mapView->setScene(scene);
But I always get size of 100x30. If I break the gridLayout, I get the correct size.
So, how should I deal with this?
Thank you.
The QGraphicsView will be resized by the QGridLayout after the widget is shown, and can be also resized later when the window is itself resized.
So you should change the size of the pixmap as a result of a QResizeEvent, either by subclassing QGraphicsView to redefine resizeEvent(), and then promoting your view object to your new class in the designer to use it instead of QGraphicsView, or by installing your MainWindow object as an event filter for the view to handle to the resize event from the MainWindow::eventFilter function.
You probably don't want to change the pixmap size in the scene, but rather adjust the view matrix so that your QGraphicsPixmapItem fits perfectly inside the view, with QGraphicsView::fitInView.
For example:
/* QGraphicsPixmapItem *pixmapItem; as a MainWindow member */
pixmapItem = scene->addPixmap(newmapImage);
/* Either always disable or enable the scrollbars (see fitInView doc) */
ui->mapView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->mapView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->mapView->installEventFilter(this);
...
bool MainWindow::eventFilter(QObject *obj, QEvent *evt) {
if(obj == ui->mapView && evt->type() == QEvent::Resize) {
ui->mapView->fitInView(pixmapItem, Qt::KeepAspectRatioByExpanding);
}
// Call the base class implementation
return QMainWindow::eventFilter(obj, evt);
}
I believe that what is happening is that Qt only applies layouts and sets widget sizes when the widget is first displayed.
One way to work that is to override QWidget::showEvent(), and put your sizing code in there.
However, one simpler way, that often works in constructors, is to ask the widget for its sizeHint(), rather than for its not-yet-layed-out size.
In your case, that would mean changing two lines of code to:
g_sizeX = ui->mapView->sizeHint().width();
g_sizeY = ui->mapView->sizeHint().height();
If your layout isn't too complicated, and if you haven't overridden the default size policies, this may well fix things for you.