I need to place several instances of a custom QPushButton subclass adjacent to one another. For some reason, the buttons overlap one another when painted. A simplified example of the problem is below.
Here is the (incorrect) output:
Here is the code:
#include <QtGui>
class MyButton : public QPushButton {
public:
explicit MyButton(Qt::GlobalColor color, QWidget *parent = NULL)
: QPushButton(parent), color_(color) {
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
}
QSize sizeHint() const {
return QSize(50, 25);
}
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setOpacity(0.5);
painter.fillRect(0, 0, width(), height(), color_);
}
private:
Qt::GlobalColor color_;
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
QWidget widget;
QHBoxLayout *layout = new QHBoxLayout;
layout->setSpacing(0);
MyButton *w1 = new MyButton(Qt::red);
MyButton *w2 = new MyButton(Qt::green);
MyButton *w3 = new MyButton(Qt::blue);
layout->addWidget(w1);
layout->addWidget(w2);
layout->addWidget(w3);
widget.setLayout(layout);
widget.show();
return app.exec();
}
What is causing this, and how do I fix it? BTW, I tried something similar with regular QWidget subclasses (instead of QPushButton subclasses), and there is no problem. It is something peculiar to QPushButton.
UPDATE: I'm really thinking now that this is a bug. I submitted it to the Qt Bug Tracker; we'll see what the Trolls think. In any case, deriving from QAbstractButton fixes the drawing problem ... I just had to re-implement some of the functionality I needed.
UPDATE 2: The Trolls at Qt provided a solution (workaround?); I posted their fix as an answer below. I'm leaving it up to their team to determine if this is a feature or bug. It apparently only behaves differently on the Mac.
The solution is to add the following to the subclass:
setAttribute(Qt::WA_LayoutUsesWidgetRect);
Apparently it is only necessary on the Mac platform; Windows and Linux display the layout as expected.
Instead of calling setSizePolicy() and reimplement sizeHint(), I would try to simply call
setFixedSize(50, 25)
in your constructor. This should update the sizeHint on its own.
Hope this helps.
Your code works fine for me. I have tested it. What version you use? I use Qt 4.6.3 and it is fine.
Related
I've written a simple "proxy widget" class in Qt; the idea is that this widget will hold a single child QWidget and represent that widget in the QWidget hierarchy. (FWIW, the motivation for doing this is to make it easy to move the child widget around the hierarchy without having to directly disturb the state of various other container-QWidgets to do so).
This seems to work fairly well; the only problem I've run into is that I want my ProxyWidget to always be laid-out the same way as its child-QWidget would be (if the child had been added to the widget hierarchy directly); but instead I find that the ProxyWidget is often sized larger than its child would be, leading to wasted space in the GUI.
Therefore, is there some way I can craft my ProxyWidget class so that Qt's layout managers to move/size it exactly the same as if its child widget was added directly?
As a minimal test/example, you can compile the following code and run it with or without the "proxy" command line argument -- my goal is that the visual results would be the same either way, and in particular that you would never see any red pixels in the window (since red pixels indicate areas where the ProxyWidget has been sized larger than the blue child widget it contains)
#include <QApplication>
#include <QStackedLayout>
#include <QWidget>
class ProxyWidget : public QWidget
{
public:
ProxyWidget(QWidget * childWidget)
: _childWidget(childWidget)
, _layout(new QStackedLayout(this))
{
_layout->addWidget(childWidget);
setSizePolicy(childWidget->sizePolicy());
}
virtual QSize sizeHint() const {return _childWidget->sizeHint();}
virtual QSize minimumSizeHint() const {return _childWidget->minimumSizeHint();}
private:
QWidget * _childWidget;
QStackedLayout * _layout;
};
static void SetWidgetBackgroundColor(QWidget * w, const QColor bc)
{
QPalette p = w->palette();
p.setColor(QPalette::Window, bc);
w->setAutoFillBackground(true);
w->setPalette(p);
}
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
QWidget * win = new QWidget;
win->setWindowTitle("Proxy Widget test");
QWidget * proxyMe = new QWidget;
proxyMe->setFixedSize(100, 50);
SetWidgetBackgroundColor(proxyMe, Qt::blue);
QBoxLayout * winLayout = new QBoxLayout(QBoxLayout::TopToBottom, win);
if ((argc >= 2)&&(strcmp(argv[1], "proxy") == 0))
{
ProxyWidget * proxyWidget = new ProxyWidget(proxyMe);
SetWidgetBackgroundColor(proxyWidget, Qt::red);
winLayout->addWidget(proxyWidget);
}
else winLayout->addWidget(proxyMe);
win->show();
return app.exec();
}
I guess minimumSize and maximumSize of ProxyWidget is different from its child widget and setting them fix things in your particular example :
ProxyWidget(QWidget * childWidget)
: _childWidget(childWidget)
, _layout(new QStackedLayout(this))
{
_layout->addWidget(childWidget);
setSizePolicy(childWidget->sizePolicy());
this->setMinimumSize(childWidget->minimumSize());
this->setMaximumSize(childWidget->maximumSize());
}
However i am not sure it's the best solution but it might gives you a hint to a better one.
My situation is, a big Qt project with many QWidget communication requirement.
For example, I've a QPushButton B and a QLabel L, I need to click thd button B to show some text on label L or hide it, etc. But the problem is neither of them can get each other's object pointer, because both of them located in a deep QWidget tree, maybe their grandgrandgrand parent widget was sibling, what ever.
So my option, is creating a global unique QObject, just for singal/slot forwad,like this:
class GlobalForward: public QObject
{
Q_OBJECT
public:
GlobalForward():QObject(null) {}
signal:
void SigForwardButton(bool toggle);
}
GlobalForward* gForwardObj;
int main(int argc, char* argv[])
{
QApplication app(argc, argv[]);
gForwardObj = new GlobalForward();
QWidget w;
QPushButton* button = new QPushButton(&w);
QWidget m;
QLabel* label = new QLabel(&m);
w.show();
m.show();
connect(button, &QPushButton::clicked, gForwardObj, &GlobalForward::SigForwardButton);
connect(gForwardObj, &GlobalForward::SigForwardButton, label, &QWidget::hide);
return app.exec();
}
Actually, the button and the label are so faraway that they cannot see each other. I want to use GlobalForward to concat the singnal to make it work . Further more, it can forwad QEvent silmiarly.
What I want to know is, is this way properly solved my problem, and what's the disadvantage of it. Also, any better solution will be appropriate, thanks.
(Note: I started learning Qt yesterday and I did my search before asking this.)
After a bit of playing with Qt Designer I decided to make a more serious program, all programatically. Whereas before, simple taskes seemed.. simple, now, diaplaying a button is hell of a complicated because it doesn't show up.
main.cpp
int main(int argc, char * argv[])
{
QApplication app(argc, argv);
PixelPeep p;
p.show();
return app.exec();
}
pixelpeep.h - relevant part
class PixelPeep : public QMainWindow
{
Q_OBJECT
public:
explicit PixelPeep(QWidget *parent = 0);
signals:
public slots:
private:
QToolBar * toolBar;
QHBoxLayout * toolbarLayout;
QToolButton * addButton; // add new image
QScrollBar * zoomBar;
};
pixelpeep.cpp - relevant part
PixelPeep::PixelPeep(QWidget *parent) :
QMainWindow(parent)
{
resize(600,375);
toolBar = new QToolBar;
addButton = new QToolButton;
addButton->setGeometry(20,20,20,20);
toolBar->addWidget(addButton);
toolbarLayout = new QHBoxLayout;
toolbarLayout->addWidget(addButton);
}
After all this, I get an empty window.
Possible cause, AFAIK:
button would go out of scope after being created in the class constructor - it's not the case here because it's dynamically allocated and pointer by the private member addButton
not being in a layout or having size 0 - it's not the case since both of these were addressed in the code
What else could it be?
Sorry for such a noob question...
Call addToolBar(toolBar); inside the PixelPeep constructor.
You didn't set any icon on your button so it will appear as invisible. Hover over it and you will see it's there:
I'm a beginner in C++ and I started learning how to use QT components through code at MVS IDE. I still don't know if that was the best option to begin, but since I'm a java programmer, I made the path I made with Java (Swing components). So, my problem is, how to comunicate two class of my code, since in one I made the window frame and in the other I made my menu bar?
In java I would make something like:
JFrame frame = new JFrame();
JMenu menu = new JMenu();
frame.add(menu);
Anyway, This is my code:
#include "Header.h"
class MainWindow{
private:
QWidget *widget;
public:
void buildWindow(QApplication& app){
widget = app.desktop();
QMainWindow *main_window = new QMainWindow();
QWidget *mainWid = new QWidget(main_window);
MyMenuBar myMenuBar(mainWid);
main_window->setWindowState(mainWid->windowState() | Qt::WindowMaximized);
main_window->setWindowTitle("QT Trainning");
main_window->show();
}
};
class MyMenuBar:QMainWindow {
public:
MyMenuBar(QWidget* mainWid){
QAction *quit = new QAction("&Quit", this);
QMenuBar *menu = new QMenuBar(mainWid);
QMenu *file;
menu->addMenu(file);
file = menuBar()->addMenu("&File");
file->addAction(quit);
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow frame;
frame.buildWindow(app);
return app.exec();
}
I tryed to create an Instance of MenuBar inside the Window class but wans't so helpfull and to be honest most of the materials I found to deal with QT interface they supose that you are using the QT GUI...Any tips about how to solve the problem or what should I really do to practice C++??
Thanks in advance
You should specify access specifier for inheritance,otherwise default mode is public.
Also, if you are going to have all the classes in the same file the ordering is important(i think). In your case MyMenuBar should come before MainWindow. So, it is a better practice to have different components in different headers and then include them as necessary.
Here is the code for the case where you need two classes separately:
class TrainingMenu:public QMainWindow {
public:
TrainingMenu(QMenuBar *menubar){
QAction *quit = new QAction("&Quit", menubar);
QMenu *file;
file = menubar->addMenu("&File");
file->addAction(quit);
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
}
};
class MainWindows:public QMainWindow{
private:
TrainingMenu* _menu;
public:
MainWindows(QMainWindow *parent = 0):QMainWindow(parent) {
_menu=new TrainingMenu(MainWindows::menuBar());
this->setWindowTitle("Qt training");
this->setWindowState(Qt::WindowMaximized);
this->show();
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindows window;
return app.exec();
}
This example should be good enough. You do the following:
Create a QMenu with the top widget as a parent
Add submenu QMenu instances to the root level menu
I have QDialog that is heavily designed with QDesigner , I saw on the web that I could add QStatusBar with code like this :
#include <QDialog>
#include <QStatusBar>
#include <QLayout>
#include <QApplication>
#include <QTextEdit>
#include <QStatusTipEvent>
class Dialog : public QDialog {
public:
Dialog() : QDialog(){
QLayout *l = new QVBoxLayout(this);
QTextEdit *te = new QTextEdit;
te->setStatusTip("XXX");
l->addWidget(te);
bar = new QStatusBar;
l->addWidget(bar);
l->setMargin(0);
l->setSpacing(0);
}
private:
QStatusBar *bar;
protected:
bool event(QEvent *e){
if(e->type()==QEvent::StatusTip){
QStatusTipEvent *ev = (QStatusTipEvent*)e;
bar->showMessage(ev->tip());
return true;
}
return QDialog::event(e);
}
};
int main(int argc, char **argv){
QApplication app(argc, argv);
Dialog dlg;
return dlg.exec();
}
Its not even working in my case .. maybe the QDialog is already have few layets that holds widget.
My question is can I some how use palceholder in the QDesigner or somehow promote widget that place hold the QStatusbar class? I don’t know …
What can I do in such case? can I implement new QStatusbar?
Thanks
I presume when you say it doesn't work, that you are not seeing the status bar when you run.
I don't see any way to do this wholly in the designer. The designer certainly resists the idea of promoting something to a QStatusBar. I suppose you could fool the designer by subclassing QStatusBar, and then promoting a QWidget to your subclass.
But I don't think we need to go that route just yet. I think with a few tweaks to the code you have above should help.
In the designer, add a layout, it doesn't matter what kind, at the bottom of your dialog. I called mine 'StatusBarLayout'. You can see the layout (the red box that's squished at the bottom). I removed the bottom margin in the dialog so that the status bar is flush at the bottom.
Now remove everything in the above code about layout l, and just do this:
bar = new QStatusBar(this);
pUI->StatusBarLayout->addWidget(bar);
pUI->textEdit->setStatusTip("XXX");
The textEdit was something added in the designer. Now when you run it you should see this:
I hope that helps
Edit:
You can also set the Status Tips for various widgets in the designer too, so there is no need to do that in the code unless you want to.
Try adding a QStatusBar like this:
QDialog dialog;
QLayout* layoutWidget = new QVBoxLayout(&dialog);
layoutWidget ->addWidget(new QTextEdit);
QStatusBar* statusBar = new QStatusBar;
layoutWidget ->addWidget(statusBar );