QMDI SubWindows switching - qt

I have an MDI application, with classes as
class MainWindow
{ GraphicsView *gv; };
class GraphicsView
{ Scene *scene; };
class Scene
I'm creating a new mdiSubWindow on every newfile() of MainWindow which creates a new pointer to the GraphicsView.
void MainWindow::newFile()
{
gv = new GraphicsView;
QMdiSubWindow *w = mdiArea->addSubWindow(gv);
mdiArea->setActiveSubWindow(w);
}
And the constructor of GraphicsView creates a new Scene.
GraphicsView::GraphicsView()
{
scene = new Scene;
setScene(scene);
}
Now when there are multiple subWindows created, I lose the ability to work in previous subWindows. Only the latest subWindow works as expected. For eg. I can draw QGraphicsItems only in the latest Sub Windows and not in the previous ones.
I think I should be using activeSubWindow() but couldn't figure out how to make every subWindow respond to the change of the tabs. How should I implement this?

To make it work.
I've created a QList<QPair>, for storing a pair of subwindow and view.
windowViewList.append(qMakePair(w, view));
Then subWindowActivated() signal is used to call the following function to update the view pointer.
void MainWindow::updatePointers()
{
QMdiSubWindow *m = mdiArea->activeSubWindow();
foreach (windowViewPair v, windowViewList)
{
if (m == v.first)
gv = v.second;
}
}

Related

How to switch between two QGraphicsView inside a QWidget

I have two different views to display inside a QWidget window. Each view has a separate QGraphicsScene.
I want to toggle between the two views on button click.
This is my current implementation:
void toggleUi(bool type){
QGraphicsView* currentView;
if(bool){
currentView = getFirstView(); // returns QGraphicsView of first type
}
else{
currentView = getSecondView(); // returns QGraphicsView of second type
}
QLayout* layout = widget->layout ();
if (layout != 0)
{
QLayoutItem *item;
while ((item = layout->takeAt(0)) != 0)
layout->removeItem (item);
delete layout;
}
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(currentView);
}
Issue: Both views are displayed over one another on toggle, even after deleting the layout and adding a new one.
Both views are rendered fine without toggle.
Is there a better/another way to do it?
Use QStackedWidget! Use the addWidget function to add widgets:
QStackedWidget *stackedWidget = new QStackedWidget(this);
stackedWidget->addWidget(getFirstView());
stackedWidget->addWidget(getSecoundView());
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(stackedWidget);
setLayout(layout);
Here's how to switch between the widgets:
void toggleUi(bool type){
if(condition)
stackedWidget->setCurrentindex(0)
else
stackedWidget->setCurrentindex(1)
I hope it helps!

Qt - Not correctly adding widgets to QHBoxLayout after clearing the layout

I have a QHBoxLayout in which I added some widgets. I need to be able to refresh the layout dynamically so I use this to clear the layout :
void ClearLayout(QLayout* layout)
{
if (!layout)
return;
QLayoutItem* item;
while ((item = layout->takeAt(0)) != nullptr)
{
delete item->widget();
ClearLayout(item->layout());
}
}
This indeed removes all widgets and layouts. After this layout->isEmpty() returns true and layout->count() returns 0.
However, when I try to add new widgets (same type of other previously added but new instance) It does not work !
AddWidget()
{
// DeviceWidget inherits QWidget
DeviceWidget* deviceWidget = new DeviceWidget;
deviceWidget->setFixedSize(150, 200);
connect(deviceWidget->GetSignalObject(), &DeviceObject::Selected, this,
&DeviceLayout::SelectedDevice);
layout->addWidget(deviceWidget, 0, Qt::AlignCenter);
}
This is the same function used previously to add the widgets to the layout and worked the first time at Construction:
MainLayout(QWidget* parent) : QHBoxLayout(parent)
{
layout = new QHBoxLayout;
addLayout(layout);
uint32 nb = GetDeviceNumber(); // returns 2
for (uint32 i = 0; i < deviceNb; ++i)
AddDeviceWidget();
}
After trying to add 2 widgets I have layout->isEmpty() returns true and layout->count() returns 2 so I'm confused …
thanks for any help provided :)
EDIT:
The problem seems to be comming from my DeviceWidget class since trying to add a simple QLabel to the cleared layout worked. Here's the DeviceWidget Constructor:
DeviceWidget::DeviceWidget(QWidget* parent) : QWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout;
QLabel* deviceIcon = new QLabel("DeviceIcon", this);
deviceIcon->setFixedSize(128, 128);
deviceIcon->setPixmap(QPixmap::fromImage(QImage("Resources/Icons/device.png")));
deviceIcon->setObjectName("DeviceIcon");
// StatusWidget inherits QWidget
// Just override paintEvent to display a colored filled disk
m_status = new StatusWidget(20, Status::Close, this);
m_status->setObjectName("DeviceStatus");
vLayout->addWidget(deviceIcon, 0, Qt::AlignCenter);
vLayout->addWidget(m_status, 0, Qt::AlignCenter);
// DeviceObjct inherits from QObject an add a signal
m_object = new DeviceObject(size());
// Function clearing the stylesheet background-color
Clear();
setLayout(vLayout);
installEventFilter(this);
setObjectName(QString("DeviceWidget"));
}
Commenting installEventFilter(this) make it work so I think I need to add an event filter to make it work but I don't know which one
As said in the Edit, The problem is coming from DeviceWidget added in the layout that override eventFilter. There is probably a way to add a case in eventFilter to make it work but in my case it was best either (1) or (2):
1. Remove eventFilter from class DeviceWidget and put it in class DeviceObject: m_object is present to emit a signal according to event:
DeviceObject.h:
DeviceObject(QObject* parent);
bool eventFilter(QObject* obj, QEvent* event) override;
signals:
void Select(uint32 i);
Then in class DeviceWidget still call installEventFilter but with m_object as parameter: installEventFilter(m_object);
For the other event (Enter/Leave) I overrode void enterEvent(QEvent* event) and void leaveEvent(QEvent* event) for class DeviceWidget. That's what lead me to the second option that seems better.
2. Completely remove eventFilter and installEventFilter as it is only used to emit a signal when widget is clicked and do things when cursor hovers the widget. Instead override enterEvent and leaveEvent for class DeviceWidget like said before for hover event.
Then in class DeviceObjecy override void mousePressEvent(QMouseEvent*) for clicked event.

