Creating a pointer to a widget(wxTextCtrl) in a function - c++

I've trying to get a pointer to a widget (in the code it's named text). But at least I've got only this
error: no matching function for call to 'std::basic_ostream::basic_ostream(wxWindow*)'
my code:
gui.h
#include <wx/wx.h>
class wxWCK : public wxFrame
{
public:
wxWCK(const wxString& title);
void OnClickCon(wxCommandEvent& event);
void OnClickSta(wxCommandEvent& event);
private:
wxButton *connect;
wxButton *start;
wxTextCtrl *text;
};
const int ID_CON = 100;
const int ID_STA = 101;
const int ID_MF0 = 102;
const int ID_TEX = 103;
void Connect();
void Start();
and gui.cpp
#include "gui.h"
wxWCK::wxWCK(const wxString& title)
: wxFrame(NULL, ID_MF0, title, wxDefaultPosition, wxSize(400,300))
{
wxPanel *panel = new wxPanel(this, -1);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL);
//Outputwidget
text = new wxTextCtrl(panel, ID_TEX, _T(""),
wxPoint(-1, -1), wxSize(1000, 1000), wxTE_MULTILINE);
//redirecting stream to the outputwidget
//std::ostream stream(text);
stream << "Hello" << std::endl;
connect = new wxButton(panel, ID_CON, _T("Connect"));
start = new wxButton(panel, ID_STA, _T("Start"));
hbox1->Add(text);
hbox2->Add(connect);
hbox2->Add(start);
vbox->Add(hbox1, 1, wxEXPAND);
vbox->Add(hbox2, 0, wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 10);
panel->SetSizer(vbox);
Connect(ID_CON, wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxWCK::OnClickCon));
//Connect(ID_STA, wxEVT_COMMAND_BUTTON_CLICKED,
// wxCommandEventHandler(wxWCK::OnClickSta));
}
void wxWCK::OnClickCon(wxCommandEvent& WXUNUSED(event))
{ void Connect(); }
void wxWCK::OnClickSta(wxCommandEvent& WXUNUSED(event))
{ void Start(); }
void Connect()
{
//Try to get a pointer to 'text'
std::ostream stream(wxWindow::FindWindowById(ID_TEX));
stream << "Connected" << std::endl;
}
/*
void Start()
{
//Try to get a pointer to 'text'
std::ostream stream(wxWindow::FindWindowById(ID_TEX));
stream << "Started" << std::endl;
}
*/
I hope somebody can help me. A other solution can be to get the stream as global. But when I try to get the stream-declaration in the header, he says he dont know any text so I move the text-declaration outside the class and I got a muliple declaration error... I think, because I include the gui.h in gui.cpp and main.h .
-Casisto
edit:
I changed the the function to:
void Connect()
{
//Try to get a pointer to 'text'
std::ostream stream((wxTextCtrl*) wxWindow::FindWindowById(ID_TEX));
stream << "Connected" << std::endl;
}
Now I don't get a error or a warning, but when I click on "Connect"-Button, the wxTextCtrl get no "Input" (I mean, there is only "Hello" in there)

