How to use Threading in QDialog to fill data in QComboBox? - c++

I have 10 QComboBox in QDialog. And i am filling data into these combobox while opening Dialog, It will take 7 second to load Dialog, how to solved this issue because there is no clicked slot in combobox . I think we can solved this issue by using Thread but i have no idea of QThread.

The solution is not easy because QT does not allow for widgets to be modified outside main thread.
Simple solution:
Use threads to put the data into QStringLists and set it using addItems.
QThreadPool is more simple to use for this usecase.
This solution will not help if the problem is the size of the data itself, not the processing.
Complex solution (nice for learning):
You can however re-implement the model of the widget (QAbstractItemModel), and use a QThread in the model.
Also you can create your own QListView derivered class that on loads data when setVisible is called for the first time with true - just overwrite setVisible(bool).
Set an instance of this class to each combo box with setView().
On how to use QThread, see examples, read tutorials... It is to complex to explain here, but there is full of examples and tutorials on the net.
Later Edit:
There is also possible to use event filters:
// Install event filter
MyDlg::MyDlg()
{
qApp->installEventFilter ( this );
}
// search for the view of the combo to be activated
// and fill it.
bool MyDlg::eventFilter ( QObject * obj, QEvent * event )
{
QSet<QAbstractItemView *> myComboViews;
my_combos.insert(combo1->view());
if ( event->type () == QEvent::Show )
{
QAbstractItemView * view = qobject_cast<QAbstractItemView*>(obj);
if ( myComboViews.contains(obj) && view->isVisible() )
{
fillComboView(view);
}
}
return QDialog::eventFilter ( obj, event );
}
The code is untested, but you can get the ideea.

Related

Troubles with undo history (QUndoStack, QUndoView and other)

