PyQt4 QFileSystemModel duplicated index - python-2.7

I have a problem with QFileSystemModel.index
When I select files or folders from treeview with one click of the mouse, the code prints the item selected twice.
This is the part of the code where I am having the problem :
import sys
import os
import sip
sip.setapi('QVariant',2)
.....
.....
self.pathRoot = QtCore.QDir.rootPath()
self.model = QtGui.QFileSystemModel(self)
self.model.setRootPath(self.pathRoot)
self.fsindex = self.model.setRootPath(self.model.myComputer())
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.fsindex)
self.treeView.clicked.connect(self.on_treeView_clicked)
self.treeView.setColumnHidden(3, True)
self.treeView.setColumnHidden(2, True)
self.treeView.setColumnWidth(0, 320)
self.treeView.setColumnWidth(1, 30)
self.treeView.resizeColumnToContents(True)
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeView_clicked(self, index):
indexItem = self.model.index(index.row(), 0, index.parent())
filePath = self.model.filePath(indexItem)
print filePath

The problem is caused by the following line in the file generated when compiling Qt Designer.
QtCore.QMetaObject.connectSlotsByName(SOME_OBJECT)
According to the docs:
QMetaObject.connectSlotsByName (QObject o)
Searches recursively for all child objects of the given object, and
connects matching signals from them to slots of object that follow the
following form:
void on_<object name>_<signal name>(<signal parameters>); Let's
assume our object has a child object of type QPushButton with the
object name button1. The slot to catch the button's clicked() signal
would be:
void on_button1_clicked();
that is to say that it connects the slots and the signals that contain that syntax, and in your case that happens so you have 2 options:
Delete the connection you make.
Or rename your slot.

Related

Pyqt get qlistwidget item when widget inside itemwidget is clicked

this is the current application looks like this:
It has a Qlistwidget listWidget_links where each item has own itemwidget set (combobox, checkbox, button, ...) Now I came upon a problem that neither google or I can solve.
If the user presses the create button or any other item's itemwidget how do I let a method know the item the widget was pressed changed inside.
item_widget.comboBox_type.currentIndexChanged.connect(self.itemupdate_linktype)
item_widget.checkBox_hide.stateChanged.connect(self.itemupdate_hidden)
cbox = self.sender() # gives the widget that released the signal
cbox.parent() #I discovered by a lucky try, returns NodeLinkItemWidgetUI.Ui_Form object which is a item's itemwidget
Here is how items get created to in order to understand the program structure better:
def createNewLink(self, nodename, nodeclass):
item = QtWidgets.QListWidgetItem(self.listWidget_links)
item_widget = NodeLinkItemWidgetUI.Ui_Form(nodename, nodeclass)
item.nodename = nodename
item.nodeclass = nodeclass
item.setSizeHint(QtCore.QSize(130, 160))
self.listWidget_links.addItem(item)
self.listWidget_links.setItemWidget(item, item_widget)
Edit: solved setting the variable item_widget.item = item seems to work, but is there a more elegant way?

QFileSystemModel rowCount does not work as expected

