Repaint QTreeWidget - c++

I have a simple class based on QTreeWidget. In some cases (when value one of columns updated), I need to repaint it. I have a function which invokes when I need to update my widget:
void TreeWidget::updated()
{
/* some actions with cells */
/* here need to repaint widget */
this->update();
/* also I'm tried this->repaint(); */
}
But line this->update(); (or this->repaint();) gave no results. Widget repaint only when I click on it.
So how can I repaint my widget?

The classes that inherit from QAbstractScrollArea as QTreeWidget have viewport() which is the widget that must be updated, so in your case the solution is:
viewport()->update();
If you want to call update from another thread you can use QMetaObject::invokeMethod():
QMetaObject::invokeMethod(viewport(), "update", Qt::QueuedConnection)

This is the solution:
viewport()->update();

I learned one interesting thing. As it turned out, you can update widgets in Qt only from the main thread. My function updated() was called by another thread, so this->update() did not work. However, all slots in Qt are executed just in the main thread, wherever they are called from. In this case, the correct solution would be to wrap this->update() inside the slot. Like this:
TreeWidget::TreeWidget()
{
/* ... */
connect(this, SIGNAL(signal_update()), this, SLOT(slot_update()));
/* ... */
}
void TreeWidget::updated()
{
/* some actions with cells */
emit signal_update();
}
void TreeWidget::slot_update()
{
this->update();
}
Yeah, it's a less beautiful solution than this->viewport()->update() but more correct.

Related

Qt show() executed after function

I would like to show a label and execute a function after displaying the label. Unfortunately, the label is always displayed after the function is executed.
void MainWindow::showLabel(){
myLabel->show();
doSomething();
}
void MainWindow::doSomething(){
QThread::msleep(3000);
myLabel->hide();
}
So, when i execute my code, the programm waits for three seconds and does show me an empty window afterwards (since it directly hides the label before even showing it; if I comment the hide function, the label is shown after waiting three seconds).
What I've tried to do is modifying the showEvent like this:
void MainWindow::showEvent(QShowEvent *event) {
QMainWindow::showEvent(event);
doSomething();
}
Am I doing something wrong by modifying the method or is there any other way to show the label before executing the followed function?
I would solve your problem in the following way:
void MainWindow::showLabel()
{
myLabel->show();
// Wait for 3sec. and hide the label.
QTimer::singleShot(3000, myLabel, SLOT(hide()));;
}
i.e. you don't need the second function and to block the current thread with QThread::msleep(), which is the reason why your label appears after the timeout is fired.
Update
If you need to do more than just hiding a label, define a slot and call it like:
void MainWindow::showLabel()
{
myLabel->show();
// Wait for 3sec. and call a slot.
QTimer::singleShot(3000, this, SLOT(doSomething()));
}
// This is a slot
void MainWindow::doSomething()
{
myLabel->hide();
[..]
// some more stuff
}
QThread::msleep(3000); is blocking the main thread where event loop is processed. So it prevent to show myLabel until sleep time is end. The solution is either to use QTimer as vahancho recomended or call event loop processing manualy by calling QEventLoop::exec() after myLabel->show();.

QToolbar force expand on too many QActions

Hi all is there any way to automatically expand a QToolbar if there is too many QActions in?
Using Qt version 5.4.1 C++11
Ive tried :ui->mainToolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred)
But this only expands it horizontally. I need it to expand vertically like the Expand button does.
Always expanding a toolbar vertically is not possible as far as I know (never seen it). A solution would be to add multiple toolbars. This way, you can arrange them one under the other.
What you can try is to add a custom widget to the toolbar that grows horizontally. This was proposed here by using a QScrollArea... Not sure whether this is exactly what you want, but it may work good enough.
This is how you can make a function to expand/retract a QToolbar. Firstly using a Forloop get all the child widgets from the QToolbar. You can use a Bool to lock to only get the first Widget which is the Expanding button/Action.
bool get_first_action{true};
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(get_first_action)
{
get_first_action = false;
// This is the expanding action!
m_action_expand = widget;
}
}
Or you can do this which is probably a bit safer.
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(widget->objectName() == "qt_toolbar_ext_button")
{
// This is the expanding action!
m_action_expand = widget;
}
}
Once you have the sneaky expanding action assign it to a member varible
// Make sure to initialize this in the constructor!
// m_action_expand = new QWidget(this // parent)
QWidget* m_action_expand;
Now create a handy function with a good name;
void MainWindow::forceToolbarExpand()
{
// Grab the position of the expanding action/widget
QPointF pos(m_action_expand->pos());
// Create a fake/dummy event that replicates the mouse button press
QMouseEvent event_press(QEvent::MouseButtonPress, pos, Qt::LeftButton,0, 0);
// Create a fake/dummy event that replicates the mouse button release
QMouseEvent event_release(QEvent::MouseButtonRelease, pos, Qt::LeftButton,0, 0);
// These two events together will fire the QAction::Toggled signal.
// Make sure to send the events!
QApplication::sendEvent(m_action_expand, &event_press);
QApplication::sendEvent(m_action_expand, &event_release);
}
And there we have it your QToolbar, if it can be expanded/retracted now will when you call this function. I'm not too sure if you can directly Moc/fake the toggled event but you can try it. I know this method works so yeah.

focusInEvent not called in QLineEdit subclass

