This is the code for populating and deleting item from the QWidget.
class ShowCommands : public QWidget
{
private:
QWidget wdg;
QVBoxLayout m_layout;
QScrollArea* m_area;
QWidget m_contents;
QVBoxLayout m_contentsLayout;
bool isWrite;
public:
ShowCommands(QWidget *parent = nullptr);
void showWindow();
void setParent(QWidget* par);
void AddCommand(std::string stdstrCommand);
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ShowCommands::ShowCommands(QWidget *parent) : QWidget(parent)
{
isWrite = true;
m_area = new QScrollArea;
m_contents.setLayout(&m_contentsLayout);
m_layout.addWidget(m_area);
m_area->setWidget(&m_contents);
m_contentsLayout.setSizeConstraint(QLayout::SetMinimumSize);
wdg.setLayout(&m_layout);
wdg.setFixedWidth(650);
wdg.setWindowTitle("Commands");
wdg.setWindowFlags(Qt::Window
| Qt::FramelessWindowHint);
wdg.hide();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ShowCommands::showWindow()
{
if (isWrite == false)
{
if (m_contentsLayout.layout() != NULL)
{
QLayoutItem* item;
while ((item = m_contentsLayout.layout()->takeAt(0)) != NULL)
{
delete item->widget();
delete item;
}
}
isWrite = true;
wdg.show();
// AddCommand() function is called now multiple times and items are populated.
}
else
{
isWrite = false;
wdg.hide();
}
}
void ShowCommands::AddCommand(std::string stdstrCommand)
{
if (isWrite)
{
QLabel *label = new QLabel;
label->setText(stdstrCommand.c_str());
m_contentsLayout.addWidget(label);
}
}
In the showWindow() function first all of the items of widget are deleted and than populate the widget with new items.
The issue is that after i delete the existing items the new items are not populated from top to bottom but they start appearing from center.
I see many problems with your code, some of them make it hard to read, but anyway I think the main problem here is how properly create those items ...
You should create your widgets as pointers and assign good parenting to them, unparented widgets will create a new window. You should do something like this.
class MyWidget: public QWidget
{
Q_OBJECT
public;
MyWidget(QWidget *parent=nullptr) : QWidget(parent)
{
w1=new QWidget(this);
}
private:
class QWidget *m_w1;
}
that way you will have a child widget that will be drawn relative to you in the same window. You can add a layout to your widget, and then add your widgets to it so it handles the location for you. ie:
class MyWidget: public QWidget
{
Q_OBJECT
public;
MyWidget(QWidget *parent=nullptr) : QWidget(parent)
{
//passing this to the construct assign layout to 'this' widget
QVBoxLayout *verticalLayout=new QVBoxLayout(this);
w1=new QWidget(this);
w2=new QWidget(this);
verticalLayout->addWidget(w1);
verticalLayout->addWidget(w2);
}
private:
class QWidget *m_w1;
class QWidget *m_w2;
}
in that case both widgets will be display one below the other in the space assigned to you. Also the space assigned to you will depend on the characteristics of the two child widgets (you probably will create something derived from QWidget).
Now about the QScrollArea, that widget it is meant to be used with a widget placed 'inside', and you should add items to the layout of that one. ie:
class MyWidget: public QWidget
{
Q_OBJECT
public;
MyWidget(QWidget *parent=nullptr) : QWidget(parent)
{
//passing this to the construct assign layout to 'this' widget
QVBoxLayout *verticalLayout=new QVBoxLayout(this);
m_scroll=new QScrollArea(this);
verticalLayout->addWidget(m_scroll);
m_containerwidget=new QWidget(this);
//this sets our container widget as the inside's scrollarea widget
m_scroll->setWidget(m_containerwidget);
//if your widget will resize along the way you use this, in your case you do
m_scroll->setWidgetResizable(true);
m_containerLayout=new QVBoxLayout (m_containerwidget);
m_w1=new QWidget(this);
m_w2=new QWidget(this);
m_containerLayout->addWidget(m_w1);
m_containerLayout->addWidget(m_w2);
}
private:
class QScrollArea *m_scroll;
class QWidget *m_containerwidget;
class QVBoxLayout *m_containerLayout;
class QWidget *m_w1,m_w2;
}
You don't usually store all that information (m_containerLaoyut,m_containerWidget) in the class as you can query the objects for them, but just for clarity I add them there.
The last example applies to your scenario, you could store your 'items widgets' in a list, or query the layout for them, and when you need to delete them you just do that and then add new ones to the layout.
Of course you can use any kind of layout, I used vertical just as an example.
Related
I have QDialog and QScrollArea inside. When content in QScrollArea is small, dialog window is also small. When content width is increasing, dialog window's width is also encreasing but only to some fixed value.
When QPushButton's width in my example is about 450 or more, vertical scrollbar appears. How can I avoid this and let dialog window expand more?
class Dialog : public QDialog {
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr) : QDialog(parent) {
auto dialogLayout = new QVBoxLayout(this);
auto scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
auto scrollWidget = new QWidget(scrollArea);
auto scrollLayout = new QVBoxLayout(scrollWidget);
scrollLayout->setAlignment(Qt::AlignTop);
scrollArea->setWidget(scrollWidget);
dialogLayout->addWidget(scrollArea);
auto button = new QPushButton("Button", this);
button->setFixedSize(500, 30);
scrollLayout->addWidget(button);
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
{
auto centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
auto mainLayout = new QVBoxLayout(centralWidget);
auto button = new QPushButton("Dialog", this);
mainLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, []() {Dialog().exec();});
}
};
I tried QDialog::setMaximumWidth, and setting Expanding size policy for both QDialog and QScrollArea but nothing helps
QScrollArea constrains the maximum size of its sizeHint() method (comes out to 468px on my current Win7 machine). You can see that here. (I didn't know this until now either... not sure why they chose to do it this way.)
So looks like you'll have to re-implement QScrollArea or find a different display strategy. To re-implement we just basically need to re-write the sizeHint() function but w/out the silly constraint.
#include <QApplication>
#include <QtWidgets>
class ScrollArea : public QScrollArea
{
public:
ScrollArea(QWidget *parent = nullptr) : QScrollArea(parent) {}
QSize sizeHint() const
{
QSize sz = QScrollArea::viewportSizeHint();
const int f = frameWidth() * 2;
sz += QSize(f, f);
if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOn)
sz.setWidth(sz.width() + verticalScrollBar()->sizeHint().width());
if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn)
sz.setHeight(sz.height() + horizontalScrollBar()->sizeHint().height());
return sz;
}
};
class Dialog : public QDialog {
public:
Dialog(QWidget *parent = nullptr) : QDialog(parent) {
auto dialogLayout = new QVBoxLayout(this);
auto scrollArea = new ScrollArea(this);
//scrollArea->setWidgetResizable(true);
auto scrollWidget = new QWidget(this);
auto scrollLayout = new QVBoxLayout(scrollWidget);
auto button = new QPushButton("Button", this);
button->setFixedSize(600, 30);
scrollLayout->addWidget(button);
scrollArea->setWidget(scrollWidget);
dialogLayout->addWidget(scrollArea);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
return Dialog().exec();
}
As a small aside, if you add the items to the scroll area in the order I show here, you do not technically need the setWidgetResizable(true) bit which I commented out (I noticed that in the original order you had it in, the inner widget wasn't being shown properly sized).
ADDED: The reason sizeHint() matters is because that's what QDialog uses to determine it's initial size. One could also (for example) re-implement QDialog::showEvent() and set a specific size for the dialog there based on whatever criteria make sense.
In Graphic View set the scene, In Graphic scene(subclass od QGraphicscene) class added Delete item slot.In scene class by delete key i able to delete item but when i call from main window it wont delete item . i am getting call in Delete item slot but selectedItems = 0. what may be causing problem?
In Graphic scene class
void GraphicScene::DeleteItems()//Delete Item slot in scene class
{
qDebug()<<"delete items"<< selectedItems().count();
foreach(QGraphicsItem* item, selectedItems())
{
removeItem(item);
delete item;
}
}
void GraphicScene::keyReleaseEvent(QKeyEvent * keyEvent)// Delete key works fine
{
if (selectedItems().isEmpty())
return;
if(keyEvent->key() == Qt::Key_Delete)
{
DeleteItems();
}
}
In MainWindow class
MainWindow::MainWindow(QWidget *parent)
{
addToolBar(Qt::TopToolBarArea, mpEditToolbar = new
QToolBar());
DeleteAction = new QAction(QIcon(":/images/delete.png"),tr("Object
&Delete"), this);
DeleteAction->setStatusTip(tr("Delete item"));
connect(DeleteAction,SIGNAL(triggered()),mpGraphView ,
SIGNAL(DeleteObject())); // grpah view connecting to delete slot
mpEditToolbar->addAction(DeleteAction);
}
When i do from delete key works fine its not working with tool box delete action. what is the problem?
In Main Window class have private members of GraphicsView and GraphicScene class(subclass)
so that it will be easy to call slot.
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
GraphFrame *mpGraphFrame;
GraphicScene *mpScene;
}
MainWindow.cpp
Connect should be in where you creating GraphicScene Object
MainWindow::MainWindow(QWidget *parent)
{
addToolBar(Qt::TopToolBarArea, mpEditToolbar = new
QToolBar());
DeleteAction = new QAction(QIcon(":/images/delete.png"),tr("Object
&Delete"), this);
DeleteAction->setStatusTip(tr("Delete item"));
connect(DeleteAction,SIGNAL(triggered()),mpGraphScene ,
DeleteItems();
mpEditToolbar->addAction(DeleteAction);
}
I have 10 Qlabels with an image on each. When i click on a label, its image should be cleared. I am able to identify which label was clicked theorotically, using the pixel clicked and size of each image. But how to use that information?
Eg. each label has dimension 100*100, the first label starting from 0,0. if pixel clicked is 250,50, i know that the third label was clicked, but how to use this to clear the label.
Im stuck.
There are a few ways how to implement it.
First. I would recommend to use a new class that inherits QLabel and overloads mouseReleaseEvent() handler where you just call clear() method. In this case the label will detect the mouse clicks itself and will clear its content internally.
class SelfClearingLabel : public QLabel
{
public:
using QLabel::QLabel;
protected:
void mouseReleaseEvent(QMouseEvent * event)
{
if (event->button()==Qt::LeftButton)
// process only clicks on the left button
{
clear();
}
QLabel::mouseReleaseEvent(event);
}
};
Second. You can catch mouseReleaseEvent() in your top widget and iterate over all of your child QLabel widgets and check which one is currently under mouse and clear the one. If you have other labels on this widget that shouldn't be cleared on mouse clicks then you can add some property to the QLabels that are under your interest.
void SomeTopFrame::createImageLabels(int count)
{
for (int i=0;i<count;i++)
{
QLabel* label=new QLabel(this);
label->setProperty("clear_on_click",true);
// assume that labels are added to layout *m_labelsLayout;
m_labelsLayout->addWidget(label);
}
}
void SomeTopFrame::mouseReleaseEvent(QMouseEvent * event)
{
if (event->button()==Qt::LeftButton)
// process only clicks on the left button
{
QList<QLabel*> labels=findChildren<QLabel*>();
foreach (QLabel* label, labels)
{
if (label->property("clear_on_click")&&label->underMouse())
{
label->clear();
break;
}
}
}
QFrame::mouseReleaseEvent(event);
}
It is a sample code just to show the principle. In production you can add a check that mouseReleaseEvent() is on the same widget as the mousePressEvent() to avoid triggering on drag and drop events.
Create the custom class that inherit QLabel :
ClickableLabel.h
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel( const QString& text="", QWidget* parent=0 );
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
ClickableLabel.cpp
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
{
setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel::mousePressEvent(QMouseEvent* event)
{
emit clicked();
}
Just connect all labels clicked signal to following slot :
MyClass::labelClicked()
{
ClickableLabel *label = (ClickableLabel*)QObject::sender;
if(label)
label->clear();
}
I'm working on an app that uses Qt's QTreeView to display hierarchical data. In some cases a single leaf in the hierarchy will contain 100,000's of child nodes.
I've discovered that QTreeView can't handle too many child nodes.
This is because when the user opens a parent node, Qt fetches ALL of the child nodes' ModelIndexes, not just the ones that are needed to fill the QTreeView display.
Why does it do this, and is there any workaround to make it fetch fewer ModelIndexes?
My previous answer was unfortunately useless due to QTreeWidget(I am using QTreeWidget to simplyfy) sliders cannot be set by user.
Your goal could be achieved by using extra QScrollBar.
MainWidget.h
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = 0);
private:
QTreeWidget* Tree;
QScrollBar* Bar;
private slots:
void tree(QTreeWidgetItem*);
void slider();
};
MainWidget.cpp
MainWidget::MainWidget(QWidget *parent): QWidget(parent) {
Tree = new QTreeWidget();
Tree->header()->hide();
connect(Tree,SIGNAL(itemExpanded(QTreeWidgetItem*)),this,SLOT(tree(QTreeWidgetItem*)));
Bar = new QScrollBar(Qt::Vertical);
Bar->setRange(0,100000);
connect(Bar,SIGNAL(sliderReleased()),this,SLOT(slider()));
for(uint i=0;i<10;i++) {
QTreeWidgetItem* Item=new QTreeWidgetItem(QStringList()<<QString::number(i)); //Add 10 topLevelItems
Tree->addTopLevelItem(Item);
for(uint j=0;j<30;j++) {
Item->addChild(new QTreeWidgetItem(QStringList()<<QString::number(j))); //Add first 30 items
}
}
QHBoxLayout* Lay = new QHBoxLayout();
Lay->addWidget(Tree);
Lay->addWidget(Bar);
setLayout(Lay);
}
void MainWidget::tree(QTreeWidgetItem* I) { //SLOT Only one item expanded
Tree->blockSignals(true); //Block SIGNAL(itemExpanded())
Tree->collapseAll();
Tree->expandItem(I);
Tree->blockSignals(false);//Allow SIGNAL(itemExpanded())
}
void MainWidget::slider() { //SLOT manage tree
for (int i=0;i<Tree->topLevelItemCount();i++) {
if (Tree->topLevelItem(i)->isExpanded()) {
for(uint j=Tree->topLevelItem(i)->childCount(); j>0;j--) { //Clear children
delete Tree->topLevelItem(i)->child(j-1);
}
uint Value = Bar->value();
for(uint j=0; j<30; j++) {
Tree->topLevelItem(i)->addChild(new QTreeWidgetItem(QStringList()<<QString::number(Value+j))); //Add 30 new children
}
break;
}
}
}
This way you have one slider to scroll the QTreeWidget and the second one to change content of expanded topLevelItem.
How can I create a QDialog with floating toolbar in Qt?
Attachment of the QMainWindow with toolbar as widget in the QDialog is not suitable.
Why not suitable? following code works like charms.
#include <QtGui>
class MyDialog : public QDialog
{
Q_OBJECT
public:
MyDialog(QWidget* parent=0)
{
QMainWindow* child = new QMainWindow;
QLabel* label = new QLabel(tr("QMainWindow with toolbar!"));
label->setAlignment(Qt::AlignCenter);
child->setCentralWidget(label);
QToolBar* toolbar = child->addToolBar(tr("Tool"));
toolbar->addAction(tr("Test"), this, SLOT(doTest()));
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(child);
}
private slots:
void doTest()
{
QMessageBox::information(this, tr("Test"), tr("ToolBar is Working!"));
}
};
look at
Can you add a toolbar to QDialog?
and try to write smth like that
myDialog->layout()->setMenuBar(myToolBar);