Stop QTextCursor::insertText() from modifying QTextDocument scrollbar range - c++

I have a QTextEdit that contains a QTextDocument, which is being programatically edited using the QTextCursor interface. The document is being edited with QTextCursor::insertText().
I load the text file being edited in chunks, so the initial size of the QTextDocument might only be 20 lines even though the document is 100,000 lines. However, I want the QTextEdit scrollbar to reflect the full size of the document instead of just the 20 line document it's currently displaying.
The QTextEdit's scrollbar range is set with QScrollBar::setMaximum() which adjusts the scrollbar to the proper size on the initial opening of the file, but when QTextCursor::insertText() is called the QScrollBar's range is recalculated.
I've already tried calling QScrollBar::setMaximum() after each QTextCursor::insertText() event, but it just makes the whole UI jerky and sloppy.
Is there any way to keep the range of the QScrollBar while the QTextDocument is being modified?

Yes. You'd depend on the implementation detail. In QTextEditPrivate::init(), the following connection is made:
Q_Q(QTextEdit);
control = new QTextEditControl(q);
...
QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()))
Here, q is of the type QTextEdit* and is the Q-pointer to the API object. Thus, you'd need to disconnect this connection, and manage the scroll bars on your own:
bool isBaseOf(const QByteArray &className, const QMetaObject *mo) {
while (mo) {
if (mo->className() == className)
return true;
mo = mo->superClass();
}
return false;
}
bool setScrollbarAdjustmentsEnabled(QTextEdit *ed, bool enable) {
QObject *control = {};
for (auto *ctl : ed->children()) {
if (isBaseOf("QWidgetTextControl", ctl->metaObject()) {
Q_ASSERT(!control);
control = ctl;
}
}
if (!control)
return false;
if (enable)
return QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), ed, SLOT(_q_adjustScrollbars()), Qt::UniqueConnection);
else
return QObject::disconnect(control, SIGNAL(documentSizeChanged(QSizeF)), ed, SLOT(_q_adjustScrollbars()));
}
Hopefully, this should be enough to prevent QTextEdit from interfering with you.

Related

How to make the text of a QComboBox bold, but not the list items?

We have a longstanding convention in our UI that items are shown in bold when they have been changed but the change is not yet committed. Strangely, until now we haven't used any combo boxes, but I have a use for one now and need to implement this behaviour. So I need to programmatically bold (and later un-bold) the text displayed by a closed combo box. However, I don't want to bold the entire list of items in the pop-up. I could accept bolding the selected item in the list if that's easier.
I've seen lots of answers doing almost this, but usually trying to modify the list items rather than the button. I've tried variations on most of them; unfortunately I didn't keep records of what I tried. For what it's worth, my code currently looks like:
myCombo->setStyleSheet(
"QComboBox {font-weight: bold;} "
"QComboBox QAbstractItemView::item {font-weight: normal;}"
);
This turns the button bold, but also the list items. The same behaviour is seen when I apply the normal weight just to the QAbstractItemView without the ::item, and when I tried a different technique based on :open and :closed on the QComboBox.
I will say I'm fairly new to Qt. I am using Qt5 on Fedora 26, but will be deploying to CentOS 7.
It seems that setting the font-style in QComboBox overrides the view's (and it shouldn't, IMHO).
But, when I tried to explicitly set a view to the combo box, this way:
view = new QListView();
myCombo->setView(view);
the stylesheet posted by the OP suddenly worked.
By the way, the new view is different from the original (e.g. has a white background, etc) and I guess the OP isn't happy with that. One could go on styling it, of course, but one would rather prefer a ready to use view, with a consistent style.
Inspecting the default QComboBox view:
QComboBox * combo = new QComboBox();
qDebug() << combo->view();
yelds this:
QComboBoxListView(0x2091880)
So, there is a specific QComboBoxListView class, which is nowhere to be found in documentation and is defined in qcombobox_p.h, not a file one could include, really, but at least we can understand where the issue come from, in the viewOptions overridden method:
QStyleOptionViewItem option = QListView::viewOptions();
option.showDecorationSelected = true;
if (combo)
option.font = combo->font(); // <--- here
return option;
That combo is a private pointer to QComboBox, initialized in construction:
QComboBoxListView(QComboBox *cmb = 0) : combo(cmb) {}
which will always override the view options font with its own.
Let's have a copy of the QComboBoxListView class, edited and renamed:
comboitemview.h
#ifndef COMBOITEMVIEW_H
#define COMBOITEMVIEW_H
#include <QListView>
#include <QComboBox>
class ComboItemView : public QListView
{
Q_OBJECT
QComboBox * _box;
public:
ComboItemView(QComboBox *box);
protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
QStyleOptionViewItem viewOptions() const;
};
#endif // COMBOITEMVIEW_H
comboitemview.cpp
#include "comboitemview.h"
#include <QPaintEvent>
#include <QPainter>
ComboItemView::ComboItemView(QComboBox * box = 0) : _box(box){}
void ComboItemView::paintEvent(QPaintEvent *event)
{
if (_box)
{
QStyleOptionComboBox opt;
opt.initFrom(_box);
opt.editable = _box->isEditable();
if (_box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, _box))
{
QStyleOptionMenuItem menuOpt;
menuOpt.initFrom(this);
menuOpt.palette = palette();
menuOpt.state = QStyle::State_None;
menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
menuOpt.menuRect = event->rect();
menuOpt.maxIconWidth = 0;
menuOpt.tabWidth = 0;
QPainter p(viewport());
_box->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
}
}
QListView::paintEvent(event);
}
void ComboItemView::resizeEvent(QResizeEvent *event)
{
resizeContents(viewport()->width(), contentsSize().height());
QListView::resizeEvent(event);
}
QStyleOptionViewItem ComboItemView::viewOptions() const
{
QStyleOptionViewItem option = QListView::viewOptions();
option.showDecorationSelected = true;
return option;
}
And finally use it to style the view font:
myCombo->setView(new ComboItemView(myCombo));
myCombo->setStyleSheet(
"QComboBox {font-weight: bold;} "
"QComboBox QAbstractItemView {font-weight: normal;}"
);

