QSoundEffect does not reload source - qt

In my program it is possible to select a sound for an action. the sound is changeable, which means the .wav file gets replaced by another file.
This may cause the problem. When i replace the file and set the source of the QSoundEffect the sound does not change.
At the moment i am having a source like this:
//variable in .h
QUrl sound = "file:///"+soundDirectory+"sound.wav";
QUrl newSound = "file:///"+soundDirectory+"newSound.wav"; ;
QSoundEffect soundeffect;
//called in setSound() in .cpp
soundEffect.setSource(sound);
the sound loads without problem and i can play that sound.
i can change that sound with this code
// changing the sound in changeSound()
soundEffect.setSource(newSound);
this also works fine. the new sound is loaded and i can play it.
But it is also possible to change the sound files in the directory:
//changeSoundFile()
QFile::remove(sound.toLocalFile());
QFile::copy(anyPossibleSound.toLocalFile(), sound.toLocalFile());
This also works and replaces the sound file in its directory with another.
If I call setSound() after changing the file. It seems like the file does not get reload. and the sound is not changed. This is also the problem if changed the sound in between (call setSound on startup, then changeSound, then changeSoundFile and the setSound again)
Am I overlooking something?

It's not mentioned in the official docs, but you can find the implementation on GitHub:
void QSoundEffect::setSource(const QUrl &url) {
if (d->source() == url)
return;
d->setSource(url);
emit sourceChanged();
}
The file does not get reloaded because the URL is the same. The implementation holds an internal cache with the data that has been loaded before, so when you play the file, nothing change.
The API does not provide a way of forcing the reset of the data source. You have two alternatives:
Re-creating the QSoundEffect instance each time you modify the file.
Changing the file name:
// Create a temporal file
const auto uuid = QUuid::createUuid();
const auto new_filename = uuid.toString() + ".wav";
// Copy the file
QFile::remove(sound.toLocalFile());
QFile::copy(anyPossibleSound.toLocalFile(), new_filename);
sound = QUrl(new_filename);
// Use it
soundEffect.setSource(sound);

Related

Pre-compile QML files under Qt Quick Controls

I am importing 2 QML files that come with Qt Controls - ScrollBar.qml and Button.qml in my project. I pre-compile all .qml files that I wrote to reduce application launch time. Is there a way to pre-compile these 2 QML files that come as part of the package?
I tried to remove these files from the qml/QtQuick/Controls/ path and placed them in the same folder as my .qml files but it still failed to load. When I reference ScrollBar in my code, it always tries to load ScrollBar.qml from qml/QtQuick/Controls/ path.
Does any one know if it is possible to pre-compile these QMLs at all? If yes, has any one successfully done it?
Appreciate any help. Thank you.
I'm assuming that you're referring to the Qt Quick Compiler as pre-compiling. The simplest way would just be to build the entire Qt Quick Controls module with the Qt Quick Compiler.
If you need to have it within your project, you could try adding an import that contains the Qt Quick Controls import. QQmlEngine::addImportPath() says:
The newly added path will be first in the importPathList().
That statement seems to imply that order matters, and the code confirms it:
QStringList localImportPaths = database->importPathList(QQmlImportDatabase::Local);
// Search local import paths for a matching version
const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(uri, localImportPaths, vmaj, vmin);
for (const QString &qmldirPath : qmlDirPaths) {
QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirPath);
if (!absoluteFilePath.isEmpty()) {
QString url;
const QStringRef absolutePath = absoluteFilePath.leftRef(absoluteFilePath.lastIndexOf(Slash) + 1);
if (absolutePath.at(0) == Colon)
url = QLatin1String("qrc://") + absolutePath.mid(1);
else
url = QUrl::fromLocalFile(absolutePath.toString()).toString();
QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
cache->versionMajor = vmaj;
cache->versionMinor = vmin;
cache->qmldirFilePath = absoluteFilePath;
cache->qmldirPathUrl = url;
cache->next = cacheHead;
database->qmldirCache.insert(uri, cache);
*outQmldirFilePath = absoluteFilePath;
*outQmldirPathUrl = url;
return true;
}
}
Your project structure might look something like this:
myproject/
qml/
main.qml
QtQuick/
Controls/
Button.qml
ScrollBar.qml
qmldir
In main.cpp you'd set the path to the qml directory (note that the path will be different depending on whether you're doing an in-source build or a shadow build of your project, so you may want to use a resource file to simplify things):
engine.addImportPath("path/to/qml");
Note that the controls import other types. For example, Button uses the Settings singleton, which comes from the QtQuick.Controls.Private import, so you'd need to copy that into the qml directory, too. Settings loads a certain style for the button (ButtonStyle), which could be any of the styles in this folder, depending on which style is in use.
In short, you need to copy all of the potential dependencies of the QML files you're using.

