I'm trying to update a label value from a background thread. I understand that there are several example, but I'm still having trouble understanding why the below code throw an stack overflow error. It seems like every time setTitle() is executed it goes through the true part of the if statement.
Set title Function:
void setTitle(char data[])
{
String^ temp = gcnew String(data);
if(this->lastSeen1->InvokeRequired)
{
setTitleDelegate^ d = gcnew setTitleDelegate(this, &setTitle);
d->Invoke(data);
}else
{
this->lastSeen1->Text = temp;
this->lastSeen1->Visible = true;
}
}
delegate:
delegate void setTitleDelegate(char data[]);
Thank you
Well, because of this:
d->Invoke(data);
See, here you're calling Delegate::Invoke, which basically means that setTitle just calls itself immediately. You need to call Control::Invoke instead, so you need to call it on an instance of Control, something like this:
this->lastSeen1->Invoke(d, /* any args here */);
I don't know why you're passing a char[] here, it's better not to mix native and managed data structures too much, if you can then just use String^ instead (but even then, C++/CLI isn't really meant for UI development either...).
Related
I am using wxAutomationObject to export data to MS Excel. I have created a few helper classes ExcelApp, ExcelWorkbook etc... all of which inherits from wxAutomationObject.
Briefly, ExcelApp is as follows:
class ExcelApp :public wxAutomationObject
{
public:
ExcelApp(WXIDISPATCH* dispatchPtr = NULL);
~ExcelApp() = default;
void Quit();
std::unique_ptr<ExcelWorkbook> CreateWorkbook(bool Visible = true);
std::vector<std::unique_ptr<ExcelWorkbook>> GetOpenedWorkbooks();
long GetNumberofOpenedWorkbooks() const;
private:
wxAutomationObject m_App;
};
The constructor is implemented as follows:
ExcelApp::ExcelApp(WXIDISPATCH* dispatchPtr):wxAutomationObject(dispatchPtr)
{
if (!m_App.GetInstance("Excel.Application"))
throw std::exception("An instance of Excel object could not be created.");
}
And to create a workbook, I used to following code:
std::unique_ptr<ExcelWorkbook> ExcelApp::CreateWorkbook(bool Visible)
{
auto wb = std::make_unique<ExcelWorkbook>();
bool suc = m_App.GetObject(*wb, "Workbooks.Add");
if (!suc)
return nullptr;
if (Visible)
m_App.PutProperty("Visible", true);
return std::move(wb);
}
This whole OLE implementation is part of a dynamic menu. Part of the code for the event handler for menu is:
void MyFrame::OnExportToExcelButtonHandler(wxCommandEvent& event)
{
auto item = static_cast<wxMenu*>(event.GetEventObject());
std::unique_ptr<ExcelWorkbook> xlsWB ((ExcelWorkbook*)event.GetEventUserData());
bool CreateNewWB = false;
try
{
//Export either the entire workbook or the active worksheet to a new Workbook
if (xlsWB == nullptr)
{
auto xlsApp = ExcelApp();
xlsWB = std::move(xlsApp.CreateWorkbook());
CreateNewWB = true;
}
In terms of exporting data and formatting, everything works fine. However, after closing the created Workbooks Excel.exe still remains in the taskbar list. I wonder what I could be missing?
By the way, I tried the very basic sample shipped with wxWidgets and there seems to remain no ghost instances of Excel after quitting. I
I am using wxWidgets 3.1.4 on Windows 10 using VS 2019.
EDIT 1:
The ribbon button that generates the dynamic menu:
void MyFrame::OnExportToExcelButtonClicked(wxRibbonButtonBarEvent& event)
{
auto excel = sys::win32::ole::ExcelApp();
auto PrepareMenu = [&](wxMenu* Menu)
{
try
{
wxMenuItem* item1 = Menu->Append(wxID_ANY, "New Excel Workbook");
item1->SetBitmap(wxArtProvider::GetBitmap(wxART_NEW));
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, item1->GetId());
auto wbs = excel.GetOpenedWorkbooks();
if (wbs.size() > 0)
{
for (int i = 0; i < wbs.size(); ++i)
{
auto ExcelWB = std::move(wbs[i]);
wxMenuItem* item1 = Menu->Append(wxID_ANY, ExcelWB->GetFullName());
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, item1->GetId(), wxID_ANY, ExcelWB.release());
}
}
}
}
The problem seems to originate from the following line:
Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, item1->GetId(), wxID_ANY, ExcelWB.release());
The ExcelWB pointer is now owned by the wxWidgets system and therefore a reference remains and thus the ghost Excel.exe remains.
To solve it, in the following procedure I added:
void MyFrame::OnExportToExcelButtonHandler(wxCommandEvent& event)
{
Unbind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnExportToExcelButtonHandler, this, event.GetId(), wxID_ANY, xlsWB.get());
But the problem still remains. I am not sure how to properly get rid of the pointer that is now owned by the menu. I redesigned some parts with shared_ptr and it did not help.
You already may be doing that but just to be sure.
When debugging such things, make sure the application instance is always visible so you can see when it for example gets stuck waiting on a user input, e.g., asking to save a modified workbook, preventing it from quitting.
I have written wxWidgets-based MS Excel automation wrapper wxAutoExcel (it uses a shared instead of unique pointer for Excel objects) and I have never encountered application ghosts, except when I terminated my application when debugging, without disposing Excel objects.
BTW, I do not get why your ExcelApp both derives from wxAutomationObject and has wxAutomationObject as a member variable (m_App). What is the IDispatch used in the ExcelApp ctor for?
Finally, it seems like I solved the issue, using the following steps:
Instead of a menu, now a dialog is used; therefore, the menu does not own the pointer anymore.
All wxAutomationObject inheriting objects are passed around via std::unique_ptr.
I am putting a QMesssageBox inline with the shortcutkey of CTRL + N (it means new file will be open). When I press and hold the shortcut key when the object is animating. Is it UI problem of Linux since I am using Linux OS and then I try in other OS and it is not happening, or any codes that I forgot?
Thank you.
If your goal is to have at most one QMessageBox appear at a time, you can ensure that in your code via something like this:
static QMessageBox * openMBox = NULL;
void MyClass :: showMessageBox()
{
if (openMBox) return; // don't open a new QMessageBox if we already have one open!
openMBox = new QMessageBox(args here...);
connect(openMBox, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(userClickedButton(QAbstractButton*)));
openMBox->show();
}
void MyClass :: userClickedButton(QAbstractButton * button)
{
if (openMBox)
{
// [code to handle button-click result could go here]
openMBox->deleteLater();
openMBox = NULL;
}
}
Note that showMessageBox() will only actually create a new QMessageBox if openMBox is NULL, which is to say, only if there isn't already a message-box present.
(The code calls deleteLater() in the userClickedButton() method instead of using the delete operator because it's likely that the userClickedButton() method is itself being called from within a method of the QMessageBox object, therefore we don't want to delete the QMessageBox object until later when it's not in the middle of a method-call)
Well I have this code in my Managed C++/Cli in Visual Studio 2008, I want to be able to access the windows forms items inside of the callback of the Thread Function, and I can't, it generates an error.
Is there another way to do that? to be able to modify the GUI stuff inside of a method of the WinForms Class by using the Thread function callback ?
This example shows what I want to do.
I need to use a thread because I want to have the other things in the Forms to be accessible, and without using threads everything just freezes until everything is done, and the "Login" function it calls, takes some time because it does HTTP Requests. and after this HTTP Request I set the values that I got from it in a Form Element.
void Login(){
this->btn_next->Enabled = false;
this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
if(this->clb_contas->CheckedItems->Count <= 0){
//...
}
}
System::Void test_login_Click(System::Object^ sender, System::EventArgs^ e) {
ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::Login);
Thread^ t = gcnew Thread(start);
t->Start();
}
Does anybody know how could I do that? if you think this can't be done and you want to suggest something something to make the GUI available while doing the process, I'm open for suggestions.
I hope I was being clear enough.
Thanks in advance.
All UI related code should be executed on the UI thread. In your case, that means that only the code you denoted with //... should be run on a separate thread. Extract that long-running code in its own method and pass that method to ThreadStart instead of Login(). Then you'll need to arrange for a way for the worker thread to notify the UI thread if and when it's complete.
Update:
Here's a crude example of how to modify your code. I would prefer to extract the long running operation in its own class if it is of sufficient complexity, but I think you get the idea.
The call to BeginInvoke ensures that LongRunningOperationComplete will be executed on the form's UI thread. You can use the same approach to call other methods that update the UI to indicate progress, even while the time-consuming operation is still running. If those methods require more parameters, you can create different delegates with the appropriate signature, and pass those parameters in the call to BeginInvoke. See here for how to do that.
// Same signature as LongRunningOperationComplete
delegate void MyInvokeDelegate();
void LongRunningOperation() {
for (int i=0; i < 100; i++) {
Thread::Sleep(100);
// The actual work that you're doing
}
// Operation complete. Update UI.
this->BeginInvoke(gcnew MyInvokeDelegate(this, &Login_Test::LongRunningOperationComplete));
}
void LongRunningOperationComplete() {
this->btn_next->Enabled = true;
this->login_accounts_facebook->Enabled = true;
}
System::Void StartMyLongRunningOperation() {
ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::LongRunningOperation);
Thread^ t = gcnew Thread(start);
t->Start();
}
void Login() {
this->btn_next->Enabled = false;
this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
if(this->clb_contas->CheckedItems->Count <= 0){
StartMyLongRunningOperation();
}
}
System::Void test_login_Click(System::Object^ sender, System::EventArgs^ e) {
Login();
}
I have some custom control inside of which i should create radiobuttons or checkboxes. The count of child controls is available only at runtime (it loads some file from which it gets this count). So i need to create variable number of controls. Which collection i should use for this purpose?
Solution 1: simply use std::vector<HWND> (or CArray<HWND>) - not suitable because i want use MFC (CButton). Of course i can Attach() and later Detach() handle to window each time i need this window, but it will give big overhead.
Solution 2: use std::vector<CButton*> or CArray<CButton*> or CList<CButton*> or... In this case i take care about making 'new' and appropriate 'delete' when control is unneeded. I am forgetful :)
MFC handle map contains pointer to CButton and i can't use simple CArray<CButton>, because it will move my objects each time when his size will grow.
... and the question is:
Which collection i should use for containing variable count of MFC control classes?
If you only want to read your file with the Count parameter, create your buttons, work with them and later delete them all then CArray<CButton*> is fine in my opinion. To make sure the buttons get deleted you could wrap the CArray into a helper like:
class CMyButtonArrayWrapper
{
public:
CMyButtonArrayWrapper();
virtual ~CMyButtonArrayWrapper();
void ClearArray();
void Add(CButton* theButton);
private:
CArray<CButton*> m_Array;
}
CMyButtonArrayWrapper::CMyButtonArrayWrapper()
{
}
CMyButtonArrayWrapper::~CMyButtonArrayWrapper()
{
ClearArray();
}
void CMyButtonArrayWrapper::ClearArray()
{
for (int i=0; i<m_Array.GetSize(); i++)
{
CButton* aButton=m_Array.GetAt(i);
if (aButton)
delete aButton;
}
m_Array.RemoveAll();
}
void CMyButtonArrayWrapper::Add(CButton* theButton)
{
m_Array.Add(theButton);
}
Then add an object of this class as a member to your custom control (m_MyButtonArrayWrapper) and add your buttons with:
CButton* aButton=new CButton;
aButton->Create( ... );
m_MyButtonArrayWrapper.Add(aButton);
If you need to add and remove buttons randomly a CList might be better suited for performance reasons. (But you won't probably notice a performance difference using InsertAt/RemoveAt of CArray, except your UI has several thousands of buttons. I guess this would be more an artwork than a user interface :))
I am very new to Qt; please help me to solve the problem.
I am using a thread to perform intensive operations in the background. Meanwhile I want to update the UI, so I am using SIGNALS and SLOTS. To update UI I emit a signal and update UI.
Let us consider below sample code,
struct sample
{
QString name;
QString address;
};
void Update(sample *);
void sampleFunction()
{
sample a;
a.name = "Sachin Tendulkar";
a.address = "India"
emit Update(&a);
}
In the above code we are creating a local object and passing the address of a local object. In the Qt document, it says that when we emit a signal it will be placed in the queue and late it will be delivered to the windows. Since my object is in local scope it will be delete once it goes out of the scope.
Is there a way to send a pointer in a signal?
You're insisting on doing the wrong thing, why? Just send the Sample itself:
void Update(sample);
//...
sample a("MSalters", "the Netherlands");
emit Update(a);
Unless you've determined that this code is a performance bottleneck you would be better to just pass a copy of the object rather than a pointer.
Really, I mean it.
However, if you must use pointers then use a boost::shared_ptr and it will delete itself.
void Update(boost::shared_ptr<sample> s);
void sampleFunction()
{
boost::shared_ptr<sample> a = boost::shared_ptr<sample>(new sample());
a->name = "Sachin Tendulkar";
a->address = "India"
emit Update(a);
}