I am trying to watch the changes in a directory using QFileSystemModel. Whenever I rename a file in the root path, only the directoryLoaded() signal is emitted. I want the fileRenamed() signal to be emitted so that I know which file is renamed to a new name. Here is my code:
model = new QFileSystemModel;
model->setRootPath("C:/test/");
QObject::connect(model, SIGNAL(fileRenamed(const QString&, const QString&, const QString&)), this, SLOT(updateRename()));
QObject::connect(model, SIGNAL(directoryLoaded(const QString&)), this, SLOT(loadDir()));
I am afraid you expect too much from this QFileSystemModel class. It does not and cannot catch if the renaming operation happens outside of the model. I looked up all uses of fileRenamed() signal and it seems that the only place where it is emitted is here: https://code.woboq.org/qt5/qtbase/src/widgets/dialogs/qfilesystemmodel.cpp.html#933
And if you go a few lines above, you can see that this is triggered when the renaming happens inside this function https://code.woboq.org/qt5/qtbase/src/widgets/dialogs/qfilesystemmodel.cpp.html#873 In other words, if you use QFileSystemModel::setData() to set a name to an item, it will rename the item and emit the signal. And it is the only way to have the signal emitted.
And this is logical, if renaming happens outside of your program, then there is no certain way to find out that a certain file was renamed. Your application only observes that some file disappeared and another file with a different name emerged. Of course, you can check whether the timestamp and size of the file is the same, whether they are also in the same parent folder, maybe also check the file content... and only if these things match and the only difference is in the file names, then you can conclude that the file was renamed. But this is something you must program yourself. QFileSystemModel will not do it for you because it does not know your specific intentions.
Related
There any way to trigger an action if a file in a specified directory ( or in a subfolder ) without fetching all modification times every time ? I'm asking because i've to check this live
You need to use the QFileSystemWatcher.
More importantly, this is the signal you need to connect to:
void QFileSystemWatcher::fileChanged(const QString & path) [signal]
This signal is emitted when the file at the specified path is modified, renamed or removed from disk.
See also directoryChanged().
So, you could write something like this in your class or function:
...
QFileSystemWatcher watcher;
watcher.addPath("/My/Path/To/The/File");
QObject::connect(&watcher, SIGNAL(fileChanged(const QString&)), receiver, SLOT(handleFileChanged(const QString&)));
...
You're looking for QFileSystemWatcher.
I am having some .jpg images in a folder which need to be displayed in QtListWidget. I am able to display the list of items in a particular folder in QtListWidget, but unable to open the item when i click it. I learned in tutorial one should use "connect" for doing it, i tried it but error is happening.
My code in .cpp file is as follows. Any help is most welcomed...
QDir myPath("/home/mit/Desktop/Ui_dev_mits_cars/visual_image");
myPath.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
myList = myPath.entryList();
ui->listWidget1->addItems(myList);
Right, so the issue is at least two-folded with your code:
connect(MyList,SIGNAL(doubleClicked(QListWidgetItem *)),this,SLOT(test(QListWidgetItem *)));
First of all, you are trying to use a QStringList value based object rather than pointer. Also, you should use the pointer pointing to the QListWidget instance.
Secondly, you are using the signal wrong. It is parameter is a QModelIndex as opposed to a QListWidgetItem. See the documentation for details:
void QAbstractItemView::doubleClicked(const QModelIndex & index) [signal]
This signal is emitted when a mouse button is double-clicked. The item the mouse was double-clicked on is specified by index. The signal is only emitted when the index is valid.
So, grab the model index and in your slot, get the data out of that model index either by using the internal pointer, or preferrably the data() method.
just started using qt,
looked through docs, google, examples, etc.. trying to find simple examples(working mind you)
that showed how to do (imho) simple things, by themselves.
well i stumbled upon my answer and i was wondering if this approach would cause an issue later as the code becomes more complex.
there are more includes than needed for this example, but this is direct from working code.
mainwindow.h:
i added
private slots:
void vpkButton_clicked();
and after
Ui::MainWindow *ui;
i added
QLineEdit *vpkPathTxt;
in mainwindow.cpp:
after
ui->setupUi(this);
i added
connect( this->ui->vpkButton, SIGNAL( clicked() ), this, SLOT(vpkButton_clicked()) );
to connect my ui button to the proper slot, the issue was getting the string from vpkButton_clicked() to display in the line edit i made in the designer,
what ended up working for me was adding this next:
vpkPathTxt = this->ui->vpkPathTxt;
the function in my main.cpp became very easy:
(QString declarations at top outside voids)
void MainWindow::vpkButton_clicked()
{
vpkName = QFileDialog::getOpenFileName(this,
tr("Open VPK File"), "~/", tr("VPK Files (*_dir.vpk)"));
vpkPathTxt->setText(vpkName);
qDebug() << vpkName;
}
the reason i am ask is because it seems a little too easy to be reliable, and the fact that i havent seen it done like this,
any input welcome
thankyou
One problem with your slot is that you don't consider the case where the user discards the "open file" dialog. In this case, the function QFileDialog::getOpenFileName returns a null QString, so you should only proceed with your logic if the return value was not a null string:
if (!vpkName.isNull()) {
...
}
The second problem is as follows and I made some assumptions since I don't see your full code:
I guess you want to load a file using the file name the user has chosen in the dialog. But you set the file name in the line edit too, which the user can edit by hand. I also guess that the actual file loading happens in a different step (i.e. after clicking another button), so after the user has edited the file name by hand in the line edit it won't be the same than in your local variable vpkName.
When loading the file I'd read the contents of the line edit instead of the variable vpkName so the edit made by hand will be respected.
A different method is to also watch for editing of the line edit and reflect the changes in your variable too. Then it will be ok to read the variable instead of the line edit when loading the file later on.
I'm trying to design a Qt GUI application with user customize-able hotkeys. The main issue I'm running into is how to synchronize the hotkeys across the application since a particular hotkey (for example, copy) may be used by multiple widgets/components.
My current strategy is to use a reference class which holds a list of QKeySequence objects for each different hotkey. Each widget would have to have a way to reference this master list and have custom implementations of low-level the keyPressEvent which would compare inputted keys vs. the hotkeys. I don't particularly like this strategy, though, as it requires significant re-implimentation in each widget and feels like I'm trying to re-invent the wheel.
I also tried using QAction objects which can hold QKeySequence shortcuts internally, then use these to trigger higher-level events which I can handle using slots & signals. However, the main issue I have here is how to manage which slots signals get routed to.
For example, say I have 2 open widgets which can both receive a copy action signal. I can connect a slot for both of these to the same signal and take advantage of the single update point for shortcuts, but then things get messy since only the active widget should act on the copy signal, not both widgets. I can re-implement the focusOutEvent and focusInEvent handlers to connect/disconnect slots manually, but this also seems to run into the same issue above where I'm trying to re-invent the wheel and doing more work than is necessary.
Is there an easier way around this problem?
I don't think there is a particularly easy/non-tedious solution to this problem, but when I needed to add user-customizable hotkeys to my application, here is how I did it:
1) Start with your application that has hard-coded key shortcuts, e.g. code like this:
QMenu * editMenu = new QMenu;
QAction * copyItem = menu->addAction(tr("Copy"), this, SLOT(CopyData()));
copyItem->setShortcut(tr("Ctrl+C"));
2) Create a GetKeySequence() function that looks something like this:
static QHash<QString, QKeySequence> _usersKeyPreferences;
static bool _usersKeyPreferencesLoaded = false;
QKeySequence GetKeySequence(const QString & keySequence, const QString & contextStr)
{
if (_usersKeyPreferencesLoaded == false)
{
// Oops, time to load in the user's saved custom-key settings from a file somewhere
_usersKeyPreferences = LoadUsersKeyPreferencesFromFile();
_usersKeyPreferencesLoaded = true; // so we'll only try to load the file once
}
if (_usersKeyPreferences.contains(contextStr))
{
return _usersKeyPreferences[contextStr];
}
else
{
// No user preference specified? Okay, fall back to using the
// hard-coded default key sequence instead.
return QKeySequence(qApp->translate(contextStr, keySequence));
}
}
3) Now the tedious part: grovel over all of your code, and anywhere you've specified a key-sequence explicitly (like in the third line of the code shown for step 1), wrap it with a call to GetKeySequence(), like this:
copyItem->setShortcut(GetKeySequence(tr("Ctrl+C"), tr("Edit_Menu|Copy")));
4) At this point, your program's key-sequences will be customizable; just make sure that the key-settings-file is present on disk before GUI-creation code runs. Here's an excerpt from my program's key-mappings file (which I store as a simple ASCII text file):
Edit_Menu|Copy = Ctrl+C
Edit_Menu|Cut = Ctrl+X
Edit_Menu|Paste = Ctrl+V
[... and so on for all other menu items, etc...]
... of course one downside to this approach is that once the GUI is created, the key-bindings can't be modified "on the fly" (at least, not without a lot of additional coding). My program gets around this simply by closing and then re-creating all windows after the user clicks "Save and Apply" in the Edit Key Bindings dialog.
5) An optional further step (which is some extra work up front but saves time in the long run) is to write a program (or script) that greps all the .cpp files in your program's codebase looking for calls GetKeySequence() in the code. When it finds a GetKeySequence() call, it parses out the two arguments to the call and prints them as a line in a key-bindings file with the default settings. This is useful because you can make this script part of your autobuild, and thereafter you'll never have to remember to manually update the default key-settings-file whenever you add a new menu item (or other key-sequence specifier) to your program.
This worked well for me, anyway. The advantage is that you don't have to refactor your existing program at all; you can just go through it inserting GetKeySequence() as necessary while leaving the larger logic/structure of the program intact.
I'm developing an app on windows with Qt and I need to detect changes in a specific folder.
So I used a QFileSystemWatcher, and I connect the directoryChanged signal to a function that will send a message in case of changes.
The problem is that the "slot" function connected to directoryChanged is not called if I modify a file's content, but only when a file or directory is removed or added.
However, the documentation says that this signal is emitted when "the directory at a specified path, is modified (e.g., when a file is added, modified or deleted) or removed from disk."
Does anyone have an explanation?
Thanks in advance =)
According to Qt source code version 4.8.2, as following:
void QFileSystemWatcherPrivate::_q_directoryChanged(const QString &path, bool removed)
{
Q_Q(QFileSystemWatcher);
if (!directories.contains(path)) {
// perhaps the path was removed after a change was detected, but before we delivered the signal
return;
}
if (removed)
directories.removeAll(path);
emit q->directoryChanged(path);
}
It seems that directoryChanged emit when a file removed, added(for the new added file not contains in directories), and renamed. The implementation does not guarantee to detect a modification of a file's content. Hope that helps :P