Qt - Add files to project

I'm making an application part of which is reading from an XML which stores some preferences. However, whenever I build the project, all the sources get copied but the preferences file does not! I have added the following to the .pro file -
RESOURCES += rsc.qrc
And my rsc.qrc contains
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>data/preferences.xml</file>
<file>data/gamedata.xml</file>
</qresource>
</RCC>
Now whenever I try to open preferences.xml
QFile preferences(":/data/preferences.xml");
if(!preferences.exists()){
preferences.open(QFile::WriteOnly);
preferences.write("abc");
qDebug() << "Written";
}
else {
qDebug() << "File exists";
}
Absolutely nothing gets printed and even the rest of the application stops working.
You don't use the resource part correctly in your example.
It will most likely not work because you try to write to a resource that is embedded into your executable after you have build your application. Reading is fine, but writing can't work by definition.
If you want a editable setting files, you have to distribute them along with your executable, or use a different method for reading/writing your settings like QSettings.
However using QSettings also means, that you will need to configure all your default settings in your loading function in case the values do not exist if you use the default configuration. Meaning you use registry on windows.
You have the option to force the use of a INI file format in the constructor of QSettings, this can make sense if you want to provide a default settings INI file instead of your xml files.
In case you want to store more complex data a xml file might be needed anyway, so if you want to stick with that you will need a way to copy your setting files to your build path. This can be done within your pro file with QMAKE_POST_LINK.
Example:
COPY_CMD = "$$PWD\\data\\preferences.xml $$OUT_PWD\\release\\data\\preferences.xml"
COPY_CMD = $${QMAKE_COPY} $$replace( COPY_CMD, "/", "\\" ) $$escape_expand( \\n\\t )
QMAKE_POST_LINK += $$COPY_CMD

Qt5: download a file without saving to HDD

I'm trying to download a file in Qt5, but the file must not be located on the HDD after download.
To clarify> My app will use a downloaded file to update some firmware, and I don't want the downloaded update to remain on the user's hard drive because it could get stolen.
So, I'm trying to make a QFile from QNetworkReply* but without saving it to some path on a hard drive.
I'm downloading a file using QNetworkAccessManager and storing the data into QNetworkReply. I always used to make a QFile with QNetworkReply*, but now I can't do that.
I have found the QTemporaryFile class where a file gets removed right after using it, but that still leaves user with some options of finding the file later.
I tried typecasting that QNetworkReply* as a QFile, but didn't manage to get that to work, seems like QFile can't be without a path on HDD.
Does anyone have any ideas how to do this, and how?
Thanks everyone.
Again not sure your intended end use case but since your data is small enough to hold in memory you can use a QByteArray or QBuffer and write into it from your QNetworkReply. QBuffer provides a QIODevice interface for the QByteArray so it may be a bit easier for you to work with.
Make sure to open the QBuffer for read/write. See the simple example below from the Qt documentation, http://doc.qt.io/qt-5/qbuffer.html#details, below:
QBuffer buffer;
char ch;
buffer.open(QBuffer::ReadWrite);
buffer.write("Qt rocks!");
buffer.seek(0);
buffer.getChar(&ch); // ch == 'Q'
buffer.getChar(&ch); // ch == 't'
buffer.getChar(&ch); // ch == ' '
buffer.getChar(&ch); // ch == 'r'
That should allow you to read back the data and use as required without creating a file on the system.