QMdiArea not adding subwindow

I have an one function which is responsible for initializing custom widget and add it in to the MdiArea.When I call this at first time it is working fine.But if i will call again, it is initializing custom widget but not adding in into the MdiArea. I have mentioned that function here:-
void CArbWaveViewWidget::newFile()
{
m_ptrWavePresenter = new CArbWavePresenter;
QMdiSubWindow *subWindow1 = new QMdiSubWindow;
subWindow1->setWidget(m_ptrWavePresenter->getTableView()); // getting customWidget
qDebug()<<"Table View ==="<<m_ptrWavePresenter->getTableView();
subWindow1->setAttribute(Qt::WA_DeleteOnClose);
QMdiSubWindow *subWindow2 = new QMdiSubWindow;
subWindow2->setWidget(m_ptrWavePresenter->getGraphView()); // getting customWidget
qDebug()<<"Graph View ==="<<m_ptrWavePresenter->getGraphView();
subWindow2->setAttribute(Qt::WA_DeleteOnClose);
mdiArea->addSubWindow(subWindow1);
mdiArea->addSubWindow(subWindow2);
}
How can i solve this problem?
1. Declare your QMdiSubWindow
When you declare a QMdiSubWindow, give mdiArea as argument
QMdiSubWindow *subWindow = new QMdiSubWindow(mdiArea);
or you can use setParent ( QWidget * parent )
QMdiSubWindow *subWindow = new QMdiSubWindow();
subWindow->setParent(mdiArea);
2. Create and add your QWidget in your QMdiSubWindow
QWidget *myWidget = new QWidget();
subWindow->setWidget(myWidget);
3. Update QMdiSubWindow content
If you need to update the subwindow content, declare your QMdiSubWindow as class variable, initialize your QMdiArea and QMdiSubWindow and set QWidget
yourClass.h
class yourClass {
public:
yourClass();
void newFile();
private:
QMdiArea *m_area;
QMdiSubWindow *m_subWindow1, *m_subWindow2;
void init();
};
yourClass.cpp
yourClass::yourClass()
{
init();
}
void yourClass::init()
{
m_area = new QMdiArea();
m_subWindow1 = new QMdiSubWindow(m_area);
m_subWindow2 = new QMdiSubWindow(m_area);
// continue to init your QMdiSubWindow
}
void yourClass::newFile()
{
// Set your QWidget (yourWidget) into your QMdiSubWindow
m_subWindow1->setWidget(yourWidget);
m_subWindow2->setWidget(anotherWidget);
}
You need invoke method "show"
QMdiSubWindow *subWindow = new QMdiSubWindow();
subWindow->setParent(mdiArea);
subWindow->setWidget(yourWidget);
subWindow->show();