The initial error must have been from the old code of Connect() because wxWindow::FindWindowById() returns a wxWindow*.
First thing to try: change wxWCK::OnClickCon() to { (*text) << "Connected\n"; }. This should just work.
Next, if you really need that void Connect(), try again without creating the std::ostream (you don't really need it); something like { (*(wxTextCtrl*)wxWindow::FindWindowById(ID_TEX)) << "Connected\n"; }.
However, this might still not work, so make ID_CON = wxID_HIGHEST + 1, ID_STA = wxID_HIGHEST + 2 etc and try again. IIRC I've seen cases where ids with low values caused curious behaviour.

The error message doesn't fit the code you show, in it text is a wxTextCtrl, which derives from std::streambuf and so can be used to construct an associated std::ostream, and not a wxWindow.
IOW your code should work as shown, you must be not showing us everything.

Related

Alternative to `wxProcess::IsInputAvailable`

According to the documentation, the method wxProcess::IsInputAvailable "allows writing simple (and extremely inefficient) polling-based code waiting for a better mechanism in future wxWidgets versions." It also points to an example code dated to "15.01.00", I've checked on GitHub that the usage of this method dates back some 16 years or more.
Well, is this method of pooling still inefficient? Is there any better alternative?
I'm debugging an issue with a process's output not being read using something similar to the exec sample, and either way my application is sensitive to the timeliness of this polling and extracting of output.
I don't think the methods for reading the redirected output or the child process are inefficient. I think the document is stating that polling on the GUI thread as in the sample will be inefficient since it involves repeatedly generating pseudo idle events.
A possible alternative is to use a secondary thread to do the reading. Here is an example of using the lame mp3 encoder to encode a wav file:
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/filename.h>
#include <wx/filepicker.h>
#include <wx/msgqueue.h>
#include <wx/thread.h>
#include <wx/process.h>
const char* pathToLame = "d:\\temp\\lame.exe";
wxDEFINE_EVENT(wxEVT_THREAD_STDIN, wxThreadEvent);
wxDEFINE_EVENT(wxEVT_THREAD_STDERR, wxThreadEvent);
class LameThread : public wxThread
{
public:
enum ThreadMessage
{
ProcessComplete,
ExitThread,
MessageLast
};
LameThread(wxEvtHandler*, wxProcess*, wxMessageQueue<ThreadMessage>& q);
~LameThread();
private:
ExitCode Entry() wxOVERRIDE;
void DrainInput();
wxMessageQueue<ThreadMessage>& m_queue;
wxEvtHandler* m_handler;
wxProcess* m_process;
char* m_buffer;
size_t m_bufferSize;
};
LameThread::LameThread(wxEvtHandler* h, wxProcess* p,
wxMessageQueue<ThreadMessage>& q)
:wxThread(wxTHREAD_JOINABLE),m_queue(q)
{
m_process = p;
m_handler = h;
m_bufferSize = 1024*1024;
m_buffer = new char[m_bufferSize];
}
LameThread::~LameThread()
{
delete[] m_buffer;
delete m_process;
}
wxThread::ExitCode LameThread::Entry()
{
ExitCode c;
while ( 1 )
{
// Check if termination was requested.
if ( TestDestroy() )
{
wxProcess::Kill(m_process->GetPid());
c = reinterpret_cast<ExitCode>(1);
break;
}
ThreadMessage m = MessageLast;
wxMessageQueueError e = m_queue.ReceiveTimeout(10, m);
// Check if a message was received or we timed out.
if ( e == wxMSGQUEUE_NO_ERROR )
{
if ( m == ProcessComplete )
{
DrainInput();
c = reinterpret_cast<ExitCode>(0);
break;
}
else if ( m == ExitThread )
{
wxProcess::Kill(m_process->GetPid());
c = reinterpret_cast<ExitCode>(1);
break;
}
}
else if ( e == wxMSGQUEUE_TIMEOUT )
{
DrainInput();
}
}
return c;
}
void LameThread::DrainInput()
{
if ( !m_process->IsInputOpened() )
{
return;
}
wxString fromInputStream, fromErrorStream;
wxInputStream* stream;
while ( m_process->IsInputAvailable() )
{
stream = m_process->GetInputStream();
stream->Read(m_buffer, m_bufferSize);
fromInputStream << wxString(m_buffer, stream->LastRead());
}
while ( m_process->IsErrorAvailable() )
{
stream = m_process->GetErrorStream();
stream->Read(m_buffer, m_bufferSize);
fromErrorStream << wxString(m_buffer, stream->LastRead());
}
if ( !fromInputStream.IsEmpty() )
{
wxThreadEvent* event = new wxThreadEvent(wxEVT_THREAD_STDIN);
event->SetString(fromInputStream);
m_handler->QueueEvent(event);
}
if ( !fromErrorStream.IsEmpty() )
{
wxThreadEvent* event = new wxThreadEvent(wxEVT_THREAD_STDERR);
event->SetString(fromErrorStream);
m_handler->QueueEvent(event);
}
}
class MyFrame: public wxFrame
{
public:
MyFrame();
private:
void OnClose(wxCloseEvent& event);
void OnEncode(wxCommandEvent& event);
void OnProcessComplete(wxProcessEvent& event);
void OnThreadInput(wxThreadEvent&);
wxThread* m_lameThread;
wxMessageQueue<LameThread::ThreadMessage> m_msgQueue;
wxFilePickerCtrl* m_filePicker;
wxTextCtrl* m_textCtrl;
wxButton* m_encodeButton;
};
MyFrame::MyFrame()
:wxFrame(NULL, wxID_ANY, "Encode", wxDefaultPosition, wxSize(600, 400))
{
m_lameThread = NULL;
wxPanel* panel = new wxPanel(this, wxID_ANY);
m_filePicker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString,
"Select a file", "*.wav");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);
m_encodeButton = new wxButton( panel, wxID_ANY, "Encode");
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(m_filePicker, wxSizerFlags().Expand().Border(wxALL) );
sizer->Add(m_textCtrl,
wxSizerFlags(1).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));
sizer->Add(m_encodeButton,
wxSizerFlags().Border(wxLEFT|wxRIGHT|wxBOTTOM).Right());
panel->SetSizer( sizer );
panel->Layout();
m_encodeButton->Bind(wxEVT_BUTTON, &MyFrame::OnEncode, this);
Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this);
Bind(wxEVT_END_PROCESS, &MyFrame::OnProcessComplete, this);
Bind(wxEVT_THREAD_STDIN, &MyFrame::OnThreadInput, this);
Bind(wxEVT_THREAD_STDERR, &MyFrame::OnThreadInput, this);
}
void MyFrame::OnClose(wxCloseEvent& event)
{
if ( m_lameThread && m_lameThread->IsRunning() )
{
m_msgQueue.Post(LameThread::ExitThread);
m_lameThread->Wait();
delete m_lameThread;
}
Destroy();
}
void MyFrame::OnEncode(wxCommandEvent& event)
{
// Make sure the input file exists and is a wav file.
wxString file = m_filePicker->GetPath();
if ( !wxFileName::FileExists(file) )
{
m_textCtrl->AppendText("file does not exist.\n");
return;
}
wxFileName fn(file);
if ( fn.GetExt() != "wav" )
{
m_textCtrl->AppendText("File is not a wav file.\n");
return;
}
// Create a process and and encoder thread.
wxProcess* process = new wxProcess(this);
process->Redirect();
m_msgQueue.Clear();
m_lameThread = new LameThread(this, process, m_msgQueue);
m_lameThread->Run();
if ( !m_lameThread->IsRunning() )
{
m_textCtrl->AppendText("Unable to launch encoder thread.\n");
delete m_lameThread;
return;
}
// Execute the encoder command.
wxString cmd = pathToLame;
cmd << " \"" << fn.GetFullPath() << "\" \"";
cmd << fn.GetPathWithSep() << fn.GetName() << ".mp3\"";
wxExecute(cmd, wxEXEC_ASYNC, process);
m_encodeButton->Disable();
}
void MyFrame::OnProcessComplete(wxProcessEvent& event)
{
if ( m_lameThread && m_lameThread->IsRunning() )
{
m_msgQueue.Post(LameThread::ProcessComplete);
m_lameThread->Wait();
delete m_lameThread;
m_lameThread = NULL;
m_encodeButton->Enable();
}
}
void MyFrame::OnThreadInput(wxThreadEvent& event)
{
m_textCtrl->AppendText(event.GetString());
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
::wxInitAllImageHandlers();
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
On windows, this looks like this:
The basic idea is to run a tight loop in the thread's Entry method. In each iteration the thread waits for a message from a message queue, and if no message is received the thread tries to check the process for input or error messages.
That's essentially the same polling technique as used in the sample, but since it's in a secondary thread it won't clog the gui thread with idle events. If you need to do any processing of the input, that will also be done on the secondary thread.
If you're having trouble with timings, you can reduce the time spent waiting on the message queue.
Note that launching the secondary procss and receiving the event when it finishes can only be done on the main thread.
Finally I should mention that I'm not 100% sure this is completely thread safe. I wish there was a way to protect the reads and writes with the input and error streams with a mutex or critcal section; but since all the writing to those streams is done under the hood, I can't see any way to do that from user code. I can say I've used this technique on both windows and linux in the past without issue.

wxWidgets C++ - dynamically wrapping wxStaticText in dialog

I am having trouble with getting a wxStaticText label to wrap dynamically in a dialog, using wxWidgets 3.0.2. I followed ideas for other questions like this one, bit I still have strange effects.
I am using the Wrap(int) function on the text, in a callback on the wxEVT_SIZE event, but it seems to have an unexpected effect on the text, and also seems to only "ratchet" down the size, and won't wrap again as the window expands.
The main part of the binding is:
CTOR(...) {
....
m_text->Bind(wxEVT_SIZE, &DIALOG_WRAPTEXT::onResize, this);
}
void CLASS::onResize( wxSizeEvent& event )
{
m_text->Wrap( event.GetSize().GetWidth() );
event.Skip();
}
The result looks OK when the dialog is first shown, but when you resize narrower and back up, you get this result:
A minimum reproducible example is:
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
class DIALOG_WRAPTEXT: public wxDialog
{
public:
DIALOG_WRAPTEXT( wxWindow* parent,
const wxString& aTitle, const wxSize aSize );
private:
void onResize( wxSizeEvent& evt );
wxBoxSizer* m_itemBoxSizer;
wxStaticText* m_text;
};
DIALOG_WRAPTEXT::DIALOG_WRAPTEXT(
wxWindow* parent, const wxString& aTitle, const wxSize aSize ):
wxDialog( parent, wxID_ANY, aTitle,
wxPoint( -1, -1 ), aSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
m_itemBoxSizer = new wxBoxSizer( wxVERTICAL );
SetSizer( m_itemBoxSizer );
wxString msg("Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
);
m_text = new wxStaticText( this, wxID_ANY, msg );
// wxEXPAND makes no difference
m_itemBoxSizer->Add( m_text, 1, wxALIGN_TOP | wxALL | wxEXPAND, 5 );
// Bind to m_text or this, same effect
m_text->Bind(wxEVT_SIZE, &DIALOG_WRAPTEXT::onResize, this);
}
void DIALOG_WRAPTEXT::onResize( wxSizeEvent& event )
{
//m_text->Freeze(); // makes no difference
const auto w = event.GetSize().GetWidth();
wxLogDebug( "Wrap to width: %d",w ); // produces sensible values
m_text->Wrap( w );
//m_text->Thaw();
event.Skip();
}
class MyApp: public wxApp
{
public:
bool OnInit() override
{
auto d = new DIALOG_WRAPTEXT(NULL, "Dialog title", wxSize(200, 200));
d->ShowModal();
d->Destroy();
}
};
wxIMPLEMENT_APP(MyApp);
What is the right way to dynamically wrap static text in a dialog?
Without any wrap(), the wxStaticText displays the text correctly (wrapping at word boundaries) using the follwing minimal code on windows with wx 3.0.2. I can resize the dialog (shrink, grow) and the wxStaticText will update itself correctly. This is not enough for your use case? Are you sure you need to use the wrap function?
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
class DIALOG_WRAPTEXT : public wxDialog
{
public:
DIALOG_WRAPTEXT(wxWindow* parent,
const wxString& aTitle, const wxSize aSize);
private:
void onResize(wxSizeEvent& evt);
wxBoxSizer* m_itemBoxSizer;
wxStaticText* m_text;
};
DIALOG_WRAPTEXT::DIALOG_WRAPTEXT(
wxWindow* parent, const wxString& aTitle, const wxSize aSize) :
wxDialog(parent, wxID_ANY, aTitle,
wxPoint(-1, -1), aSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
m_itemBoxSizer = new wxBoxSizer(wxVERTICAL);
SetSizer(m_itemBoxSizer);
wxString msg("Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
"Lots and lots of text to wrap hopefully. "
);
m_text = new wxStaticText(this, wxID_ANY, msg);
// wxEXPAND makes no difference
m_itemBoxSizer->Add(m_text, 1, wxALIGN_TOP | wxALL | wxEXPAND, 5);
// Act on dialog resize
Bind(wxEVT_SIZE, &DIALOG_WRAPTEXT::onResize, this);
}
void DIALOG_WRAPTEXT::onResize(wxSizeEvent& event)
{
// layout everything in the dialog
Layout();
event.Skip();
}
class MyApp : public wxApp
{
public:
bool OnInit() override
{
auto d = new DIALOG_WRAPTEXT(NULL, "Dialog title", wxSize(200, 200));
d->ShowModal();
d->Destroy();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
Encountered similar problem. I have to store the unwrapped message somewhere, when wxStaticText is resized, set the message and call wrap. otherwise the line may not be wrapped nicely.
void MyFrame::onResize(wxSizeEvent& evt)
{
const auto w = evt.GetSize().GetWidth();
m_text->SetLabel(m_msg); // unwrapped message
m_text->Wrap(w);
evt.Skip();
}

Updating wxWidgets Radio Item Dynamically

I'm trying to dynamically change the state of a group of radio items and I could not figure out how to do it. I attached my code and the CMakeLists.txt file below. When the Test method is called, I expect the radio item 3 to be the checked, but radio item 1 remains checked. Can anyone show me where do I do wrong?
Thank you.
#CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(menu_radio_item)
find_package(wxWidgets REQUIRED core)
if(wxWidgets_FOUND)
include(${wxWidgets_USE_FILE})
add_executable(radio_item main.cpp)
target_link_libraries(radio_item ${wxWidgets_LIBRARIES})
endif(wxWidgets_FOUND)
Code:
#include <wx/wx.h>
// Application class declaration
class MyApp : public wxApp {
public:
virtual bool OnInit();
};
DECLARE_APP(MyApp)
// Frame class declaration
#define wxID_RADIO_ITEM_1 wxID_HIGHEST + 1
#define wxID_RADIO_ITEM_2 wxID_HIGHEST + 2
#define wxID_RADIO_ITEM_3 wxID_HIGHEST + 3
#define wxID_TEST wxID_HIGHEST + 4
class MyFrame : public wxFrame {
public:
MyFrame(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnRadioItemPressed(wxCommandEvent& event);
void OnTest(wxCommandEvent& event);
void OnRadioItemUpdate(wxUpdateUIEvent& event);
private:
wxMenuBar *menubar;
wxMenu *file;
int item_controller;
DECLARE_EVENT_TABLE()
};
// Application class implementation
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit() {
MyFrame *frame = new MyFrame(wxT("Simple Menu and Toolbar"));
frame->Show();
return true;
}
// Frame class definition
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
EVT_MENU(wxID_RADIO_ITEM_1, MyFrame::OnRadioItemPressed)
EVT_MENU(wxID_RADIO_ITEM_2, MyFrame::OnRadioItemPressed)
EVT_MENU(wxID_RADIO_ITEM_3, MyFrame::OnRadioItemPressed)
EVT_MENU(wxID_TEST, MyFrame::OnTest)
EVT_UPDATE_UI(wxID_RADIO_ITEM_1, MyFrame::OnRadioItemUpdate)
EVT_UPDATE_UI(wxID_RADIO_ITEM_2, MyFrame::OnRadioItemUpdate)
EVT_UPDATE_UI(wxID_RADIO_ITEM_3, MyFrame::OnRadioItemUpdate)
END_EVENT_TABLE()
MyFrame::MyFrame(const wxString& title) :
wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(280, 180)),
item_controller(1) {
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_EXIT, wxT("&Quit"));
file->AppendSeparator();
file->AppendRadioItem(wxID_RADIO_ITEM_1, wxT("One"));
file->AppendRadioItem(wxID_RADIO_ITEM_2, wxT("Two"));
file->AppendRadioItem(wxID_RADIO_ITEM_3, wxT("Tree"));
file->AppendSeparator();
file->Append(wxID_TEST, wxT("Test"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
Centre();
}
void MyFrame::OnQuit(wxCommandEvent& event) {
Close(true);
}
void MyFrame::OnRadioItemPressed(wxCommandEvent& event) {
if(event.GetId() == wxID_RADIO_ITEM_1) {
std::cout << "Item 1 is pressed" << std::endl;
}
else if(event.GetId() == wxID_RADIO_ITEM_2) {
std::cout << "Item 2 is pressed" << std::endl;
}
else if(event.GetId() == wxID_RADIO_ITEM_3) {
std::cout << "Item 3 is pressed" << std::endl;
}
else {
std::cout << "No match!" << std::endl;
}
}
void MyFrame::OnRadioItemUpdate(wxUpdateUIEvent& event) {
std::cout << "OnRadioItemUpdate" << std::endl;
wxMenuItem* item;
switch(item_controller) {
case 1:
item = GetMenuBar()->FindItem(wxID_RADIO_ITEM_1);
item->Check(true);
break;
case 2:
item = GetMenuBar()->FindItem(wxID_RADIO_ITEM_2);
item->Check(true);
break;
case 3:
item = GetMenuBar()->FindItem(wxID_RADIO_ITEM_3);
item->Check(true);
break;
}
/*
std::cout << "OnRadioItemUpdate" << std::endl;
switch(event.GetId()) {
case wxID_RADIO_ITEM_1:
event.Check(item_controller == 1);
break;
case wxID_RADIO_ITEM_2:
event.Check(item_controller == 2);
break;
case wxID_RADIO_ITEM_3:
event.Check(item_controller == 3);
break;
}
*/
}
void MyFrame::OnTest(wxCommandEvent& event) {
item_controller = 3;
UpdateWindowUI();
}
You shouldn't change the state of anything directly from wxEVT_UPDATE_UI handlers, this is not how it works. You need to call event.Check() instead to indicate whether the item for which the event is being handled should be checked or not. As a consequence of this, you can't reuse the same handler for all the items -- unless you want all of them to be enabled or disabled at the same time, that is.
It is, on one hand, much simpler than you think, but on the other hand very different too. Update UI mechanism is "functional" in nature: you don't change the state, you just define what the state should be.
Read the documentation and, maybe more usefully, look at the examples of using wxUpdateUIEvent in the samples code (just grep them for it) to see how it should be used.

How to drag and drop between two (or more) wxListCtrl (wxGTK)?

I'm trying to organize a wxListCtrl-derived control (xList) which support DnD between items (LC_REPORT view). So, I catch BEGIN_DRAG event
Connect(wxEVT_COMMAND_LIST_BEGIN_DRAG,
(wxObjectEventFunction)&xList::OnBeginDrag
);
and OnBeginDrag function is designed in a way to catch mouse motion and mouse left button up events for each instance of xList (list):
list->Connect(wxEVT_MOTION,
wxMouseEventHandler(xList::OnMoveDrag)
);
list->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(xList::OnEndDrag)
);
(and OnEndDrag disconnect them all). When I have single xList instance (one panel) it works perfectly, but when I have two it looks like motion and left-up events are caught only for the panel I start dragging from: I can DnD inside single panel, but when I drag mouse from one panel to another it still acts like xList::OnMoveDrag is still working for first panel. What I am missing to?
Is wxEVT_MOTION handled for each widget separately? If so, why program acts like this. If not, why it is always handled for widget I start dragging from, not the last connected one?
Here is a sample code (as far simple as I could reach) to show what's happening:
#include <wx/wx.h>
#include <vector>
class xList;
// class to store group of xList to DnD between
class DnDxList
{public:
void BeginDrag ();
void EndDrag ();
void AddList (xList* l) {list.push_back (l); }; // register new xList object
private:
std::vector<xList*> list;
};
class xList: public wxListCtrl
{public:
xList (DnDxList& dnd,
wxWindow *parent,
wxWindowID winid = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxLC_ICON,
const wxValidator &validator = wxDefaultValidator,
const wxString &name = wxListCtrlNameStr
);
virtual ~xList () {};
void OnBeginDrag(wxListEvent& event);
void OnEndDrag(wxMouseEvent& event);
void OnMoveDrag(wxMouseEvent& event);
DnDxList& dndsource; // keep reference to common DnDxList object
};
void DnDxList::BeginDrag () // connect motion and left-up events for all lists in group
{for (std::vector<xList*>::const_iterator i = list.begin(); i != list.end(); i++)
{(*i)->Connect(wxEVT_MOTION,
wxMouseEventHandler(xList::OnMoveDrag)
);
(*i)->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(xList::OnEndDrag)
);
}
};
void DnDxList::EndDrag () // disconnect all
{for (std::vector<xList*>::const_iterator i = list.begin(); i != list.end(); i++)
{(*i)->Disconnect(wxEVT_MOTION,
wxMouseEventHandler(xList::OnMoveDrag)
);
(*i)->Disconnect(wxEVT_LEFT_UP,
wxMouseEventHandler(xList::OnEndDrag)
);
}
}
xList::xList (DnDxList& dnd,
wxWindow *parent,
wxWindowID winid,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator &validator,
const wxString &name
): wxListCtrl (parent, winid, pos, size, style, validator, name),
dndsource (dnd)
{Connect(wxEVT_COMMAND_LIST_BEGIN_DRAG,
(wxObjectEventFunction)&xList::OnBeginDrag
);
dndsource.AddList (this);
};
void xList::OnBeginDrag(wxListEvent& event) // begin drag
{SetCursor(wxCursor(wxCURSOR_HAND));
dndsource.BeginDrag();
}
void xList::OnMoveDrag(wxMouseEvent& event)
{std::cout << "Movie: " << this << std::endl; // to show the object for which the move event is called for
}
void xList::OnEndDrag(wxMouseEvent& event)
{std::cout << "End: " << this << std::endl;
dndsource.EndDrag();
SetCursor(wxCursor(*wxSTANDARD_CURSOR));
}
class xFrame: public wxFrame
{
public:
xFrame (const wxString& title,
const wxPoint& pos,
const wxSize& size
);
~xFrame () { }
private:
void OnExit(wxCommandEvent& event);
DECLARE_EVENT_TABLE();
DnDxList* dndxlist;
xList* lp;
xList* rp;
wxPanel* panel;
};
BEGIN_EVENT_TABLE(xFrame, wxFrame)
EVT_MENU(wxID_EXIT, xFrame::OnExit)
END_EVENT_TABLE()
xFrame::xFrame(const wxString& title,
const wxPoint& pos,
const wxSize& size
): wxFrame(NULL, wxID_ANY, title, pos, size)
{
panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
// create common DnDxList
dndxlist = new DnDxList();
// create two panels
lp = new xList (*dndxlist,
panel,
wxID_ANY,
wxDefaultPosition,
wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL
);
rp = new xList (*dndxlist,
panel,
wxID_ANY,
wxDefaultPosition,
wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL
);
// some contents
lp->InsertColumn(0, _("A"));
lp->InsertColumn(1, _("B"));
lp->InsertColumn(2, _("C"));
lp->InsertColumn(3, _("D"));
lp->SetColumnWidth(0, 100);
lp->SetColumnWidth(1, 100);
lp->SetColumnWidth(2, 100);
lp->SetColumnWidth(3, 100);
for (long i = 0; i < 100; i++)
{lp->InsertItem(i, 1);
for (int j = 0; j < 4; j++)
{wxString s;
s << _("lp [") << i << _(", ") << j << _("]");
lp->SetItem (i, j, s);
}
}
rp->InsertColumn(0, _("A"));
rp->InsertColumn(1, _("B"));
rp->InsertColumn(2, _("C"));
rp->InsertColumn(3, _("D"));
rp->SetColumnWidth(0, 100);
rp->SetColumnWidth(1, 100);
rp->SetColumnWidth(2, 100);
rp->SetColumnWidth(3, 100);
for (long i = 0; i < 100; i++)
{rp->InsertItem(i, 1);
for (int j = 0; j < 4; j++)
{wxString s;
s << _("rp [") << i << _(", ") << j << _("]");
rp->SetItem (i, j, s);
}
}
sizer->Add(lp,1, wxEXPAND | wxALL, 10);
sizer->Add(rp,1, wxEXPAND | wxALL, 10);
panel->SetSizer(sizer);
}
void xFrame::OnExit(wxCommandEvent& event)
{
Close( true );
}
class xApp: public wxApp
{
public:
virtual bool OnInit();
};
IMPLEMENT_APP(xApp);
bool xApp::OnInit()
{
xFrame *frame = new xFrame(_("Frame"), wxPoint(50, 50), wxSize(450, 340) );
frame->Show(true);
return true;
}
Looking into console output one can find that mouse motion event and mouse left-up event always calls methods for the same object, where dragging starts from, not the object mouse is actually on.
wxListCtrl-specific drag events are indeed only meant to be used for dragging the items inside the same control. To drag and drop stuff between different controls, or even between different applications, you need to use the general wxDragSource and wxDropTarget classes.

OSG with QWidget shows extra border

I have modified the osgViewerQt example in order to load a point cloud and visualize it in a Qt application. As you can see in the attached image, the cloud point cloud is shown but there is an extra border in the window (see the arrows).
I spent all the weekend trying to figure how to "expand" the window in order to remove that border, but it keeps showing.
Do you know what can I do to remove it? I'll post the code for the modified osgViewerQt and the piece of code where I use it.
viewer_widget.h
#ifndef VIEWER_WIDGET_H
#define VIEWER_WIDGET_H
#include "osgViewer/CompositeViewer"
#include <QTimer>
#include <QWidget>
class QGridLayout;
class QWidget;
class ViewerWidget : public QWidget, public osgViewer::CompositeViewer {
private:
std::string cloud_file;
std::string cloud_filepath;
QTimer timer_;
QWidget* widget;
QGridLayout* grid;
osg::ref_ptr<osgViewer::View> view;
private:
ViewerWidget(const ViewerWidget& V);
ViewerWidget& operator=(const ViewerWidget& V);
private:
QWidget* AddViewWidget(osg::Camera* camera,osg::Node* scene);
osg::Camera* CreateCamera(int x,int y,int w,int h,const std::string& name="",
bool windowDecoration=false
);
osg::Node* ReadOctree(const std::string& file);
public:
ViewerWidget(const std::string& filename,const std::string& filepath,bool color,
osgViewer::ViewerBase::ThreadingModel threadingModel
= osgViewer::CompositeViewer::ThreadPerCamera
);
virtual ~ViewerWidget(void){}
void AddCloud(void);
void StartFrameTimer(int msec=10) { timer_.start(msec); }
virtual void paintEvent( QPaintEvent* event ) { frame(); }
};
#endif // VIEWER_WIDGET_H
osg_viewer.cpp
#include "viewer_widget.h"
#include "osgDB/ReadFile"
#include "osgGA/TrackballManipulator"
#include "osgQt/GraphicsWindowQt"
#include "osgViewer/ViewerEventHandlers"
#include <QGridLayout>
#include <QDebug>
ViewerWidget::ViewerWidget(const std::string &filename,const std::string &filepath,
bool color, osgViewer::ViewerBase::ThreadingModel threadingModel
) :
QWidget(),
cloud_file( filename ),
cloud_filepath( filepath )
{
// this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
connect( &(this->timer_), SIGNAL(timeout()), this, SLOT(update()) );
}
QWidget* ViewerWidget::AddViewWidget(osg::Camera *camera,osg::Node *scene) {
view = new osgViewer::View;
view->setCamera( camera );
view->setSceneData( scene );
osg::Stats* stats = this->getViewerStats();
if(stats) stats->report(std::cout);
addView( view );
view->addEventHandler( new osgViewer::StatsHandler );
view->setCameraManipulator( new osgGA::TrackballManipulator );
osgQt::GraphicsWindowQt* gw = dynamic_cast<osgQt::GraphicsWindowQt*>(
camera->getGraphicsContext()
);
return gw ? gw->getGLWidget() : 0;
}
osg::Camera* ViewerWidget::CreateCamera(int x,int y,int w,int h,const std::string &name,
bool windowDecoration
) {
osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->windowName = name;
traits->windowDecoration = windowDecoration;
traits->x = x;
traits->y = y;
qDebug() << "w:" << w << " h:" << h;
traits->width = w;
traits->height = h;
traits->doubleBuffer = true;
traits->alpha = ds->getMinimumNumAlphaBits();
traits->stencil = ds->getMinimumNumStencilBits();
traits->sampleBuffers = ds->getMultiSamples();
traits->samples = ds->getNumMultiSamples();
osg::ref_ptr<osg::Camera> camera = new osg::Camera;
camera->setGraphicsContext( new osgQt::GraphicsWindowQt(traits.get()) );
camera->setClearColor( osg::Vec4(0,0,0,1) );
camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
camera->setProjectionMatrixAsPerspective(
30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0f, 10000.0f );
return camera.release();
}
osg::Node* ViewerWidget::ReadOctree(const std::string &file) {
osg::Group* group = new osg::Group;
group->addChild( osgDB::readNodeFile(file, options) );
return group;
}
void ViewerWidget::AddCloud() {
std::cout << "Loading cloud from file:" << cloud_file.c_str() << "\n";
QWidget* widget = AddViewWidget(
CreateCamera(0,0,100,100,"cam1",true),
ReadOctree(cloud_file)
);
grid = new QGridLayout;
grid->addWidget( widget, 0, 0 );
this->setLayout( grid );
}
Now, in where this widget is used (simplified a bit to show only the relevant parts):
cloud.h
#ifndef CLOUD_H
class Cloud: public QObject {
Q_OBJECT
private:
osg::ref_ptr<ViewerWidget> osg_widget;
QDockWidget* dock;
/// MORE ATTRIBUTES
public:
Cloud(){
/// ...
dock = new QDockWidget;
osg_widget = new ViewerWidget( getFileName(), getFilePath(), has_color);
dockWidget->setAllowedAreas(Qt::RightDockWidgetArea);
dockWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
dockWidget->addWidget(osg_widget);
}
/// More methods, cloud manipulators, etc.
};
#endif
When specifying the size policy, I have also tried with Minimum, MinimumExpanding and
Ignored, but with the same effect. I tried to specify the size policy directly inside the ViewerWidget (as it inherits from QWidget) and to specify it its widget attribute, too, but with no success.
You're using a grid layout to insert the view widget in your target window, if I understand correctly:
grid = new QGridLayout;
grid->addWidget( widget, 0, 0 );
this->setLayout( grid );
Layouts usually insert padding around their elements (called margin in the Qt docs). You can tune that using QLayout::setContentsMargin(), so here something in the spirit of this
grid->setContentsMargins(0,0,0,0);
should do the trick.