I am try an example in Model/View Programming.
http://doc.qt.io/qt-5/model-view-programming.html
To demonstrate how data can be retrieved from a model, using model indexes, we set up a QFileSystemModel without a view and display the names of files and directories in a widget. Although this does not show a normal way of using a model, it demonstrates the conventions used by models when dealing with model indexes.
We construct a file system model in the following way:
QFileSystemModel *model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
int numRows = model->rowCount(parentIndex);
In this case, we set up a default QFileSystemModel, obtain a parent index using a specific implementation of index() provided by that model, and we count the number of rows in the model using the rowCount() function.
This is my code:
QFileSystemModel* model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
qDebug() << QDir::currentPath();
// "/media/Local Data/Files/Programming/C++/build-DemostrateQModelIndex-Desktop_Qt_5_5_1_GCC_64bit-Debug"
qDebug() << "RowCount is " << model->rowCount(parentIndex);
But RowCount is always 0.
In the "build-DemostrateQModelIndex-Desktop_Qt_5_5_1_GCC_64bit-Debug" folder, there is files and folder inside. I expect row count should be the number of items inside.
I also tried initialized the QFileSystemModel;
QFileSystemModel* model = new QFileSystemModel;
model->setRootPath(QDir::rootPath());
QModelIndex parentIndex = model->index(QDir::currentPath());
qDebug() << "RowCount is " << model->rowCount(parentIndex);
RowCount is still 0.
Update 1:
Applying the suggestion from Johannes Schaub. I add an QEventLoop to my code.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileSystemModel* model = new QFileSystemModel;
model->setRootPath(QDir::rootPath());
QModelIndex parentIndex = model->index(QDir::currentPath());
qDebug() << QDir::currentPath();
// "/media/Local Data/Files/Programming/C++/build-DemostrateQModelIndex-Desktop_Qt_5_5_1_GCC_64bit-Debug"
qDebug() << "First RowCount Call is " << model->rowCount(parentIndex);
QEventLoop loop;
QObject::connect(model, &QFileSystemModel::directoryLoaded, &loop, &QEventLoop::quit);
loop.exec();
qDebug() << "RowCount Call after eventloop is " << model->rowCount(parentIndex);
return a.exec();
}
I still get a row count of 0.
QFileSystemModel utilizes lazy and deferred loading. You need to watch on its signals, which will be emitted constantly until the entire directory has been loaded.
In particular, the docs say
Unlike QDirModel, QFileSystemModel uses a separate thread to populate itself so it will not cause the main thread to hang as the file system is being queried. Calls to rowCount() will return 0 until the model populates a directory.
In your case, you could probably run a local QEventLoop and connect the respective signals (directoryLoaded) of the model with the quit() slot of the event loop to wait for the population. I am unsure whether canFetchMore and fetchMore can be used for this scenario aswell to block on waiting for the population (afaik its main use is lazy loading when the user scrolls down in an infinite list, like for example a facebook pinwall stream). It's worth an attempt, at least.
#Kuba notes correctly that a local event loop is not intrinsically required. If you can afford leaving the context in which you create the QFileSystemModel (by storing it as a pointer member for example), and acting on the slot as a normal member function.
The principle to use is:
Create the model and set its root path. At this stage you can assum the model is still empty or very few data have been loaded.
Let the model load data in its separate internal thread. Connect the directoryLoaded signal to a slot. When the model will have loaded the root path, the signal will be sent.
In the slot, check if the folder you are interested in is fully loaded. It won't at first. The reason is the model loads using lazy methods, and likely only the root path folder will be ready.
If your folder is not fully ready, asks the model to load it. This is done by using model.canFetchMore and model.fetchMore with the index of the folder of interest, and returning immediately (or you could try to work with folder entries already ready, but this alternative requires to manage the progress of the model readiness).
When the slot is ready (likely just after calling model.fetchMore, the test canFetchMore will return False, meaning the folder you're interested in is fully loaded and model.rowCount will now return the correct value. If you're interested in another folder, use model.canFetchMore and model.fetchMore again with the index of the new folder.
Instead of using model.canFetchMore, you may also compare the path of the folder of interest with the argument passed to the slot. This string indicates the path the signal was sent for.
You indicated you're using C++, I don't have a code in this language, however the following code in Python can be easily translated (self = this, and indented lines are equivalent to a pair of brackets in delimiting a block)
class MyWidget(QPlainTextEdit):
def __init__(self):
# Init superclass
super(MyWidget, self).__init__()
self.setReadOnly(True)
self.show()
# FS model, set root path
self.model = QFileSystemModel()
path = "C:/"
self.model.setRootPath(path)
# Perform next tasks after model root path is loaded
self.model.directoryLoaded.connect(self.on_loaded)
def on_loaded(self, loaded_path):
# Folder to list
folder_path = "C:/Users" # <--- we are interested in this folder,
# not the root folder
folder_index = self.model.index(folder_path)
# Check the folder we are interested in is completely loaded
if self.model.canFetchMore(folder_index):
self.model.fetchMore(folder_index)
return
# Folder is now loaded, list children
num_rows = self.model.rowCount(folder_index)
for row in range(num_rows):
# Child data
num_columns = self.model.columnCount(folder_index)
if num_columns > 0:
# Child name in first column
index = self.model.index(row, 0, folder_index)
text += index.data(Qt.DisplayRole)
# etc
int rowCount = ui->tableView->verticalHeader()->count();

Qt. QSplitter. Handle double click when cursor is under splitter

I need to handle double click for QSplitter when cursor is under splitter.
I redefined mouseDoubleClickEvent. But this does not work for this case.
When I do doulble click when cursor is under splitter (ready to move splitter) the method is not calling.
You can use an event filter to filter all events going to the handle of Qsplitter :
bool MyClass::eventFilter(QObject * obj, QEvent * event)
{
if(event->type()==QEvent::MouseButtonDblClick)
{
...
}
return false;
}
Also don't forget to install event filter in the constructor of your class :
MyClass::MyClass(QWidget *parent):QWidget(parent)
{
...
ui->splitter->handle(1)->installEventFilter(this);
...
}
I needed the same in order to be able to evenly space the widgets in the splitter when the user double clicks on the handle (this was my use case). Overriding QSplitter.mouseDoubleClickEvent() does not work because it seems that the handle consumes the double click event itself so it is not propagated to the parent QSplitter. The solution proposed in the accepted answer using eventFilter is quite good but is has the disadvantage that it is not 'dynamic', i.e. the event filter is not installed when the user adds new widgets to the splitter during runtime. So we need to find a way to install the event filter dynamically. There are two options to achieve this:
Override QSplitter.addWidget() and QSplitter.insertWidget():
# inside class MySplitter
def addWidget(self, widget):
super(MySplitter, self).addWidget(widget) # call the base class
self.handle(self.count() - 1).installEventFilter(self)
def insertWidget(self, index, widget):
super(MySplitter, self).insertWidget(index, widget) # call the base class
self.handle(index).installEventFilter(self)
but this is a bit problematic when the user adds widgets by not using these two methods but by setting parent to the child widget, though this is discouraged by the docs - see: http://doc.qt.io/qt-5/qsplitter.html#childEvent
Intercept the childEvent(), which feels a bit hacky but is error proof:
# inside class MySplitter
def childEvent(self, event):
if event.added():
# Note that we cannot test isinstance(event.child(), QSplitterHandle)
# because at this moment the child is of type QWidget,
# it is not fully constructed yet.
# So we assume (hacky but it works) that the splitter
# handle is added always as the second child after
# the true child widget.
if len(self.children()) % 2 == 0:
event.child().installEventFilter(self)
super(MySplitter, self).childEvent(event) # call the base class
I am using b) and it works for me quite well. This has the advantage that you do not need to subclass (I do, however, for teh sake of simplicity), you can install another event filter to intercept the childEvent and install the event filter from outside.
Sorry my code is in PyQt, but I think it is idiomatic enough and easily translated to C++.

