Problem when dereferencing std::unique_ptr with QT (Segmentation fault) - c++

(Sorry for my future mistakes, I am fr)
I am creating a program to control the pc with a phone in c++ ( https://github.com/titicplusplus/PCPhone )
The server part works good, so I would like to add graphics interfaces for user, I decided to use QT.
When I stop the program QT, I have segmentation fault, and that stops the server.
My function that run the graphic interface:
void start_g(int argc, char **argv)
{
QApplication app(argc,argv);
QFile File(":/stylesheet.qss");
File.open(QFile::ReadOnly);
QString StyleSheet = QLatin1String(File.readAll());
app.setStyleSheet(StyleSheet);
ui interface; //QWidget window
interface.start();
std::cout << "start gui" << std::endl;
interface.show();
app.exec(); //return 0
return;
}
I encapsulated the code in a function because I would like to run it in a thread, but for now during my tests, I don't do this.
Here is my class ui (user interface)
class ui : public QWidget
{
public:
ui();
void start();
void change_file();
void change_port();
void freset();
void fapply();
~ui(); //Here is the problem
private:
openf f_open; //to open the extension s file
std::unique_ptr<QFormLayout> formLayout;
std::unique_ptr<QGroupBox> settings;
std::unique_ptr<QVBoxLayout> main_layout;
std::unique_ptr<QGridLayout> grid;
std::vector< std::vector< std::unique_ptr<QPushButton> > > tab2d;
std::unique_ptr<QGroupBox> all_button;
std::unique_ptr<QSpinBox> port_l;
std::unique_ptr<QPushButton> button_f;
std::unique_ptr<QPushButton> apply;
std::unique_ptr<QPushButton> reset;
std::unique_ptr<QLabel> text_f;
};
and the important function
ui::ui() {}
void ui::start()
{
grid = std::make_unique<QGridLayout>( this );
/** Code that you don't need, I guess **/
std::cout << "setting part" << std::endl;
all_button = std::make_unique<QGroupBox>("Slide");
all_button->setLayout(grid.get());
port_l =std::make_unique<QSpinBox>(this);
port_l->setMaximum(65535);
port_l->setValue( config_json["port"].get<int>() );
button_f = std::make_unique<QPushButton>("Open Image" ,this);
text_f = std::make_unique<QLabel>(QString::fromStdString(fs::absolute(config_json["image"].get<std::string>())), this);
apply = std::make_unique<QPushButton>(" Apply " ,this);
reset = std::make_unique<QPushButton>("Reset" ,this);
//connect(button_f.get(), &QPushButton::clicked, this, &ui::change_file);
connect(port_l.get(), QOverload<int>::of(&QSpinBox::valueChanged), this, &ui::change_port);
connect(apply.get(), &QPushButton::clicked, this, &ui::fapply);
connect(reset.get(), &QPushButton::clicked, this, &ui::freset);
//auto fileName = QFileDialog::getOpenFileName(&window, "Open Image", "/home/$USER", ("Image Files (*.png *.jpg *.bmp)"));
formLayout = std::make_unique<QFormLayout>();
formLayout->addRow("The port", port_l.get());
formLayout->addRow(text_f.get(), button_f.get()); //////////The problem is here
//formLayout->addRow(apply.get(), reset.get());
settings = std::make_unique<QGroupBox>("Settings");
settings->setLayout(formLayout.get());
main_layout = std::make_unique<QVBoxLayout>(this);
main_layout->addWidget(all_button.get());
main_layout->addWidget(settings.get());
}
So I saw on google, the problem come with the pointers and they destroy, so I try this
ui::~ui()
{
main_layout = nullptr;
settings = nullptr;
all_button = nullptr;
std::cout << "1u" << std::endl;
button_f = nullptr;
std::cout << "2u" << std::endl;
text_f = nullptr;
std::cout << "3u" << std::endl;
apply = nullptr;
std::cout << "4u" << std::endl;
reset = nullptr;
std::cout << "5u" << std::endl;
grid = nullptr;
std::cout << "bye bye" << std::endl;
}
The result is [ the errors are in french so I try to translate it ]:
1u
Segmentation fault (core dumped)
When I delete this line formLayout->addRow(text_f.get(), button_f.get());
There are no problems the deference of text_f and button_f, but with the next (apply and reset).
1u
2u
3u
Segmentation fault (core dumped)
On google, I saw a lot a time the people use the pointer and not the std::unique_ptr with qt, but I saw in general that people say "the smart pointer are better than the normal pointer.
Also, I try to replace button_f.get() to std::move(button_f).get() but that doesn't work.
For information, I am on Ubuntu 20.04, with g++-9.3 and qt5.
Thank you in advance for your answers !

Thanks G.M. and vahancho
I understand why I don't need to do std::unique_ptr .
When I learn C++ and pointer, there are rule : write one delete for every new, or better, use the smart pointer.
But with this two topics:
https://codereview.stackexchange.com/questions/43189/deleting-pointers-in-qt5/43201
Deleting Pointer to widget Qt C++
For the QT object, it's QT that deletes the pointers when the class is delete.
So I just need to need change all of my std::unique_ptr in raw pointer.

Related

Problems Displaying Mat Image in Windows Forms C++ Visual Studio 2019

I am new to C++ gui design and I am not too familiar with using pointers. Recently I have run into a few problems when trying to get an OpenCV Mat Image to display in a PictureBox in a constructed gui. I have searching online and even found a very similar post to my question but when trying to implement the guidance I ran into an exception when trying to operate the gui.
A very similar post
Displaying webcam feed in cv::Mat format in a picturebox
Code I grabbed from this post to save you a click:
void DrawCVImage(System::Windows::Forms::Control^ control, cv::Mat& colorImage)
{
System::Drawing::Graphics^ graphics = control->CreateGraphics();
System::IntPtr ptr(colorImage.ptr());
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,System::Drawing::Imaging::PixelFormat::Format24bppRgb,ptr);
System::Drawing::RectangleF rect(0,0,control->Width,control->Height);
graphics->DrawImage(b,rect);
delete graphics;
}
Now I am trying to display a "video" feed (really an array of cv::Mat Objects) but I am having a
"Source Not Available"/System.ArgumentException: 'Parameter is not valid.'
screen come up when I attempt to call the function that houses that playback. I also know specific line of code that throws the issue is the line
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,System::Drawing::Imaging::PixelFormat::Format24bppRgb,ptr);
Now for specifics, the form has an event (currently the click on the picture box, but I want to move to a "play" button in the future) and on this event, the code:
private: System::Void leftEyeImage_Click(System::Object^ sender, System::EventArgs^ e) {
std::cout << "Click Received" << std::endl;
cv::Mat& frame = cv::imread("Desktop/testcapture.png");
std::cout << "Import successful" << std::endl;
drawLeftEye(this, frame);
}
This code does get execute on click and starts the function drawLeftEye. The function is a modified version of the code from the other post and is below:
This is on the ShowResults.cpp file
namespace DevProject {
void ShowResults::drawLeftEye(System::Windows::Forms::Control^ control, cv::Mat& framebmp) {
System::Drawing::Graphics^ graphics = control->CreateGraphics();
std::cout << "Now Here" << std::endl;
System::IntPtr ptr(framebmp.data);
// Issue line
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(framebmp.cols, framebmp.rows, framebmp.step, System::Drawing::Imaging::PixelFormat::Format32bppRgb, ptr);
std::cout << "Converted successfully" << std::endl;
System::Drawing::RectangleF rect(0, 0, control->Width, control->Height); //No issue
graphics->DrawImage(b, rect);
std::cout << "Now Here before delete" << std::endl;
delete graphics;
//delete b;
}
}
I know based on my cout statements that I do make it into my function and I know my code compiles and runs through the function if I comment out the line:
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(framebmp.cols, framebmp.rows, framebmp.step, System::Drawing::Imaging::PixelFormat::Format32bppRgb, ptr);
//As well as (but only because b is defined in the line above^
graphics->DrawImage(b, rect);
I am not quite sure how to fix this, my friend mentioned this could be a memory issue but I don't know how I would go about fixing it. It is also very possible I have made a simple mistake in the design of this or where my pointers are going and I am just not competent enough to know the error.
By the way the pointer for "this" that is called in the function is linked to the Windows Autogenerated code (from gui construction) of:
this->leftEyeImage->Anchor = System::Windows::Forms::AnchorStyles::None;
this->leftEyeImage->BorderStyle = System::Windows::Forms::BorderStyle::FixedSingle;
this->leftEyeImage->Location = System::Drawing::Point(11, 836);
this->leftEyeImage->Name = L"leftEyeImage";
this->leftEyeImage->Size = System::Drawing::Size(991, 646);
this->leftEyeImage->TabIndex = 4;
this->leftEyeImage->TabStop = false;
this->leftEyeImage->Click += gcnew System::EventHandler(this, &ShowResults::leftEyeImage_Click);
Any and all advice or tests would be very appreciated as I am both interested in the answer and the reasoning. Thanks in advance!

