Display Audio & Video Files In TreeView on Clicking Any Drive (QFileSystemModel) - qt

I want to traverse the drives present in my system and search for audio/video files inside it. Basically traverse the sub directories and display the file inside the treeview. I have 2 tree views, One to display System Directories and other to display audio/video files.
// Displays System Drives inside TreeView(Drive View) When Application Starts
void PanasonicViewer::onCamStartup()
{
m_SystemModel = new QFileSystemModel(this);
m_SystemListViewModel = new QFileSystemModel(this);
m_SystemModel->setRootPath(QDir::currentPath());
ui->DriveView->setModel(m_SystemModel); //Left side TreeView
ui->DriveListView->setModel(m_SystemListViewModel); //Right Side TreeView
// regard less how many columns you can do this using for:
for(int nCount = 1; nCount < m_SystemModel->columnCount(); nCount++)
ui->DriveView->hideColumn(nCount);
}
//On Clicking The TreeView, it should display Audio and Video files in DriveListView
void PanasonicViewer::on_DriveView_clicked(const QModelIndex &index)
{
QStringList sDriveFilters;
QString sPath = m_SystemModel->fileInfo(index).absoluteFilePath();
ui->DriveListView->setRootIndex(m_SystemListViewModel->setRootPath(sPath));
m_SystemModel->setRootPath(QDir::currentPath());
m_SystemModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs );
m_SystemListViewModel->setFilter( QDir::Files | QDir::NoDotAndDotDot );
sDriveFilters << "*.aac" << "*.wmv" << "*.avi" << "*.mpeg" << "*.mov" << "*.3gp" << "*.flv" << "*.mp3" ;
m_SystemListViewModel->setNameFilters(sDriveFilters);
m_SystemListViewModel->setNameFilterDisables(false);
}
You can notice in the above click event that I have set Filter to selected extensions. This seems to work and displays audio and video files when I click a drive i.e. E:\ but doesnt display the files present inside subfolders. Where am i going wrong?

The problem is due to what you're setting for QFileSystemModel::filter.
The default setting for is QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs, where QDir::AllEntries translates to QDir::Dirs | QDir::Files | QDir::Drives. If we expand it out, the default setting is:
QDir::Dirs | QDir::Files | QDir::Drives | QDir::NoDotAndDotDot | QDir::AllDirs
By calling m_SystemModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs ), you're telling the model that you only want to see standard directories, and want to exclude all files. Just remove your call to setFilter altogether and you should be fine.

