Who owns the items in a Qt collection such as QList or QContiguousCache?
Also, Qt collections seem to take a reference instead of a pointer, does it internally clone the object using copy constructor or do I need to make sure the object is not destroyed?
I've been getting weird behavior with QContiguousCache and I can't seem to find the ownership information anywhere. If it helps, I'm trying to put some QString into the QContiguousCache from within one function and access them in another. I'm constantly getting null pointers in this case.
Qt containers are comparable to std containers – they just store values (which might be QObject* or QWidget*) but they don't take ownership in the sense of how Qt manages ownership (e.g. for child widgets of widgets).
That said, I had a look into the doc. of QList to understrike this:
A common requirement is to remove an item from a list and do something with it. For this, QList provides takeAt(), takeFirst(), and takeLast(). Here's a loop that removes the items from a list one at a time and calls delete on them:
QList<QWidget *> list;
...
while (!list.isEmpty())
delete list.takeFirst();
Though, mentioning takeAt() made me uncertain for a second. takeAt() sounds like “stealing ownership”. Could I be wrong?
Hence, I made a small sample to illustrate the issue:
For this, I made a thin-wrapper Label for QLabel to report construction and destruction.
testQListOwnership.cc:
// Qt header:
#include <QtWidgets>
struct Label: public QLabel {
Label(const QString &text): QLabel(text)
{
qDebug() << "Label::Label(" << text << ")";
}
virtual ~Label()
{
qDebug() << "Label::~Label(" << text() << ")";
}
Label(const Label&) = delete;
Label& operator=(const Label&) = delete;
};
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Test QList Ownership");
QVBoxLayout qVBox;
QList<QLabel*> pQLbls;
for (int i = 1; i <= 3; ++i) {
QLabel *pQLbl = new Label(QString("Label ") + QString().number(i));
qDebug() << "pQLbls.append(pQLbl);";
pQLbls.append(pQLbl);
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
qDebug() << "qVBox.addWidget(pQLbl);";
qVBox.addWidget(pQLbl);
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
}
qDebug() << "&qWinMain:" << &qWinMain;
qDebug() << "qWinMain.setLayout(&qVBox);";
qWinMain.setLayout(&qVBox);
for (QLabel *pQLbl : pQLbls) {
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
}
QPushButton qBtnRemoveLabels("Remove Labels");
qVBox.addWidget(&qBtnRemoveLabels);
qWinMain.show();
// install signal handlers
QObject::connect(&qBtnRemoveLabels, &QPushButton::clicked,
[&]() {
for (QLabel *pQLbl : pQLbls) {
qDebug() << "pQLbl->setParent(nullptr);";
pQLbl->setParent(nullptr);
qDebug() << "Label(" << pQLbl->text() << ").parent():" << pQLbl->parent();
}
qDebug() << "pQLbls.clear();";
pQLbls.clear();
});
// runtime loop
return app.exec();
}
and the Qt project to build testQListOwnership.pro:
SOURCES = testQListOwnership.cc
QT += widgets
Output:
Qt Version: 5.13.0
Label::Label( "Label 1" )
pQLbls.append(pQLbl);
Label( "Label 1" ).parent(): QObject(0x0)
qVBox.addWidget(pQLbl);
Label( "Label 1" ).parent(): QObject(0x0)
Label::Label( "Label 2" )
pQLbls.append(pQLbl);
Label( "Label 2" ).parent(): QObject(0x0)
qVBox.addWidget(pQLbl);
Label( "Label 2" ).parent(): QObject(0x0)
Label::Label( "Label 3" )
pQLbls.append(pQLbl);
Label( "Label 3" ).parent(): QObject(0x0)
qVBox.addWidget(pQLbl);
Label( "Label 3" ).parent(): QObject(0x0)
&qWinMain: QWidget(0x2332b5f818)
qWinMain.setLayout(&qVBox);
Label( "Label 1" ).parent(): QWidget(0x2332b5f818)
Label( "Label 2" ).parent(): QWidget(0x2332b5f818)
Label( "Label 3" ).parent(): QWidget(0x2332b5f818)
QWindowsWindow::setGeometry: Unable to set geometry 102x102+960+460 on QWidgetWindow/'QWidgetClassWindow'. Resulting geometry: 120x102+960+460 (frame: 8, 31, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 102x102, maximum size: 16777215x16777215).
The QList<QLabel*> pQLbls doesn't take ownership.
Even the QVBoxLayout qVBox doesn't take ownership.
Ownership is taken by QWidget qWinMain after the qVBox is set as layout of it.
Output after clicking on ×:
Label::~Label( "Label 1" )
Label::~Label( "Label 2" )
Label::~Label( "Label 3" )
The QWidget qWinMain ensured that the Label instances are deleted when it is destroyed itself.
Now, what happens when the Labels are “stolen” from qWinMain:
Output after clicking on Remove Labels:
pQLbl->setParent(nullptr);
Label( "Label 1" ).parent(): QObject(0x0)
pQLbl->setParent(nullptr);
Label( "Label 2" ).parent(): QObject(0x0)
pQLbl->setParent(nullptr);
Label( "Label 3" ).parent(): QObject(0x0)
pQLbls.clear();
Oops! By clicking on Remove Labels, I produced some memory leaks.
Output after clicking on ×:
… in words: nothing.
Memory of leaked Label instances is surely freed by OS (like anything else) but no proper destruction happens anymore.
Further reading (found when searching the Web):
Understand the Qt containers
Related
QSharedMemory is changing sizes on me.
create:
...
int size = buffer->size();
qDebug() << "buffer->size()" << size << "points" << points->size() << "share name" << sharedMemoryName;
if (!m_sharedMemory->create(size)) {
qCritical() << tr("Unable to create shared memory segment.");
return;
}
m_sharedMemory->lock();
char *to = (char*)m_sharedMemory->data();
memcpy(to, buffer->data(), qMin(m_sharedMemory->size(), size));
m_sharedMemory->unlock();
read:
QSharedMemory sharedMemory(sharedMemoryName);
if (!sharedMemory.attach(QSharedMemory::AccessMode::ReadOnly)) {
qCritical() << "Unable to attach to shared memory segment.";
return nullptr;
}
qDebug() << sharedMemoryName << sharedMemory.size();
Not the same size.
when create the size is 658824
when read the size is 659456
ok - sounds crazy now, but I run the read multiple times and all the sudden the size was correct. Then I restarted everything (same size on create) and the error came back.
edit:
I just realized that the size of the QSharedMemory is not necessarily then same as the QBuffer
memcpy(to, buffer->data(), qMin(m_sharedMemory->size(), size));
why is that and how can I know "on the other side" the correct size (without making an ugly workaround)
edit 2:
I may found it. looks like QSharedMemory reserves the memory in 4096 blocks.
solution for me is to check the QByteArray for empty
if(in.at(i) == QChar(0))
break;
I have rich text items implemented using QGraphicsTextItem
To set font size, for example:
void set (int fontSize) {
QTextCursor _cursor = textCursor();
QTextCharFormat _format;
_format.setFontPointSize(fontSize);
_cursor.mergeCharFormat(_format);
setTextCursor(_cursor); }
A lot more complicated is to read the font size.
Assuming I have a selection, I must iterate through the document, through all QTextBlock, QTextFragment, reading the QTextCharFormat ...
But the simple option, if there is no selection, just reading the font size at cursor:
int get () {
return textCursor().charFormat().fontPointSize(); }
This works, but I found 3 issues:
1) Setting font size by QGraphicsTextItem properties:
QFont f = font();
f.setPointSize(20);
setFont(f);
this returns 0 by my get function above. To set the font size for the entire item, I have to use the same method as in the set function.
Shouldn't the setFont method set a font that can be read from the QTextCursor ?
2) setHtml can set formatting - but I don't see any way to read that formatting
How can I read the rich text formatting from an html fragment ? Is the only posiblity, parsing the html ?
3) (my current stumbling block)
Copy formatted text from an outside source and paste in the QGraphicsTextItem seems to maintain the formatting of the source - but how can I read that formatting ?
The get method above reads font size 0 if the text was pasted from outside.
font().pointSize() always returns 8. (I have not set it so I imagine that is a default)
Is there another method to read the text format ?
is the clipboard text formatted using html ?
How can I find the font size (or any other formatting) from the pasted text ?
(The same questions apply to block formatting, like alignment).
I think most of your problems could be solved by getting the QTextDocument for your QGraphicsTextItem object and work with it. QTextDocument and its methods (like QTextFormat::property(int propertyId)) can help you to get a lot of properties for your text.
1) If you set the size using the QFont object, you should get the size using the same way.
2) When you set the text using html, QGraphicsTextItem::font() is not useful so you need to get the QTextDocument and use their functions instead.
3) Same as 2. I think... because I don't have your code to test it :)
Well, here you have a code as an example. I hope this answer helps you.
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsTextItem>
#include <QTextCursor>
#include <QTextCharFormat>
#include <QFont>
#include <QDebug>
#include <QTextDocument>
#include <QTextBlock>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
QGraphicsView view(&scene);
/* ITEM 1 */
QGraphicsTextItem* item_1 = new QGraphicsTextItem("QGraphicsTextItem 1");
item_1->setTextInteractionFlags(Qt::TextEditorInteraction);
QFont f = item_1->font();
f.setPointSize(30);
item_1->setFont(f);
qDebug() << "textCursor().position() (returns 0): " <<
item_1->textCursor().position();
qDebug() << "textCursor().charFormat().fontPointSize() (returns 0): " <<
item_1->textCursor().charFormat().fontPointSize();
qDebug() << "font().pointSize() (returns 30 - OK!): " <<
item_1->font().pointSize();
QTextDocument* doc = item_1->document();
f = doc->defaultFont();
qDebug() << "pointSize (returns 30 - OK!): " << f.pointSize();
scene.addItem(item_1);
/* ITEM 2 */
QGraphicsTextItem* item_2 = new QGraphicsTextItem();
item_2->setPos(0, 50);
item_2->setHtml("<html><head/><body><p>"
"<span style=\"font-size:14pt; font-weight:600;\">QGraphics</span>"
"<span style=\"font-size:24pt; font-weight:600;\">TextItem 2</span>"
"</p></body></html>");
qDebug() << "font().pointSize() (returns 8, the default value): "
<< item_2->font().pointSize();
doc = item_2->document();
f = doc->defaultFont();
qDebug() << "pointSize (returns 8, the default value): " << f.pointSize();
QVector<QTextFormat> formats = doc->allFormats();
QVectorIterator<QTextFormat> i(formats);
while (i.hasNext()) {
QTextFormat format = i.next();
if (format.property(QTextFormat::FontPointSize).isValid())
qDebug() << "format.property (returns 14 or 24): " <<
format.property(QTextFormat::FontPointSize).toInt();
}
/*
* Get the block of text. In this example, we only have one block, but
* two text fragments (see below)
*/
QTextBlock text_block = item_2->document()->findBlock(1);
QTextBlock::iterator it;
for (it = text_block.begin(); !(it.atEnd()); ++it) {
QTextFragment currentFragment = it.fragment();
if (currentFragment.isValid())
qDebug() << "currentFragment.text(): " << currentFragment.text();
qDebug() << "currentFragment.charFormat().font().pointSize() "
"(returns 14 or 24, depending on"
"the current text fragment): " <<
currentFragment.charFormat().font().pointSize();
}
scene.addItem(item_2);
view.setFixedSize(640, 480);
view.show();
return a.exec();
}
I am trying to drop some mime encoded text onto a tree view. It is working - the dropMimeData() method is called, I can decode the mime data into the strings that were dropped, I can insert a child into the model which shows up in the view, but ... I can't find a way to set the text value of the new item/row to the string dragged and dropped (or any string for that matter).
Here is some of the code i've tried inside the dropMimeData() method:
if ( ( row == -1) && (column == -1) && parent.isValid() ) {
int mdlidx = this->data(parent, Qt::DisplayRole).ModelIndex;
qDebug() << "mdlidx: " << mdlidx;
// treet text - the text of the cell that gets dropped onto
QString tt = this->data(parent, Qt::DisplayRole).toString();
qDebug() << "tree text: " << tt;
TreeItem *item = this->getItem(parent);
int ccnt = item->childCount();
qDebug() << "ccnt: " << ccnt ;
if ( item->insertChildren(0, 1, 0) ) {
qDebug() << "Child Inserted";
// how do I access the new child item here ???
} else {
qDebug() << "Failed";
}
How do I access the new child item in order to set the text that would be visible in the view?
I'm using the QStandardItemModel, if that makes any difference.
My solution to this is to create a signal and slot - I emot the signal in the dropMimeData() method and the slot is in a part of the code that has the view and model, so can easily update the model.
I send the mime data and the parent across using the signal.
I'm not sure if this is the correct way of doing this, but it is working.
Mouse button clicki am trying to create an automatic mouse click event at a particular co ordinate.
This source code moves the mouse pointer to the co ordinate region but it is not clicking.
please help me to solve this problem or suggest any new idea to automate mouse click event.
Note: i am using QT 3.0.3
void mMouseClickFunction()
{
QWidget *d = QApplication::desktop()->screen();
int w=d->width(); // returns desktop width
int h=d->height();
printf("w=%d\nh=%d\n",w,h);
int x,y;
printf("Enter the points...\n");
scanf("%d%d",&x,&y);
QApplication::desktop()->cursor().setPos(x,y);
QPoint pt(x,y);
std::cout << pt.x() << " " << pt.y() << std::endl;
QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, pt,Qt::LeftButton, 0);
QApplication::sendEvent(d, e);
std::cout << "in contentsMousePressEvent"<< e->x() << " " << e->y() << std::endl;
QMouseEvent *p = new QMouseEvent(QEvent::MouseButtonRelease, pt,Qt::LeftButton, 0);
QApplication::sendEvent(d, p);
std::cout << "in contentsMouseReleaseEvent"<< p->x() << " " << p->y() << std::endl;
}
QApplication::sendEvent sends an event internal to the QApplication, not a system wide event. You probably need to be system specific to send out an event like that. Here is the function call for windows:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
But even with that kind of call, you will be limited to certain windows, unless you have UIAccess to be true, and your program is a signed application located in the right part of the harddrive.
EDIT: Here is a page that has some examples for sending input in Linux:
http://www.linuxquestions.org/questions/programming-9/simulating-a-mouse-click-594576/
Hope that helps.
Is there any way to check properties and constraints for each column in a QSqlRelationalTableModel? For example, I'd like to ask my QSqlRelationalTableModel object whether a certain column can contain nulls, or what datatype is valid for that column.
You'll need to get a QSqlField value for each column of the model, which is given by
QSqlRecord record = model->database().record(model->tableName());
QSqlField field = record.field(columnIndex);
then you'll be able to check if a field can be null with QSqlField::requiredStatus() (if the driver supports querying that property) and to get its data type with QSqlField::type().
From alexisdm's answer above, I wrote this simple code snippet to output the properties of each field in a table. Posting it here to save typing for anyone else who is interested.
I also discovered a gotcha: if you use table_model::record() or table_model::record(int) you get unexpected (to me) results for some properties, e.g., isAutoValue seems to always return false, even for fields designated as autoincrement fields in the database. However, you do get a real value for typeID() (though I haven't been able to determine what typeID() is), whereas typeID() always returned -1 for me using model->database().record(model->tableName()).
QSqlRecord record = table_model->database().record(table_model->tableName());
// the following get isAutoValue() wrong; but have a real typeID()
//QSqlRecord record = table_model->record();
//QSqlRecord record = table_model->record(table_model->rowCount() - 1);
qDebug() << "********** table" << table_model->tableName() << "*********";
for (int i = 0; i < table_model->columnCount(); ++i) {
QSqlField field = record.field(i);
qDebug() << "---------- field" << i << field.name() << "--------";
qDebug() << "default value" << field.defaultValue();
qDebug() << "is auto value" << field.isAutoValue();
qDebug() << "is generated" << field.isGenerated();
qDebug() << "is null" << field.isNull();
qDebug() << "is read only" << field.isReadOnly();
qDebug() << "is valid" << field.isValid();
qDebug() << "length" << field.length();
qDebug() << "precision" << field.precision();
qDebug() << "required status" << field.requiredStatus();
qDebug() << "type" << field.type();
qDebug() << "type id" << field.typeID();
qDebug() << "value" << field.value();
}