Cannot change QAction's icon - c++

I'm trying to toggle QAction's icon. It depends on context of a specific table. Actually, it's working whenever I start message.cpp, but it wouldn't change when I'm on message.cpp. I expect updateIcon() will change my icon if I click on m_action4, but it doesn't. Could I repaint QAction?
Firstly, I set variable on header:
message.h:
private:
QAction *m_action4;
Here is my code on source:
message.cpp:
QSqlQuery query0;
m_action4 = new QAction(QIcon(":/images/silent1.png"), tr("Mute"), this);
m_muteActive = false;
query0.prepare("SELECT state FROM mute_state WHERE p_id=?");
query0.addBindValue(m_pid);
query0.exec();
if (query0.next() && query0.value(0) == "1" )
{
{
m_muteActive = true;
m_action4->setIcon(QIcon(":/images/speaker.png"));
}
}
m_actionBar->addButton(m_action4);
connect(m_action4, SIGNAL(triggered()), this, SLOT(muteMessages()));
muteMessages()
{
QSqlQuery query;
query.prepare("UPDATE mute_state SET state=? , duration= ? , type=? WHERE p_id=?");
if (m_muteActive)
{
query.addBindValue("0");
query.addBindValue("0");
m_muteActive = false;
}
else
{
query.addBindValue("1");
query.addBindValue("525949");
m_muteActive = true;
}
query.addBindValue("private");
query.addBindValue(m_id);
if (query.exec())
qDebug()<<"query was executed";
}
QTimer::singleShot(100 , this, SLOT(updateIcon()));
}
updateIcon()
{
if (m_muteActive)
m_action4->setIcon(QIcon(":/images/silent1.png"));
else
m_action4->setIcon(QIcon(":/images/speaker.png"));
}