How do you delete a sub-string in a string(qstring) in Qt

Is there a function in Qt similar to delete() and copy() in Delphi.
I am reading data from a device connected to my computer via USB and storing it as a QString. Every line that is read is not the same (or is cut short, even while using readyRead). I created a buffer sting to add these "half sting" (eg. string = "This" instead of "This is a string#") to and now I want to copy the string up until the '#' and then delete the string so if new "half stings" get added I can do the same with them. The code below is what I tried
void MainWindow::readSerial()
{
QByteArray serialData = port->readAll();
serialBuffer += serialData;
QByteArray serialString = serialBuffer.
qDebug() << serialString;
ui -> textEdit ->append(serialString);
//serialBuffer.replace(serialString,"");
}
The above code only returns an empty string.
void MainWindow::readSerial()
{
QByteArray serialData = port->readAll();
serialBuffer += serialData;
QString serialString = serialBuffer.mid(serialBuffer.indexOf("$"),serialBuffer.indexOf("\r\n"));
qDebug()<< "index of \r\n" << serialBuffer.indexOf("\r\n");
qDebug() << "SerialString" <<serialString;
ui -> textEdit ->append(serialString);
qDebug() << "SerialBuffer: " << serialBuffer;
serialBuffer.replace(serialString + "\r\n","");
}
the above code works. Thanks all.
regards