I have two separate threads.
First thread for GUI, and second for application data.
Initially, I wanted to use QUndoStack and QUndoView.
But there was a problem - this view works directly with the stack:
https://code.woboq.org/qt5/qtbase/src/widgets/util/qundoview.cpp.html#_ZN10QUndoModel20setStackCurrentIndexERK11QModelIndex
In this case I got race condition.
To solve this problem I wrote custom myUndoView using QListView and QAbstractListModel.
Now all my slots using queued connections and I store a lightweight copy of the "real" undo stack in the custom view model.
This is same size and same order of the "real" undo stack elements.
A lightweight element contains only type of the undo command and text.
Now I have another problem. I'm not blame for this ))
I have a QLineEdit that emits signal on value changed when I click Enter key or lost focus.
This value in turn is sent to object (app model) with "real" undo stack. It works.
But this does not work when I interact with undo view too.
Repeat, I'm not blame for this. QUndoView has the same behavior.
Step by step:
QLineEdit in focus.
Changing value, still in focus.
Click the mouse in the undo view.
Oops.. currentIndexChanged() signal from undo view can be sent first,
or signal from QLineEdit can be sent first.
It always differs ..
If signal from QLineEdit was sent first - it works correctly.
The history of changes not lost.
I want to make enter/blur and other changes (not in history view) always invoked first. Probably I can use QTimer::singleShot() for delay of emit undo view signals . But not curentIndexChanged() because this signal emit with user interactions and when undo stack updated programmatically. We can not determine who make changes - user or application.
What I tried?
Intercept mouse clicks:
myUndoView::mousePressEvent(QMouseEvent *event)
{
event->ignore();
qDebug() << "catched!";
}
But sometimes it loses the clicks.
At the bottom of the list item (under the letters) is an area that pass a click to the item.
This may be a Qt bug, found in my environment: Debian, Mate, GTK+ Qt-style.
I think, I can place another transparent widget over list, and get coordinates of the click and use it:
http://doc.qt.io/qt-5/qabstractitemview.html#indexAt
to get the selected index.
Or I make all wrong?
Maybe there is an easier way?
How to make it right?
I would try blocking the list model signals while the line edit is focused.
Let's have an event filter like this:
class EventFilter : public QObject
{
Q_OBJECT
public:
EventFilter(QObject * model) : _model(model){}
bool eventFilter(QObject *watched, QEvent *event);
private:
QObject * _model;
};
which keeps a private reference to the list model as a pointer to QObject, passed in constructor argument.
The filter implementation:
bool EventFilter::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QEvent::FocusIn)
{
_model->blockSignals(true);
}
return false;
}
Keep a reference to an instance of the filter in the window class (Form, in my example), along with the list model instance reference:
private:
EventFilter * filter;
QAbstractListModel * model;
The filter has to be instantiated and installed in line edit, in Form constructor (don't forget to delete it in the destructor):
filter = new EventFilter(model); //the model is passed to the filter in construction
ui->lineEdit->installEventFilter(filter);
At this point, model events will be blocked when the line edit gets focus. To unlock them, use the line edit editingFinished slot:
void Form::on_lineEdit_editingFinished()
{
model->blockSignals(false);
}

How to add hyperlinks in Qt without QLabel?

I have some labels and layouts nested inside a QWidget to build a part of a sidebar. Each QWidget is its own section and one component currently looks like this:
To my understanding, you can only set hyperlinks with QLabel, but I'm trying to get the whole area between the white lines clickable. This is including the icon and the whitespace. Is there any way to achieve this?
This got marked as a duplicate to the opposite of what I was asking, so I'd like to reiterate that I'm trying to implement a hyperlink without QLabel.
You can easily have a widget open a link on click:
class Link : public QWidget {
Q_OBJECT
public:
Link(QUrl url, QWidget p = nullptr) : QWidget(p), _url(url) {}
QUrl _url;
void mouseReleaseEvent(QMouseEvent *) { QDesktopServices::openUrl(_url); }
}
You can avoid any extra signals and connections, and have each link widget store its own link internally, the url can be set on construction and changed at any time. Not using signals and slots makes it easier to change the link too, without having to disconnect previous connections.
IMO going for a signals and slots solution is only justified when you want different arbitrary behavior. In this case you always want the same - to open a particular link, so you might as well hardcode that and go for an easier and more computationally efficient solution.
I would just manually catch the SIGNAL for clicked() and use desktop services to open the url in code.
bool QDesktopServices::openUrl ( const QUrl & url ) [static]
Opens the given url in the appropriate Web browser for the user's desktop environment, and returns true if successful; otherwise returns false.
http://doc.qt.io/qt-4.8/signalsandslots.html
Using this type of syntax, or in the designer, you can also connect a signal to a slot.
connect(widgetThatRepresentsURL, SIGNAL(clicked()),
handlerThatWillOpenTheURL, SLOT(clicked_on_url()));
For widgets that don't have a signal set up for clicked (or whatever event you are interested in), you can subclass the widget in question and reimplement...
void QWidget::mousePressEvent ( QMouseEvent * event ) [virtual protected]
Specifically for creating a signal, there is emit. I've used this in the past like the following
void Cell::focusInEvent(QFocusEvent *e)
{
emit focus(this, true);
QLineEdit::focusInEvent(e);
}
with the following in the header
signals:
void focus(Cell *, bool);

Adding event to the context menu in QPlainTextEdit

This is my Context Menu after right click on QPlainTextEdit. I want to add function to load data from file in Context Menu. Can I? How?
Method 1: QPlainTextEdit::contextMenuEvent
You should override the QPlainTextEdit::contextMenuEvent as mentioned in the Qt documentation:
void MyQPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = createStandardContextMenu();
menu->addAction(tr("My Menu Item"));
//...
menu->exec(event->globalPos());
delete menu;
}
You can connect the QAction::triggered signal to your method (slot) to load the data or you can use one of the QMenu::addAction overloads, which allows you to specify a slot directly.
If you do not want to subclass QPlainTextEdit (to override contextMenuEvent), you can use event filtering in Qt.
Note that contextMenuEvent() is only called when contextMenuPolicy is not set (or set to its default value Qt::DefaultContextMenu)
Method 2: QWidget::customContextMenuRequested
As an alternative, you can use Qt's signal and slot mechanism to create the context menu when requested by the user.
The contextMenuPolicy property should be set to Qt::CustomContextMenu, in which case the QWidget::customContextMenuRequested signal is invoked whenever a context menu is requested by the user. This signal should be connected to your own slot, which should create the context menu as shown in the code above (Method 1).
Using MyQPlainTextEdit in Qt Designer
To use your MyQPlainTextEdit in a .ui file, you should implement it as a promoted QPlainTextEdit and use it in your .ui file instead of a regular QPlainTextEdit. See the Qt documentation for more information.
To be able to use your class in the Qt Designer, you should not forget to implement a constructor accepting a parent QWidget as is done in the AnalogClock example. Note that implementing such a constructor is always a good idea, because Qt typically manages ownership through a child-parent relationship.
Building on #m7913d answer.
The downside to the techniques is you must derive from the QPlainTextEdit class for a very minor extension. My preferred method, especially when using designer based widgets, is to add an eventFilter, and filter out the mouse event that is a mousebutton press
MyWidget::MyWidget(...)
{
...
ui->plainTextEdit->installEventFiler( this )
}
MyWidget::eventFilter( QObject * obj, QEvent * event )
{
if ( ( obj == ui->plainTextEdit )
&& ( event->type() = QEvent::MouseButtonPress )
&& ( dynamic_cast< QMouseEvent * >( event )->buttons() & Qt::MouseButton::RightButton )
{
// create menu
auto menu = ui->plainTextEdit->createStandardContextMenu();
// modify menu
menu->exec( mouseEvent->globalPos() );
delete menu;
return true;
}
return false;
}

How to issue signal each time a row is edited in QListWidget?

class genericTaskList : public QListWidget
{
Q_OBJECT
public:
QListWidgetItem *defaultText;
genericTaskList (QWidget *parentWidget)
{
setParent (parentWidget);
setFixedSize (445, 445);
defaultText = new QListWidgetItem ("Double click here to compose the task");
defaultText->setFlags (defaultText->flags () | Qt :: ItemIsEditable);
insertItem (0, defaultText);
QObject :: connect (this, SIGNAL (currentRowChanged (int)), this, SLOT (addDefaultText (int)));
}
public slots:
void addDefaultText (int rr)
{
std::cout << "\ndsklfjsdklfhsdklhfkjsdf\n";
insertItem (++rr, defaultText);
}
};
This code is supposed to issue a signal each time the row gets edited.
After I call "insertItem" in the constructor, the signal is issued.
But, that's it. It never gets issued after that - no matter how many times I edit the row.
What am I missing?
At first it seems like QListWidget::itemChanged is the way to go, but soon you run into a problem: the signal is sent for everything - inserts, removes, changing colors, checking boxes, etc! So then you end up trying to put in flags and filter everywhere by intercepting various signals to find out if editing was the actual event. It gets very messy.
There is also QAbstractItemModel::dataChanged , which would seem like a good solution. It even has a parameter "const QVector& lstRoles" so you could scan for Qt::EditRole and see if it was really edited. Alas, there's a catch - it gets called for everything just like QListWidget::itemChanged and unfortunately, for QListWidget anyway, the roles parameter is always empty when it's called (I tried it). So much for that idea...
Fortunately, there's still hope... This solution does the trick! :
http://falsinsoft.blogspot.com/2013/11/qlistwidget-and-item-edit-event.html
He uses QAbstractItemDelegate::closeEditor, but I prefer using QAbstractItemDelegate::commitData.
So make a connect like so...
connect(ui.pLstItems->itemDelegate(), &QAbstractItemDelegate::commitData, this, &MyWidget::OnLstItemsCommitData);
Then implement the slot like this...
void MyWidget::OnLstItemsCommitData(QWidget* pLineEdit)
{
QString strNewText = reinterpret_cast<QLineEdit*>(pLineEdit)->text();
int nRow = ui.pLstItems->currentRow();
// do whatever you need here....
}
Now you have a slot that gets called only when the list item's text has been edited!
currentRowChanged indicates the row selection has changed, not the content of the row. Perhaps you want to use currentTextChanged or itemChanged instead.
The reuse of the word current and changed in the QT docs is quite confusing.
Warning: A QListWidgetItem can only be added to a QListWidget once. Adding the same QListWidgetItem multiple times to a QListWidget will result in undefined behavior.
So even if it will emit the signal I think you should better to add newly created Item.
And when do you want the new row to be inserted ? -
as soon as item is double clicked or finishing edit - they differ.

Clickable menu item with submenu in Qt

I am writing in Qt 4.6. I am wondering if it's possible to achieve such a menu item, that it's possible to be triggered, but also has a submenu. Clicking it triggers associated action, hovering it causes submenu to appear.
Let me start by saying that this is not a good plan of attack. There are corner cases here that will take a rediculous amount of time and code to get just right, and will probably require per-operating system customization.
With that said, however, the actual implementation isn't too complicated. Just subclass the QMenu that you're making your submenu from, and override the event handlers, forcing the parent menu closed when a 'selection' is made. Something like the following basically works:
from PyQt4 import QtCore, QtGui
import sys
app = QtGui.QApplication(sys.argv)
widget = QtGui.QMainWindow()
widget.resize(250,150)
menu = widget.menuBar().addMenu("test")
class submenu(QtGui.QMenu): #Override the submenu class
def __init__(self,name):
QtGui.QMenu.__init__(self,name)
def mouseReleaseEvent(self,event): #catch mouseRelease Events
global menu
QtGui.QMenu.mouseReleaseEvent(self,event)
if not self.rect().contains(event.pos()):
print("Parent Selected")
menu.hide() #If the parent was selected, hide it
else: #Likely ignore these
print("Parent NOT Selected")
def c():
print("Sub-item selected")
cMenu = submenu("Sub-menu")
menu.addMenu(cMenu)
actionC = QtGui.QAction("sub-item",widget)
actionC.triggered.connect(c)
cMenu.addAction(actionC)
widget.show()
sys.exit(app.exec_())
This behavior is a bit confusing, but i am trying to develop a UI with as little clicking as possible. Although a bit unexpected, this behavior makes it a bit faster to use when you get used to it.
I haven't wrote that in my previous message, but i am writing in c++, and i have no idea about python... Anyway i managed to translate idea to c++, and it works but it's quite ugly... I found a bit better approach by looking through qt source (when i was asking this question I was hoping there is some better, "intended" method)
class MyMenu : public QMenu
{
Q_OBJECT
public:
MyMenu(QWidget* parent);
~MyMenu();
virtual void mouseReleaseEvent( QMouseEvent * event );
};
MyMenu::MyMenu(QWidget* parent):QMenu(parent)
{
}
MyMenu::~MyMenu()
{
}
void MyMenu::mouseReleaseEvent( QMouseEvent * event ){
QAction* act = menuAction();
if (act){
QMenu* men = act->menu();
act->setMenu(0);
QMenu::mouseReleaseEvent(event);
act->setMenu(men);
}else{
QMenu::mouseReleaseEvent(event);
}
}
The only disadvantage is that such a menu would react to clicking on all options with submenus, not only desired ones. Perhaps it would be a good idea to check if anything is connected to action's signals?
On my ubuntu it works. However i guess it wouldn't work on windows, where system manages menus exclusively (unless qt uses some windows with menu's look and feel, not system menus), but i am too lazy to instal windows just to check it ;)
void CustomMenu::mouseReleaseEvent( QMouseEvent* event )
{
QAction* action = menuAction();
// Filter out submenus. TODO: Is there a better way to do this?
if ( action && !rect().contains( event->pos() ) ) {
QMenu* menu = action->menu();
action->setMenu( 0 );
QMenu::mouseReleaseEvent( event );
emit customTriggeredSignal();
action->setMenu( menu );
}
else {
QMenu::mouseReleaseEvent( event );
}
}
Combining both helpful answers, from j_kubik and jkerian, this mouseReleaseEvent() in the top-level menu subclassing QMenu ignores unwanted invocations of submenus.
I will report back as to whether the approach is suitable on Windows.