Qt Drag and Drop QTreeView - c++

My TreeView has various folder with children. each child has a icon and text-name. the user can select one or more item and drag them into QMDI area. as image for my darg drop i have a local picture drag->setPixmap(QPixmap(myPixImage)). What I need is: how i make the selected item with icon and text as myPixImage dynamically.

It's easy to set the pixmap when your own code create the QDrag object, however using QXxxViews (not only QTreeView) you have only control on the QMimeData (when overriding QAbstractItemModel::mimeData()).
Therefore if you really need that, the only way I now is to subclass QTreeView, overriding (well... reimplenting) mouseMoveEvent() and the like.
If you do so, you can get the selected items when creating the QDrag through e.g. selectionModel()->selectedItems().

First of all: there's no simple way to do this. Basically because QDrag::exec is started when you (as developer) allow the start of the drag (example here) and it doesn't return until the drag is finished.
You want to change your QDrag object while dragging.
Your best option here is:
After creating the Drag object and before running QDrag::exec store the object in some pool that can be accessed anywhere in the code. Probably something like static map<QDrag *> pool
Set up a recurrent QTimer event that constantly updates the the QDrag object, which can be obtained from the pool. Do not try using Qthreads as you'll end up with some "QObject can't be moved from/to thread" error.
When the drag's ended (as user), clean up the object from the pool
Do mind that you'll have to visually tune the QTimer repeated events accordingly.

Related

How to get UI objects to only appear in dialog AFTER button is clicked

I have a dialog that initially has several buttons, let's call them Write, View, OK, and Cancel.
The way it should to is to have the dialog upon creation only have those three buttons and nothing more.
When the Write button is cancelled, it's supposed to create a QLineEdit object in the window above the buttons where the user can enter a new string,which when OK is then clicked will be added to an external QStringList.
When View is clicked, LineEdit should go away (if it's up) and a QListView to come up instead to view everything in that list.
The problem is, I know how to use hide() to get objects that are already in the dialog to NOT appear.
but I am having trouble figuring out how to get an object not currently on the table to appear. I'm new to using Qt so it may be something easy I'm just accidentally overlooking (in fact I hope it is).
Could anyone please offer advice? Thanks!
Just create the items normally and then set:
ui->control->setVisible(false);
after you have created the UI (after ui->setupUi(this);) possibly in the constructor (in case you use code generated by Qt Creator).
And when you need them:
ui->control->setVisible(true);
Doc for this:
http://qt-project.org/doc/qt-4.8/qwidget.html#visible-prop
when using a QListView you should also have a QListModel that provides the data to it, if you only have QStrings then a QStringListModel is premade for you to use
to add a row you can do:
int rows = model->rowCount();
model->addRow(rows,1);
QModelIndex index = model->index(rows,0);
model->setData(index, string);

C++ Qt Inherit QMessageBox to delay user input in order to prevent unintended action

Problem
Windows has a system setting that will cause the mouse pointer to jump (move) to a new focus element automatically, e.g. the default button of a dialog that pops up. While the advantage is an increase in speed and a reduction of mouse movements, it has a disadvantage:
If this happens just when before the user clicks on another element, the user is unable to abort his/her action in time and will immediately accept the dialogs default button because the focus is moved by the system. Usually this may entail cumbersome work to retrace the steps up to this point (think a file chooser dialog that forgot the very long path you input previously) but it could also mean triggering an irreversible process (e.g. file deletion).
Aim
Essentially I would like to disable the dialog's inputs for a small amount of time, just enough to prevent an inadvertant mouse click or keyboard button press.
Question
It comes down to a C++ question, namely how to access the base classes' objects (GUI widgets) from the inheriting class, i.e.
disable the button widgets of a QMessageBox
start a single shot QTimer and connect it to a slot that
enables the previously disabled widgets
(As alternative, I probably could reimplement input event handlers that suppress all input for a specific amount of time, but although I intend to keep that time very short (e.g. 100 ms), the user is not informed of the disabled input using that method.)
A simple class derived from QDialogBox can be found at http://www.qtforum.org/article/24342/messagebox-auto-close-mouse-event-close.html.
Do you need to use one of the "native"-ish message boxes provided by the QMessageBox static functions?
Otherwise, that's pretty simple to achieve, by building a QMessageBox and adding standard buttons to it:
QMessageBox *messageBox = new QMessageBox;
QPushButton *okButton = messageBox->addButton(QMessageBox::Ok);
okButton->setEnabled(false);
// use a QTimer to add logic to reenable the button
// use QCursor to move the mouse cursor on the button
// add a nice countdown in the button's label, like Firefox does
// add other nice UX touches as wanted
Last points are left as an exercise to the reader :)
To en/disable the buttons in QMessagebox one would need access to them.
qmessagebox.cpp uses buttonBox = new QDialogButtonBox; and the addButton() method
d->buttonBox->addButton(button, (QDialogButtonBox::ButtonRole)role);
d->customButtonList.append(button);
But I don't understand Qt internals and am unable to find these in qmessagebox.h and thus fail to find out if there is a chance to access the buttons..