Qt focusObjectChanged example?

I'm looking for an example on how to process the QObject pointer received by the slot when a 'focusObjectChanged' signal is received. I'd like to know how to identify the object that is getting the focus?
The documentation indicates that the pointer is to the focus'd object, but how do I know which?
I've tried analysing the pointer in the debugger but I don't see anything obvious.
You can determine the class and object name, widget properties...:
QObject::connect(qApp, &QApplication::focusObjectChanged, [](QObject *obj){
if(obj) qWarning() << obj->metaObject()->className() << obj->objectName();
QWidget* widget = qobject_cast<QWidget*>(obj);
if(widget) qWarning() << widget->geometry(); // or other properties
BaseType* baseType = qobject_cast<BaseType*>(obj);
if(baseType) qWarning() << baseType->some_actions();
Type1* type1 = qobject_cast<Type1*>(obj);
if(Type1) qWarning() << type1->some_actions();
Type2* type2 = qobject_cast<Type2*>(obj);
if(Type2) qWarning() << type2->some_actions();
});
or compare the pointer with widget pointers to find the widget you need:
SomeClassSlot(QObject* obj) {
if(!obj) return;
if(obj == m_mainWindows) ....
if(obj->parent() == m_table) ...
MyClass myClass = qobject_cast<MyClass*>(obj);
if(myClass) myClass->some_method();
}

Open QWidget or QWindow from the current one