The code you've posted works fine. Here it is, after removing the SQL queries that are not necessary to demonstrate it:
#include <QApplication>
#include <QAction>
#include <QToolBar>
#include <QGridLayout>
#include <QPainter>
class Ui : public QWidget {
QGridLayout m_grid;
QToolBar m_actionBar;
QIcon m_silent, m_speaker;
QAction m_action4;
bool m_muteActive;
QPixmap drawText(const char * text, int size = 64) {
QPixmap pix(size, size);
QPainter p(&pix);
p.setFont(QFont("helvetica", size*0.8));
p.fillRect(pix.rect(), Qt::white);
p.drawText(pix.rect(), QString::fromUtf8(text));
return pix;
}
public:
Ui() :
m_grid(this),
m_silent(drawText("🔇")),
m_speaker(drawText("🔊")),
m_action4(tr("Mute"), this),
m_muteActive(false)
{
m_grid.addWidget(&m_actionBar, 0, 0);
m_actionBar.addAction(&m_action4);
connect(&m_action4, &QAction::triggered, this, &Ui::muteMessages);
updateIcon();
}
Q_SLOT void muteMessages() {
m_muteActive = !m_muteActive;
updateIcon();
}
Q_SLOT void updateIcon() {
m_action4.setIcon(m_muteActive ? m_silent : m_speaker);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Ui ui;
ui.show();
return a.exec();
}

Related

QGraphicsPixmapItem doesn't show in QGraphicsView

I subclassed QGraphicsView (class Display) and implemented drag and drop from a QTableView. The QGraphicsScene has in it preexisting items and the scene()->itemsBoundingRect covers the entire earth.
If I artificially set the QGraphicsPixmapItem's boundingRect to the viewPort() of the QGraphicsView, I don't see the QPixmap I set in the QGraphicsPixmapItem and added to the scene(). If I fitInView the scene()->itemsBoundingRect, the pixmap is dropped on the whole world. I would like it dropped on my viewport, but essentially fitInView viewPort doesn't show the item, even though according to debug it's visible and notObscured().
void Display::dragEnterEvent(QDragEnterEvent* event)
{
event->acceptProposedAction();
update();
}
void Display::dragMoveEvent(QDragMoveEvent* event){
event->acceptProposedAction();
update();
}
void TableView::mouseMoveEvent(QMouseEvent *event)
{
QDrag* drag = new QDrag(this);
QMimeData *mime = new QMimeData();
drag->setMimeData(mime);
QImage image("/Users/sb/Downloads/puffin.jpeg");
mime->setImageData(image);
drag->setPixmap(QPixmap::fromImage(image).scaled(10,10));
drag->exec();
QTableView::mouseMoveEvent(event);
}
Edited to try #Eyllanesc suggestion
void Display::dropEvent(QDropEvent* event)
{
setCacheMode(QGraphicsView::CacheBackground);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
if(event->mimeData()->hasImage())
{
pixMap = qvariant_cast<QPixmap>(event->mimeData()->imageData());
pixMap = pixMap.scaledToHeight(10);
pixMap = pixMap.scaledToWidth(10);
bool h = pixMap.isNull(); //returns false
posn = mapToScene(event->pos());
QRectF ff = mapToScene(viewport()->geometry()).boundingRect();
bool ps = ff.contains(posn); //returns true
item = new QGraphicsPixmapItem(pixMap);
scene()->addItem(item);
item->setPos( posn);
item->setZValue(100);
item->setVisible(true);
item->setOpacity(1.0);
item->update();
scene()->update();
auto lView = qobject_cast<QGraphicsView*>(this);
lView->fitInView(ff, Qt::KeepAspectRatio);// no pixmap is shown
invalidateScene(ff);
bool i = item->isObscured(); //returns false
repaint();
update();
}
event->acceptProposedAction();
}
-
In the next part I show you a working example of how to implement drag-and-drop from a QTableView to a QGraphicsView:
#include <QApplication>
#include <QDrag>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QMimeData>
#include <QMouseEvent>
#include <QTableView>
#include <QGraphicsPixmapItem>
#include <random>
#include <QStandardItemModel>
static std::random_device rd;
static std::mt19937 rng(rd());
class TableView: public QTableView{
public:
using QTableView::QTableView;
protected:
void mousePressEvent(QMouseEvent *event){
if (event->button() == Qt::LeftButton)
dragStartPosition = event->pos();
}
void mouseMoveEvent(QMouseEvent *event){
if (!(event->buttons() & Qt::LeftButton))
return;
if ((event->pos() - dragStartPosition).manhattanLength()
< QApplication::startDragDistance())
return;
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
// create image
QImage image(100, 100, QImage::Format_RGB32);
std::uniform_int_distribution<int> uni(0, 255);
image.fill(QColor(uni(rng), uni(rng), uni(rng)));
mimeData->setImageData(image);
drag->setPixmap(QPixmap::fromImage(image).scaled(10,10));
drag->setMimeData(mimeData);
drag->exec();
QTableView::mouseMoveEvent(event);
}
private:
QPoint dragStartPosition;
};
class Display: public QGraphicsView{
public:
using QGraphicsView::QGraphicsView;
protected:
void dragMoveEvent(QDragMoveEvent *event){
if(event->mimeData()->hasFormat("application/x-qt-image"))
event->acceptProposedAction();
}
void dropEvent(QDropEvent *event){
if (event->source() == this)
return;
QPixmap pixmap = qvariant_cast<QPixmap>(event->mimeData()->imageData());
QGraphicsPixmapItem *pixmap_item = scene()->addPixmap(pixmap);
pixmap_item->setPos(mapToScene(event->pos()));
event->acceptProposedAction();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QHBoxLayout* lay = new QHBoxLayout(&w);
QGraphicsScene scene;
Display view(&scene);
view.setAcceptDrops(true);
TableView table;
QStandardItemModel model(10, 10);
table.setModel(&model);
lay->addWidget(&table);
lay->addWidget(&view);
w.show();
return a.exec();
}

How to drag the QLabel from one window to another in Qt?

I am learning Qt for fun. And I got a question:
How could I drag and drop the QLabel in Qt among two different windows?
Here is what I have so far:
As you can tell from the .gif(which does not want to become downloaded and visible here for some reasons, but if you click on the link to it, you can clearly see it) provided above right now there are two main problems:
I can not move the QLabel outside of the window (and hence am not able to register the drag and drop event).
The label is flashing for some reasons when I am moving it.
Here is the relevant part of the implementation from the .gif:
#ifndef DRAGGERP_H
#define DRAGGERP_H
#include <QLabel>
#include <QApplication>
#include <QMouseEvent>
#include <QPoint>
class DraggerP : public QLabel
{
QPoint offset;
QPoint startingPosition;
public:
DraggerP(QWidget* parent = nullptr) : QLabel(parent){ }
protected:
void enterEvent(QEvent* event) override
{
QApplication::setOverrideCursor(Qt::PointingHandCursor);
}
void leaveEvent(QEvent* event) override
{
QApplication::restoreOverrideCursor();
}
void mousePressEvent(QMouseEvent* event) override
{
startingPosition = pos();
offset = QPoint(
event->pos().x() - pos().x() + 0.5*width(),
event->pos().y() - pos().y() + 0.5*height()
);
}
void mouseMoveEvent(QMouseEvent* event) override
{
move(event->pos() + offset);
}
void mouseReleaseEvent(QMouseEvent* event) override
{
move(startingPosition);
}
};
#endif // DRAGGERP_H
This is the extension of the QLabel I am using to create the drag and drop effect.
I do not need the whole solution, at least an idea of how to accomplish this and what am I doing wrong here.
Here is a pretty good example and I used it as a starting point.
That strange movement that the QLabel suffers is because the position of the QLabel now depends on the layout, the job of the layout is to establish the position of the widgets depending on the policies you establish.
The solution is not to implement those actions in the QLabel but in the MainWindow as I show below:
#include <QApplication>
#include <QLabel>
#include <QMainWindow>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QTime>
#include <QDrag>
#include <QMimeData>
#include <QMouseEvent>
class MainWindow: public QMainWindow {
QScrollArea scrollArea;
QWidget contentWidget;
QVBoxLayout lay;
public:
MainWindow(QWidget* parent=nullptr): QMainWindow(parent){
qsrand((uint) QTime::currentTime().msec());
setCentralWidget(&scrollArea);
scrollArea.setWidget(&contentWidget);
contentWidget.setLayout(&lay);
scrollArea.setWidgetResizable(true);
for(int i=0; i< 20; i++){
QLabel *label = new QLabel(QString("label %1").arg(i));
QPalette pal = label->palette();
pal.setColor(QPalette::Background, QColor(10 +qrand() % 240, 10 +qrand() % 240, 10 +qrand() % 240));
label->setAutoFillBackground(true);
label->setPalette(pal);
lay.addWidget(label);
}
setAcceptDrops(true);
}
protected:
void mousePressEvent(QMouseEvent *event){
QMainWindow::mousePressEvent(event);
QWidget *child = childAt(event->pos());
if(qobject_cast<QLabel *>(child))
createDrag(event->pos(), child);
}
void dropEvent(QDropEvent *event){
QByteArray byteArray = event->mimeData()->data("Label");
QWidget * widget = *reinterpret_cast<QWidget**>(byteArray.data());
QLabel * new_label = qobject_cast<QLabel *>(widget);
QWidget *current_children = childAt(event->pos());
QLabel * current_label = qobject_cast<QLabel*>(current_children);
int index = 0;
if(new_label){
if(current_label)
index = lay.indexOf(current_label);
else{
index = 0;
QLayoutItem *item = lay.itemAt(index);
while(item->widget()->pos().y() < event->pos().y() && item)
item = lay.itemAt(index++);
}
lay.insertWidget(index, new_label);
}
}
private:
void createDrag(const QPoint &pos, QWidget *widget){
if(widget == Q_NULLPTR)
return;
QByteArray byteArray(reinterpret_cast<char*>(&widget),sizeof(QWidget*));
QDrag *drag = new QDrag(this);
QMimeData * mimeData = new QMimeData;
mimeData->setData("Label",byteArray);
drag->setMimeData(mimeData);
QPoint globalPos = mapToGlobal(pos);
QPoint p = widget->mapFromGlobal(globalPos);
drag->setHotSpot(p);
drag->setPixmap(widget->grab());
drag->exec(Qt::CopyAction | Qt::MoveAction);
}
protected:
void dragEnterEvent(QDragEnterEvent *event){
if(event->mimeData()->hasFormat("Label"))
event->acceptProposedAction();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w1;
MainWindow w2;
w1.show();
w2.show();
return a.exec();
}

Change position of submenus in a QMenu

In my project, I have a QMenu with a submenu item. The submenu has a lot of items so it's height is relatively large.
I want to vertically center the submenu relative to the item that executed the submenu.
I have already subclassed the submenu I want to reposition and tried changing the geometry on "aboutToShow" just to test things, but this has no effect:
class MySubMenu : public QMenu
{
Q_OBJECT
public:
QuickMod();
~QuickMod();
private slots:
void centerMenu();
};
MySubMenu::MySubMenu()
{
connect(this, SIGNAL(aboutToShow()), this, SLOT(centerMenu()));
}
MySubMenu::~MySubMenu()
{
}
void MySubMenu::centerMenu()
{
qDebug() << x() << y() << width() << height();
setGeometry(x(), y()-(height()/2), width(), height());
}
Here is an image I quickly MS Painted that I hope visually explains what I'm trying to achieve: (Before and After)
Thanks for your time!
aboutToShow is emited before the geometry is updated so the changes are overwritten later. The solution is to change the position an instant after they are displayed, for this we can use a QTimer with a small time.
Example:
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QTimer>
class CenterMenu: public QMenu{
Q_OBJECT
public:
CenterMenu(QWidget *parent = Q_NULLPTR):QMenu{parent}{
connect(this, &CenterMenu::aboutToShow, this, &CenterMenu::centerMenu);
}
CenterMenu(const QString &title, QWidget *parent = Q_NULLPTR): QMenu{title, parent}{
connect(this, &CenterMenu::aboutToShow, this, &CenterMenu::centerMenu);
}
private slots:
void centerMenu(){
QTimer::singleShot(0, [this](){
move(pos() + QPoint(0, -height()/2));
});
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;
auto fileMenu = new QMenu("Menu1");
w.menuBar()->addMenu(fileMenu);
fileMenu->addAction("action1");
fileMenu->addAction("action2");
auto children_menu = new CenterMenu("children menu");
children_menu->addAction("action1");
children_menu->addAction("action2");
children_menu->addAction("action3");
children_menu->addAction("action4");
children_menu->addAction("action5");
children_menu->addAction("action6");
fileMenu->addMenu(children_menu);
w.show();
return a.exec();
}
#include "main.moc"

How to communicate between QDialog Window and QMainWindow with Qt C++

I am an application using Qt Creator in C ++. The int main () function is as follows.
I am starting a QDialog Window where I am getting the "url" variable content.
How can I get this variable into the mainwindow.cpp file.
QApplication a(argc, argv);
MainWindow *w; //Put it here in case you have several cases where you want to initialize it
QLoginDialog d; //Your login dialog
if(d.exec()==QDialog::Accepted)
{
if(d.isLogged()) //Check the variable used to validate the login
{
w = new MainWindow;
w->show();
}
else
{
//Handle bad login
return -1; //Don't forget to return something here
}
}
else
{
//Handle dialog not validated
return -1; //Don't forget to return something here
}
return a.exec();
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new UÄ°::MainWindow)
{
QDialog s;
s.show();
s.setmodel(true);
if(s.exec()==QDialog::Accepted)
{
if(s.URL=="true")
{
ui.setupUi(this);
}
}
}
QDialog.cpp=Start.cpp source code:
#include "start.h"
#include "ui_start.h"
Start::Start(QWidget *parent) :
QDialog(parent),
ui(new Ui::Start)
{
ui->setupUi(this);
}
Start::~Start()
{
delete ui;
}
void Start::on_pushButton_2_clicked()
{
QString dirName=QFileDialog::getExistingDirectory(
this,
tr("Select a Directory"),
QDir::currentPath());
if(!dirName.isNull())
ui->lineEdit_2->setText(dirName);
}
void Start::on_pushButton_3_clicked()
{
URL=ui->lineEdit->text();
Directory=ui->lineEdit_2->text();
if(URL.isEmpty() && Directory.isEmpty())
{
QMessageBox::warning(this,tr("Error"),tr("Please, fill in the blanks"));
}
else if(URL.isEmpty())
{
QMessageBox::warning(this,tr("Error"),tr("is not a valid URL or Path!!"));
}
else if(Directory.isEmpty())
{
QMessageBox::warning(this,tr("Error"),tr("is not a select directory!!"));
}
ControlConfiguration();
}
void Start::ControlConfiguration()
{
QString configPosition= ui->lineEdit_2->text();
QString str1="CHECKOUT_HOME";
QString str2="new_checkout.csh";
QString str3="setup_prjsvn.csh";
QMessageBox *msgBox=new QMessageBox(this);
int sayac=0;
QDir path(configPosition);
QStringList files=path.entryList(QDir::Files);
for(int i=0; i<files.length(); i++)
{
if(files[i].compare(str1)==0)
{
sayac=sayac+1;
}
}
for(int i=0; i<files.length(); i++)
{
if(files[i].compare(str2)==0)
{
sayac=sayac+1;
}
}
for(int i=0; i<files.length(); i++)
{
if(files[i].compare(str3)==0)
{
sayac=sayac+1;
}
}
if(sayac>=3)//Attention Flurent. if true, open QMainWindow
{
QMessageBox::information(this,tr(" "),tr("Configuration Directory"));
//s_path=ui->lineEdit->text();
this->hide();
s_path="true";
URL=ui->lineEdit_2->text();
}
else
{
msgBox->setInformativeText("Would you like to configure?");
msgBox->setStandardButtons(QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes);
msgBox->setDefaultButton(QMessageBox::Yes);
int ret = msgBox->exec();
switch(ret){
case QMessageBox::Yes:
Configuration();
break;
case QMessageBox::No:
//ui->locationEdit->clear();
break;
case QMessageBox::Cancel:
break;
}
}
}
void Start::Configuration()
{
QString projePosition= ui->lineEdit_2->text(); // Proje konumu alınır.
QProcess* process=new QProcess(this);
QMessageBox *msgBox=new QMessageBox(this);
process->setWorkingDirectory(projePosition);
QString svn_export="svn export http://atlas/svn/prjsvn/trunk/common/common_bin/istanbul/new_checkout.csh";
QString s_newcheckout="source new_checkout.csh";
QString s_prjsvn="source setup_prjsvn.csh";
process->start("tcsh -c \""+svn_export+";"+s_newcheckout+";"+s_prjsvn+"\"");
process->waitForBytesWritten();
process->waitForFi
}
You should have a proper look at signals and slots, to pass variables between objects. The documentation is here : http://doc.qt.io/qt-4.8/signalsandslots.html
An other way would be to execute the QDialog inside your MainWindow, that would be the best practice. Use controls in the scope where they are useful. You should not have to use dialogs in the main function.
So, have a look at both solutions. The best, for simple variables, being to execute your dialog inside the class where the value will be used, the second being the use of signals and slots if values have to be passed between complex classes.
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow *w; //Put it here in case you have several cases where you want to initialize it
QLoginDialog d; //Your login dialog
if(d.exec()==QDialog::Accepted)
{
if(d.isLogged()) //Check the variable used to validate the login
{
w = new MainWindow(d.getUrl()); //I guess the URL comes from your login dialog, so make a function that returns it
w->show();
}
else
{
//Handle bad login
return -1; //Don't forget to return something here
}
}
else
{
//Handle dialog not validated
return -1; //Don't forget to return something here
}
return a.exec();
}
mainwindow.cpp
//Don't forget to add the "QString sUrl" to your mainwindow.h
MainWindow::MainWindow(QString sUrl, QWidget *parent): QMainWindow(parent), ui(new UÄ°::MainWindow)
{
ui.setupUi(this);
qDebug()<<s.URL; //It will be displayed here
}
Your dialog.cpp
//Use this to return the boolean value that represents if the path is valid or not.
bool QDialog::isLogged()
{
return this->s_path.equals("true");
}
A standard generated main file for a qt application contains the following lines:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
The main window is created there.
So in your case you could create the main window before your return.
By adding a QString as parameter in the constructor the url would be available in the mainwindow.
so the mainWindow.cpp constructor changes to:
MainWindow::MainWindow(QString url, QWidget *parent) :
QMainWindow(parent), ui(new UÄ°::MainWindow)
{
ui.setupUi(this);
//Place your code here that uses the url,
}
so the mainWindow.h constructor changes to:
MainWindow::MainWindow(QString url = "", QWidget *parent = 0);
and the main.cpp changes to:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString url; //Fill this from your dialog.
MainWindow w(url);//Pass the url to the main window
w.show();
return a.exec();
}
If all you want to do is to ask for an input string, i suggest you to have a look at QInputDialog
Example:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
bool ok;
QString message = QInputDialog::getText
(0, "Input","URL:", QLineEdit::Normal,"", &ok);
qDebug()<<message;
ui->setupUi(this);
}