QTextEdit - conditional drag and drop according to QCursor position

I have a QTextEdit with text. The user is allowed to change the text only from the QCursor position stored in startPos variable to the end of document. The begining of the text must remain the same.
I managed to do that by conditioning of QCursor position.
But user can at any moment drag and drop some text in forbidden area.
I want to make a conditional drag and drop according to QCursor position. So, if user drop some text in forbidden area (before cursor position startPos) I want to put that text at the end of the document. And if user drop text after cursor position startPos, user to be allowed to do so.
class BasicOutput : public QTextEdit, public ViewWidgetIFace
{
Q_OBJECT
public:
BasicOutput();
~BasicOutput();
virtual void dragEnterEvent(QDragEnterEvent *e);
virtual void dropEvent(QDropEvent *event);
private:
int startPos;
};
and the rest of simplified (non-functional) code:
BasicOutput::BasicOutput( ) : QTextEdit () {
setInputMethodHints(Qt::ImhNoPredictiveText);
setFocusPolicy(Qt::StrongFocus);
setAcceptRichText(false);
setUndoRedoEnabled(false);
}
void BasicOutput::dragEnterEvent(QDragEnterEvent *e){
e->acceptProposedAction();
}
void BasicOutput::dropEvent(QDropEvent *event){
QPoint p = event->pos(); //get position of drop
QTextCursor t(textCursor()); //create a cursor for QTextEdit
t.setPos(&p); //try convert QPoint to QTextCursor to compare with position stored in startPos variable - ERROR
//if dropCursorPosition < startPos then t = endOfDocument
//if dropCursorPosition >= startPos then t remains the same
p = t.pos(); //convert the manipulated cursor position to QPoint - ERROR
QDropEvent drop(p,event->dropAction(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers(), event->type());
QTextEdit::dropEvent(&drop); // Call the parent function w/ the modified event
}
The errors are:
In member function 'virtual void BasicOutput::dropEvent(QDropEvent*)':
error: 'class QTextCursor' has no member named 'setPos' t.setPos(&p);
error: 'class QTextCursor' has no member named 'pos'p = t.pos();
How to protect the forbidden text area from user drag and drop?
Rspectfully,
Florin.
FINAL CODE
void BasicOutput::dragEnterEvent(QDragEnterEvent *e){
if (e->mimeData()->hasFormat("text/plain"))
e->acceptProposedAction();
else
e->ignore();
}
void BasicOutput::dragMoveEvent (QDragMoveEvent *event){
QTextCursor t = cursorForPosition(event->pos());
if (t.position() >= startPos){
event->acceptProposedAction();
QDragMoveEvent move(event->pos(),event->dropAction(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers(), event->type());
QTextEdit::dragMoveEvent(&move); // Call the parent function (show cursor and keep selection)
}else
event->ignore();
}
You currently have...
QTextCursor t(textCursor()); //create a cursor for QTextEdit
t.setPos(&p);
If you want a QTextCursor associated with the proposed drop location you should use...
QTextCursor t = cursorForPosition(p);
That should fix the first compilation error. Unfortunately there doesn't appear to be any obvious way to get the QPoint associated with a QTextCursor (though there may be a way going via QTextDocument and QTextBlock, I haven't checked). If that's the case then you'll have to perform the drop yourself...
if (t.position() < startPos)
t.movePosition(QTextCursor::End);
setTextCursor(t);
insertPlainText(event->mimeData()->text());
However, can I suggest that what you are attempting to do might prove very confusing to the user. There should be some visual indicator as to what will happen if the text is dropped. How is the user to know that if they drop the text on the forbidden area it will be appended to the end of the current text -- which may not even be visible on a large document?
With that in mind a better approach might be to override dragMoveEvent...
void BasicOutput::dragMoveEvent (QDragMoveEvent *event)
{
QTextCursor t = cursorForPosition(p);
if (t.position() >= startPos)
event->acceptProposedAction();
}
Here the proposed drop action is only accepted if the mouse pointer is not in the forbidden region. Otherwise the user will see (via the pointer glyph or whatever) that the drop will not be accepted.

QListView with millions of items slow with keyboard

I'm using a QListView with a custom model derived from QAbstractItemModel. I have on the order of millions of items. I have called listView->setUniformItemSizes(true) to prevent a bunch of layout logic from being called when I'm adding items to the model. So far, everything works as expected.
The problem is that using the keyboard to navigate the list is slow. If I select an item in the list, then press up/down, the selection moves fast until the selection needs to scroll the list. Then it becomes extremely laggy. Pressing page-up or page-down is also very laggy. The problem seems to be when an item is selected (aka the "current item") with the keyboard and the list is also scrolled up/down.
If I use the mouse, navigating the list is fast. I can use the mouse wheel, which is fast. I can drag the scroll bar up/down as fast as I want--from the top of the list to the bottom--and the list view updates wickedly fast.
Any ideas on why the combination of changing selections and scrolling the list is so slow? Is there a viable work-around?
Update 9/9/15
In order to better illustrate the issue, I'm providing amplifying information in this update.
Performance Issues with KEYBOARD + SCROLLING
This is mostly a performance question, although it does tie in with the user experience (UX) somewhat. Check out what happens as I use the keyboard to scroll through a QListView:
Notice the slow-down near the bottom? This is the focal point of my question. Let me explain how I am navigating the list.
Explanation:
Starting at the top, the first item in the list is selected.
Pressing and holding the down arrow key, the current item (selection) is changed to the next item.
Changing selection is fast for all of the items that are currently in view.
As soon as the list needs to bring the next item into view, the selection rate slows down significantly.
I expect that the list should be able to scroll as fast as the typematic rate of my keyboard--in other words, the time it takes to select the next item should not slow down when the list is scrolled.
Fast Scrolling with MOUSE
Here's what it looks like when I use the mouse:
Explanation:
Using the mouse, I select the scroll bar handle.
Quickly dragging the scroll bar handle up and down, the list is scrolled accordingly.
All movements are extremely fast.
Note that no selections are being made.
This proves two main points:
The model is not the problem. As you can see, the model has no problem whatsoever performance-wise. It can deliver the elements faster than they can be displayed.
Performance is degraded when selecting AND scrolling. The "perfect storm" of selecting and scrolling (as illustrated by using the keyboard to navigate through the list) causes the slowdown. As a result, I surmise that Qt is somehow doing a lot of processing when selections are being made during scrolling that aren't normally performed.
Non-Qt Implementation is FAST
I want to point out that my issue seems to be specific to Qt.
I have already implemented this type of thing before using a different framework. What I am trying to do is within the scope of model-view theory. I can do exactly what I am describing at blazing fast speeds using juce::ListBoxModel with a juce::ListBox. It's stupid fast (plus, there's no need to create a duplicate index such as a QModelIndex for every single item when each item already has a unique index). I get that Qt needs a QModelIndex for each item for its model-view architecture, and although I don't like the overhead cost, I think I get the rational and I can live with it. Either way, I don't suspect that these QModelIndexes are what is causing my performance slow-down.
With a JUCE implementation, I can even use the page-up & page-down keys to navigate the list, and it just blazes through the list. Using the Qt QListView implementation, it chugs along and is laggy, even with a release build.
A model-view implementation using the JUCE framework is extremely fast. Why is the Qt QListView implementation such a dog?!
Motivating Example
Is it hard to imagine why you'd need so many items in a list view? Well, we've all seen this kind of thing before:
This is the Visual Studio Help Viewer index. Now, I haven't counted all of the items--but I think we'd agree that there are a lot of them! Of course to make this list "useful," they added a filter box that narrows down what is in the list view according to an input string. There aren't any tricks here. It's all practical, real-world stuff we've all seen for decades in desktop applications.
But are there millions of items? I'm not sure it matters. Even if there were "only" 150k items (which is roughly accurate based on some crude measurements), it's easy to point out that you have to do something to make it useable--which is what a filter will do for you.
My specific example uses a list of German words as a plain text file with slightly more than 1.7 million entries (including inflected forms). This is probably only a partial (but still significant) sample of words from the German text corpus that was used to assemble this list. For linguistic study, this is a reasonable use case.
Concerns about improving the UX (user experience) or filtering are great design goals, but they are out of the scope of this question (I'll certainly address them later in the project).
Code
Want a code example? You got it! I'm not sure how useful it will be; it's as vanilla as it gets (about 75% boilerplate), but I suppose it will provide some context. I realize that I'm using a QStringList and that there is a QStringListModel for this, but the QStringList that I'm using to hold the data is a placeholder--the model will eventually be somewhat more complicated, so in the end, I need to use a custom model derived from QAbstractItemModel.
//
// wordlistmodel.h ///////////////////////////////////////
//
class WordListModel : public QAbstractItemModel
{
Q_OBJECT
public:
WordListModel(QObject* parent = 0);
virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
virtual QModelIndex parent(const QModelIndex& index) const;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
public slots:
void loadWords();
signals:
void wordAdded();
private:
// TODO: this is a temp backing store for the data
QStringList wordList;
};
//
// wordlistmodel.cpp ///////////////////////////////////////
//
WordListModel::WordListModel(QObject* parent) :
QAbstractItemModel(parent)
{
wordList.reserve(1605572 + 50); // testing purposes only!
}
void WordListModel::loadWords()
{
// load items from file or database
// Due to taking Kuba Ober's advice to call setUniformItemSizes(true),
// loading is fast. I'm not using a background thread to do
// loading because I was trying to visually benchmark loading speed.
// Besides, I am going to use a completely different method using
// an in-memory file or a database, so optimizing this loading by
// putting it in a background thread would obfuscate things.
// Loading isn't a problem or the point of my question; it takes
// less than a second to load all 1.6 million items.
QFile file("german.dic");
if (!file.exists() || !file.open(QIODevice::ReadOnly))
{
QMessageBox::critical(
0,
QString("File error"),
"Unable to open " + file.fileName() + ". Make sure it can be located in " +
QDir::currentPath()
);
}
else
{
QTextStream stream(&file);
int numRowsBefore = wordList.size();
int row = 0;
while (!stream.atEnd())
{
// This works for testing, but it's not optimal.
// My real solution will use a completely different
// backing store (memory mapped file or database),
// so I'm not going to put the gory details here.
wordList.append(stream.readLine());
++row;
if (row % 10000 == 0)
{
// visual benchmark to see how fast items
// can be loaded. Don't do this in real code;
// this is a hack. I know.
emit wordAdded();
QApplication::processEvents();
}
}
if (row > 0)
{
// update final word count
emit wordAdded();
QApplication::processEvents();
// It's dumb that I need to know how many items I
// am adding *before* calling beginInsertRows().
// So my begin/end block is empty because I don't know
// in advance how many items I have, and I don't want
// to pre-process the list just to count the number
// of items. But, this gets the job done.
beginInsertRows(QModelIndex(), numRowsBefore, numRowsBefore + row - 1);
endInsertRows();
}
}
}
QModelIndex WordListModel::index(int row, int column, const QModelIndex& parent) const
{
if (row < 0 || column < 0)
return QModelIndex();
else
return createIndex(row, column);
}
QModelIndex WordListModel::parent(const QModelIndex& index) const
{
return QModelIndex(); // this is used as the parent index
}
int WordListModel::rowCount(const QModelIndex& parent) const
{
return wordList.size();
}
int WordListModel::columnCount(const QModelIndex& parent) const
{
return 1; // it's a list
}
QVariant WordListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
else if (role == Qt::DisplayRole)
{
return wordList.at(index.row());
}
else
{
return QVariant();
}
}
//
// mainwindow.h ///////////////////////////////////////
//
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void updateWordCount();
private:
Ui::MainWindow *ui;
WordListModel* wordListModel;
};
//
// mainwindow.cpp ///////////////////////////////////////
//
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->listView->setModel(wordListModel = new WordListModel(this));
// this saves TONS of time during loading,
// but selecting/scrolling performance wasn't improved
ui->listView->setUniformItemSizes(true);
// these didn't help selecting/scrolling performance...
//ui->listView->setLayoutMode(QListView::Batched);
//ui->listView->setBatchSize(100);
connect(
ui->pushButtonLoadWords,
SIGNAL(clicked(bool)),
wordListModel,
SLOT(loadWords())
);
connect(
wordListModel,
SIGNAL(wordAdded()),
this,
SLOT(updateWordCount())
);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::updateWordCount()
{
QString wordCount;
wordCount.setNum(wordListModel->rowCount());
ui->labelNumWordsLoaded->setText(wordCount);
}
As noted, I've already reviewed and taken Kuba Ober's advice:
QListView takes too long to update when given 100k items
My question is not a duplicate of that question! In the other question, the OP was asking about loading speed, which as I've noted in my code above, is not a problem due to the call to setUniformItemSizes(true).
Summary Questions
Why is navigating a QListView (with millions of items in the model) using the keyboard so slow when the list is scrolled?
Why does the combination of selecting and scrolling items cause a slow-down?
Are there any implementation details that I am missing, or have I reached a performance threshold for QListView?
1. Why is navigating a QListView (with millions of items in the model)
using the keyboard so slow when the list is scrolled?
Because when you navigate through your list using the keyboard, you enter the internal Qt function QListModeViewBase::perItemScrollToValue, see stack:
Qt5Widgetsd.dll!QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, QAbstractItemView::ScrollHint hint, Qt::Orientation orientation, bool wrap, int itemExtent) Ligne 2623 C++
Qt5Widgetsd.dll!QListModeViewBase::verticalScrollToValue(int index, QAbstractItemView::ScrollHint hint, bool above, bool below, const QRect & area, const QRect & rect) Ligne 2205 C++
Qt5Widgetsd.dll!QListViewPrivate::verticalScrollToValue(const QModelIndex & index, const QRect & rect, QAbstractItemView::ScrollHint hint) Ligne 603 C++
Qt5Widgetsd.dll!QListView::scrollTo(const QModelIndex & index, QAbstractItemView::ScrollHint hint) Ligne 575 C++
Qt5Widgetsd.dll!QAbstractItemView::currentChanged(const QModelIndex & current, const QModelIndex & previous) Ligne 3574 C++
Qt5Widgetsd.dll!QListView::currentChanged(const QModelIndex & current, const QModelIndex & previous) Ligne 3234 C++
Qt5Widgetsd.dll!QAbstractItemView::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Ligne 414 C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, int signalOffset, int local_signal_index, void * * argv) Ligne 3732 C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, const QMetaObject * m, int local_signal_index, void * * argv) Ligne 3596 C++
Qt5Cored.dll!QItemSelectionModel::currentChanged(const QModelIndex & _t1, const QModelIndex & _t2) Ligne 489 C++
Qt5Cored.dll!QItemSelectionModel::setCurrentIndex(const QModelIndex & index, QFlags<enum QItemSelectionModel::SelectionFlag> command) Ligne 1373 C++
And this function does:
itemExtent += spacing();
QVector<int> visibleFlowPositions;
visibleFlowPositions.reserve(flowPositions.count() - 1);
for (int i = 0; i < flowPositions.count() - 1; i++) { // flowPositions count is +1 larger than actual row count
if (!isHidden(i))
visibleFlowPositions.append(flowPositions.at(i));
}
Where flowPositions contains as many items as your QListView, so this basically iterates through all your items, and this will definitely take a while to process.
2. Why does the combination of selecting and scrolling items cause a slow-down?
Because "selecting and scrolling" makes Qt call QListView::scrollTo (to scroll the view to a specific item) and this is what ends up calling QListModeViewBase::perItemScrollToValue. When you scroll using the scroll bar, the system does not need to ask the view to scroll to a specific item.
3. Are there any implementation details that I am missing, or have I reached a performance threshold for QListView?
I'm afraid you are doing the things right. This is definitely a Qt bug. A bug report must be done to hope having this fixed in later releases. I submitted a Qt bug here.
As this code is internal (private data classes) and not conditionnal to any QListView setting, I see no way to fix it except by modifying and recompiling the Qt source code (but I don't know exactly how, this would require more investigation). The first function overidable in the stack is QListView::scrollTo but I doubt it would be easy to oevrride it without calling QListViewPrivate::verticalScrollToValue...
Note: The fact that this function goes through all items of the view was apparently introduced in Qt 4.8.3 when this bug was fixed (see changes). Basically, if you don't hide any items in your view, you could modify Qt code as below:
/*QVector<int> visibleFlowPositions;
visibleFlowPositions.reserve(flowPositions.count() - 1);
for (int i = 0; i < flowPositions.count() - 1; i++) { // flowPositions count is +1 larger than actual row count
if (!isHidden(i))
visibleFlowPositions.append(flowPositions.at(i));
}*/
QVector<int>& visibleFlowPositions = flowPositions;
Then you'll have to recompile Qt and I'm pretty sure this will fix the issue (not tested however). But then you'll see new problems if you one day hide some items...to support filtering for instance!
Most likely the right fix would have been to have the view maintain both flowPositions and visibleFlowPositions to avoid creating it on the fly...
I have made the following test:
First of all i create a class to check in the calls:
struct Test
{
static void NewCall( QString function, int row )
{
function += QString::number( row );
map[ function ]++;
}
static void Summary( )
{
qDebug() << "-----";
int total = 0;
QString data;
for( auto pair : map )
{
data = pair.first + ": " + QString::number( pair.second );
total += pair.second;
qDebug( ) << data;
}
data = "total: " + QString::number( total ) + " calls";
qDebug() << data;
map.clear();
}
static std::map< QString, int > map;
};
std::map<QString,int> Test::map;
Then I insert a call to NewCall in index, parent and data methods of WordListModel. Finally i add a QPushButton in the dialog, the clicked signal is linked to a method which call to Test::Summary.
The steps of the test are the next:
Select the last showed item of the list
Press the Summary button to clear the calling list
With tab key select the list view again
Perform a scroll with the direction keys
Press Summary button again
The printed list shows the problem. QListView widget makes a big number of calls. It seems the widget is reloading all the data from the model.
I don't know if it can be improved but you can't do anything but filter the list to limit the number of items to show.
Unfortunately, I believe that you can't do much about this.
We don't have much control over widgets.
Although you can avoid that issue by using ListView instead.
If you try my quick example below you'll notice how fast it can be even using delegates which is costly.
Here is the example:
Window{
visible: true
width: 200
height: 300
property int i: 0;
Timer {
interval: 5
repeat: true
running: true
onTriggered: {
i += 1
lv.positionViewAtIndex(i, ListView.Beginning)
}
}
ListView {
id:lv
anchors.fill: parent
model: 1605572
delegate: Row {
Text { text: index; width: 300; }
}
}
}
I put a Timer to simulate the scrolling, but of course you can turn on or off that timer depending on whether keys are pressed as well as changing i += 1 by i += -1 if ▲ is pressed instead of ▼. You'd have to add overflow and underflow checks too.
You can also choose the scrolling speed by changing interval of Timer. Then it's just a matter of modifying the selected element's color etc. to show it's selected.
On top of which you can use cacheBuffer with ListView to cache more elements but I don't think it is necessary.
If you want to use QListView anyway take a look at this example: http://doc.qt.io/qt-5/qtwidgets-itemviews-fetchmore-example.html
Using the fetch method allow to keep performance even with big datasets. It allows you to fill the list as you scroll.

Clear ExtraSelections Qt on slot disconnect

I am implementing a focus mode in QT QTextEdit in which I am highlighting single line where cursor is present. So far I can enable focus mode but when I disable focus mode, i want the state restored back to what it was.
The function that calls connect and disconnect is:
void MainWindow::onFocus_Mode_triggered()
{
QTextEdit *texed = qobject_cast<QTextEdit*>(ui->tabWidget->currentWidget());
if(ui->actionFocus_Mode->isChecked()){
connect(texed, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
}
else {
disconnect(texed, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); //First disconnect and then call method to clear ExtraSelections
BacktoNormal(); //Help needed in implementing this
}
}
Now when the menu item actionFocus_Mode is checked, the line where the cursor is currently present is highlighted in yellow by the function given below.
void MainWindow::highlightCurrentLine() {
QTextEdit *texed = qobject_cast<QTextEdit*>(ui->tabWidget->currentWidget());
QList<QTextEdit::ExtraSelection> extraSelections;
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = texed->textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
texed->setExtraSelections(extraSelections);
}
So I am able to highlight it in yellow but if(!ui->actionFocus_Mode->isChecked()), i.e, if menu item (focus mode) is unchecked then I wish to revert back to normal mode. How would I implement BacktoNormal() function.
What I think right now is that I should set lineColor to transparent or something to get it back to normal (if it is possible at all).
I am unable to find anything related to this. Any help would be useful as I am completely stuck at this point.
In your BackNormal try to set just nothing as extra selections.
QTextEdit *texed = qobject_cast<QTextEdit*>(ui->textEdit);
QList<QTextEdit::ExtraSelection> extraSelections;
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = texed->textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
extraSelections.clear();//nothing
texed->setExtraSelections(extraSelections);
When did I try this on my computer(with another code), this selections was successfully removed.
Smaller version:
QTextEdit *texed = qobject_cast<QTextEdit*>(ui->textEdit);
QList<QTextEdit::ExtraSelection> extraSelections;//empty list
texed->setExtraSelections(extraSelections);

Editable QComboBox: synchronize edit text with item text

I've got a QComboBox which I want to be "automatically" editable. That is, every time a user manually changes current item's text, that text should "fall" to the underlying model automatically.
So far, I've reached this via a custom signal handler:
void setupUi() {
...
connect( someComboBox,
SIGNAL(editTextChanged(QString)),
SLOT(comboBoxEditTextChanged(QString)) );
...
}
void comboBoxEditTextChanged( const QString& text ) {
someComboBox->setItemText( someComboBox->currentIndex(), text );
}
So I wonder, is there a possibility to do this with less code? I've tried QComboBox::setInsertPolicy(QComboBox::InsertAtCurrent), but that didn't help.
EDIT: Current method with a custom slot works properly - but I'm asking if there's a method that does not involve any signals/slots.
To set the Text Automatically when USER changes it, we can edit your slot as follows:
void comboBoxEditTextChanged( const QString& text )
{
int index = someComboBox->findText(text);
if(index != -1)
{
someComboBox->setCurrentIndex(index);
}
someComboBox->setItemText( someComboBox->currentIndex(), text );
}
I hope this will resolve your issue
QComboBox can add items manually using
combo->additem("X");
combo->addItem(QString Y);
whereas you can manage the maximum number of items in it. Please go through the following link for details.
a link
So, in your slot,
void comboBoxEditTextChanged( const QString& text )
{
someComboBox->addItem(text);
}