I know this was asked here, but it doesn't work...
Here's my problem: my program actually has 2 forms: LoginWindow and MainWindow, the programme starts on LoginWindow and then I called a class Login where are all methods and attributes for checking users
Here's some code:
//in LoginWindow class
Login log;
log.checkID(ui->usrnmle->text().toStdString(), ui->passwdle->text().toStdString());
//in Login class
void Login::checkID(string usr, string passwd)
{
if(usr == "Test" && passwd == "root")
{
cout << "Ok!" << endl;
MainWindow mw = MainWindow();
mw.show();
LoginWindow lw = LoginWindow();
lw.hide();
}
else
{
cout << "Connection failed " << endl;
}
}
I type the good username and password, I got the 'Ok!' in the output console but MainWindow never appears.
Someone can help?
Your MainWindow mw is a local variable that is destroyed as soon as you are out of checkID function (or even out of if but that does not really matters)
You need to create it on heap
MainWindow *mw = new MainWindow();
mw->show();
Then to avoid memory leak you would need to destroy it when it is of no further use:
delete mw;
I'm not a QT developer, but
{
cout << "Ok!" << endl;
MainWindow mw = MainWindow();
mw.show();
LoginWindow lw = LoginWindow();
lw.hide();
}
You are creating two local windows here, which are destroyed on the spot when the scope ends.
you need to keep these instances alive throught their use. for example, make them member variables of some bigger object like "App" class os so.

Qt - Updating in the function called by QSignalMapper

