I want to have a list view just like the one in Windows' file explorer: The data is shown into columns. So I set up a QListView with the code below.
It looks totally the same (see picture).
But one major drawback: When resizing the window, the wrapping is very slow. On the contrary, the Window's file browser is very fast.
How can I speed up the wrapping in the QListView?
h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void resizeEvent(QResizeEvent *);
private:
Ui::MainWindow *ui;
};
cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStandardItemModel* m=new QStandardItemModel;
for (int i=100;i<1000;++i){
m->insertRow(i-100,new QStandardItem(QString::number(i).repeated(5)+" "));
}
ui->listView->setModel(m);
ui->listView->setWrapping(true);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::resizeEvent(QResizeEvent *e)
{
QMainWindow::resizeEvent(e);
ui->listView->setWrapping(ui->listView->isWrapping());
}
I tried your code under Linux with Qt 5.5.1 and wrapping is fast and instant.
What Qt version are you using? Qt 4.x has some performance issues under windows.
You can speed things up with NoAntialias
QFont fnt;
fnt.setStyleStrategy(QFont::NoAntialias);
ui->listView->setFont(fnt);
If your list grows bigger and you wan't to insert new data you will get bad performance. You should avoid QStandardItemModel for large sets of data.
You don't need to set wrapping every time the widget is resized. Qt is looking for the isWrapping property when resized and decides if a new segment is needed. You just need to set the flow property LeftToRight or TopToBottom and the isWrapping property once when the widget is created. Remove the code from the resizeEvent.
Also you can look at the layoutMode property for the performance purposes.
Related
I created a small prototype which contains a QGraphicsView that I bind to a GraphicsScene to which I can add or remove QGraphicsTextItem. Here is the the cpp file that does the job
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsScene>
#include <QGraphicsTextItem>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene* scene = new QGraphicsScene();
ui->graphicsView->setScene(scene);
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(addGraphicsItem()));
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(removeGraphicsItem()));
}
MainWindow::~MainWindow()
{
auto scene = ui->graphicsView->scene();
ui->graphicsView->setScene(nullptr);
delete scene;
delete ui;
}
void MainWindow::addGraphicsItem()
{
QGraphicsTextItem* item = new QGraphicsTextItem("jgfkdljkdj");
_items.push_back(item);
ui->graphicsView->scene()->addItem(_items.back());
}
void MainWindow::removeGraphicsItem()
{
auto item = _items.back();
ui->graphicsView->scene()->removeItem(item);
_items.erase(_items.end()-1);
delete item;
}
I have trouble to understand how to manage safely my memory when creating/deleting the scene and/or its underlying items. Reading many posts about this such as this one, I came up to the aformentionned code but I have the feeling that it is overkilling code and that Qt might do the job without it. Is that really the way to do especially in MainWindow::removeGraphicsItem slot when removing and deleting one item from the scene and MainWindow::~MainWindow destructor when deleting the scene ?
The simplest thing to do is to let the language and the framework manage the memory for you.
Store QGraphicsScene by value, and leverage the fact that the scene is a resource manager, tightly coupled to the items. It manages the lifetime of the items and guarantees that no items will outlive the scene, i.e. it will take care of disposing of any items that weren't disposed before its destructor runs.
To remove an item from the scene, or from a parent item, simply delete it. The item will inform the scene and any parents that it's about to vanish, and the scene will remove it from its list of items. That's how QObject memory management works as well.
You may also wish to use QGraphicsScene::items or QGraphicsItemGroup::childItems instead of managing the object list manually. Using a manual list requires paying attention to synchronizing the list's contents with object lifetimes. A scene or an item group does it automatically.
If were to write it, I'd do it as follows. I also show how to mix different item lifetimes in a scene, and how to hold items by value.
// mainwindow.h
#pragma once
#include "ui_mainwindow.h"
#include <QGraphicsScene>
class MainWindow : public QMainWindow {
Q_OBJECT
Ui::MainWindow ui;
QGraphicsScene m_scene;
QGraphicsTextItem m_text{tr("foo")};
QGraphicsItemGroup m_dynamicItems;
template <typename T, typename ...Ar> T* newItem(Ar&&... args);
public:
MainWindow(QWidget *parent = {});
Q_SLOT void addItem();
Q_SLOT void removeItem();
Q_SLOT void removeAllItems();
};
// mainwindow.cpp
#include "mainwindow.h"
#include <utility>
template <typename T, typename ...Ar>
T* MainWindow::newItem(Ar&&... args) {
return new T{&this->m_dynamicItems, std::forward<Ar>(args)...};
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(&m_scene);
m_scene.addItem(&m_text);
m_scene.addItem(&m_dynamicItems);
auto const clicked = &QPushButton::clicked;
connect(ui.pushButton, clicked, this, &MainWindow::addItem);
connect(ui.pushButton2, clicked, this, &MainWindow::removeItem);
}
void MainWindow::addItem() {
newItem<QGraphicsTextItem>(tr("jfslkdfjd"));
}
void MainWindow::removeItem() {
auto const &items = std::as_const(m_dynamicItems.childItems());
if (!items.isEmpty())
delete items.back();
}
void MainWindow::removeAllItems() {
for (auto *item : std::as_const(m_dynamicItems.childItems()))
delete item;
// or (deprecated but you may run into such code)
qDeleteAll(std::as_const(m_dynamicItems.childItems()));
}
I have very simple code, that displays file structure:
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
Ui::MainWindow *ui;
QFileSystemModel model;
QTreeView treeView;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
model.setRootPath(QDir::rootPath());
ui->treeView->setModel(&model);
ui->treeView>setSelectionMode(QAbstractItemView::SingleSelection);
ui->treeView->setDragEnabled(true);
ui->treeView->viewport()->setAcceptDrops(true);
ui->treeView->setDropIndicatorShown(true);
ui->treeView->setDragDropMode(QAbstractItemView::InternalMove);
ui->treeView->setAcceptDrops(true);
ui->tableView->setModel(&model);
}
I can select file and drag & drop it directly to folder or on desktop, but when I try to do something internally (move or copy, doesn't matter) it shows this example - even cursor shows that I can't drop
It seems to me, that I've tried all options, Did I forget something to write or set other option ?
I have implemented QTreeView-based UI widget with QFileSystemModel as data source and
enabled drag and drop for the view but still cannot see the cursor
showing it is ready for the "drop" action. What did I miss?
Answering you from experience after peeking at similar code. In order to resolve the item accepting the drop a bit more work needed on model's side:
// MyFileSysModel is a child from model class used in your example.
// Mind that specific application drag and drop logic may differ.
// I in fact modified that from QSortFilterProxyModel-type of class
// but that should be similar.
Qt::ItemFlags MyFileSysModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QFileSystemModel::flags(index);
if (!index.isValid())
return defaultFlags;
const QFileInfo& fileInfo = this->fileInfo(index);
// The target
if (fileInfo.isDir())
{
// allowed drop
return Qt::ItemIsDropEnabled | defaultFlags;
}
// The source: should be directory (in that case)
else if (fileInfo.isFile())
{
// allowed drag
return Qt::ItemIsDragEnabled | defaultFlags;
}
return defaultFlags;
}
... and of course we need to use the derived model class now:
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
Ui::MainWindow *ui;
MyFileSysModel model; // has virtual function member "flags"
QTreeView treeView;
};
I paint some text in subclassed menubar. And QFontMetrics return rectangle with cropped width. This happens in Windows 7. But it works as I expect in Debian with KDE. Why is it happen and how can I fix it?
class MainMenuBar : public QMenuBar
{
public:
explicit MainMenuBar(QWidget *parent = 0);
protected:
virtual void paintEvent(QPaintEvent *event);
private:
QFont _font;
};
MainMenuBar::MainMenuBar(QWidget *parent) : QMenuBar(parent)
{
_font = font();
}
void MainMenuBar::paintEvent(QPaintEvent *event)
{
QMenuBar::paintEvent(event);
QPainter painter(this);
painter.setFont(_font);
QRect rect = geometry();
rect.setRight(200);
rect.setLeft(rect.right() - QFontMetrics(_font).width("WWW")); // Cuts
//rect.setLeft(rect.right() - QFontMetrics(font()).width("WWW")); // Doesn't cut
painter.drawText(rect, Qt::AlignVCenter, "WWW");
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setMenuBar(new MainMenuBar(this));
menuBar()->addAction(".");
}
In a similar situation I would not restrict the text like that. Just allocate the rectangle as long as possible (better) or maybe to fit 4 W (not as good).
painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, "WWW");
And Qt::AlignRight will do the trick. No idea why the rendering is slightly different, though. If you clarify on your task then we will be able to come up with better approach.
The best practice would be not even that but QVBoxLayout for the entire window and QHBoxLayout for the upper widget with 'stretch' (here unsure whether you just want to right-align the text or make a fixed-width left stretch before it?) on the left plus QLabel added as widget (maybe with the right alignment). But you don't ask that and I can only assume that you did not try the better layout approach.
I have two "QMainWindows" A and B.
A has a pointer to B as a class member (and creates B dynamically on constructor). The program implementation is in two different monitors. 'A' is shown on monitor 'X' and 'B' is shown on monitor 'Y'.
'A' controls what is shown on 'B', and since I have them in two different monitors, in 'A' I would like to see whats happening on 'B' (kind of a 'screen capture on real time'). Is there any way to do this?
I've thought to make a "QWidget *clone = B->ui->centralWidget;" on 'A's constructor, since in 'B' I have all the information under a 'QWidget centralWidget', but it doesn't seem to work.
Any illuminating idea would be deeply apreciated!
You should be able to use QPixmap::grabWindow and a QTimer. QPixmap::grabWindow returns a QPixmap that you can simply display on e.g. a QLabel
Dummy header file for the class that displays
class MainWindow: public QMainWindow, public Ui::MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = NULL);
~MainWindow() {};
public slots:
void update();
private:
MainWindow2 *window;
QTimer *timer;
};
Implementation
MainWindow::MainWindow(QWidget *parent)
{
setupUi(this);
window = new MainWindow2;
window->show();
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(5000);
}
void MainWindow::update()
{
if( window ) {
QPixmap pixmap = QPixmap::grabWindow( window->winId() );
label->setPixmap(pixmap);
}
}
EDIT: added a check to make sure that window is not NULL before asking for its window ID.
This way a screenshot of the child window will be shown on in a QLabel of the parent window and will be updated every 5 seconds (which you can obviously adjust to your needs). Does this cover your use case?
I'm trying to create custom widget inheriting QFrame. All works fine, but I'm unable to draw the focus rectangle around my widget. Below is the sample code I use for drawing:
frame.h
class Frame : public QFrame {
Q_OBJECT
public:
Frame(QWidget *parent = 0);
~Frame();
protected:
void paintEvent(QPaintEvent *event);
private:
Ui::Frame *ui;
};
frame.cpp
Frame::Frame(QWidget *parent) :
QFrame(parent),
ui(new Ui::Frame)
{
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
}
Frame::~Frame()
{
delete ui;
}
void Frame::paintEvent(QPaintEvent *event)
{
QFrame::paintEvent(event);
if (hasFocus()) {
QStylePainter painter(this);
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().dark().color();
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}
What I mean by 'unable to draw focus frame' is that when you click a standard widget that accepts focus (let's say QLineEdit), it has a blue rectangle drawn around it. When I click my widget there is no such rectangle drawn. Are there any more things I should do besides setting focusPolicy on my widget?
It might have something to do with the style your app is using. When I try your code with the "gtk" and "cleanlooks" style, no focus rectangle is drawn. With "plastique" and "windows" it is. Since I'm on Linux, I cannot test "windowsxp" and "macintosh". Try running with the -style option and see what happens.
try also
setFocusPolicy(Qt::StrongFocus);
setAttribute( Qt::WA_MacShowFocusRect);