How do you access an objects properties? - qt

Sorry if this is a stupid question. I'm not being lazy. I am following along with C++ GUI Programming with Qt 4 and one of the examples in the book diverges from how Qt is working for me.
It has me make a dialog and then hook up some signals and slots. Well that does not work and so I tried some reality checks. I can't even edit the objects properties manually.
Here is main
#include <QApplication>
#include <QDialog>
#include <iostream>
#include "ui_gotocelldialog.h"
using namespace std;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Ui::GoToCellDialog ui;
QDialog *d = new QDialog;
ui.setupUi(d);
d->show();
return app.exec();
}
and here is my dialog .cpp file
#include "gotocelldialog.h"
#include "ui_gotocelldialog.h"
#include <iostream>
using namespace std;
GoToCellDialog::GoToCellDialog(QWidget *parent) :
QWidget(parent),
ui(new Ui::GoToCellDialog)
{
cout << "!!!!!!!!!!!!!!!!!!!!!"; // i never see this
ui->setupUi(this); // but if i comment out this it doesnt init
ui->okButton->setEnabled(true); // this does nothing
}
GoToCellDialog::~GoToCellDialog()
{
delete ui;
}
Here is the GoToCellDialog form in design mode
I am just trying to enable okButton, which is disabled by default. Also, if I cout << "abc"; it doesn't show up. Can you please shed some light on this?

There are several issues in your code.
You've created a GoToCellDialog class and implemented its constructor. But you don't create an object of this class. Of course, your constructor is not being executed. You need to replace QDialog to GoToCellDialog in your main function.
If you really need QDialog, not QWidget, you need to derive GoToCellDialog class from QDialog, not QWidget. There is an option for that while you create a designer form class in Qt Creator.
You create a Ui::GoToCellDialog in the main function, but another one is created in the GoToCellDialog class (note the ui private member). If you would instantiate your class, it would give you 2 instanses of Ui::GoToCellDialog which shouldn't be done. Remove Ui::GoToCellDialog from your main function.
I think you've confused GoToCellDialog class defined by you and Ui::GoToCellDialog class generated internally. Ui::GoToCellDialog ui is private member of GoToCellDialog, you don't need to instantiate it anywhere else.
See also: Calculator Form Example.

Related

Issue with QTableView and QStandardItemModel

I am facing strange thing with dynamically adding items to my QStandardItemModel and showing them with QTableView. If the time between events is long enough (like 50 ms) then the view is scrollable, but in case of faster event (10 ms) the view always scrolls down and doesn't allow me to do anything.
So here is the simple code snippet:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#include <QMainWindow>
#include <QTimer>
#include <QTableView>
#include <QStandardItemModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QTimer dataTimer;
QStandardItemModel *model;
QTableView *view;
public slots:
void insertData();
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
view = new QTableView(this);
this->setCentralWidget(view);
model = new QStandardItemModel(this);
model->setColumnCount(1);
view->setModel(model);
QObject::connect(&dataTimer, &QTimer::timeout, this, &MainWindow::insertData);
dataTimer.start(10); // 50 here gives correct behaviour while 10 does not
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::insertData()
{
QList<QStandardItem *> items;
items.append(new QStandardItem(QTime::currentTime().toString("hh:mm:ss.zzz")));
model->insertRow(0, items);
if (model->rowCount() > 20){
model->removeRow(model->rowCount()-1);
}
}
dataTimer.start(50) works fine, while dataTimer.start(10) brakes behavior. It is for my laptop and maybe for someone else the numbers will be different but I think logic is clear.
I was thinking that it is kind of too fast and some methods like beginInsertRows() doesn't work well. But that is for custom models usually and here it is just a standard one.
Can someone tell me why that happens?
Thank you
10 ms timer in a GUI thread to affect GUI elements is a bad idea itself. For example, in Windows 10 ms timeout is unattainable by standard means. Does your user really need to see updates of some table data more than 1 time per second? What does he really need? Based on this, you should determine an optimization method.
Then you could choose:
Decrease timeout of the timer and affect several rows on each event. Seems you have to decrease timeout of the GUI timer in anyway.
Reserve the required number of rows periodically in advance, for example by model->setRowCount(n);
Temporarily disable table refresh by setUpdatesEnabled() method:
setUpdatesEnabled(false);
bigVisualChanges();
setUpdatesEnabled(true);
Sublass one of the Qt model classes and reimplement insertRows() or something more.
Read
Fetch More Example.
Qt Model/View Programming.
HelloThere. Okay so when the timer is set to 10ms, it adds and removes a row with every screen refresh.
The Qtimer is linked to the screen refresh rate. Which means if the timer goes off in less than 16ms, it will go off on every screen refresh.
When you add an item to the list, Qt runs additional UI events on the list for layout and view bounds. Because you're adding and removing with every screen refresh your list stuck in a constant loop of running these events. Not to mention your list is essentially being replaced entirely every 20 frames.
I'm not sure why you need to add items to the list so often, but maybe you should batch the adds and removes into a list and then every 100ms you can do a bulk add and remove? That would solve the issue.