Qt - How to create widget asynchronously?

I would like to create a widget in asynchronously and then assign it to a dialog in the main thread. I have a functions to create the widget:
shared_ptr<QTableWidget> createTable() {
auto table = make_shared<QTableWidget>()
// ... add some items to the table
return table;
}
When I call this function synchronously everything works as expected. Problem is when I call this function asynchronously:
auto futureWidget = QtConcurrent::run(createWidget);
I use QFutureWatcher::finished() signal and when the widget creation is done, I try to add that widget to the dialog in the main/GUI thread.
The problem is that sometimes it just crashes with SIGSEG when I add an item to the table. Sometimes when it completes the created widget is not shown. Also there is a lot of warnings on the standard output about using pixmap outside GUI thread.
Is that not possible to create widgets in different thread in Qt? If it is possible, how to do it?
No, it is only possible to create GUI widgets within the main thread.
Usually, it does not take much time to create GUI part of widget. If you have some operation, that takes a lot of time, you probably should extract it from process of widget creation, process it in separate thread and then pass result of this operation to the newly created widget.
EDIT
If you have some complex tables in the widget, you should use Qt MVC system, i.e. QTableView and model based on QAbstractTableModel. You can then fill model in another thread and only then assign it to view, created in main thread.

Toggle dynamically created objects using dynamically created QRadioButtons

I'm working on a QT application. In it the user can create several objects, each one will be added to a list. In the next step the user shall position the objects. The choice which one of the objects shall be positioned, shall be made by clicking QRadioButtons (seems logical as QRadioButtons added to a QGroupbox ensure that only one of them is active).
The problem is I can't find a signal that allows me tell which radio button is active or was clicked. I already connected all radio buttons to one slot, but all I get is a boolean value. How can I get the sender of the signal? Or what would be the standard way to achieve my goal?
QObject::sender is a bit too abstract. Try QButtonGroup::buttonClicked
You can get the sender of the signal using... QObject::sender() method within your slot!

Callback for button in Qt Designer?

I just started using QtCreator tonight, and it seems it puts all of the interface stuff inside of the ui file. I followed a tutorial to create a resource for my icons, then I added them to a menu bar at the top.
I need to make a connection when one of them is clicked though, and cannot figure out how to make a callback for it.
Am I going to have to completely create them through code or is there some way to add a callback for them (rather than just making them interact with other objects).
Menu bar items are action objects. To do something when they are clicked, you need to catch the triggered() signal from the action. Read more about signals and slots here.
To do this, you need to declare a new slot in your MainWindow class. Qt also supports doing this automatically, without the need to connect anything, but I prefer doing it myself. If you're not interested, just skip this part.
First, we declare a new slot in your window class:
private slots:
void clickMenuButton();
Then, in your constructor, you need to connect the triggered signal to your new slot:
connect(ui.actionObject, SIGNAL(triggered()), this, SLOT(clickMenuButton()));
The first argument is the object that holds the signal we'll listen to (your menu button). The second is the name of the signal. The third is the object that holds the receiving slot (in this case, our window). The fourth is the slot.
And just like that, clickMenuButton() will be called whenever the action is clicked.
As I said before, Qt can also automatically connect signals to slots. The disadvantage here seems to be that you can't change the slot's name, but you don't need to connect it either.
Qt Creator supports creation of slots for widgets: in the case of your menu action, you should go to the form designer, and you should see a list of actions in your form (if you don't, find the Action Editor). Right click the action you want, and push Go to slot.... There, double click triggered().
Qt Creator will then open the new slot in your code editor, and you can do whatever you want to here!
To do that you'll need to add a QAction, add it to the menu, associate an icon with it and then create a callback for it. I'm using the VS Integration so I don't know the details of how to do it in Creator but it should be possible without creating stuff in code.
There should be somewhere an actions editor. from there you add an action, then right-click it or something to add an icon to it, then drag it do the menu and then possibly double click it to create a slot for it. This is how it works in the VS Integration atleast.