I am trying to implement a Qt push button and some of its properties are going to change under certain circumstances, such as the size, the color and the text field. While I have implemented most of the functionality, I'm looking to add (probably) another text field at a custom place over the button which is going also to change according to different input. For example:
Desired button layout
In this example, I want to be able to change dynamically both the numeric value and the currency. Is there a way to do it, or something similar?
EDIT: I tried to derive my button class from QPushButton and add some extra text in it, but with no luck. Below you'll find the relevant code:
.h file:
namespace Ui {
class myButton;
}
class myButton : public QPushButton
{
Q_OBJECT
public:
explicit myButton(QWidget *parent = 0);
~myButton();
void setSize(int _xs, int _ys);
void setPosition(int _xp, int _yp);
private:
Ui::Tile *ui;
int xSize = 95;
int ySize = 95;
protected:
void paintEvent(QPaintEvent *);
};
.cpp file
myButton::myButton(QWidget *parent) :
QPushButton(parent),
ui(new Ui::myButton)
{
ui->setupUi(this);
}
myButton::~myButton()
{
}
//Paint event of button
void myButton::paintEvent(QPaintEvent *paint){
QPushButton::paintEvent(paint);
QPainter p(this);
p.save();
p.drawText(QPoint(80,10),"FirstName"); // Simple Text.
p.setPen(Qt::blue); // Changing the color of pen.
p.setFont(QFont("Arial", 50)); // Changing the font.
p.drawText(QPoint(80,20),"MiddleName");
p.drawText(QPoint(80,30),"Lastname");
p.restore();
}
Then I'm calling my new button with something like:
myButton *newBtn2 = new myButton(this);
newBtn2->show();
Related
I'm trying to create a puzzle game with the following layout:
It's a basic slide puzzle so clicking on a button will swap its value with an adjacent button only if it's empty.
I'm having trouble adding buttons from the QButtonGroup container to the QGridLayout. I can't seem to find any relevant examples online about this issue. I can access the individual buttons in the button group and modify their values, but I can't seem to get them to display properly in the window. Any help would be appreciated!
Header File:
/** A single tile in the GUI of the 9 puzzle */
class Tile : public QPushButton {
Q_OBJECT
public:
Tile(int tileNumber);
int getNumber() {return m_Number;}
private:
int m_Number;
};
/** A view for the puzzle */
class PuzzleView : public QWidget {
Q_OBJECT
public:
PuzzleView(PuzzleModel* model);
public slots:
void refresh();
void tryToSlide(QAbstractButton* button);
private:
PuzzleModel *m_Model;
QGridLayout *m_Layout;
QButtonGroup m_Tiles;
};
Implementation file:
Tile::Tile(int tileNumber): m_Number(tileNumber) {
}
PuzzleView::PuzzleView(PuzzleModel* model) : m_Model(model) {
// Create the buttons
// This is where I'm having an issue (simplified to one button for clarity)
m_Tiles.addButton(new Tile(1), 0);
m_Layout = new QGridLayout();
m_Layout->addWidget(m_Tiles.button(0), 0, 0);
connect (&m_Tiles, SIGNAL(buttonClicked(QAbstractButton*)),
this, SLOT(tryToSlide(QAbstractButton*)));
}
I'm trying to write an editor for an rpg (Role Playing Game) (npc / quests / items etc.). I need to create an icon with a "white background" that represents the npc's image. It should be clickable (when it's clicked, current selected npc's icon ID will be set according to the selection).
I've managed to build a pop-up dialog to show all the icons, but couldn't manage to find a way to create clickable icons. Which class should I implement in order to get it working?
Clickable icons can be achieved using either QPushButton or QToolButton:
QPushButton* button = new QPushButton;
button->setIcon(QIcon("/path/to/my/icon"));
Clickable QLabel : https://wiki.qt.io/Clickable_QLabel
Use with a QPixmap : http://doc.qt.io/qt-4.8/qlabel.html#pixmap-prop
Header
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel( const QString& text="", QWidget* parent=0 );
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
Source
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
{
setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel::mousePressEvent(QMouseEvent* event)
{
emit clicked();
}
I've done something similar but didn't want something that looked like a button, nor did I want to get into style overrides or special painting. Instead, I created a ClickableLabel class that derives from QLabel.
The pertinent part of the code is:
class ClickableLabel : public QLabel
{
protected:
virtual void mouseReleaseEvent (QMouseEvent *evt)
{
emit clicked (evt->button ());
}
signals:
void clicked (int button);
...rest of class definition...
}
You can adjust the signal parameters as desired.
FYI I am using Qt Creator 3.3.2 on Linux Mint.
Okay first I would like to admit I have seen a few questions very similar to the one I am asking... Such as How to add a widget (QPushButton for example) dynamically to a layout built in designer. However (for whatever reason) I have had no success in either understanding the answer or implementing it. I am extremely new to Qt and this is my first project in it so please be through so I can understand the answer.
What I have so far:
The UI:
I have a scroll area named srcFolderPairSelect and under that (in the 'object' window) I have a scroll contents named scrLayFolderPairSelect. In a different section/area (different frame and layout) I have a button that is to add a button, to the before mentioned scroll contents, named btnAddNewFolderPair, upon click. PS: I need it to be able to be clicked more than once (need to be able to add multiple dynamic buttons to the scroll contents).
The header file:
#ifndef SYNCCENTER_H
#define SYNCCENTER_H
#include <QMainWindow>
namespace Ui {
class SyncCenter;
}
class SyncCenter : public QMainWindow
{
Q_OBJECT
public:
explicit SyncCenter(QWidget *parent = 0);
~SyncCenter();
private slots:
void on_btnAddFolderPair_clicked();
private:
Ui::SyncCenter *ui;
};
#endif // SYNCCENTER_H
The UI cpp (synccenter.cpp):
#include "synccenter.h"
#include "ui_synccenter.h"
SyncCenter::SyncCenter(QWidget *parent) : QMainWindow(parent), ui(new Ui::SyncCenter)
{
ui->setupUi(this);
}
SyncCenter::~SyncCenter()
{
delete ui;
}
void SyncCenter::on_btnAddFolderPair_clicked()
{
QPushButton* button = new QPushButton("test");
button->setVisible(true);
ui->scrLayFolderPairSelect->layout()->addWidget(button);
}
Also how do I form onclick events for the dynamic buttons?
In a very fast way you can do it like this:
void SyncCenter::on_btnAddFolderPair_clicked()
{
static int count{};
QPushButton* button = new QPushButton(QString("test %1").arg(++count), this);
button->setVisible(true);
connect(button, &QPushButton::clicked, [&count](){
std::cout << "Clicked button number: " << count << std::endl;
});
ui->scrLayFolderPairSelect->layout()->addWidget(button);
}
The main idea is to store slots(handlers), then, if you need - store buttons itselves.
This my "very fast" solution is based on not a good way of implementing this.
You have a lot of ways of implementing this, however, I think, the best solution is to create QButtonGroup because it has a signals such as:
void buttonClicked(QAbstractButton *button);
void buttonClicked(int id);
So, your code will be like:
class SyncCenter {
//....
private slots:
void buttonInGroupClicked(QAbstractButton *);
private:
QButtonGroup *buttonGroup;
};
SyncCenter::SyncCenter(/*...*/) {
buttonGroup = new QButtonGroup(this);
connect(buttonGroup, SIGNAL(buttonClicked(QAbstractButton*)), SLOT(buttonInGroupClicked(QAbstractButton*)));
}
void SyncCenter::buttonInGroupClicked(QAbstractButton *b) {
// do anything with your button
}
void SyncCenter::on_btnAddFolderPair_clicked()
{
static int count{};
QPushButton* button = new QPushButton(QString("test %1").arg(++count), this);
button->setVisible(true);
buttonGroup->addButton(button);
ui->scrLayFolderPairSelect->layout()->addWidget(button);
}
I am trying to implement a widget in Qt that has 2 child widgets of its own: one is a render area where I draw some points and connect lines between them and the other one is a ListBox where I would like to insert the list of all the points I drew with their coordinates from the render area. The 2 widgets where added with Qt Designer. Here is my code until now:
renderarea.h:
class RenderArea : public QWidget
{
Q_OBJECT
public:
RenderArea(QWidget *parent = 0);
QPoint point;
QList<QPoint> list;
protected:
void mousePressEvent(QMouseEvent *);
void paintEvent(QPaintEvent *event);
void updateList(QPoint p);
};
renderarea.cpp:
RenderArea::RenderArea(QWidget *parent)
: QWidget(parent)
{
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}
void RenderArea::mousePressEvent(QMouseEvent *e)
{
point = e->pos();
updateList(point);
this->update();
}
void RenderArea::updateList(QPoint p)
{
list.append(p);
}
void RenderArea::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.setPen(QPen(Qt::black,2));
for (int i = 0; i < list.size(); ++i)
painter.drawPoint(list[i]);
if (list.size()>1)
for(int j = 0; j < list.size()-1; ++j)
painter.drawLine(list[j], list[j+1]);
}
paintwidget.h:
class PaintWidget : public QWidget
{
Q_OBJECT
public:
explicit PaintWidget(QWidget *parent = 0);
~PaintWidget();
private:
Ui::PaintWidget *ui;
};
paintwidget.cpp:
PaintWidget::PaintWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::PaintWidget)
{
ui->setupUi(this);
}
PaintWidget::~PaintWidget()
{
delete ui;
}
My question is how to transmit from the render area widget to my ListBox that I drew another point and it should be displayed along with its coordinates in the list?
The general approach used in QT development is using signal/slots for communication between components of software. So basically you need to define a signal in your source component (for instance RenderArea or whereever your like) and connect your slot defined in another component somewhere (i.e your Form Window) and fire a signal upon an action.
There are examples in the referenced link too.
OrcunC gave you a good advice.
If your are new to signal/slots implementation here some hints you can start from.
renderarea.h
signal:
void pointAdded(QPoint*);
renderarea.cpp
void RenderArea::updateList(QPoint p)
{
list.append(p);
emit pointAdded(&list.back());
}
listbox.h
public slots:
void onPointAdded(QPoint*);
listbox.cpp
void ListBox::onPointAdded(QPoint* point)
{
//lets assume your ListBox is a QListWidget
addItem( QString::number(point->x()) + "," + QString::number(point->y()))
}
somewhere instance of ListBox and RenderArea are accessible
QObject::connect( renderArea, SIGNAL(pointAdded(QPoint*),
listBox, SLOT(onPointAdded(QPoint*)));
NOTE: nameing is very important for readability and maintenance the void RenderArea::updateList(QPoint p) in this case it's more void RenderArea::addPoint( const QPoint& p) (also notice the const reference telling the compiler that we are not changing p event if we have it's reference)
I'm trying to modify the fridge magnets example by adding a button that will reload the widget where the draggable labels are drawn, reflecting any changes made to the text file it reads. I defined another class that would contain the button and the DragWidget object, so there would be an instance of this class instead of DragWidget in main():
class wrapWidget: public QWidget
{
Q_OBJECT
public:
wrapWidget();
};
wrapWidget::wrapWidget()
{
QGridLayout *gridlayout = new QGridLayout();
DragWidget *w = new DragWidget();
QPushButton *b = new QPushButton("refresh");
gridlayout ->addWidget(w,0,0);
gridlayout ->addWidget(b,1,0);
setLayout(gridlayout );
connect(b,SIGNAL(clicked()),w,SLOT(draw()));
}
The call to connect is where I'm trying to do the refresh thing. In the original fridge magnets example, all the label drawing code was inside the constructor of the DragWidget class. I moved that code to a public method that I named 'draw()', and called this method from the constructor instead. Here's DragWidget definition and implementation:
#include <QWidget>
QT_BEGIN_NAMESPACE
class QDragEnterEvent;
class QDropEvent;
QT_END_NAMESPACE
class DragWidget : public QWidget
{
public:
DragWidget(QWidget *parent = 0);
public slots:
void draw();
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
};
DragWidget::DragWidget(QWidget *parent)
: QWidget(parent)
{
draw();
QPalette newPalette = palette();
newPalette.setColor(QPalette::Window, Qt::white);
setPalette(newPalette);
setMinimumSize(400, 100);//qMax(200, y));
setWindowTitle(tr("Fridge Magnets"));
setAcceptDrops(true);
}
void DragWidget::draw(){
QFile dictionaryFile(":/dictionary/words.txt");
dictionaryFile.open(QFile::ReadOnly);
QTextStream inputStream(&dictionaryFile);
int x = 5;
int y = 5;
while (!inputStream.atEnd()) {
QString word;
inputStream >> word;
if (!word.isEmpty()) {
DragLabel *wordLabel = new DragLabel(word, this);
wordLabel->move(x, y);
wordLabel->show();
wordLabel->setAttribute(Qt::WA_DeleteOnClose);
x += wordLabel->width() + 2;
if (x >= 245) {
x = 5;
y += wordLabel->height() + 2;
}
}
}
}
I thought that maybe calling draw() as a slot would be enough to reload the labels, but it didn't work. Putting the draw() call inside the widget's overriden paintEvent() instead of the constructor didn't work out as well, the program would end up in an infinite loop.
What I did was obviously not the right way of doing it, so what should I be doing instead?
My quick guess is, you haven't added Q_OBJECT macro to dragwidget.h header, the moc file for DragWidget class wasn't generated and the connect failed with "no such slot as draw()" error.
It might be also a good idea to add "CONFIG += console" to .pro file - you'll see all warning messages (like the one about connect error), so tracking such mistakes would be easier. You might also check return value of connect.
I noticed that you opened file this way:
QFile dictionaryFile(":/dictionary/words.txt");
Note that the file name starts with ":", and it means that the file will be read from your qrc resource package instead of your local disk. So if you made the change on words.txt, it will be read by code only when you compiled qrc file next time. So you must have understood how to fix it, right? Good Luck:)