Correct way of promoting QTableWidget

I need to have a custom qtablewidget for which I've promoted built in QTableWidget as follows :
Just created a class called Inventory, then inherited it from QTableWidget, added a qtablewidget into mainwindow from the qt designer and promoted it to Inventory class
//inventory.h
#ifndef INVENTORY_H
#define INVENTORY_H
#include <QTableWidget>
class Inventory : public QTableWidget
{
public:
Inventory(QTableWidget* parent = 0);
};
#endif // INVENTORY_H
//inventory.cpp
#include "inventory.h"
Inventory::Inventory(QTableWidget *parent)
: QTableWidget(parent)
{
setRowCount(3);
setColumnCount(3);
horizontalHeader()->setDefaultSectionSize(160);
verticalHeader()->setDefaultSectionSize(160);
}
but for some reason it just won't build correctly, throwing this instead:
error: invalid conversion from ‘QWidget*’ to ‘QTableWidget*’ [-fpermissive]
tableWidget = new Inventory(centralWidget);
^
in a ui_mainwindow.h file at the line
where tableWidget is declared as Inventory* tableWidget
what is wrong ??
How to fix this ?
p.s.
building with qt 5.7.1
and qtcreator 4.2.0
I think you are confused between your derived class which base is QTableWidget and the constructor for your derived Inventory class.
Obviously for Qt and Qt Editor to work, you need to define a
Inventory::Inventory(QWidget* parent = 0)
constructor, taking a QWidget as a parent widget. ( parent here in the sense of container widget, often a layout)
Your constructor is taking a QTableWidget* which seems very fishy to me, and your compiler is telling you that a QWdget* is not a to QTableWidget*, which makes sense.
Change the signature of the Inventory constructor should make the job

How to receive event whenever a QWidget is added to QApplication's widget tree?

I want to inspect something application wide. Specifically I'd like to inspect every widget that is added to the application.
Similar thing can be done in JavaScript/HTML, where you can add DOM mutation listener which fires on DOM changes.
Can I listen on added widgets upon QApplication? Specifically, capture widgets added to QApplication object as children OR widgets added to any of the top level widgets or their children, subchildren, etc...
If not possible, what is best workaround available?
The most stable solution is likely to be to walk the object hierarchy every so often, starting with the QApplication as the root, and check each object with your predicate. This is going to be inefficient, which is why I asked for more information about what objects you actually want to query. On the other hand, for a test framework, you may not care so much about efficiency.
It might be possible to do something else, however. You can install event-filtering objects on any QObject, which define how they respond when they receive an event from Qt's event system. You could install an event filter on the root QApplication object (and recursively on any children created) that would check if the event is a QChildEvent and if added() returns true for it. If that's true, meaning a new child was added, you could then install this event filter onto that child as well. All other events would be passed on untouched. You can read more about installing event filters here. Essentially, a filter is a QObject subclass that defines the eventFilter() function, which returns true for events that should be filtered (stopped) and false otherwise. When this returns true, run your predicate on the newly-created object and install the same event filter on it.
One issue to be aware of with this is that the eventFilter() function only receives QObjects. You can learn if the object is a QWidget by calling isWidgetType(), but you don't know more than that. So as long as your predicate can make do with only methods and data defined for these general base classes, that should be OK.
As per #bnaecker's answer, here is some code:
AddChildEventFilter.h
#include <QObject>
class QEvent;
class AddChildEventFilter: public QObject {
Q_OBJECT
public:
AddChildEventFilter(QObject* parent=0);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};
AddChildEventFilter.cpp
#include "AddChildEventFilter.h"
#include <QEvent>
#include <QDebug>
#include <QWidget>
#include <QChildEvent>
AddChildEventFilter::AddChildEventFilter(QObject* parent):QObject(parent) {}
bool AddChildEventFilter::eventFilter(QObject* obj, QEvent* event) {
if(QWidget* widget = dynamic_cast<QWidget*>(obj)) {
if(QChildEvent* chevent = dynamic_cast<QChildEvent*>(event)) {
if(chevent->added()) {
QObject* child = chevent->child();
qDebug()<<"Child added: "<<child->metaObject()->className()<<"to"<<widget->metaObject()->className();
child->installEventFilter(new AddChildEventFilter(child));
}
}
}
return false;
}
Usage:
#include "AddChildEventFilter.h"
#include <QWidget>
#include <QApplication>
void PrintAllEvents()
{
for(QWidget* widget: QApplication::allWidgets()) {
widget->installEventFilter(new AddChildEventFilter(widget));
}
}