I have a QTreeWidget in which each of its items has a QComboBox in a column. I have connected it to a slot with a QSignalMapper, and I'm successfully retrieving both the item and the index in the combobox when it is triggered. I did it like this:
foreach(Workplace *wp, allWorkplaces){
QTreeWidgetItem *workplaceItem = new QTreeWidgetItem;
workplaceItem->setText(0, wp->workplaceName());
workplaceItem->setText(1, wp->workplaceDescription());
myWorkplaceUi->treeWidget->addTopLevelItem(workplaceItem);
QComboBox *combo = new QComboBox();
combo->addItems(allShiftModels);
combo->setAutoFillBackground(true);
ShiftModel *shiftModel = qobject_cast<ShiftModel *>(wp->usedShiftModel);
myWorkplaceUi->treeWidget->setItemWidget(workplaceItem,2, combo);
if(shiftModel && !shiftModel->shiftModelName().isEmpty()){
qDebug()<<"after the cast: "<< shiftModel->shiftModelName();
combo->setCurrentIndex(combo->findText(shiftModel->shiftModelName(), Qt::MatchExactly));
}else{
combo->setCurrentIndex(combo->findText("None", Qt::MatchExactly));
}
connect(combo, SIGNAL(currentIndexChanged(int)), signalMapper, SLOT(map()));
signalMapper->setMapping(combo, QString("%1").arg(wp->workplaceName()));
}
connect(signalMapper, SIGNAL(mapped(const QString &)),this, SLOT(changed(const QString &)));
My objective is, after retrieving both the Workplace and the ShiftModel, to update them in the instances of my already created Workplaces. So, basically, I try to find the Workplace and the ShiftModel that were selected, because depending on the ShiftModel selected, I will change a pointer to the ShiftModel in the Workplace class:
class Workplace : public QObject
{
Q_OBJECT
public:
(...)
ShiftModel *usedShiftModel;
(...)
}
And the changed slot:
void workplacesdialog::changed(QString position){
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
QList<ShiftModel*> allShiftModels = this->myProject->myFactory->listShiftModels();
foreach(Workplace* workplace, allWorkplaces){
foreach(ShiftModel *shiftmodel, allShiftModels){
qDebug() <<"workplace:"<< workplace->workplaceName();
qDebug() <<"shiftmodel:"<< shiftmodel->shiftModelName();
QString wp = position;
QTreeWidgetItem* item=(QTreeWidgetItem*)myWorkplaceUi->treeWidget->findItems(wp,Qt::MatchExactly,0).at(0);
QComboBox *combo = (QComboBox*)myWorkplaceUi->treeWidget->itemWidget(item,2);
if(combo && item){
QString sm = combo->currentText();
qDebug() << "selected shiftmodel "<< sm << " on workplace "<< wp;
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
workplace->usedShiftModel = shiftmodel;
break;
}
else{
workplace->usedShiftModel = 0;
return;
}
}else{
qDebug() << "cast failed!";
return;
}
}
}
}
So, my problem with this is, when I click one of the comboboxes, successfully retrieve both the item and index selected, but then, when I try to traverse them with the two foreach loops in the slot, it does not work as I expected. I hoped that every time I clicked on an index in one of the comboboxes, this would be called, and it is. Although, for some reason, the method I'm using to match what the user selected with what was already instatiated doesn't work.
Also, it looks like it only hits both the 1st workplace on the allWorkplaces list as well as the 1st shiftmodel on the ShiftModels list, and this is my problem.
If anyone knows how to fix this or has any ideas to share, please let me know. Thank you.
The problem is this:
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
workplace->usedShiftModel = shiftmodel;
break;
}
else{
workplace->usedShiftModel = 0;
return;
}
If either the workplace name does not match, or the shift model name does not match, the relation between the work place and its currently linked shift model is removed and your function returns.
I could restructure the two for-loops for you, but there is an easier and less error-prone way:
Note: I marked some code paths with "TODO" which I skipped due to lack of time. You should be able to figure them out yourself though.
// Set up hashes for quick lookup
QHash< QString, Workplace* > workplaceHash;
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
foreach( Workplace* workplace, allWorkplaces )
{
workplaceHash.insert( workplace, workplace->workplaceName() );
}
// TODO: Do a similar thing for the shift models here
// Find the selected workplace
if( !workplaceHash.contains( position ) )
{
// TODO: Error handling (An unknown/No workplace was selected)
return;
}
// else: A valid workplace was selected
Workplace* selectedWorkplace = workplaceHash.value( position );
// TODO: Retrieve the name of the shift model (stored in variable sm)
// Find the selected shiftmodel
if( !shiftplaceHash.contains( sm ) )
{
// No shift model was selected
selectedWorkplace->usedShiftModel= 0;
return;
}
// Else: Both work place and shift model were selected
Shiftplace* selectedShiftModel = shiftplaceHash.value( sm );
selectedWorkplace->usedShiftModel = selectedShiftModel;
A few ideas for refactoring:
You could maybe do the creation of the hashes outside of this method and store them in member variables. Just make sure that the hash is always updated when a workplace is added or removed.
You could make spotting errors easier by extracting parts of the code into separate methods, e.g. QString getSelectedShiftModelName() etc.
So, In the end I figured out my loops were really messed up... This is working now:
void workplacesdialog::changed(QString position){
QList<Workplace* > allWorkplaces = this->myProject->listMyWorkplaces();
QList<ShiftModel*> allShiftModels = this->myProject->myFactory->listShiftModels();
qDebug() << allWorkplaces.size() << " workplaces";
qDebug() << allShiftModels.size() << " ShiftModels";
QString wp = position;
QString sm;
QTreeWidgetItem* item=(QTreeWidgetItem*)myWorkplaceUi->treeWidget->findItems(wp,Qt::MatchExactly,0).at(0);
QComboBox *combo = (QComboBox*)myWorkplaceUi->treeWidget->itemWidget(item,2);
if(combo && item){
sm = combo->currentText();
qDebug() << "selected shiftmodel "<< sm << " on workplace "<< wp;
}else{
qDebug() << "cast failed!";
return;
}
foreach(Workplace* workplace, allWorkplaces){
foreach(ShiftModel *shiftmodel, allShiftModels){
qDebug() <<"workplace:"<< workplace->workplaceName();
qDebug() <<"shiftmodel:"<< shiftmodel->shiftModelName();
if(workplace->workplaceName()==wp && shiftmodel->shiftModelName()==sm){
qDebug() << "found match!: "<< wp << " >>>>> " << sm;
workplace->usedShiftModel = shiftmodel;
return;
}else if(workplace->workplaceName()==wp && sm=="None"){
qDebug() << "clear match: "<< wp << " >>>>> " << sm;
workplace->usedShiftModel = 0;
return;
}
}
}
}