I have a Qt/cpp code and display a subclassed QLineEdit. When double-clicking the QLineEdit, the focusInEvent is never called (launched in Maya).
void myQLineEditClass::focusInEvent(QFocusEvent *e)
{
MGlobal::displayInfo(MQtUtil::toMString(QString().sprintf("HERE")));
QLineEdit::focusInEvent(e);
}
HERE is never displayed, event if the focusInEvent is present in the .h protect part. Any idea how to get focusInEvents ?
Try the below. For several occasions that worked for me when focusInEvent did not.
void YourWidget::changeEvent(QEvent* event)
{
if (event->type() == QEvent::ActivationChange)
{
if (isActiveWindow())
{
// gaining the focus
}
else
{
// loosing the focus
}
}
// or whatever *parent* class call is
QWidget::changeEvent(event);
}
The event gets intercepted by the editor widget. See QItemDelegate::createEditor. The widget returned there will get it.
The issue was linked to the fact that the QLineEdit was in a QGraphicsView that was in another QGraphicsView. Bringing the QLineEdit to the higher-level QGraphicsView made it work.

QDialog exec() and getting result value

I have subclassed QDialog to implement functionality similar to QMessageBox ( I needed this to allow for customization). It has a text message and OK, Cancel buttons. I am showing the dialog using exec() to make it blocking. Now, how do I return values of true/false when the user clicks on OK/Cancel?
I tried connecting the buttons to setResult() and then, return the result value when clicked, but
Clicking the buttons does not close the dialog box
the return value is incorrect.
Following is the code I have written. I think I am wrong in the exec/result part - but I am not sure how to fix it.
class MyMessageBox : public QDialog {
Q_OBJECT
private slots:
void onOKButtonClicked() { this->setResult(QDialog::Accepted); }
void onCancelButtonClicked() { this->setResult(QDialog::Rejected); }
public:
MyMessageBox(QMessageBox::Icon icon, const QString& title,
const QString& text, bool showCancelButton = true,
QWidget* parent = 0);
virtual void resizeEvent(QResizeEvent* e);
QDialog::DialogCode showYourself()
{
this->setWindowModality(Qt::ApplicationModal);
this->exec();
return static_cast<QDialog::DialogCode>(this->result());
}
};
The user will instantiate the class and call showYourself() which is expected to return the value and also close(and delete) the dialog.
I have posted partial code. Let me know if you need more and I will post the complete version.
Some points :
Rather than using setResult() yourself, use QDialog::accept() and QDialog::reject().
It seems you are not taking full advantage of the signals and slots. You need the object which create the dialog (or another one) to listen to the signals of the dialog.
In your code you are not connecting signals to slots either.
With my fix onOKButtonClicked and onCancelButtonClicked are unnecessary.
With my fix you don't need showYourself(). Just call exec and with the events
information will flow.
You need to add this code before showing the dialog (this assume it is in a dialog method):
QObject::connect(acceptButton, SIGNAL(clicked()), this, SLOT(accept()));
QObject::connect(rejectButton, SIGNAL(clicked()), this, SLOT(reject()));
In the caller object you have
void someInitFunctionOrConstructor(){
QObject::connect(mydialog, SIGNAL(finished (int)), this, SLOT(dialogIsFinished(int)));
}
void dialogIsFinished(int){ //this is a slot
if(result == QDialog::Accepted){
//do something
return
}
//do another thing
}
Another solution:
// set signal and slot for "Buttons"
connect(YesButton, SIGNAL(clicked()), dlg, SLOT(accept()));
connect(NoButton, SIGNAL(clicked()), dlg, SLOT(reject()));
// show modal window event loop and wait for button clicks
int dialogCode = dlg->exec();
// act on dialog return code
if(dialogCode == QDialog::Accepted) { // YesButton clicked }
if(dialogCode == QDialog::Rejected) { // NoButton clicked }
Case 1 Clicking the buttons does not close the dialog box.
For this you have to close the dialog on respective SLOTS, so Use
void onOKButtonClicked(){ this->setResult(QDialog::Accepted); this->close();}
void onCancelButtonClicked(){ this->setResult(QDialog::Rejected);this->close();}
Note: Only after you have clicked the Ok button or Cancel button in a standard QMessageBox, setResult() function is triggered and the status is changed. It's not the same effect when done vice versa.
Case 2 The return value is incorrect.
I think only after your dialog gets closed, you will have the result available in result() function. So I guess it will be solved, after you have made the changes specified in Case 1.
If it still persists, use your own private member function to resolve it.

Update QProgressBar from within another class

I'm relatively new to Qt and GUI programming, and I'm kind of stumped.
I would like to update a QProgressBar's progress such that the progress reflects actions taken in a class. The class is doing a lot of operations and I would like to be able to update the QProgressBar with the progress of these operations:
class Worker
{
...
public:
void do_many_operations()
{
...
quint64 total_operations = ...;
for (...)
{
/* do some operations */
...
/* update the progress bar */
}
}
}
The part that I've got stuck at is how to, in as best an OO way as possible, update the QProgressBar.
One thought I had might be to make my Worker class a QObject and emit an update when necessary, or to pass the QProgressBar object to the Worker class's do_many_operations().
Any insight would be greatly appreciated.
EDIT: Updated to reflect that I meant QProgressBar not QStatusBar
Most widgets do not draw any changes until Qt can process events.
You should be able to call the regular QStatusBar slot methods and then call the application's processEvents method.
void do_many_operations()
{
/* Startup status bar */
myStatusBar.showMessage("Beginning many operations.");
QApplication::instance().processEvents();
quint64 total_operations = ...;
for (...)
{
/* do some operations */
/* update the status bar (probably want a more dynamic message... */
myStatusBar.showMessage("Doing work still");
QApplication::instance().processEvents();
}
/* clear the status bar, no need to processEvents. */
myStatusBar.clearMessage();
}
Simply call setValue(int) will do, or if you have some signals, connect them with setValue(int).
and of course, you have to setMaximum() before.