QLineEdit hover-over Signal - when mouse is over the QlineEdit

I have a QLineEdit, and I need to know if there is a signal which can track mouse hover over that QLineEdit, and once mouse is over that QLineEdit it emits a signal.
I have seen the documents, and found we have the following signals:
cursorPositionChanged ( int old, int new )
editingFinished ()
returnPressed ()
selectionChanged ()
textChanged ( const QString & text )
textEdited ( const QString & text )
However, none of this is exactly for hover-over. Can you suggest if this can be done by any other way in PyQt4?
There is no built-in mouse-hover signal for a QLineEdit.
However, it is quite easy to achieve something similar by installing an event-filter. This technique will work for any type of widget, and the only other thing you might need to do is to set mouse tracking (although this seems to be switched on by default for QLineEdit).
The demo script below show how to track various mouse movement events:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.edit = QtGui.QLineEdit(self)
self.edit.installEventFilter(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
def eventFilter(self, source, event):
if source is self.edit:
if event.type() == QtCore.QEvent.MouseMove:
pos = event.globalPos()
print('pos: %d, %d' % (pos.x(), pos.y()))
elif event.type() == QtCore.QEvent.Enter:
print('ENTER')
elif event.type() == QtCore.QEvent.Leave:
print('LEAVE')
return QtGui.QWidget.eventFilter(self, source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 100)
window.show()
sys.exit(app.exec_())
You can use enterEvent, leaveEvent, enterEvent is triggered when mouse enters the widget and leave event is triggered when the mouse leaves the widget. These events are in the QWidget class, QLineEdit inherits QWidget, so you can use these events in QLineEdit. I you don't see these events in QLineEdit's documentation, click on the link List of all members, including inherited members at the top of the page.

QDialog on accept return custom class object

I'm using qt-creator to build a little QT application.
I have a main window where I have some controls like a "new contact" button.
Pressing the button a QDialog is shown, it contains 3 line edits: name, mobile and email.
The dialog is shown through the Signal/Slot system. It works fine but I want to create a Contact object when OK is clicked and I want to give back that Contact to my main window in order to put it in a QList created in the main window code.
The approach is:
QMainWindow -> new contact -> QDialog is shown
QDialog -> ok -> QMainWindow
Should I pass the QList from the main window to the QDialog as argument or there is a best way?
Should I pass the QList from the main window to the QDialog as argument or there is a best way?
In my opinion, best would be a custom QDialog subclass with three QLabels and 3 QLineEdits.
The labels would get the following type of values:
Label 1: name
Label 2: mobileNumber
Label 3: email
You would use then QLabels and QLineEdits to display them with the input coming from the user.
Then, as your program probably already does, just handle the "accept" event respectively. You could use the following method to retrieve the text entered by the end user:
text : QString
This property holds the line edit's text.
Setting this property clears the selection, clears the undo/redo history, moves the cursor to the end of the line and resets the modified property to false. The text is not validated when inserted with setText().
The text is truncated to maxLength() length.
By default, this property contains an empty string.
Then, in the handler of the accepted signal, you could call three accessor methods, like:
QString name() const { return nameLineEdit->text(); }
QString mobileNumber() const { return mobileNumberLineEdit->text(); }
QString email() const { return emailLineEdit->text(); }
You could also store that in a dedicated structure depending on you build up your data representation, so the structure would be something like this:
struct Contact {
QString name;
QString mobileNumber;
QString email;
};
and then you would have the accessor for that as follows
Contact contact() const;
Make a subclass of QDialog. Call Yourclass::exec() to show the dialog (exec is a function in QDialog), then afterwards Yourclass::contactDetails() to get them. contactDetails is a perfectly ordinary member function that you have to write.