access to QGraphicsView on each tab (in QTabWidget)

I opened each Image in a new tab on QGraphicsScene and QGraphicsView by this
void MainWindow::on_actionOpen_triggered()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath());
if (!fileName.isEmpty()) {
QImage image(fileName);
if (image.isNull()) {
QMessageBox::information(this, tr("Master Measure"),
tr("Cannot load %1.").arg(fileName));
return;
}
scene = new QGraphicsScene;
view = new QGraphicsView;
view->setScene(scene);
tabWidget->addTab(view,"someTab");
scene->addPixmap(QPixmap::fromImage(image));
scene->setBackgroundBrush(QBrush(Qt::lightGray, Qt::SolidPattern));
QFileInfo fileInfo = fileName;
tabWidget->setTabText(ui->tabWidget->count()-1, fileInfo.baseName());
tabWidget->setCurrentIndex(ui->tabWidget->count()-1);
}
}
I want to draw something on each image by clicking.
so I did this by click press event
void MainWindow::mousePressEvent(QMouseEvent *event)
{
QPen pen(Qt::black);
QBrush brush(Qt::red);
pen.setWidth(6);
scene->addEllipse(0,0,1000,500,pen,brush);
}
it just draw Ellipse on last opened image (tab).
I don't know how to solve this problem.
I appreciate any idea.
Thank you.
Obviously scene variable points to the last created scene. When you create new scene, old pointer is lost because you don't save it anywhere. So you need to preserve all scene and view pointers somewhere and use the currently visible objects.
I advise you to create a QGraphicsView's subclass (let's call it MyView) that will be responsible to every tab's contents. Pass the filename to the constructor of this object. In the constructor create a scene and store it in a member variable. Reimplement MyView::mousePressEvent to perform drawing.
Then you can add new tabs like this:
MyView* view = new MyView(filename);
view ->addTab(view,"someTab");
When the user clicks a view, MyView::mousePressEvent method or the respective MyView object will be invoked. Each view will see only its own scene variable, and the respective scene will be edited.

Displaying two widgets in MainWindow

I am new to Qt. As it stands, I have a table with a button btn. When the button is clicked the setCentralWidget(view) takes over the window so I can no longer see the table obviously. But if I remove the setCentralWidget(view), nothing displays when I click the button.
Is there a way I can display both in the same window? Split or dock maybe?
(I have removed code that is irrelevant to my question)
MainWindow::MainWindow()
{
//etc
packet = new QTabWidget;
setCentralWidget(packet)
}
//other code
void MainWindow::create(const QString &a)
{
QTableWidget* table = new QTableWidget;
int tabIndex = packet->addTab(table, a);
packet->setCurrentIndex(tabIndex);
table->setRowCount(1);
table->setColumnCount(2);
table->setHorizontalHeaderLabels(QString("a;Simulator").split(";"));"));
table->setItem(0,0,new QTableWidgetItem(a));
QPushButton *btn = new QPushButton("load", this);
connect(btn, SIGNAL(clicked()), this, SLOT(sim()));
table->setCellWidget(0,1, btn);
}
void MainWindow::sim()
{
QGraphicsScene* scene = new QGraphicsScene(QRect(-10, -10, 100, 50));
QGraphicsView* view = new QGraphicsView();
scene->addText("Network");
view->setScene(scene);
view->setGeometry(QRect(10, 10, 100, 50));
setCentralWidget(view);
}
You should look at the QMdiArea class - it's designed to be used as the central widget of a Main Window that can have many inner widgets. It has a variety of layout styles as well - scroll down on that page to see the examples.
Hope that helps!
You should subclass QWidget. For example make your own MyWidget class. And this class will include a QGraphicsView and a QTabWidget in a splitter for example. And in your MainWindow set the central widget as an instance of MyWidget.

Resources