QTableView, conditional row selection in ExtendedSelection mode - row

I use QTableView in ExtendedSelection mode with SelectItems behavior.
I would like to implement the following behavior:
1) When only one cell is selected - do nothing
2) When more than one cell is selected - select full row for each selected cell.
Here is an example:
void BaseTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
QTableView::selectionChanged(selected, deselected);
QItemSelection currentSelection = selectionModel()->selection();
//table has selection
if(!currentSelection.isEmpty())
{
QItemSelectionRange selectionRange = currentSelection.first();
//select whole row if more than one cell is selected
if(currentSelection.count() > 1 || selectionRange.height() > 1 || selectionRange.width() > 1)
{
if(!deselected.isEmpty())
selectionModel()->select(deselected, QItemSelectionModel::Deselect | QItemSelectionModel::Rows);
if(!selected.isEmpty())
selectionModel()->select(currentSelection, QItemSelectionModel::Select | QItemSelectionModel::Rows);
}
}
}
This code somehow works. But I can't find a better solution.
Problems:
1) Blinking of selection when mouse is used. Probably due to double call of selectionChanged or something like this.
2) Selection with Shift key does not work as it should. When selection area with shift key pressed is changed deselected is always empty. So selection is always increases.
what is a better solution to implement described behavior?
How to fix selection with a pressed Shift key?

I found another solution.
I reimplemented select() method of QItemSelectionModel. Than it's enough to do some check and add Rows flag to the initial flag.

Related

Qt / C++ - Traverse Menu with Keys (Not Mouse)

I'm trying to do what you'd see in a video game menu while playing with a controller, but with a keyboard and QT/C++.
I don't know where to begin and because I don't know what the term is I don't know what to search for. Can't find anything on Google.
I am making a menu where left/right/up/down select items. No mouse input.
Can anyone help with a starting point, please?
Here is a sample method for handling key presses.
The class Widget is assumed to subclass QWidget
_rows member variable is number of menu rows
_row member variable is the currently selected row index starting from 0
updateSelectedMenuItem() is a method for doing whatever you want to do to have certain menu item selected, such as moving some marker on screen or changing colors.
// overriding superclass method
void Widget::keyPressEvent(QKeyEvent *ev)
{
switch(ev->key()) {
case Qt::Key_Up:
if (--_row < 0) _row = _rows-1;
updateSelectedMenuItem();
break;
case Qt::Key_Down:
if (++_row >= _rows) _row = 0;
updateSelectedMenuItem();
break;
default:
// call superclass event handler method
QWidget::keyPressEvent(ev);
}
// Note: QKeyEvent starts with isAccepted() == true
}
Note that this will rely on normal keyboard repeating, so it will behave the same as if you pressed up/down in any normal application where cursor moves. If you want different behaviour (for example if you are controlling a game, instead of moving in a menu), you should also override keyReleaseEvent, so you know when key is actually pressed down and released again.

CListCtrl selection

I'm trying to do something that I think is simple but I can't seem to make it work!
I have a CListCtrl and I want to select the last element in the list if the user clicks in the view empty space. I can do that just by calling Select(lastElementInList), but the element that was previously selected and that is now unselected still has a "bounding rectangle" around it.
The code that Implements this is as follows:
int nSel = GetNextItem(-1, LVNI_SELECTED);
if (nSel != -1)
SetItemState(nSel, 0, LVIS_SELECTED);
Select(lastElementInList);
Any hints? What am I missing?
The "bounding rectangle" you see indicates that the element currently is "focused", ie. in a state where a user interaction, such as pressing the down and up arrows, would start off from this point.
Change focused element
To move focus to your newly selected element you'll have to use SetItemState together with LVIS_FOCUSED, as in the below example:
if (nSel != -1)
SetItemState (nSel, ~LVIS_FOCUSED, LVIS_FOCUSED); // (1)
SetItemState (lastElementInList, LVIS_FOCUSED, LVIS_FOCUSED); // (2)
// (1) -> Remove focus from `nSel`
// (2) -> Add focus to `lastElementInList`

Difference between '&' and '==' while using Qt mouse functions?