QFileDialog that accepts a single file or a single directory

Is it possible to show a QFileDialog where the user can select a file or a directory, either one?
QFileDialog::getOpenFileName() accepts only files, while QFileDialog::getExistingDirectory() is directories-only, but I need to show a file dialog that can accept both a file or a directory (it makes sense for my program). QFileDialog::​Options didn't have anything promising.
QFileDialog currently does not support this. I think the main problem for you here is that the FileMode is not a Q_FLAGS and the values are not power of 2, either, and so, you cannot write this to solve this issue.
setFileMode(QFileDialog::Directory|QFileDialog::ExistingFiles)
To solve this, you would need quite a bit of fiddling, e.g.:
Override the open button click operation.
Get the "treeview" indices properly for both files and directories.
My attempt below demonstrates the former, but I did not really go as far as solving the second because that seems to involve some more fiddling with the selection model.
main.cpp
#include <QFileDialog>
#include <QApplication>
#include <QWidget>
#include <QTreeWidget>
#include <QPushButton>
#include <QStringList>
#include <QModelIndex>
#include <QDir>
#include <QDebug>
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
explicit FileDialog(QWidget *parent = Q_NULLPTR)
: QFileDialog(parent)
{
setOption(QFileDialog::DontUseNativeDialog);
setFileMode(QFileDialog::Directory);
// setFileMode(QFileDialog::ExistingFiles);
for (auto *pushButton : findChildren<QPushButton*>()) {
qDebug() << pushButton->text();
if (pushButton->text() == "&Open" || pushButton->text() == "&Choose") {
openButton = pushButton;
break;
}
}
disconnect(openButton, SIGNAL(clicked(bool)));
connect(openButton, &QPushButton::clicked, this, &FileDialog::openClicked);
treeView = findChild<QTreeView*>();
}
QStringList selected() const
{
return selectedFilePaths;
}
public slots:
void openClicked()
{
selectedFilePaths.clear();
qDebug() << treeView->selectionModel()->selection();
for (const auto& modelIndex : treeView->selectionModel()->selectedIndexes()) {
qDebug() << modelIndex.column();
if (modelIndex.column() == 0)
selectedFilePaths.append(directory().absolutePath() + modelIndex.data().toString());
}
emit filesSelected(selectedFilePaths);
hide();
qDebug() << selectedFilePaths;
}
private:
QTreeView *treeView;
QPushButton *openButton;
QStringList selectedFilePaths;
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
FileDialog fileDialog;
fileDialog.show();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT += widgets
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Quite old question but I think I have a simpler solution than most for the lazy ones.
You can connect the signal currentChanged of QFileDialog with a function that dynamically changes the fileMode.
//header
class my_main_win:public QMainWindow
{
private:
QFileDialog file_dialog;
}
//constructor
my_main_win(QWidget * parent):QMainWindow(parent)
{
connect(&file_dialog,QFileDialog::currentChanged,this,[&](const QString & str)
{
QFileInfo info(str);
if(info.isFile())
file_dialog.setFileMode(QFileDialog::ExistingFile);
else if(info.isDir())
file_dialog.setFileMode(QFileDialog::Directory);
});
}
And then simply call file_dialog as you would.
if(file_dialog.exec()){
QStringList dir_names=file_dialog.selectedFiles():
}
What worked for me was to use:
file_dialog.setProxyModel(nullptr);
as suggested here, or
class FileFilterProxyModel : public QSortFilterProxyModel
{
protected:
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
QFileSystemModel* fileModel = qobject_cast<QFileSystemModel*>(sourceModel());
return (fileModel!=NULL && fileModel->isDir(sourceModel()->index(sourceRow, 0, sourceParent))) || QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
};
...
FileFilterProxyModel* proxyModel = new FileFilterProxyModel;
file_dialog.setProxyModel(proxyModel);
as suggested here, or
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
explicit FileDialog(QWidget *parent = Q_NULLPTR)
: QFileDialog(parent)
{
m_btnOpen = NULL;
m_listView = NULL;
m_treeView = NULL;
m_selectedFiles.clear();
this->setOption(QFileDialog::DontUseNativeDialog, true);
this->setFileMode(QFileDialog::Directory);
QList<QPushButton*> btns = this->findChildren<QPushButton*>();
for (int i = 0; i < btns.size(); ++i) {
QString text = btns[i]->text();
if (text.toLower().contains("open") || text.toLower().contains("choose"))
{
m_btnOpen = btns[i];
break;
}
}
if (!m_btnOpen) return;
m_btnOpen->installEventFilter(this);
//connect(m_btnOpen, SIGNAL(changed()), this, SLOT(btnChanged()))
m_btnOpen->disconnect(SIGNAL(clicked()));
connect(m_btnOpen, SIGNAL(clicked()), this, SLOT(chooseClicked()));
m_listView = findChild<QListView*>("listView");
if (m_listView)
m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_treeView = findChild<QTreeView*>();
if (m_treeView)
m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
}
QStringList selectedFiles()
{
return m_selectedFiles;
}
bool eventFilter( QObject* watched, QEvent* event )
{
QPushButton *btn = qobject_cast<QPushButton*>(watched);
if (btn)
{
if(event->type()==QEvent::EnabledChange) {
if (!btn->isEnabled())
btn->setEnabled(true);
}
}
return QWidget::eventFilter(watched, event);
}
public slots:
void chooseClicked()
{
QModelIndexList indexList = m_listView->selectionModel()->selectedIndexes();
foreach (QModelIndex index, indexList)
{
if (index.column()== 0)
m_selectedFiles.append(this->directory().absolutePath() + "/" + index.data().toString());
}
QDialog::accept();
}
private:
QListView *m_listView;
QTreeView *m_treeView;
QPushButton *m_btnOpen;
QStringList m_selectedFiles;
};
as suggested here. Credits for the original authors and me.
Connect to the currentChanged signal and then set the file mode to the currently selected item (directory or file). This is a Python3 implementation:
class GroupFileObjectDialog(QFileDialog):
def __init__(self, parent):
super().__init__(parent)
self.setOption(QFileDialog.DontUseNativeDialog)
self.setFileMode(QFileDialog.Directory)
self.currentChanged.connect(self._selected)
def _selected(self,name):
if os.path.isdir(name):
self.setFileMode(QFileDialog.Directory)
else:
self.setFileMode(QFileDialog.ExistingFile)
Tested on PyQt 5.14 running on linux / Ubuntu18.04.