Try changing
ui->DriveListView->setRootIndex(m_SystemListViewModel->setRootPath(sPath));
to
m_SystemListViewModel->setRootPath(sPath)
ui->DriveListView->setRootIndex(m_SystemListViewModel->index(sPath);
I'm skeptical whether the QModelIndex returned by setRootPath is the one you think it is. Troubleshoot this by removing the filter from m_SystemListViewModel to make sure that you're actually viewing the folder that you think you are.
I know that this should work if sPath is a directory path. I'm not sure if it'll work as expected if it's a file path.

What you expect is not what the standard QFileSystemModel is supposed to do. What you need is to manually extract the multimedia files recursively in QStringListModel and set it on your QListView, or directly use QListWidget to add items directly while enumerating. When the selection over the QFileSystemModel changes you can get the path of the selected folder and enumerate the files and the subfolder files recursively. Note that if you click accedentally some drive you will end-up waiting for all the multimedia files in your drive. If that is what you want then maybe you may consider enumerating in worker thread which is not so easy.

Related

QTableView: how to edit non-editable cells in the program?

How should this be done by using the model->setData() method call?
I have derived a class called "MyStandardItemModel" from QStandardItemModel. I have made my third and fourth columns non-editable by overriding the protected virtual flags method. This is how it goes:
#define TX_PACKET_COLUMN (4u)
#define RX_PACKET_COLUMN (5u)
Qt::ItemFlags MyStandardItemModel::flags(const QModelIndex& index) const
{
if (index.column() == TX_PACKET_COLUMN || index.column() == RX_PACKET_COLUMN)
{
return (QStandardItemModel::flags(index) & ~Qt::ItemIsEditable);
}
else
{
return QStandardItemModel::flags(index);
}
}
...
//Set model
ui->testCaseTableView->setModel(model);
Having this done, I am not able to edit the cells in the third and fourth column.
Now, I want that when I double click on these cells, a pop-up dialog comes up. I will modify some data in the editable field of that dialog, and then copy it back to the non editable cells inside the code.
I tried to just write a doubleclick() handler for the QTreeView and just copy some data to the cells just to see if it is possible to copy data to the non-editable cells.
This operation is failing, and the data is not written into the non-editable cells.
Here you can find the double click handler:
void MainWindow::on_testCaseTableView_doubleClicked(const QModelIndex &index)
{
QVariant variant;
variant.toString() = "AA";
if((index.column() == TX_PACKET_COLUMN)||(index.column() == RX_PACKET_COLUMN))
{
model->setData(index, variant); // set new value
}
}
The setData(..) operation is clearing the already written data in the cells, but string "AA" is not getting written. Please suggest how to copy some data to non-editable cells inside the code.
QVariant set is empty. Nothing needs to be wrong in your model. Error is on this line:
variant.toString() = "AA";
change to:
QVariant variant("AA"); // just for testing anyway
As I indicated in my comment, you have to fix this first issue:
instead of:
QVariant variant;
variant.toString() = "AA";
you should write
QVariant variant = QLatin1String("AA");
In general, you would look into the setData(...) implementation for such cases whether you emit the data changed signal properly and so forth, but here you are entering a prior issue which can lead to problems, so let us fix that.
Note, you should use QLatin1String to avoid the unnecessary explicit conversion from raw char* to QString. This is a good practice in general, and this is available with Qt 4 as well as Qt 5.
Although, you could also use the QStringLiteral macro for creating a QString very efficiently with template magic out of your raw literal, but that requires C++11.

How do I select a row in a QTreeview programatically?

Update: Let me try to simplify the issue and steps. Does anyone see where I've gone wrong? Also, this is on OSX 10.7.5/Qt 5.1 – perhaps it is an OSX issue?
I pass an absolute path which points to a directory of images and set the root path of my QFileSystemModel instance and use the index returned by that to set my QTreeView instance root index
QModelIndex idx = dirmodel->setRootPath(dPath);
myTreeView->setRootIndex(idx);
I then set the current index of the treeview to that index. Which I guess is pointing to the directory – perhaps this is wrong?
myTreeView->setCurrentIndex(idx);
I have tried using the selectionModel of the treeView to make the selection using that index:
myTreeView->selectionModel()->select(idx,
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
I have also tried accessing a child of the directory and using that to set the current index and selection:
QModelIndex next_index =myTreeView->currentIndex().child(1,0);
But I still am not getting the selection on my treeview.
Context: For a Qt learning project I am building an image viewer. I have a 'QTreeView' with a backing 'QFileSystemModel.' The user has the option to select a directory of images and only the contents of that directory will be displayed in the QTreeView (i.e. it is not displaying the entire file system, just the subdirectory the user selects).
I would like to have the first item in the QTreeView selected when the user changes directories. I have seen this question (Selecting a row in QTreeView programatically) but it didn't work for me. I've been poking at this for a few hours now. What am I doing wrong? I am able to select directories and display the images but I am missing how to set the QTreeView selection.
My code is below, with various attempts commented out. The console output from the qDebug statement is always:
next_index -1 -1
void DirBrowser::on_actionSet_Image_Directory_triggered()
{
QString dPath = QFileDialog::getExistingDirectory(this,
tr("Set Image Drectory"), QDir::currentPath());
if (!dPath.isEmpty()) {
QModelIndex idx = dirmodel->setRootPath(dPath);
myTreeView->setRootIndex(idx);
myTreeView->setCurrentIndex(idx);
QModelIndex next_index =myTreeView->currentIndex().sibling(1,0);
//QModelIndex next_index = myTreeView->model()->index(1, 0);
//QModelIndex next_index =idx.child(0,0);
qDebug() << "next_index" << next_index.row() << next_index.column();
//myTreeView->setCurrentIndex(next_index);
myTreeView->selectionModel()->select(next_index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
QString path = dirmodel->fileInfo(next_index).absoluteFilePath();
qDebug() << "set Dir" << path;
}
}
Figured it out. The problem was that I was trying to make a selection on the QTreeView before the specified directory had finished being loaded. In other words, tryng to make the selection within the method where I was changing directories.
The solution is to listen for the directoryLoaded(QString) SIGNAL that QFileSystemModel emits.
void QFileSystemModel::directoryLoaded ( const QString & path ) [signal]
This signal is emitted when the gatherer thread has finished to load the path.
This function was introduced in Qt 4.7.
connect(dirmodel,
SIGNAL(directoryLoaded(QString)),
this,
SLOT(model_directoryLoaded(QString)));

Need to display in qtextEdit real time

I have an arm board with a touchscreen display, where I want to display the output from a certain function, vcm_test(). The output of this function is saved to a file called
test.txt . Now I am able to read the contents of the file test.txt and display it in my qtextEdit only if it is less than 50-60 lines. Whereas I have more than 7000 lines in the test.txt . When I try to display 7000 lines the arm board keeps reading and nothing is displayed until reading is complete. Is there any way to read and display after every line or say every 10 lines. I thought of using qProcess in readfile too, but I have no idea how I can do that.
connect(ui->readfil, SIGNAL(clicked()), SLOT(readfile()));
connect(ui->VCMon, SIGNAL(clicked()), SLOT(vcm_test()));
connect(ui->Offloaderon, SIGNAL(clicked()), SLOT(offloader_test()));
connect(ui->quitVCM, SIGNAL(clicked()),vcmprocess, SLOT(kill()));
connect(ui->quitoffloader, SIGNAL(clicked()),offloaderprocess, SLOT(kill()));}
MainWindow::~MainWindow(){
delete ui;}
void MainWindow::readfile(){
QString filename="/ftest/test.txt";
QFile file(filename);
if(!file.exists()){
qDebug() << "NO file exists "<<filename;}
else{
qDebug() << filename<<" found...";}
QString line;
ui->textEdit->clear();
if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream stream(&file);
while (!stream.atEnd()){
line = stream.readLine();
ui->textEdit->setText(ui->textEdit->toPlainText()+line+"\n");
qDebug() << "line: "<<line;}
}
file.close();}
void MainWindow::vcm_test(){
vcmprocess->start("/ftest/vcm_test_2");}
void MainWindow::offloader_test(){
offloaderprocess->start("/ftest/off_test_2");}
Any advice is really appreciated.Thanks.
You could use QApplication::processEvents() after reading every line and appending it to your text edit. But you should be really careful when using this, and I would not recommend doing so. You should also consider using QTextEdit::Append() instead of setText.
A better solution is to read the file in another thread and use signals and slots to send read data that you want to append to your QTextEdit.

How to split large style file (qss file) in QT to small file and load them all

What I am trying to do I need to split my large style file to small style files
because now it is hard to read the style file(qss file) and add new styling to it.
after that i need to load those small qss files to apply them all
I am loading my Big file by calling function on main I have created
void Utilities::loadEnglishStyle()
{
QFile file(":/EnglishClasses.qss");
file.open(QFile::ReadOnly);
QString StyleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(StyleSheet);
file.close();
}
I thought about split the big file, add the small files to the resources, open all of them by QFile and after that concatenate them all in one string
but every time when adding new qss file i still need to do the same process again
Is there any efficient way to do this ?!!
Your approach sounds correct. The process is not as complex as it sounds. The only thing you need is to add the "smaller" qss file in the resources, under a specific prefix (eg. stylesheets), and then automatically load and concatenate all these files. Sample code follows:
QDir stylesheetsDir(":/stylesheets");
QFileInfoList entries = stylesheetsDir.entryInfoList();
QString completeStylesheet = "";
foreach (QFileInfo fileInfo, entries)
{
QFile file(":/stylesheets/" + fileInfo.fileName(););
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
continue;
QTextStream in(&file);
completeStylesheet += in.readAll()
}
you means some API shoud add as:
QWidget::addStyleSheet(StyleSheet);
QWidget::clearStyleSheet(StyleSheet);

Overwrite text file vs append

I'm looking to overwrite data in a text file but all I can seem to do is append to it
mFile.open(QFile::ReadWrite)
QTextStream in(&mFile);
QString first = in.readLine(); //discard the headers
QString dataLine = in.readLine(); //headers
QStringList sql_row = dataLine.split("\t"); //first row (sake of proj only 1 row)
if(sql_row[1].isEmpty()) //no user name registered
{
QByteArray user= getenv("USERNAME"); //for windows
if(user.isEmpty())
{
user = getenv("USER"); ///for MAc or Linux
}
dataLine = dataLine.insert(dataLine.indexOf("\t")+ 1,user);
in << first << endl << dataLine << endl;
mFile.flush();
mFile.close();
Change
mFile.open(QFile::ReadWrite);
to
mFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text);
The QIODevice vs QFile distinction isn't necessary, but I personally favor using the base class. The Truncate flag will overwrite (i.e., delete) an existing file.
Alternatively, you can follow the other suggestion and open your text file directly using one of QTextStream's constructors. The same QIODevice::OpenMode conventions apply. This only works if mFile is a FILE object and not a QFile, which isn't the case in your example.
A couple additional notes for beginners.
Related Note 1
You didn't ask about this, but I also added the QIODevice::Text flag to ensure that newline characters get translated to/from the local encoding (plain \n vs. \r\n) when you use endl.
A really common mistake is to use \r\n AND QIODevice::Text, which results in text files with double-returns \r\r\n on Windows. Just use QIODevice::Text when opening and simply \n or endl and you'll never have this problem.
Related Note 2
Using QTextStream::endl will automatically call flush() each time. If your loop is large, use "\n" instead to prevent a slowdown unless you actually need to flush every line. The stream will automatically write to disk as its buffer gets full, or when it's closed.
QFile::close() also calls flush(), which makes your mFile.flush() at the end redundant.
Use an overloaded constructor of QTextStream:
QTextStream in(&mFile, QIODevice::ReadWrite | QIODevice::Truncate);
The QIODevice::Truncate will remove all the previous content of the file, and QIODevice::ReadWrite will open it for read and write access.

Resources