Select text of QLineEdit on focus

I have created a dialog using QtDesigner. There is a QLineEdit object in the dialog with some default content. When the dialog initializes and the focus goes to the QLineEdit, I want the default content to be auto selected, so once the user start writing, the previous content will be overwritten.
EDIT:
In constructor:
dialog->accept();
and
connect( dialog, SIGNAL(accepted()), QlineObj, SLOT( selectAll() ) );
This is an older question, but nevertheless, I ended up here searching for a solution this exact problem. It can be solved in the following way:
Create a class derived from QLineEdit and override the focusInEvent in the header:
void focusInEvent(QFocusEvent *event) override;
Then implement it like so:
void MyLineEdit::focusInEvent(QFocusEvent *event)
{
// First let the base class process the event
QLineEdit::focusInEvent(event);
// Then select the text by a single shot timer, so that everything will
// be processed before (calling selectAll() directly won't work)
QTimer::singleShot(0, this, &QLineEdit::selectAll);
}
Just in case anybody else wonders how this can be done ;-)
Call
lineEdit->selectAll();
after you set the default text. (In the dialog constructor, perhaps.)
There is a simpler method to get almost the same behaviour, which is to set the default content using setPlaceholderText() instead of setText(). This will show the default content grayed out and as soon as the QLineEdit gains focus, it will disappear.
You can use QApplication::focusChanged along with QTimer::singleShot to select all the text on a changed focus.
Normally your QApplication is declared in main, so use QObject::connect there, eg:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
My_Main_Window w();
QObject::connect(&a, SIGNAL(focusChanged(QWidget*, QWidget*)),
&w, SLOT(my_focus_changed_slot(QWidget*, QWidget*)));
w.show();
return a.exec();
}
Remember to make the public slot in the My_Main_Window class:
public slots:
void my_focus_changed_slot(QWidget*, QWidget*);
Then in your definition of my_focus_changed_slot, check if the second QWidget* (which points to the newly focused widget) is the same as the QLineEdit you wish to select all of, and do so using QTimer::singleShot so that the event loop gets called, then the QLineEdit has the selectAll slot called immediately after, eg
void My_Main_Window::focus_changed(QWidget*, QWidget* new_widget) {
if (new_widget == my_lineedit) {
QTimer::singleShot(0, my_lineedit, &QLineEdit::selectAll);
}
}
where my_lineedit is a pointer to a QLineEdit and is part of the My_Main_Window class.

Please let me know its an functionality behaviour or not

I have a listview filled with items. By default, the 0th item will be selected.
If I try to navigate the list using the mobile keypad, it's not gaining focus - instead I need to use my mobile select key for focus. In this process my mobile left soft key gets changed to “Done”. Why is the "Done" menu appearing?
How do I provide default focus to the listview? And how do I avoid the display of “Done” at left soft key?
Here is my code:
#include "Test_Doco.h"
#include <QtGui>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListView *listui = new QListView();
listui->setSelectionMode(QAbstractItemView::SingleSelection);
listui->viewport()->setFocusPolicy(Qt::WheelFocus);
listui->viewport()->setFocus();
QStandardItemModel* listModel = new QStandardItemModel();
for(int i =0; i<10;i++)
{
QStandardItem *item1 = new QStandardItem("AOL");
listModel->appendRow(item1);
}
QModelIndex index = listModel->index(0,0);
listui->setCurrentIndex(index);
listui->setModel(listModel);
listui->showMaximized();
return a.exec();
}
Edit: I have updated the code. Please check it.
For the default focus, stop calling listui->viewport()->setFocus(); and call listui->setFocus() to give it focus when it is created.
As for the display of "Done", I'm not too sure, but you might need to post more code to show the dialog you are creating. Most have a set of default buttons or a button set to default. The "Done" key might be related to that. As seen here "Exit" is the softkey shown.
The issue is w.r.t Qt 4.6.2 and the issue is fixed in Qt 4.6.3

Resources