I created a custom widget composed of QPushButtons placed on a broken layout since I need to place them on specific places with no geometric pattern. I've previously used QAbstractTableModel for QTableView with success and I would like to have the same ability of letting a model be in charge of the front-end updating. The buttons will mostly provide visual feedback changing text and color based on backend data changes. I included a link (end of post) that has something of use for me but I'm unable to see how I would achieve what you get by, for example, reemplementing Model::data() when role is Qt::DisplayRole. How would QModelIndex apply in something like this?
The first thing that comes to mind with a rough pseudocode:
class MyModel : QAbstractItemModel
{
QMap<int, BtnData> m_btnDataMap;
QVariant data(QModelIndex mIdx, int role) {
if (role == DisplayRole) {
return m_btnDataMap[mIdx.row()]; // returns a BtnData item
}
}
void updateButtonData(int btnKey, BtnData d) {
m_btnDataMap[btnKey] = d;
Q_EMIT dataChanged(btnKey);
}
};
class RandomlyPlacedButtonsWidget : public QWidget
{
QMap<int, QPushButton*> m_btnMap;
MyModel m_myModel;
RandomlyPlacedButtonsWidget() {
connect(m_myModel, dataChanged(int btnKey), this, updateButtonVisual(int btnKey));
}
void updateButtonVisual(int btnKey) {
BtnData data = m_myModel->data(index, DisplayRole);
m_btnMap[index]->text = data.text;
m_btnMap[index]->color = data.color;
}
};
Is this a proper way of using the dataChanged() signal on the widget side? Would something like this make sense to do? It would be ignoring columns, would that conflict with it? Would QAbstractListModel be better because of that?
I don't need specific code, I'm more in need of a way to properly approach this while keeping things clean and easy to maintain.
Using Qt Model/View with non-table like data and non-table/list UI?
I have a QLineEdit in my Qt application which needs to take a hexadecimal value as an input. I want the application to display a warning Message box if the character entered is not hexadecimal and delete the invalid character from the QLineEdit. This check has to be performed at the entry of each character in the QLineEdit. So far I have got this:
void MemoryTestWindow::on_lineEditAddress1_textChanged(const QString &arg1)
{
QString text1=ui->lineEditAddress1->text();
int len=text1.length();
QChar ch=text1.at(len-1).toUpper();
if(!((ch>='A'&& ch<='F')|| (ch>='0' && ch<='9')))
{
text1.remove(len-1,1);
//*arg1="";
QMessageBox::warning(this,"Invalid Character!","Please enter hexadecimal characters 0-9, A-F");
}
}
I am using the textChanged slot of the QLineEdit since I need to perform the check after each character entry. I can't figure how to change the value in QLineEdit i.e. the last invalid character should be deleted after clicking 'OK' in the warning Message box. How can I do this?
Don't reinvent the wheel ;) You will probably have better results using the Qt tools designed to achieve what you want.
QLineEdit::mask let you define what characters are allowed in your edit control. In particular, defining a mask using H chars will let you determine how many hexadecimal characters are allowed in the control.
If this is not enough to what you want to do, looks around validators for more complex and complete validation process.
The standard way to do this is to use a QValidator
QRegExpValidator *v = new QRegExpValidator("[a-fA-F0-9]*", this); // or QRegularExpressionValidator
ui->lineEdit->setValidator(v);
As QValidator inherits QObject you can make it emit a signal to detect when the user type an invalid character:
class Validator : public QRegExpValidator
{
Q_OBJECT
public:
Validator(const QRegExp &rx, QObject *parent = nullptr) :
QRegExpValidator(rx, parent)
{}
State validate(QString &input, int &pos) const override
{
State state = QRegExpValidator::validate(input, pos);
if (state == Invalid)
emit error();
return state;
}
signals:
void error();
}
And then you just have to connect to error() to show the error to the user:
Validator *v = new Validator ("[a-fA-F0-9]*", this);
ui->lineEdit->setValidator(v);
connect(v, &Validator::error, this, &MyClass::handleWrongInput);
// Where handleWrongInput is the slot that handles the error and eventually show an error message
Addendum
If in MyClass::handleWrongInput(), you do something that causes the QLineEdit to recall QValidator::validate(), like changing the keyboard focus, you will create a loop as the first call to validate() still has not returned. To fix this, you should let the call to validate() return before calling MyClass::handleWrongInput().
To do so the easiest solution is to use queued connection:
connect(v, &Validator::error, this, &MyClass::handleWrongInput, Qt::QueuedConnection);
What about using ui->lineEditAddress1->setText(text1.remove(len-1, 1))? Also, not sure if that has any chance to work, but maybe backspace() method will work just fine?
http://doc.qt.io/qt-4.8/qlineedit.html#backspace
I need to know when the verticalScrollBar of my QTableWidget is being shown.
I am currently using the following instruction:
Header:
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QDebug>
#include <QWidget>
#include <QScrollBar>
namespace Ui {
class MyClass;
}
class MyClass: public QWidget
{
Q_OBJECT
public:
explicit MyClass(QWidget *parent = 0);
~MyClass();
private:
void populateTable(QVector<QString> content);
private:
Ui::MyClass *ui;
};
#endif // MYCLASS_H
Populate table function:
void MyClass::populateTable(QVector<QString> content)
{
while( ui->myTableWidget->rowCount() > 0 )
{
ui->myTableWidget->removeRow(0);
}
QTableWidgetItem* item;
for (int row = 0; row < content.length(); ++row)
{
ui->myTableWidget->insertRow(row);
item = new QTableWidgetItem( QString::number(row) );
item->setTextAlignment(Qt::AlignCenter);
ui->myTableWidget->setItem(row, 0, item);
item = new QTableWidgetItem( content.at(row) );
item->setTextAlignment(Qt::AlignCenter);
ui->myTableWidget->setItem(row, 1, item);
}
qDebug() << "This : " << this->isVisible();
qDebug() << "QTableWidget : " << ui->myTableWidget->isVisible();
qDebug() << "VerticalScrollBar : " << ui->myTableWidget->verticalScrollBar()->isVisible(); // <-HERE
}
Output:
// Called from the constructor
This : false
QTableWidget : false
VerticalScrollBar : false
// Called by a button pressed
This : true
QTableWidget : true
VerticalScrollBar : true
This : true
QTableWidget : true
VerticalScrollBar : false
But it returns a wrong value. When the ScrollBar is visible it returns false and when it is not visible it returns true. Note: myTableWidget (QTableWidget) is always visible.
Is there any other way that I can do this?
I'm using Qt version 5.3.2
In general the code you are using is supposed to work - checked on Qt 5.3.0.
However, you must be sure that when you are making the call the QTableWidget itself is visible.
For example if you make the call inside the MainWindow constructor you will certainly get a false answer. Only after the form is shown the call to isVisible() on particular scrollbar would return the correct value.
EDIT:
With your code pasted I was able to reproduce the issue. I needed to go through the Qt code a bit to see whats going on. Basically it turns out that for QTableView which is parent class of QTableWidget scroll bar values are updated via updateGeometries (do not confuse it with the regular updateGeometry the one I'm mentioning is protected). Internally this method is called either directly or the event is processed through the event loop. In short, it depends on whether you add columns or rows.
In your example, if you insertColumn instead of insertRow (and switch the arguments in setItem) after checking the visibility of horizontalScrollBar you will get the proper result right away.
I could confirm this by subclassing the QTableWidget and overriding event method. It shows that when adding columns following events are executed: MetaCall (invoke call) and LayoutRequest. On the other hand, when adding rows first event passed is Timer.
I'm not Qt implementer so I'm not sure what is the purpose the difference. However, this info helps solving your problem in a more elegant way.
You can implement MyTableWidget which overrides the event method.
class MyTableWidget: public QTableWidget
{
Q_OBJECT
public:
bool event(QEvent *e) override
{
const bool result = QTableWidget::event(e);
if (e->type() == QEvent::LayoutRequest)
{
// call what you need here
// or emit layoutUpdated(); and connect some slots which perform
// processing dependent on scrollbar visibility
}
return result;
}
signals:
void layoutUpdated();
}
However, such event might get called in other situations not only when the view needs to be updated due to model data updates.
Another solution would be to avoid overriding the event method but creating your own method to trigger the required updates. For example:
void MyTableWidget::updateLayout()
{
QEvent ev{QEvent::LayoutRequest};
QTableWidget::updateGeometries();
QTableWidget::event(&ev);
}
This would call directly updateGeometries which recalculates scrollbar min/max values and perform a direct event method call for LayoutRequest (without processing through eventloop). Which if I'm correct indirectly updates scrollbar visibility.
Calling this method before checking the visibility should also fix your problem.
ui->myTableWidget->updateLayout();
qDebug() << "VerticalScrollBar : " << ui->myTableWidget->verticalScrollBar()->isVisible();
// prints "VerticalScrollBar : true false"
I wanted to get notified via a signal, when a QScrollBar gets visible. I'm writing this answer here, since this is the first google result I got and no result had a proper answer.
First of all, there is no signal for changed visibility, so we will use valueChanged.
Now you can add a function that checks if the scrollbar isVisible() or not.
Problem: It won't work (and someone with more knowledge in Qt could probably explain why, I sadly cannot).
The trick is, to use a QTimer (Python code):
self.horizontalScrollBar().valueChanged.connect(lambda x: QTimer.singleShot(0, some_func))
I need to simplify validations on qlineedit, which invokes a function that returns a capital letter when I'm typing. I have this:
void dg_cliente::on_lineEdit_4_textChanged(const QString &arg1)
{
Cls_Validaciones *Valido = new Cls_Validaciones;
ui->lineEdit_4->setText(Valido->Validar_Mayuscula(arg1));
}
The code is the very similar for lineEdit5, lineEdit6, lineEdit7, etc., which is redundant. Is there a better way to do this that removes the redundancy?
I interpret your question as:
How can I recognize which widget is an actual signal sender?
By calling sender() function in your slot.
void DlgClient::onLineEditTextChanged(const QString &arg1)
{
QLineEdit* pLineEditSender = qobject_cast<QLineEdit*>( sender() );
if (pLineEditSender) // also verify that is required type of sender
{
// FYI: setText also signals textChanged
// make sure the code is not looping here
// so bool m_forcedSetText initially set false
if ( ! m_forcedSetText)
{
m_forcedSetText = true;
pLineEditSender->setText( myTransform(arg1) );
}
else
m_forcedSetText = false;
}
}
P.S. Maybe the other type of solution as suggested in comments is better? But the answer is explicit to what you ask. The info on sender() does warn that the OOP principle of modularity is violated etc. while in certain cases the function is still useful.
I basically have multiple events signals which I want to connect to the same slot. What I want to know is how can I pass string based parameters to that same slot so that the slot knows which is this signal coming from. One alternative is to make as many slots as there are signals and then connect them in a 1:1 manner, but this is efficient, considering that the code for all the processing is very similar. I tried doing this but I'm getting some errors:
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button1")));
connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button2")));
The error is related to the parameters I'm passing in the last 2 commands .. And backgroundTypeChoiceMade is declared like this:
void backgroundTypeChoiceMade(QString);
Can someone tell me what the error is in the above code ?
You can use QSignalMapper. Although the QSignalMapper is the answer to your question, I think jon hanson's answer is the way you should take. You get much more cleaner code that way.
Four methods. One doesn't suck.
QSignalMapper. Works, but makes for messy code.
Named slots. Messy for any significant number of senders, and doesn't work for dynamically-generated senders (e.g., buttons in a list).
sender()-compare. Can handle dynamic senders, but is still kinda ugly.
Subclass the sender. Doesn't suck. Gives you what you really wanted all along: parameterized signals.
Especially when you're using a small number of signals and sender types and when the senders are dynamically generated, subclassing the sender is the cleanest way. This lets you overload the existing signals to contain whatever parameters you need.
And now, wiring up the signals and slots just works:
Keypad::Keypad(QWidget *parent) : QWidget(parent)
{
for (int i = 0; i < 10; ++i)
{
// KeypadButton keeps track of the identifier you give it
buttons[i] = new KeypadButton(i, this);
// And passes it as a signal parameter. Booyah.
connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int)));
}
createLayout();
}
void Keypad::digitClicked(int digit)
{
// The slot can find the clicked button with ease:
dial(button[i]); // or whatever
//...
}
and the extra code is out-of-sight in a subclass you'll never have to touch again.
See http://doc.qt.digia.com/qq/qq10-signalmapper.html#thesubclassapproach for an example implementation of subclassing QPushButton to emit clicked(int) signals. Also discusses all four methods - named slots ("the trivial solution"), sender(), subclassing, and signal mapper.
Caveat: Obviously works best for small numbers of sender types. But that's usually the case. And in that case, it's worth it.
What is inefficient about using separate slots? If there's commonality in the slot handlers then move that into a function, e.g. extending ereOn's example:
void YourClass::YourClass() :
m_button1(new QPushButton()),
m_button2(new QPushButton())
{
connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1()));
connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2()));
}
void YourClass::common(int n)
{
}
void YourClass::yourSlot1()
{
common (1);
}
void YourClass::yourSlot2()
{
common (2);
}
You can't pass constants to connect() because the effective parameters are deduced at execution time, not compile time.
However, while this is against the OO principle, you can use QObject::sender() which gives a pointer to the emitter QObject.
Example below:
void YourClass::YourClass() :
m_button1(new QPushButton()),
m_button2(new QPushButton())
{
connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot()));
connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot()));
}
void YourClass::yourSlot()
{
if ((QPushButton* button = dynamic_cast<QPushButton*>(sender()))
{
// Now button points to a QPushButton* that you can compare with the pointers you already have
if (button == m_button1)
{
// Whatever
} else
if (button == m_button2)
{
// Whatever
}
}
}
If you have many buttons, you may also use a QSignalMapper by providing an identifier for each button.
You can now really bind a value when connecting. Qt5 added support for that.
Example:
connect(sender, &Sender::valueChanged,
tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));
See more info.
NB: you can of course use std::bind or boost::bind instead of tr1::bind.
If you really don't want to use QSignalMapper, you could do something like this:
class SignalForwarderWithString: public QObject
{
Q_OBJECT
public:
SignalForwarderWithString(QString data = "", QObject *parent = 0) : QObject(parent), _data(data) {}
QString _data;
signals:
void forward(QString);
public slots:
void receive() { emit forward(_data); }
};
...
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
SignalForwarderWithString *sfws;
sfws = new SignalForwarderWithString("button1", this);
connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));
sfws = new SignalForwarderWithString("button2", this);
connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));
but QSignalMapper is just as easy...
QSignalMapper *mapper = new QSignalMapper(this);
connect(button1, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button1, "button 1");
connect(button2, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button2, "button 2");
// you might have to tweak the argument type for your slot...
connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString)));