Qt QFileSystemWatcher on Windows

I have the following issue: I create a QFileSystemWatcher and it runs and works nicely on Linux, but no way on Windows 7. Can you spot anything in the code that might make it not to work?
Thx.
Here is the code to initialize it:
mConfigChangeWatcher = new QFileSystemWatcher();
mConfigChangeWatcher->addPath(config_file_name);
QObject::connect(mConfigChangeWatcher,
SIGNAL(fileChanged(QString)),
this,
SLOT(configFileChanged(QString)));
and this is supposed to be the slot getting the work done:
void MyClass::configFileChanged(const QString &file)
{
qDebug() << "Changed: " << file ;
}
When you check if the file is added to the watcher using QFileSystemWatcher::files() method after the first modification in the file do you get the correct list?
I was with the issue that some applications, when modifing a file, delete the old file from the system and write it again.
Note that QFileSystemWatcher stops monitoring files once they have been renamed or removed from disk, and directories once they have been removed from disk.
I was using QFileSystemWatcher to watch an image file edited by Photoshop. Somehow the file gets removed from the list of files being watched.
I had the same problem and solved it very fast.
Within the slot that manages the fileChanged signal I noted the path disappears from files(). I simply make a check and re-add it if necessary
if (! watcher_.files().contains(path))
{
watcher_.addPath(path);
}
I hope this helps
Fabio

Qt: read video dimension without creating a video player

I need to read the dimensions of a given video file (its width and height), without constructing a video player, like Phonon, e.g. My question is which class I should use to get access to this data. I have already tried using QPixmap and QMovie, but niether of them supports *.mov.
Thank you!
Pavlo, you can try this:
QMediaContent media("myMovie.mov");
QSize resolution = media.canonicalResource().resolution();
The code uses QMediaResource class from Qt Mobility project. I haven't tried it yet and I suppose you need at least a correct backend (plugin that is capable of reading MOV format). I'm giving this answer only from API overview.
Hope this helps.
I finally solved my problem and I thought I'd share my solution with everybody else.
In the class constructor I initialize the following two variables:
media = new Phonon::MediaObject(this);
videoWidget = new Phonon::VideoWidget;
I connect a signal of media to a slot in my class:
connect(media,SIGNAL(stateChanged(Phonon::State,Phonon::State)),
this,SLOT(videoState(Phonon::State,Phonon::State)));
I let the user choose a video file:
QString filename = QFileDialog::getOpenFileName(this,tr("Choose video file"),QDir().homePath(),tr("Video files (*.mov *.mpg *.avi)"));
And apply this file to the media object:
media->setCurrentSource(filename);
Phonon::createPath(media,videoWidget);
Because media object is already connected to a slot, every change in media can be monitored with its help.
void VideoModuleDialog::videoState(Phonon::State newState, Phonon::State oldState)
{
if(newState == Phonon::PlayingState || newState == Phonon::StoppedState)
{
width->setText(QString().number(videoWidget->sizeHint().width()));
height->setText(QString().number(videoWidget->sizeHint().height()));
}
if(newState == Phonon::ErrorState)
{
QMessageBox::critical(this,tr("Video file error!"),
tr("Video file error: ") + media->errorString(),QMessageBox::Ok);
}
}
I must admit, however, that this code seems to me to be quite slow. Phonon library is used in my program only in one place, and this is here, in a dialog window where user can choose a video clip to embed, and i want the video dimensions to be read from file. It takes some time until this dialog window opens, so I guess, this solution is a bit too harsh for my problem. However, I was not able to find another solution. If there are different opinions as to the subject of this post, I'd be glad to hear them.

Resources