I started Qt Designer to create a window for the Maya tool.
I'm making the same GUI as Snap of Blender. How do I make the display change like this?
What is it called in Qt to show a different one for each button?
Two options I can think of
1. Use a StackedWidget (left)
A StackedWidget is a container that contains multiple widgets, but shows only one at a time. You can set which one is visible with .setCurrentIndex(int index) and .setCurrentWidget(QWidget*).
This is basically what you want to do, except by default it is going stay as tall as the tallest widget, even with a spacer (as visible on the screenshot).
This can be forced manually, or you can even create your own widget to do that (https://www.qtcentre.org/threads/28514-How-can-i-achieve-an-auto-resizing-QStackedWidget), but it defeats a bit its ease of use.
2. Widgets, hide/show, spacer (right)
The second option, really basic, is to create different Widgets that will act as containers, and that you will toggle (.show()/.hide) whenever a button is pressed. This can even be done from the Designer of QtCreator if you are looking for the easiest way to achieve it.
Make your buttons checkable, and exclusive (as you probably did)
Connect to the clicked(bool checked) or toggled(bool checked) signal
Call show/hide on the container widgets to show only one. Et voila!
MyWidget:MyWidget(...)
{
connect(ui->vertexButton, &QPushButton::clicked, this, &MyWidget::onButtonClicked);
}
void MyWidget::onButtonClicked(bool checked) {
button = sender();
if (button == ui->vertexButton) {
ui->vertexContent->show();
ui->edgeContent->hide();
} else if ...
}
Disclaimer: I haven't tried to compile the code above
Related
So I currently have got a custom widget, and I want to add them to the main window after clicking a button. I would like to add them all to one fixed position first and then I will be able to drag them wherever I like. I am able to create and display these custom widgets with help of QHBoxLayout or QVBoxLayout, but in this case they will not be in the same position after I create them. Any help will be appreciated!
As the names suggest, the QLayout classes manage the position and geometry of the items added to them. You cannot move (eg. drag) an item out of a layout w/out first removing it from the layout (QLayout::removeItem() and derivatives). For example when you drag a toolbar or dock widget out of a QMainWindow it goes through all sorts of machinations to remove it from the MW layout, change the widget's window flags, remember the old position in the layout, and so on. And the reverse to dock it again.
To do what you describe (drag widgets arbitrarily around a window) you would need to not use a QLayout and position the widgets manually by specifying a QWidget::setGeometry() for example. After initial position, and assuming the user has some way to grab the widget (title bar or drag handle you made, etc), you'll probably still need to manage their positions, for example if the main window is resized (if you care about keeping them contained). Essentially you'd have a bunch of separate widgets acting as individual windows and probably need some way to keep track of them.
I don't know what kind of widgets you're talking about, but one option may be a QMdiArea which lets the user drag windowed widgets around, tabify them, save/restore state, and so on.
For more flexibility you could also look into the Qt Graphics Framework. The graphics scene has a lot of features for user-movable items/widgets, keeping track of them, and so on. It is probably the most flexible method overall, and you can also use regular QWidgets inside a graphics scene.
A couple other Q/A about arbitrarily positioning widgets (I'm sure there are more to be found):
QPushButton alignment on top another widget
How to keep Push Buttons constant in relative to change of Label Size in PyQt4
I have a QGLWidget that renders an OpenGL scene inside a Qt application. I would like to add some other translucent Qt widgets that are overlaid on top of the QGLWidget. This is more difficult than with standard widgets since the OpenGL drawing context is unknown to Qt's painter classes. So, if I just do the obvious thing and place a transparent toolbar on top of the QGLWidget for instance, the transparent part of the toolbar instead renders black (it doesn't have access to the OpenGL frame buffer when painting).
It seems that the recommended way for handling this sort of thing is to overpaint the 2D content after drawing the OpenGL scene. The linked example seems very straightforward, as long as you're just drawing simple shapes. Instead, what I would like to do is to defer the painting of some child QWidget objects to be done inside the paint event for the QGLWidget.
So, my problem boils down to this:
Prevent the overlay QWidgets from painting in the normal, 2D context.
In the paint event handler for the QGLWidget, paint the overlays after painting the 3D scene that makes up the background.
The second item on this list seems to be simple: I can use QWidget::render() inside the QGLWidget's paint event to draw the desired widgets on top of the viewport. This works just fine.
The first item is more tricky: I need a way to prevent the widgets from painting during the normal course of events and only paint them in the QGLWidget's paint handler. One obvious way to do this is to hide the overlays using QWidget::hide(). This does allow me to paint the widgets atop the OpenGL scene as I would like. However, since the widgets are hidden, they do not respond to mouse or keyboard events. As an example, I'm using a QToolBar with a few buttons on it, and the toolbar is painted properly, but is non-functional (none of the buttons respond to clicks). So, if going down this path, it seems that I would need a way to force the widget to still respond to events even though it is hidden.
Another approach that I've tried is to intercept the QToolBar's paint event using an event filter, with the goal of preventing the toolbar from painting itself. However, the toolbar is still rendered; I'm assuming that this is because the various buttons on the toolbar are child widgets that are still painted, even if I intercept the parent's paint event.
Any ideas on a way that I could accomplish my goal?
I don't understand your issue completely, but I'll try to answer the question stated in the title. You should use event filters. Install an event filter using widget->installEventFilter(object), where widget is a widget you want to block painting, and object is an object of any of your QObject-derived classes. Reimplement eventFilter of this class:
bool MyClass::eventFilter(QObject* object, QEvent* event) {
if (event->type() == QEvent::Paint) { return true; }
return false;
}
When you return true from your eventFilter, the event is filtered and paint doesn't occur.
You can also try to use widget->setUpdatesEnabled(false) to temporarily disable painting. Don't forget to re-enable it when you're done.
it's not the first time that I want a scroll area which behaves like the following (imagine a log or chat window, but too complex to use a simple QTextBrowser):
I want to add multiple widgets, which appear one below the other (like when placed in a QVBoxLayout)
Each widget within this layout should have a fixed size or a height-for-width (like a simple label)
The scroll area should auto-scroll to the most recently added one
(optional) When there is space left (scroll bar not yet enabled), the contents should be aligned to bottom
Using QScrollArea:
My attempt in the past was using a QScrollArea using a QVBoxLayout inside. But this seems to be not as simple as I thought: Whenever I add a widget to the layout, the layout doesn't resize the scroll area content widget immediately, resulting in a delayed adjustment of the contents. For one short moment, the widgets contained in the layout are resized so that the total size equals the total size before the add operation, resulting in a too small size per widget. Also, scrolling to the newly added widget is thus not possible until the layout corrected its size to the new total size of widgets, so even a QTimer::singleShot(0, ...) doesn't help here. Even with a timeout of 20 or so, there are situations in which the layout needs more time to resize. It's not deterministic, and thus far away from a nice solution.
In order to get the bottom alignment behaviour, I initially place a spacer item in the layout. It won't require any space as soon as there is no space left and scrolling gets enabled.
Using QListView:
As my items are too complex, they need to be QWidgets. They can't have the focus, aren't selectable, so an item-based solution seems to be just "the wrong way". Also, this solution sounds too "heavy".
I just can't believe that there is no easy way, so I think I just haven't seen it yet!
QListView should be fine. You claim that your items are static, there's no interaction with them: no focus, no selection. It'd seem that a QWidget is an overkill for such items. You only need something that has a fixed size and can draw itself. That is precisely what delegates in the Qt's model-view system are for. Just implement one or more QAbstractItemDelegates for your items, and provide an implementation of a model for the data they will be rendering. The QAbstractItemView is is already a QAbstractScrollArea!
If you want to paint HTML within a delegate, it's easy to do -- again, QWidget is an overkill for a static display! There is a very food reason why it's "hard" to use QWidget for this -- the API guides you to the correct solution. Assuming your model contains html for each item, here's how you can paint it. You can go fancy with the sizeHint, of course, and should be caching the text document, ideally storing it in the model I'd think.
void MyDelegate::paint(QPainter* p, const QStyleOptionViewItem & opt, const QModelIndex & index) const
{
QTextDocument doc;
doc.setHtml(index.data().toString());
doc.drawContents(p, QRect(QPoint(0,0), sizeHint(opt, index)));
}
QSize MyDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize(100, 200);
}
I have implemented Finger Scrolling to one of my QListWidget.
I have taken refernce from
http://www.developer.nokia.com/Community/Wiki/Qt_Kinetic_scrolling_-_from_idea_to_implementation
Now the problem is on_current_row_changed event of QListWidget gets fired when i scroll up and down my List.
How i can avoid this on click only it should behave like click not on Scroll.
Starting from Qt5 this is as simple as:
#include <QScroller>
...
QScroller::grabGesture(myListWidget, QScroller::LeftMouseButtonGesture);
For touch screens use TouchGesture instead of LeftMouseButtonGesture.
If the widget doesn't inherit QAbstractScrollArea (e.g. QWebView):
QScrollArea *scrollArea = new QScrollArea;
scrollArea->setWidget(myWidget);
QScroller::grabGesture(scrollArea, QScroller::LeftMouseButtonGesture);
Be sure to resize the widget to its content size.
You need to test whether the finger has moved between mousePress and mouseRelease. You can extract the position of the press, so if you store it in an intermediate variable, then test it against the position of the mouseRelease (say using QPoint::manhattanLength()), you can tell whether the finger has moved. If it has, the user is scrolling, if not, they're clicking.
EDIT: Looking at the code you've linked to, they're already doing the above. Could we see some more of your reimplemenation?
Just a quick question.
I'm building my app interface at the moment with Qt.
I set a background image for the mainpage and I use stackedWIdgets to slide from one window to another.
setStyleSheet("background-image: url(:/spherebackground.png);"
"background-repeat: no-repeat;"
"background-position: center center");
When the application starts, a page appears which is made of 3 layouts:
1) One that contains a topToolbar Widget with QPushButtons, and a label displaying the PageTitle
2) in the middle, a mainPageLayout that contains the SlidingStackedWidgets
3) the BottomToolBar
The mainFrameWidget contains a mainFrameLayout:
mainPageWidget=new QWidget();
mainPageLayout=new QVBoxLayout();
//I add the buttons and others
mainPageLayout->addWidget(addEntryButton);
mainPageLayout->addWidget(vocaButton);
mainPageLayout->addWidget(exprButton);
mainPageLayout->addWidget(rulesButton);
mainPageLayout->addWidget(learnButton);
mainPageWidget->setLayout(mainPageLayout);
Then, I have the other pages created with the designer
And then a function that add the pages to the slidingStacked
void MainWindow::createSlidingStackedWidget() {
//the slidingStacked is the Widget that contains the subslidingWidgets
slidingStacked= new SlidingStackedWidget(this);
slidingStacked->addWidget(mainPageWidget);
quickAddView = new QuickAddController(); //which is a UI widget
slidingStacked->addWidget(quickAddView);
}
And then, when a button in the mainLayout is pressed, it triggers a function like this
void MainWindow::slideInAdd(){
topToolBar->clear();
slidingStacked->setVerticalMode(true);
slidingStacked->slideInIdx(1);
setupTopToolBar("Terminer","Ajout Entrée","Modifier");
bottomToolBar->hide();
QObject::connect(goBackButton,SIGNAL(clicked()),this,SLOT(backFromAdd()));
}
The thing is that I'm trying to port an application I created for the IPhone and I want it to have the same "Look and Feel" but when I slide from one page to another...
1) the animation flickers
2) I would like the sliding widgets to be transparent except for the controls (QPushButtons...)
but they have the same sphere background as the one I set up at the beginning of the code
3) My labels and controls also have the same background image when I would like them to be standard
(eg : a label should have a white background)
I can't figure out why...
Hope this will give you a better idea of what's going on...
So far as 2) and 3) go, that is because widgets inherit their parent's palette by default. To fix this, you can explicitly style them, or assign their palette to be the default application palette after they are created. I don't know how to handle the first problem.