I have a signal that triggers the following slot on QT:
void MainWindow::stream_frame_changed(bool change, const QImage& image)
{
if (stream == true && change == true)
{
QPixmap aux = QPixmap::fromImage(image);
item->setPixmap(aux);
ui->graphicsView->scene()->addItem(item);
}
}
The signal is emmited every 100 milliseconds. I am noticing that there is a memory leak, and it makes sense, because I am adding many items to the scene without ever deleting them.
I do not know how to delete the previous items. If i execute the clear() function, the window closes, so it is not an option apparently.
Instead of creating new items you must reuse the existing item:
*.cpp
on constructor:
ui->graphicsView->scene()->addItem(item);
and
void MainWindow::stream_frame_changed(bool change, const QImage& image)
{
if (stream == true && change == true)
{
QPixmap aux = QPixmap::fromImage(image);
item->setPixmap(aux);
}
}
Related
I have a button in my Custom QDialog, I am emitting a signal when pushbutton 1 is clicked
void MyCustomDialog::on_pushButton_1()
{
this->hide(); //i need to hide this window before 'OnButton_1_Clicked' stuffs starts
emit button_1_clicked();
}
In my main window I have connected the slot and created the instance as shown below
void MainWindow::MainWindow()
{
MyCustomDialog *dlg = MyCustomDialog::getInstance(this); //only single instance created
connect(dlg, &MyCustomDialog::button_1_clicked, this, &MainWindow::OnButton_1_Clicked);
}
I am displaying my custom dialog from a function in mainwindow as below
void MainWindow::dispayCustomDialog()
{
MyCustomDialog *dlg = MyCustomDialog::getInstance();
dlg->show();
}
Below shows how my 'OnButton_1_Clicked' slot. In which I am capturing the screenshot using below line
void MainWindow::OnButton_1_Clicked()
{
//capture the screen shot
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap *map = new QPixmap(screen->grabWindow(0));
bool result = map->save("D:/test.jpg", "JPG");
}
Once I captured screen using above function, I can still see my 'MyCustomDialog' in test.jpg file. Qt doc says QGuiApplication::primaryScreen captures the initial state of application. So i think, this is expected in my case. Do we have any other solution to grab screen with current state ?
What I am trying to achieve is grab the screen in OnButton_1_Clicked() function after hiding my 'MyCustomDialog'.
I found a solution. Used a singleslot timer with delay of 500 msec before capturing the screen as below. This wait for my custom dialog to hide properly.
void MainWindow::OnButton_1_Clicked()
{
QTimer::singleShot(500, this, &MainWindow::shootscreen);
}
void MainWindow::shootscreen()
{
//capture the screen shot
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap map = screen->grabWindow(0);
bool result = map.save("D:/test.jpg", "JPG");
}
On a QML Map, using "onCenterChanged" to capture a user-activated move, the filtering process of the points to be displayed on, starts as soon as the move is initiated.
Given the large number of data to be processed during this operation, I want it to begin only after the total stabilization of the Map (stop sliding/zooming, left mouse button released and mouse wheel inactive).
here is a snippet of the QML Map
Map {
id: mainMap
anchors.centerIn: parent;
anchors.fill: parent
plugin: Plugin {name: "osm"}
center: startingPoint
zoomLevel: 4.5
onCenterChanged: {
updateBoundingBox()
}
MapItemView {
id:viewPointOnMap
model: navaidsFilter
delegate: Marker{}
}
onMapReadyChanged: {
updateBoundingBox()
}
function updateBoundingBox(){
navaidsFilter.bBox = mainMap.visibleRegion.boundingGeoRectangle() //boundingBox
}
}//Map
and the filter snippets :
void NavaidsFilter::setBBox(const QGeoRectangle &bbox)
{
if(m_processedZone.isEmpty()|| !m_processedZone.contains(bbox)){ //First bbox or displacement/zoom out of the previous box
m_processedZone = bbox;
m_boundaryZone = bbox;
invalidateFilter();
}
}
bool NavaidsFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if(!m_boundaryZone.isValid()){
return false;
}
QModelIndex ix = sourceModel()->index(sourceRow, 0, sourceParent);
QGeoCoordinate pos = ix.data(NavaidsModel::PositionRole).value<QGeoCoordinate>();
return m_boundaryZone.contains(pos);
}
How can we achieve this?
Thanks for help
I have never worked with Map so I don't know a specific solution. Here are two general solutions that work in many cases:
Introduce a property that aggregates all states that are relevant
readonly property bool moving: mainMap.gesture.rotationActive
| mainMap.gesture.tiltingActive
| ...`
once this property changes to false do what is necessary. (onMovingChanged)
Use a Timer that you restart, whenever something changes. If for some ms nothing changes, let it trigger and do what is necessary.
I'm making a program where the user can add multiple people (participants) in a list. When the "Add" button is clicked, a new row is added and "edit" is called for the name field. All is well so far, but there is a thing I'd like to implement, and I can't seem to figure out how: when the user closes the editing field (presses enter or escape, clicks elsewhere, etc.) and if the name field remains empty, I'd like the row to be deleted. In other words, a name has to be filled in. Here is what I have so far:
void MainWindow::addParticipant()
{
QList<QStandardItem *> newRow;
newRow << new QStandardItem()
<< new QStandardItem();
participantModel->appendRow(newRow);
participantView->edit(participantModel->index(participantModel->rowCount()-1, 0));
}
Here participantModel is a QStandardItemModel and participantView is a QTreeView. I tried using signals and slots to detect when a row is empty and to delete it, but it hasn't worked and the syntax is elusive to me.
Ideally I'd be able to detect when the name field is not being edited anymore, so that I can delete the row if need be.
Here is ugly but working solution: subclass from QItemDelegate and check input data inside setModelData member function. As far setModelData has a const qualifier you can not modify model inside it, so you need some trick: in the following example the model is modified inside handler of closeEditor signal.
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget ()
{
QStandardItemModel * model = new QStandardItemModel ();
ItemDelegate * delegate = new ItemDelegate ();
table->setItemDelegate (delegate);
connect (delegate, & ItemDelegate::closeEditor, [=](){
if (isEmpty) {
model->removeRow (emptyRow);
isEmpty = false;
emptyRow = -1;
}
});
connect (delegate, & ItemDelegate::cellEdited, [=](const int row){
isEmpty = true;
emptyRow = row;
});
}
bool isEmpty;
int emptyRow;
};
class ItemDelegate : public QItemDelegate
{
Q_OBJECT
signals:
void cellEdited (int) const;
public:
void setModelData (QWidget * widget, QAbstractItemModel * model, const QModelIndex & index) const override
{
if (0 == index.column () ) {
if (QLineEdit * cellWidget = qobject_cast <QLineEdit *> (widget) ) {
if (cellWidget->text ().isEmpty () ) {
emit cellEdited (index.row () );
return;
}
}
}
QItemDelegate::setModelData (widget, model, index);
}
};
Complete example available at GitLab.
The comments/answers posted thus far have urged me to look more into item delegates. Quite embarrassingly, after relatively little googling I found the following solution for my problem:
void MainWindow::addParticipant()
{
QStyledItemDelegate *participantDelegate = new QStyledItemDelegate;
participantView->setItemDelegateForColumn(0, participantDelegate);
QList<QStandardItem *> newRow;
newRow << new QStandardItem()
<< new QStandardItem();
participantModel->appendRow(newRow);
connect(participantDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(checkRow()));
participantView->edit(participantModel->index(participantModel->rowCount()-1, 0));
}
Apparently the closeEditor signal (only available to delegates) is exactly what I was looking for. When the editor is closed, the slot checkRow() checks if the name field of the participant is empty and decides whether or not to delete the row.
Is there any way to check that the ui elements(line edit,combo box,etc.) of the dialog has been changed.
What I want is to show a message to the user if he changes the value of any single ui element, saying that details have been partially filled.
What i can do is use connect for each ui element & based on the value changed of each element i am setting a boolean flag & at the time of close event i am checking that boolean flag.
But Its quite complicate to check it for each widget.
Is there any easier way.
Code that I am using for single ui element is,
connect(ui->leAge,SIGNAL(textChanged(QString)),this,SLOT(functChanged())); //In Constructor
void DemoDialog::functChanged() //Will be called if value of line edit (ui->leAge) is changed
{
flag=true;
}
void DemoDialog::closeEvent(QCloseEvent *event)
{
if (flag) {
if (QMessageBox::warning(this,"Close","Do you want to close?",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes) {
this->close();
}
}
You can't reimplement closeEvent to prevent closing a window. The close() call that you do is either redundant or an error (infinite recursion), since a closeEvent method call is just a way of being notified that a closing is imminent. At that point it's too late to do anything about it.
Keep in mind the following:
Closing a dialog usually is equivalent to canceling the dialog. Only clicking OK should accept the changes.
When a user wants to close a dialog, you don't have to ask them about it. They initiated the action. But:
It is proper to ask a user about dialog closure if there are changes have not been accepted - on platforms other than OS X.
So, you have to do several things:
Reimplement the void event(QEvent*) method. This allows you to reject the close event.
Offer Apply/Reset/Cancel buttons.
Your flag approach can be automated. You can find all the controls of the dialog box and set the connections automatically. Repeat the statement below for every type of control - this gets tedious rather quickly:
foreach(QTextEdit* w, findChildren<QTextEdit*>())
connect(w, SIGNAL(textChanged(QString)), SLOT(functChanged()));
You can leverage the meta property system. Most controls have a user property - that's the property that holds the primary value of the control (like text, selected item, etc). You can scan all of the widget children, and connect the property change notification signal of the user property to your flag:
QMetaMethod slot = metaObject().method(
metaObject().indexOfSlot("functChanged()"));
foreach (QWidget* w, findChildren<QWidget*>()) {
QMetaObject mo = w->metaObject();
if (!mo.userProperty().isValid() || !mo.userProperty().hasNotifySignal())
continue;
connect(w, mo.notifySignal(), this, slot);
}
Each widget is a QObject. QObjects can have properties, and one of the properties can be declared to be the user property. Most editable widget controls have such a property, and it denotes the user input (text, numerical value, selected index of the item, etc.). Usually such properties also have change notification signals. So all you do is get the QMetaMethod denoting the notification signal, and connect it to your function that sets the flag.
To determine the changed fields, you don't necessarily need a flag. In many dialog boxes, it makes sense to have a data structure that represent the data in the dialog. You can then have a get and set method that retrieves the data from the dialog, or sets it on the dialog. To check for changed data, simply compare the original data to current data:
struct UserData {
QString name;
int age;
UserData(const QString & name_, int age_) :
name(name_), age(age_) {}
UserData() {}
};
class DialogBase : public QDialog {
QDialogButtonBox m_box;
protected:
QDialogButtonBox & buttonBox() { return m_box; }
virtual void isAccepted() {}
virtual void isApplied() {}
virtual void isReset() {}
virtual void isRejected() {}
public:
DialogBase(QWidget * parent = 0) : QDialog(parent) {
m_box.addButton(QDialogButtonBox::Apply);
m_box.addButton(QDialogButtonBox::Reset);
m_box.addButton(QDialogButtonBox::Cancel);
m_box.addButton(QDialogButtonBox::Ok);
connect(&m_box, SIGNAL(accepted()), SLOT(accept()));
connect(&m_box, SIGNAL(rejected()), SLOT(reject()));
connect(this, &QDialog::accepted, []{ isAccepted(); });
connect(this, &QDialog::rejected, []{ isRejected(); });
connect(&buttonBox(), &QDialogButtonBox::clicked, [this](QAbstractButton* btn){
if (m_box.buttonRole(btn) == QDialogButtonBox::ApplyRole)
isApplied();
else if (m_box.buttonRole(btn) == QDialogButtonBox::ResetRole)
isReset();
});
}
}
class UserDialog : public DialogBase {
QFormLayout m_layout;
QLineEdit m_name;
QSpinBox m_age;
UserData m_initialData;
public:
UserDialog(QWidget * parent = 0) : QDialog(parent), m_layout(this) {
m_layout.addRow("Name", &m_name);
m_layout.addRow("Age", &m_age);
m_age.setRange(0, 200);
m_layout.addRow(&buttonBox());
}
/// Used by external objects to be notified that the settings
/// have changed and should be immediately put in effect.
/// This signal is emitted when the data was changed.
Q_SIGNAL void applied(UserData const &);
UserData get() const {
return UserData(
m_name.text(), m_age.value());
}
void set(const UserData & data) {
m_name.setText(data.name);
m_age.setValue(data.age);
}
void setInitial(const UserData & data) { m_initialData = data; }
bool isModified() const { return get() == m_initialData; }
protected:
void isAccepted() Q_DECL_OVERRIDE { emit applied(get()); }
void isApplied() Q_DECL_OVERRIDE { emit applied(get()); }
void isReset() Q_DECL_OVERRIDE { set(m_initialData); }
};
If you're only checking whether the input fields are filled when the Dialog closes, you don't need the flags you can only check if there is any input.
If you are filling the input fields programatically at some points but are also only interested in the change when the dialog closes, you can also check in the close function whether the current input is equal to the one you set earlier.
From the code you posted, I can't really see what you need the flags for.
I'm displaying some information to the user in QScrollArea.
The user should have seen all contents, before she can proceed (at least the content should have been scrolled to the end)
How could I detect this in an easily?
Is the reimplementing of virtual void scrollContentsBy (int dx,int dy) the only way?
EDIT
I was able to solve it, but had to use some workarounds:
Scroll-action value sent by the signal actionTriggered(int) had never the value QAbstractSlider::SliderToMaximum (Qt4.8, Windows 7). So I've checked manually, if the slider value is close to maximum.
Even if scroll-bar widget was dragged by mouse till the bottom, the value of the scroll-bar is never the maximum. Only if the scroll-bar widget is moved by any other event such as arrow-down or mouse wheel, the value may become maximum. I've work-arounded it with recheckPosition()
I hope, there are better solutions.
void NegativeConfirmation::recheckPosition()
{
processScrollAction(1);
}
void NegativeConfirmation::processScrollAction( int evt)
{
if ( evt == QAbstractSlider::SliderToMaximum) // Have not managed to receive this action
{
ui->bConfirm->setEnabled(true);
}
//Another approach
QWidget * sw = ui->scrollArea->widget();
if ( sw ) //any content at all ?
{
QScrollBar * sb = ui->scrollArea->verticalScrollBar();
if ( sb )
{
int sbv = sb->value();
int sbm = sb->maximum()-10;
if ( sbm>0 && sbv >= sbm )
{
ui->bConfirm->setEnabled(true);
}
else
{
QTimer::singleShot(1000, this, SLOT(recheckPosition()));
}
}
}
}
QScrollArea inherits QAbstractSlider which provides this signal: -
void QAbstractSlider::actionTriggered(int action)
Where action can be QAbstractSlider::SliderToMaximum.
I expect you can connect to the this signal and test when the action is QAbstractSlider::SliderToMaximum, representing that the user has scrolled to the bottom.