I was reading the documentation of Qt example of "scribble". There I stumbled across the following piece of code:
void ScribbleArea::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && scribbling)
drawLineTo(event->pos());
}
void ScribbleArea::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && scribbling) {
drawLineTo(event->pos());
scribbling = false;
}
}
One question arised in my mind on whether there's actually any difference between event->button() == Qt::LeftButton and (event->buttons() & Qt::LeftButton). Could you please explain? Thanks.
The bitwise AND operator (&) compares each bit of the first operand to the corresponding bit of the second operand.
The operator== compare both operands to see if the values are equal.
That is, for you:
event->buttons() & Qt::LeftButton
Will be true if the LeftButton bit is set and other bits may also be set.
event->button() == Qt::LeftButton
Will be true if only the LeftButton bit is set and other bits should not be set.
An example on how it works :
enum
{
BUTTON_LEFT = 1 << 0,
BUTTON_RIGHT= 1 << 1,
BUTTON_MID = 1 << 2
};
int a = 0;
a |= BUTTON_LEFT;
a |= BUTTON_RIGHT;
a |= BUTTON_MID;
if ( a & BUTTON_RIGHT )
std::cout << "The button right is pressed." << std::endl;
if ( a == BUTTON_RIGHT )
std::cout << "There is only the button right." << std::endl;
The output of this will be: The button right is pressed.. http://ideone.com/BunrTs
event->buttons() & Qt::LeftButton
This is true if the LeftButton bit is set; other bits may also be set.
event->button() == Qt::LeftButton
This is true if only the LeftButton bit is set; other bits may not be set.
Looks like a bug to me.
Assuming the mouse buttons are represented as unique bit "flags", the first tests if the left mouse button is pressed, while filtering out any other buttons that might have been pressed at the same time.
The second, however, requires that only the left button has been released. So if you do a double-press to start scribbling, then release both buttons at the same time, it should fail to detect the release.
UPDATE:
This random Qt reference seems to support the assumption, at least:
Qt::LeftButton 0x00000001 The left button is pressed, or an event refers to the left button. (The left button may be the right button on left-handed mice.)
Qt::RightButton 0x00000002 The right button.
Qt::MidButton 0x00000004 The middle button.
Also, as pointed out by brilliant commentor #svk, the two use different ways to access the mouse button flags, so it should be fine.
The fact that release events don't ever report multiple buttons is one of those things you learn after working with Qt for a while, something I haven't done. Sorry for the confusion.
Please note that in case of mouseMoveEvent method event->buttons() is used (it has "s" at the end) and it returns state of multiple buttons. So if many buttons are down many flags will be set.
In contradiction in case of mouseReleaseEvent and mousePressEvent method event->button() is used (no "s" at the end), and this return which button invoke this event this means that only ONE flag is set and it is possible to use equal operator.
When you note that check already excepted answer from Mike Seymour.
Off-topic: in Qt flags have also testFlag method.

Find out which column is selected in a QTableWidget

I have a QTableWidget with SelectionMode set to SingleSelection, and SelectionBehavior set to SelectColumns. This means that only a single column can be selected.
But I later need to find out which column is selected, and the only functions I can use are selectedIndexes() or selectedItems(), both of which return entire lists, which is wasteful.
Is there a way to do this more efficiently?
Your approach with the selectedItems() was correct.
As QT cant know that you've set your widget to singlerow/column selection it offers those functions to return a QList<>.
in your case you can work on those by using .first().
Evne though I suggest to use the signals currentColumnChanged() to react in your application
( http://harmattan-dev.nokia.com/docs/library/html/qt4/qitemselectionmodel.html#currentColumnChanged )
you could always iterate over all columns of the selected row via selectionModel()->isColumnSelected()
( http://qt-project.org/doc/qt-4.8/qitemselectionmodel.html#isColumnSelected )
connect(tableWidget, SIGNAL(currentCellChanged(int,int,int,int), this, SLOT(onCellChanged(int,int,int,int)));
void Class::onCellChanged(int curRow, int curCol, int preRow, int preCol)
{
current_Col = curCol;
// curRow, preRow and preCol are unused
}
connect(tableWidget->selectionModel()
, SIGNAL(currentColumnChanged(QModelIndex,QModelIndex))
, SLOT(onColumnChanged(QModelIndex)));
...
void Class::onColumnChanged(const QModelIndex &index)
{
int col = index.column();
}
It seems that the function selectedRanges() does what I need. It returns a list of the selected ranges, but since it's a single column, this list would have only one item (so it's efficient, no big list need to be created).
int column = ui->tableWidget->selectedRanges().front().leftColumn();
currentColumn() returns an int of the current selected column.

How to prevent user from entering invalid values in wxGrid cell?

I'd like to create wxGrid where user can edit some cells, but prohibit entering incorrect valued. For example, only strings of length 4 could be entered there. So, if user enter string of another length, I'd like to show a error message and return to cell editor. How to do it?
If I handle a cell change event EVT_GRID_CELL_CHANGE, for example
void Frame::OnGridCellChange(wxGridEvent& event)
{
int r = event.GetRow(); // get changed cell
int c = event.GetCol(); // get changed cell
if (Grid->GetCellValue(r, c).length() != 4)
{Error E (this);
/* Create the Error message */
E.ShowModal();
// The error message shown, uses clicks OK
// So, what to do here?
}
Grid->ShowCellEditControl(); is not a solution because cell change won't be generated if user edit nothing, but just click another cell - incorrect value would appear in Grid.
Handling EVT_GRID_EDITOR_HIDDEN seems not suitable since it appears before new value actually saved to cell.
You need to use your own specialization of the cell editor.
http://docs.wxwidgets.org/trunk/classwx_grid_cell_editor.html
Perhaps using an event of type wxEVT_GRID_CELL_CHANGING would work for you? If the string that is returned by calling GetString() on the event is not four characters long then you can veto the event, something like:
void Frame::OnGridCellChanging(wxGridEvent& event)
{
if (event.GetString().length() != 4)
{
//Veto the event so the change is not stored
event.Veto();
Error E (this);
E.ShowModal();
}
This does seem to require a wxWidgets 2.9.x release however.