I am testing some Qt code using a QComboBox.
When the QComboBox is clicked and expands, it expands out from the currently selected items position from within the list. This behaviour does not seem correct, though I have done nothing that would change the default behaviour.
So if I expand the box for the first time, is expands downwards since the first item in the box is selected. If I then select the middle item and close and open it again, when it opens it expands half upwards and half downwards. This is because the middle item is selected. If I select the bottom item, and then close and expand again, it will expand completely upwards since the selected item is in the bottom of the list of items.
I am running the application on Linux Mint 18.2. Is this just the way that QComboBoxes are designed, or is there a way I can change this behavior.
The only code at all manipulating this box is...
for ( QStringList::const_iterator i = colorNames.constBegin();
i != colorNames.constEnd(); ++i ) {
QPixmap solidPixmap( 20, 10 );
solidPixmap.fill( QColor( *i ) );
QIcon* solidIcon = new QIcon( solidPixmap );
foreColorBox->addItem( *solidIcon, *i );
backColorBox->addItem( *solidIcon, *i );
}
This could probably be a Linux-Mint specific behaviour. On Windows-7, my QCombobox opens the selection drawer down, regardless of the selection.
On macOS High Sierra, the same combobox has a behaviour as in your question.
So, it is evident that QCombobox implementation uses the native behaviour of the platform it runs.
Related
I have added tooltips to my checkbox elements, but they are too annoying. They appear immediately after hovering the mouse cursor over the element and do not disappear after the cursor has left the checkbox.
I could start a timer, but I don't know how I can check if the cursor is within the desired element or has left it.
And the second question is, is there any event like wxEVT_LEAVE_WINDOW, but for the checkbox to remove the tooltip when the cursor goes out of bounds.
Thanks, #New Pagodi
I am still dont get normal behaivor of tips, but your trich works. I can get tip window from wxCheckBox element just calling GetChildren().
wxRichToolTip* tip = new wxRichToolTip(wxT("INFO"), msg);
tip->SetTimeout(0, 500);
tip->ShowFor(it->second.first);
wxWindowList tipWindow = it->second.first->GetChildren();
auto a = tipWindow.GetLast()->GetData();
a->Bind(wxEVT_MOTION, &DeviceListDialog::onLeaveCheckbox, this);
Environment:
Qt 4.7.1
Qt Creator 2.1.0
c++
Problem:
I have a QTableWidget. I add a row, and then select the first cell.
What I want is to be able to immediately type into the cell after the program selects it, so I don't have to reach for the mouse.
The behavior of the select is to highlight the cell, not put a cursor in it. I have to click on the cell with the mouse before I can type.
Everything I've found so far to do with selection behavior has to do with row, column, or cell selection options; nothing about what the selection of a cell actually does.
Here's my code thus far, works as described; rc is the index to the last row, already determined:
ui->thetable->scrollToBottom();
QModelIndex index = ui->thetable->model()->index(rc, 0);
ui->thetable->selectionModel()->select(index,QItemSelectionModel::Select);
You can use the edit method this way:
ui->thetable->edit(index);
using the index you already computed, or you could connect a custom signal of yours to void QAbstractItemView::edit ( const QModelIndex & index ) slot inherited by QTableWidget's items.
I had a dialog with a menu and a CTabCtrl. The CTabCtrl had one tab, which contained a CDialog. In turn, that contained a few static texts and a CRichEditCtrl. There was no particular issue with the window gaining and losing focus.
I since added a second identical tab, and now every time the tab is changed, all text in the CRichEditCtrl is apparently selected. It is shown in an inverted color scheme and all text is replaced should you hit a key.
The description of flag ECO_NOHIDESEL, says (emphasis mine):
Negates the default behavior for an edit control. The default behavior
hides the selection when the control loses the input focus and shows
the selection when the control receives the input focus. If you
specify ECO_NOHIDESEL, the selected text is inverted, even if the
control does not have the focus.
"shows the selection" to me sounds like "show whatever the selection was the last time this control had the focus," which is not what's happening. Normally nothing is selected before focus is lost, but if I do try to leave a selection, go back to the other tab and return, the entire text, as usual, is selected.
Can this selection be prevented?
void EditorDialog::OnTabSelChange( NMHDR * phdr, LRESULT* pResult ) {
CTabCtrl* ptab = (CTabCtrl*) GetDlgItem( IDC_TAB );
int iPageActive = ptab->GetCurSel();
if ( iPageActive >= appage.N() ) {
AKS( AKSWarn, "got tab change to tab %d when I only have %d ppages", iPageActive, appage.N() );
return;
}
ppageActive = appage[ iPageActive ];
SetActivePagePos();
SCWinUtilSetWindowTextVA( this, "Editor: %s", ppageActive->pszFileName );
}
void EditorDialog::SetActivePagePos() {
// STEP 1: Make the proper tab page visible.
for ( int i = 0; i < appage.N(); i++ )
appage[i]->ShowWindow( SW_HIDE );
ppageActive->ShowWindow( SW_SHOW );
// STEP 2: Make the new tab page the right size and position.
CTabCtrl* ptab = (CTabCtrl*) GetDlgItem( IDC_TAB );
CRect rectTab, rectItem;
ptab->GetClientRect( &rectTab );
ptab->GetItemRect( 0, &rectItem );
int iPageX = rectItem.left + 2;
int iPageY = rectItem.bottom + 4;
int iPageW = rectTab.right - 2 - iPageX;
int iPageH = rectTab.bottom - 2 - iPageY;
ppageActive->SetWindowPos( &wndTop, iPageX, iPageY, iPageW, iPageH, SWP_SHOWWINDOW | SWP_NOZORDER );
// STEP 3: Give the window focus and let it know to redraw.
ppageActive->SetFocus();
// When the tab changes the entire content of the RichEdit is selected for some reason.
// As a workaround I manually clear the selection.
CRichEditCtrl* prich = (CRichEditCtrl*) ppageActive->GetDlgItem( IDC_PATCH );
prich->SetSel(-1,-1);
// Redrawing just the prich, or the ppageActive, or the ptab, doesn't
// cause the RichEdit to redraw correctly, but Redrawing the entire dialog does.
RedrawWindow();
}
The default behavior for Edit and Rich Edit controls is to make the selection invisible when the control does not have the input focus and only make it visible when the control has the focus. The selection is not, however, changed. The ES_NOHIDESEL style overrides this default behavior and causes the selection to always appear in the control, regardless of whether or not it has the focus. You have certainly seen this behavior before: it is what both Microsoft Word and Visual Studio do.
As such, your understanding of the SDK documentation is perfectly correct. Unfortunately, there is another aspect of the Rich Edit control's behavior that is getting in the way. Whenever an Edit or Rich Edit control hosted in a dialog box receives the focus, it automatically selects all of its text, obliterating the current caret position in the process. The ES_NOHIDESEL does not have any effect on this behavior; it just changes whether or not the selection is visible when the control is unfocused. You can certainly override this select-all-on-focus behavior by doing as IInspectable suggested and subclassing the control to customize its handling of the WM_GETDLGCODE message.
But there is a much simpler solution. Along the same lines as ES_NOHIDESEL, you want to set the ES_SAVESEL style for the control upon creation. Although you can set ES_NOHIDESEL in the resource editor ("No Hide Selection"), there is no equivalent property for ES_SAVESEL. You can manually add it to the RC file, but there's no guarantee that it won't be obliterated when Visual Studio regenerates that file.
Alternatively, you can send the Rich Edit control a EM_SETOPTIONS message specifying the ECO_SAVESEL option. In MFC, the SetOptions member function wraps the sending of this message. For example, in your OnInitDialog function, you might have the following:
m_myRichEditCtrl.SetOptions(ECOOP_OR, ECO_SAVESEL); // maintain selection across focus events
The default behavior for Edit and RichEdit controls is to select the entire contents when they gain focus. ES_NOHIDESEL does not modify this behavior, but simply instructs the control to always show its selection, even if it doesn't have the input focus.
To change the default behavior of the RichEdit control to retain its selection you have to derive from it and provide your custom OnGetDlgCode implementation:
UINT RichEditSelectionPreserving::OnGetDlgCode() {
// Call the base class implementation
UINT code = CRichEditCtrl::OnGetDlgCode();
// And mask out the undesired feature
code = code & ~DLGC_HASSETSEL;
return code;
}
I'm using a QTreeView with a QStandardItemModel and I'm trying to figure out how to move items up and down the tree using buttons . I can do drag and drop no problem, but what I would like to do is have some buttons associated with "move up" and "move down" functions. I just cant find anything on the subject. There seems to be a "moveRow()" function for the model object, but I cant find any documentation on it so I'm not sure if its what I need. Any information you could give to point me in the right direction would be greatly appreciated!
PS Here are my QT Creator stats:
Qt Creator 2.6.2
Based on Qt 5.0.1 (64 bit)
Your hunch is correct. moveRow() is the right function to call.
To move items within one parent (it's a tree, after all), you'd do moveRow(parent, index.row(), parent, index.row() + delta), where delta is set to 1 or -1 depending on whether you move down or up, respectively.
If you want to allow items to be moved between parents, you'll need additional logic to figure out the destination parent if the item would be moved past its parent.
Do note that it's considered bad design if the move button are separate from the items to be moved. Your delegate should display up and down arrows for each item, in its row, so that you can move things with one click. When there is a contiguous selection, the delegates should merge the up/down arrows to cover all of the items. When the selection is non-contiguous, the up/down arrows should disappear.
With separate buttons, you need two clicks: first select the item, then click up/down. This sucks from user experience point of view.
moveRow doesn't seem to work in QTreeView.
Here's a simple (PyQt5) solution for moving an item "up one" relative to its sibling(s). It must have at least one sibling "above". Also this is only for moving between siblings, not where you want to move a row to a different parent. However, I think that these issues could be engineered without difficulty on the basis of this code.
One thing to be aware of here is that the QStandardItem doesn't change in operations like this, but that the associated QModelIndex does.
# in almost all QTreeView implementations the "tree" structure is at column 0...
curr_index = self.selectionModel().currentIndex().siblingAtColumn(0)
if curr_index.isValid():
curr_item = self.model().itemFromIndex(curr_index)
curr_row = curr_index.row()
# if this is the top sibling of its parent it cant be moved up...
if curr_row > 0:
parent_item = self.model().itemFromIndex(curr_index.parent())
# NB parent_item is None in the case of root children:
# in that case you therefore use "takeRow" and "insertRow" methods of the model, not the item!
take_row = self.model().takeRow if parent_item == None else parent_item.takeRow
insert_row = self.model().insertRow if parent_item == None else parent_item.insertRow
row_to_move = take_row(curr_row)
insert_row(curr_row - 1, row_to_move)
new_index = self.model().indexFromItem(curr_item)
# now set the (single) selection, and set current, back to the moved item
# (if you are implementing single-selection, obviously)
flag = QtCore.QItemSelectionModel.SelectionFlag
self.selectionModel().setCurrentIndex(new_index, flag.Clear | flag.SelectCurrent)
I've got a custom model inherited from QTreeView. I've enabled drag and drop and can currently drop an item onto the tree. However, you can currently drop onto either an existing item or between items. I would like to restrict this so that you can only drop onto existing items.
I've set DragDropOverwriteMode to true (actually this is the default for QTreeView). However, this doesn't stop you from dropping between items - it just means you can also drop onto existing items.
I know that I can ignore the "insert" drops in dropMimeData (which I am reimplementing), by checking whether row and column are valid (drops onto existing items have row and column set to -1 and parent set to the current item) and I am doing this. However, I would prefer not to get these drops. Ie. I would like it so that you are always dropping over either the item above or the item below, never between items.
Any ideas?
Thanks for any advice,
Giles
You'' need to catch the drag enter events by reimplementing the dragEnterEvent method in your custom view. The example from the Qt docs is:
void Window::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("text/plain"))
event->acceptProposedAction();
}
In your case, you would probably need to compare the x and y position in the event with the x and y position of the closest item or something similar and reject or accept the proposed action based on that information.
From the QAbstractItemModel::dropMimeData documentation:
It is the responsibility of the view to provide a suitable location for where the data should be inserted.
Which I have interpreted to mean that the view should reject the drop if it's not something that's supported by an underlying model, such as yours.
As of Qt 5.4 (and I assume this was true even in Qt 4.8), setting DragDropOverwriteMode to true will correctly cause the drags to be droppable only on existing items and prevents the "above/below items" drop targets from appearing.
Also, unlike what the question claims, DragDropOverwriteMode is set to false by default for QTreeView (I didn't check, maybe it's newer Qt versions), so it needs to be set to true manually.
However it is still useful to be able to calculate on what positions the item can be dropped. For example in QTreeView, one cannot drop a dragged thing on the left margin of the items, i.e the red area below:
If something is dropped in the invalid red area, dropMimeData will be called with a parent argument set to NULL. So it would be useful to ignore the dragMoveEvent in advance to show a 'you can't drop here' cursor to the user so they know they can't drop there. Qt doesn't implement changing the mouse cursor on invalid areas while dragging (as of Qt 5.4), but we can do it like this:
bool SubclassedTreeView::dropResultsInValidIndex(const QPoint& pos)
{
QTreeWidgetItem* item = itemAt(pos);
if (item == NULL || !indexFromItem(item).isValid())
return false;
return visualRect(indexFromItem(item)).adjusted(-1, -1, 1, 1).contains(pos, false);
}
virtual void SubclassedTreeView::dragMoveEvent(QDragMoveEvent* event)
{
QTreeWidget::dragMoveEvent(event);
if (!event->isAccepted())
return;
if (dropResultsInValidIndex(event->pos()))
event->accept();
else
event->ignore(); //Show 'forbidden' cursor.
}
virtual bool SubclassedTreeView::dropMimeData(QTreeWidgetItem* parent, int index, const QMimeData* data, Qt::DropAction action)
{
Q_UNUSED(index);
//Modify the following action and data format checks to suit your needs:
if (parent == NULL || action != Qt::CopyAction || !data->hasFormat("my/preferred-type"))
return false;
QModelIndex modelIndex = indexFromItem(parent);
//modelIndex is where the data is dropped onto. Implement your custom drop action here...
return true;
}
The above code contains a little part visualRect….adjusted(-1, -1, 1, 1) which was stolen from QAbstractItemViewPrivate::position sources. Actually the sources of this function can be used to calculate the overwrite/insert/invalid areas for the item when QAbstractItemViewPrivate::position is false too.
I would like to suggest a solution based on the current position of the drop indicator (QAbstractItemView::DropIndicatorPosition). It's pretty simple to implement, but unfortunately requires the drop indicator to be shown. However, this might be acceptable in some cases.
TreeView::TreeView(QWidget* parent) : QTreeView(parent)
{
setDropIndicatorShown(true);
}
void TreeView::dragMoveEvent(QDragMoveEvent* event)
{
QTreeView::dragMoveEvent(event);
if (dropIndicatorPosition() != QTreeView::OnItem)
event->setDropAction(Qt::IgnoreAction);
}