I have an issue concerning wxWidgets 2.9 and the wxComboBox AutoComplete feature. Below is my event table which takes the ENTER event of my ComboBox, on enter I fire OnComboEnter. If I do that I'm not able to select an item from the AutoComplete list since it directly executes the OnComboEnter method on the text the user typed into the ComboBox.
BEGIN_EVENT_TABLE(LVFilterPanel, wxPanel)
EVT_TEXT_ENTER(wxID_ANY, LVFilterPanel::OnComboEnter)
EVT_CONTEXT_MENU(LVFilterPanel::OnComboContextMenu)
END_EVENT_TABLE()
My ComboBox is declared like this:
mFilterString = new wxComboBox(this, LV_FILTER_STRING, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER);
AutoComplet is done using the default AutoComplete method found in wxWidgets 2.9:
mFilterString->AutoComplete(historyarr);
historyarr is a wxArrayString filled with the strings which were previously typed in by the user.
The OnComboEnter method looks like this:
void LVFilterPanel::OnComboEnter(wxCommandEvent& event) {
wxCommandEvent ce(wxEVT_COMMAND_BUTTON_CLICKED, LV_FILTER);
static_cast<wxButton*>(FindWindow(LV_FILTER))->Command(ce);
}
My question now is, how could I change the event handling in a way that it is able to select the item first and then processes OnComboEnter, so the user is able to select an item first (or may not select an item at all and directly hit enter to launch the event and the OnComboEnter method).
Thanks in advance.
Greets,
Roin
If you need to execute your event handler after the standard handling takes place, the usual trick is to do nothing in your event handler (which means also calling event.Skip(), of course!) except setting some internal flag and check this flag in EVT_IDLE handler. If it is set, then do whatever you need (e.g. button->Command() in your case) and reset the flag.
This approach ensures that the handler is ran "soon after" the event happens without interfering with normal event processing.
I had same problem but wxTextCtrl, this is my solution:
TextCtrl2 = new wxTextCtrl(this, ID_TEXTCTRL2, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_TEXTCTRL2"));
TextCtrl2->SetHint("Search...");
TextCtrl2->AutoComplete(m_AutoCompleteChoices);
TextCtrl2->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(StartFrame::OnKeyDown),NULL, this);
void StartFrame::OnKeyDown(wxKeyEvent& event)
{
switch (event.GetKeyCode()) {
case WXK_RETURN:
QueryCache(TextCtrl2->GetValue()); // <- This is anything to do!
break;
}
event.Skip();
}
I could use wxSearchCtrl but Autocomplete not working in that control and I don't know why.
Related
I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?
You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.
I've found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler. In my case I am a bit stuck as I am getting the editFinished signal twice even when my slot handler is doing nothing.
For a test, I have an array of QLineEdit which use a signalMapper to connect the editingFinished() signals to a single slot. The signalMapper passes the array index so I can see where the signal came from.
eg:
testenter::testenter(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testenter)
{
// setup the UI according to the .h file
ui->setupUi(this);
signalMapper = new QSignalMapper(this);
// init the labels and edit boxes
for (int i = 0; i < 10; i++)
{
pm_label[i] = new QLabel(ui->scrollArea);
QString text = QString("Number %1").arg(i);
pm_label[i]->setText(text);
pm_label[i]->setGeometry(10,20+i*30, 50, 20);
pm_label[i]->show();
pm_editBox[i] = new QLineEdit(ui->scrollArea);
pm_editBox[i]->setGeometry(80,20+i*30, 50, 20);
pm_editBox[i]->show();
signalMapper->setMapping(pm_editBox[i], int(i));
connect(pm_editBox[i], SIGNAL(editingFinished()), signalMapper, SLOT(map()));
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(data_entry(int)));
}
void testenter::data_entry(int entry)
{
//dummy
}
When run in the debugger, if I enter data into one box then either hit return or select another box with the mouse (ie change focus) , then it calls data_entry twice, the first time with index of the box that is losing focus and the 2nd time with the box which gets the focus.
So my question: Am I missing something? Is this expected behaviour or a bug?
If a bug, anyone know a way round it as I wanted to use this signal to do custom validation on data when it is entered (by either return, tab or mouse click to change focus).
First off, no this isn't expected behavior, i.e. selecting a QLineEdit should not cause it's editingFinished signal to be emitted.
There are a couple of possible things that may cause this problem:
You've accidentally connected a signal twice to a slot
The slot map() is causing the newly selected box to lose focus
In the same vain, if you're debugging and using a break point to detect when the slots are getting called you may be causing the QLineEdit to lose focus when the active application changes from your QWidget to your debugger, again causing the signal to be sent again.
If you're having problems because of a doubly connected slot, which doesn't seem to be the case because you're specifically getting a signal from two different QLineEdits, you can make sure that this isn't happening by specifying the connection type, the connect method actually has an additional optional argument at the end which allows you to change the type from a DefaultConnection to a UniqueConnection.
That being said, data validation is something that Qt has an established mechanism for, and I suggest that you use it if possible, look into extending the QValidator abstract base class Ref Doc. You then tell each of your QLineEdit's to use the same validator.
I have run into the same issue. It really does emit the signal twice, which is a known bug: https://bugreports.qt.io/browse/QTBUG-40 which however has not been addressed for a very long time.
Finally I found that the best solution in my case is to change the signal from editingFinished to returnPressed. As a side effect this behaves much more predictably from the user perspective. See also here: http://www.qtforum.org/article/33631/qlineedit-the-signal-editingfinished-is-emitted-twice.html?s=35f85b5f8ea45c828c73b2619f5750ba9c686190#post109943
The OP "found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler." Well, that is my situation also, and here is where I ended up. So, at the risk of going off topic...
In my situation, when my slot receives the editingFinished signal sent from the QLineEdit, I launch a modal QMessageBox to ask the user something. The appearance of that message box is what triggers the QLineEdit to send the second, undesirable editingFinished signal.
A post in the bug report (https://bugreports.qt.io/browse/QTBUG-40) mentioned by #V.K. offers a workaround which helped me. The following is my implementation of the workaround. I let Qt magic mojo automatically connect the QLineEdit signal to my MainWindow slot.
void MainWindow::on_textbox_editingFinished( void )
{
QLineEdit * pTextbox = qobject_cast<QLineEdit *>( QObject::sender() );
if ( !pTextbox->isModified() )
{
// Ignore undesirable signals.
return;
}
pTextbox->setModified( false );
// Do something with the text.
doSomething( pTextbox->text() );
}
void MainWindow::doSomething( QString const & text )
{
QMessageBox box( this );
box.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
box.setText( "Are you sure you want to change that text value?" );
if ( box.exec() == QMessageBox::Yes )
{
// Store the text.
m_text = text;
}
}
I've created a wxGrid, populated it with data, and have created a column that contains checkboxes, and made them editable. All good so far.
co_Grid->SetReadOnly(at_RowCount, 24, false);
co_Grid->SetCellRenderer(at_RowCount, 24, new wxGridCellBoolRenderer);
co_Grid->SetCellEditor(at_RowCount, 24, new wxGridCellBoolEditor);
What I want to be able to do now is to add an event handler for the checkbox toggle event.
I've tried using the OnCellValueChanged event for the grid, but that only fires after the user leaves the cell, because before then the editor is still open (and the cell hasn't actually changed yet)
I'm pretty sure that I need to create an event handler for the wxGridCellBoolEditor but that's where I'm struggling.
I tried connecting an event in the OnEditorShown event, but that didn't go well (unhandled exception when I click on the cell to open the editor):
void cTeamGrid::OnEditorShown( wxGridEvent& ev )
{
int row = ev.GetRow(),
col = ev.GetCol();
co_Grid->GetCellEditor(row, col)->GetControl()->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
wxCommandEventHandler(cTeamGrid::OnGridCheckChange), NULL, this);
}
What am I doing wrong?
I had a similar problem myself. I bypassed it by setting the checkbox column to read-only and having the wxGrid control manually handle the click event to toggle the checkbox state (you also have to manage the double-click). This method is not the most orthodox, also because now each click on the cell, and not on the checkbox, will change the state. In my opinion, however, this can also be a desirable behaviour. In addition, this enables you to let the user change the checkbox with the keyboard (by capturing the KeyPress events).
I am populating a sytem tray icon menu (QMenu) from entries in an xml file which is read when my application starts up.
I am unsure of how to properly set up the SLOT end of the action:
QList<CMenuItem> menuItems = m_layout->getMenuItems();
QListIterator<CMenuItem> iter(menuItems);
while (iter.hasNext())
{
CMenuItem menuItem = iter.next();
QAction *action = new QAction(menuItem.qsTitle, this);
connect(action, SIGNAL(triggered()), this, SLOT(launchMenuItem()));
trayIconMenu->addAction(action);
}
How does my "launchMenuItem()" SLOT know which menu item was triggered? I can't make a SLOT for each menu item as I don't know how many items will exist until run time.
I can think of some ugly ways to do this, but I am looking for the RIGHT way.
What I usually do is to use QAction::setData(const QVariant&) to store whatever action ID I need. Then on slot side I retrieve ID with QAction::data() and behave accordingly.
Note that QVariant obviously accepts much more than basic int (which is what I use to identify actions), you can pass any QVariant-compatible info.
edit : oh! btw, this is somehow ugly because I make use of QObject::sender() to cast triggered action back. Sorry for that, but it works anyway.
I am creating my own subclass of wxDialog and it works well when used as a modeless dialog like so:
AddDialog newAddDialog = new AddDialog(this, wxID_ANY, _T("Dialog Title"), wxDefaultPosition, wxDefaultSize, 0);
if (newAddDialog.ShowModal() == wxID_OK)
{
//do something
}
When using ShowModal(), the flow of the program stops until OK or Cancel is pressed. I need the dialog to show up, but not stop the flow of the GUI, so I tried this:
AddDialog newAddDialog = new AddDialog(this, wxID_ANY, _T("Dialog Title"), wxDefaultPosition, wxDefaultSize, 0);
if (newAddDialog.Showl() == wxID_OK)
{
//do something
}
When using Show(), the dialog briefly shows up and then disappears. I thought it might be a scope issue, so I used a pointer for newAddDialog. Then, the dialog shows up, when when I click OK or Cancel, the dialog closes, but the if statement code does not execute even if OK is clicked.
Does anyone know how to proceed? Thanks.
Further clarification:
I have virtual void function in my Dialog subclass that I overide in another class. I can't seem to get the event working when I overide, however, if I have a void in the actual Dialog subclass, I get the event call. This seems to be an overide problem, but I don't know what the problem would be. This is not the main GUI that I'm calling in the OnInit() call - could that be a problem?
ShowModal blocks execution of your program and returns the outcome (like wxID_OK). On the other hand, Show just shows the dialog and returns immediately, so you can't check the outcome (what the user pressed for buttons) from it's return value. Instead you have to communicate the outcome of that dialog-box by sending an event from withing the dialog or something like that.
AddDialog newAddDialog = new AddDialog(...
Does this compile? Really?
The 'new' operator returns a pointer to void, so the code you have posted looks very odd indeed.
The usual way of doing this is:
AddDialog * newAddDialog = (AddDialog *) new AddDialog( ...
Or
AddDialog newAddDialog( ...