Skip to content

Commit 8d77a6b

Browse files
committedMar 11, 2019
New gui class QgsLayoutComboBox
Shows a list of layouts
1 parent cba2277 commit 8d77a6b

File tree

7 files changed

+436
-0
lines changed

7 files changed

+436
-0
lines changed
 
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/layout/qgslayoutcombobox.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
class QgsLayoutComboBox : QComboBox
12+
{
13+
%Docstring
14+
The QgsLayoutComboBox class is a combo box which displays available layouts from a QgsLayoutManager.
15+
16+
.. versionadded:: 3.8
17+
%End
18+
19+
%TypeHeaderCode
20+
#include "qgslayoutcombobox.h"
21+
%End
22+
public:
23+
24+
explicit QgsLayoutComboBox( QWidget *parent /TransferThis/ = 0, QgsLayoutManager *manager = 0 );
25+
%Docstring
26+
QgsLayoutComboBox creates a combo box to display a list of items in a
27+
layout ``manager``. The layouts can optionally be filtered by type.
28+
%End
29+
30+
void setLayoutManager( QgsLayoutManager *manager );
31+
%Docstring
32+
Sets the layout ``manager`` containing the layouts to list in the combo box.
33+
%End
34+
35+
QgsLayoutManagerProxyModel::Filters filters() const;
36+
%Docstring
37+
Returns the current filters used for filtering available layouts.
38+
39+
.. seealso:: :py:func:`setFilters`
40+
%End
41+
42+
void setFilters( QgsLayoutManagerProxyModel::Filters filters );
43+
%Docstring
44+
Sets the current ``filters`` used for filtering available layouts.
45+
46+
.. seealso:: :py:func:`filters`
47+
%End
48+
49+
void setAllowEmptyLayout( bool allowEmpty );
50+
%Docstring
51+
Sets whether an optional empty layout ("not set") option is present in the combobox.
52+
53+
.. seealso:: :py:func:`allowEmptyLayout`
54+
%End
55+
56+
bool allowEmptyLayout() const;
57+
%Docstring
58+
Returns ``True`` if the combobox includes the empty layout ("not set") choice.
59+
60+
.. seealso:: :py:func:`setAllowEmptyLayout`
61+
%End
62+
63+
QgsMasterLayoutInterface *currentLayout() const;
64+
%Docstring
65+
Returns the layout currently selected in the combo box.
66+
%End
67+
68+
QgsMasterLayoutInterface *layout( int index ) const;
69+
%Docstring
70+
Returns the layout at the specified ``index``.
71+
%End
72+
73+
public slots:
74+
75+
void setCurrentLayout( QgsMasterLayoutInterface *layout );
76+
%Docstring
77+
Sets the currently selected ``layout`` in the combo box.
78+
%End
79+
80+
signals:
81+
82+
void layoutChanged( QgsMasterLayoutInterface *layout );
83+
%Docstring
84+
Emitted whenever the currently selected layout changes
85+
%End
86+
87+
};
88+
89+
/************************************************************************
90+
* This file has been generated automatically from *
91+
* *
92+
* src/gui/layout/qgslayoutcombobox.h *
93+
* *
94+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
95+
************************************************************************/

‎python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@
302302
%Include auto_generated/layertree/qgslayertreeview.sip
303303
%Include auto_generated/layertree/qgslayertreeviewdefaultactions.sip
304304
%Include auto_generated/layertree/qgslayertreeviewindicator.sip
305+
%Include auto_generated/layout/qgslayoutcombobox.sip
305306
%Include auto_generated/layout/qgslayoutcustomdrophandler.sip
306307
%Include auto_generated/layout/qgslayoutdesignerinterface.sip
307308
%Include auto_generated/layout/qgslayoutitemcombobox.sip

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ SET(QGIS_GUI_SRCS
165165
layertree/qgslayertreeviewindicator.cpp
166166
layertree/qgslayertreeviewitemdelegate.cpp
167167

168+
layout/qgslayoutcombobox.cpp
168169
layout/qgslayoutcustomdrophandler.cpp
169170
layout/qgslayoutitemguiregistry.cpp
170171
layout/qgslayoutitemcombobox.cpp
@@ -715,6 +716,7 @@ SET(QGIS_GUI_MOC_HDRS
715716
layertree/qgslayertreeviewindicator.h
716717
layertree/qgslayertreeviewitemdelegate.h
717718

719+
layout/qgslayoutcombobox.h
718720
layout/qgslayoutcustomdrophandler.h
719721
layout/qgslayoutdesignerinterface.h
720722
layout/qgslayoutitemcombobox.h

‎src/gui/layout/qgslayoutcombobox.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/***************************************************************************
2+
qgslayoutcombobox.cpp
3+
--------------------------------------
4+
Date : March 2019
5+
Copyright : (C) 2019 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
17+
#include "qgslayoutcombobox.h"
18+
#include "qgslayoutmodel.h"
19+
20+
QgsLayoutComboBox::QgsLayoutComboBox( QWidget *parent, QgsLayoutManager *manager )
21+
: QComboBox( parent )
22+
{
23+
setLayoutManager( manager );
24+
25+
connect( this, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutComboBox::indexChanged );
26+
}
27+
28+
void QgsLayoutComboBox::setLayoutManager( QgsLayoutManager *manager )
29+
{
30+
if ( mModel )
31+
mModel->deleteLater();
32+
if ( mProxyModel )
33+
mProxyModel->deleteLater();
34+
35+
mModel = new QgsLayoutManagerModel( manager, this );
36+
mProxyModel = new QgsLayoutManagerProxyModel( this );
37+
mProxyModel->setSourceModel( mModel );
38+
39+
connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayoutComboBox::rowsChanged );
40+
connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayoutComboBox::rowsChanged );
41+
setModel( mProxyModel );
42+
mProxyModel->sort( 0, Qt::AscendingOrder );
43+
}
44+
45+
QgsLayoutManagerProxyModel::Filters QgsLayoutComboBox::filters() const
46+
{
47+
return mProxyModel->filters();
48+
}
49+
50+
void QgsLayoutComboBox::setFilters( QgsLayoutManagerProxyModel::Filters filters )
51+
{
52+
mProxyModel->setFilters( filters );
53+
}
54+
55+
void QgsLayoutComboBox::setAllowEmptyLayout( bool allowEmpty )
56+
{
57+
mModel->setAllowEmptyLayout( allowEmpty );
58+
}
59+
60+
bool QgsLayoutComboBox::allowEmptyLayout() const
61+
{
62+
return mModel->allowEmptyLayout();
63+
}
64+
65+
void QgsLayoutComboBox::setCurrentLayout( QgsMasterLayoutInterface *layout )
66+
{
67+
if ( !mModel )
68+
return;
69+
70+
QModelIndex idx = mModel->indexFromLayout( layout );
71+
if ( idx.isValid() )
72+
{
73+
QModelIndex proxyIdx = mProxyModel->mapFromSource( idx );
74+
if ( proxyIdx.isValid() )
75+
{
76+
setCurrentIndex( proxyIdx.row() );
77+
return;
78+
}
79+
}
80+
setCurrentIndex( allowEmptyLayout() ? 0 : -1 );
81+
}
82+
83+
QgsMasterLayoutInterface *QgsLayoutComboBox::currentLayout() const
84+
{
85+
return layout( currentIndex() );
86+
}
87+
88+
void QgsLayoutComboBox::indexChanged( int i )
89+
{
90+
Q_UNUSED( i );
91+
emit layoutChanged( currentLayout() );
92+
}
93+
94+
void QgsLayoutComboBox::rowsChanged()
95+
{
96+
if ( count() == 1 )
97+
{
98+
//currently selected item has changed
99+
emit layoutChanged( currentLayout() );
100+
}
101+
else if ( count() == 0 )
102+
{
103+
emit layoutChanged( nullptr );
104+
}
105+
}
106+
107+
QgsMasterLayoutInterface *QgsLayoutComboBox::layout( int index ) const
108+
{
109+
const QModelIndex proxyIndex = mProxyModel->index( index, 0 );
110+
if ( !proxyIndex.isValid() )
111+
{
112+
return nullptr;
113+
}
114+
115+
QModelIndex sourceIndex = mProxyModel->mapToSource( proxyIndex );
116+
if ( !sourceIndex.isValid() )
117+
{
118+
return nullptr;
119+
}
120+
121+
return mModel->layoutFromIndex( sourceIndex );
122+
}

‎src/gui/layout/qgslayoutcombobox.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/***************************************************************************
2+
qgslayoutcombobox.h
3+
--------------------------------------
4+
Date : March 2019
5+
Copyright : (C) 2019 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSLAYOUTCOMBOBOX_H
17+
#define QGSLAYOUTCOMBOBOX_H
18+
19+
#include <QComboBox>
20+
#include "qgis_sip.h"
21+
#include "qgis_gui.h"
22+
#include "qgslayoutmanager.h"
23+
24+
/**
25+
* \class QgsLayoutComboBox
26+
* \ingroup gui
27+
* \brief The QgsLayoutComboBox class is a combo box which displays available layouts from a QgsLayoutManager.
28+
* \since QGIS 3.8
29+
*/
30+
class GUI_EXPORT QgsLayoutComboBox : public QComboBox
31+
{
32+
Q_OBJECT
33+
34+
public:
35+
36+
/**
37+
* QgsLayoutComboBox creates a combo box to display a list of items in a
38+
* layout \a manager. The layouts can optionally be filtered by type.
39+
*/
40+
explicit QgsLayoutComboBox( QWidget *parent SIP_TRANSFERTHIS = nullptr, QgsLayoutManager *manager = nullptr );
41+
42+
/**
43+
* Sets the layout \a manager containing the layouts to list in the combo box.
44+
*/
45+
void setLayoutManager( QgsLayoutManager *manager );
46+
47+
/**
48+
* Returns the current filters used for filtering available layouts.
49+
*
50+
* \see setFilters()
51+
*/
52+
QgsLayoutManagerProxyModel::Filters filters() const;
53+
54+
/**
55+
* Sets the current \a filters used for filtering available layouts.
56+
*
57+
* \see filters()
58+
*/
59+
void setFilters( QgsLayoutManagerProxyModel::Filters filters );
60+
61+
/**
62+
* Sets whether an optional empty layout ("not set") option is present in the combobox.
63+
* \see allowEmptyLayout()
64+
*/
65+
void setAllowEmptyLayout( bool allowEmpty );
66+
67+
/**
68+
* Returns TRUE if the combobox includes the empty layout ("not set") choice.
69+
* \see setAllowEmptyLayout()
70+
*/
71+
bool allowEmptyLayout() const;
72+
73+
/**
74+
* Returns the layout currently selected in the combo box.
75+
*/
76+
QgsMasterLayoutInterface *currentLayout() const;
77+
78+
/**
79+
* Returns the layout at the specified \a index.
80+
*/
81+
QgsMasterLayoutInterface *layout( int index ) const;
82+
83+
public slots:
84+
85+
/**
86+
* Sets the currently selected \a layout in the combo box.
87+
*/
88+
void setCurrentLayout( QgsMasterLayoutInterface *layout );
89+
90+
signals:
91+
92+
//! Emitted whenever the currently selected layout changes
93+
void layoutChanged( QgsMasterLayoutInterface *layout );
94+
95+
private slots:
96+
void indexChanged( int i );
97+
void rowsChanged();
98+
99+
private:
100+
QgsLayoutManagerModel *mModel = nullptr;
101+
QgsLayoutManagerProxyModel *mProxyModel = nullptr;
102+
103+
};
104+
105+
#endif // QGSLAYOUTCOMBOBOX_H

‎tests/src/python/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ ADD_PYTHON_TEST(PyQgsLayerTreeView test_qgslayertreeview.py)
8787
ADD_PYTHON_TEST(PyQgsLayout test_qgslayout.py)
8888
ADD_PYTHON_TEST(PyQgsLayoutAlign test_qgslayoutaligner.py)
8989
ADD_PYTHON_TEST(PyQgsLayoutAtlas test_qgslayoutatlas.py)
90+
ADD_PYTHON_TEST(PyQgsLayoutComboBox test_qgslayoutcombobox.py)
9091
ADD_PYTHON_TEST(PyQgsLayoutExporter test_qgslayoutexporter.py)
9192
ADD_PYTHON_TEST(PyQgsLayoutFrame test_qgslayoutframe.py)
9293
ADD_PYTHON_TEST(PyQgsLayoutManager test_qgslayoutmanager.py)
@@ -307,3 +308,4 @@ IF (WITH_SERVER)
307308
ADD_PYTHON_TEST(PyQgsServerRequest test_qgsserver_request.py)
308309
ADD_PYTHON_TEST(PyQgsServerResponse test_qgsserver_response.py)
309310
ENDIF (WITH_SERVER)
311+
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsLayoutComboBox
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = '(C) 2019 by Nyall Dawson'
10+
__date__ = '11/03/2019'
11+
__copyright__ = 'Copyright 2019, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
from qgis.PyQt.QtXml import QDomDocument
18+
19+
from qgis.core import (QgsPrintLayout,
20+
QgsLayoutManager,
21+
QgsLayoutManagerModel,
22+
QgsLayoutManagerProxyModel,
23+
QgsProject,
24+
QgsReport,
25+
QgsMasterLayoutInterface)
26+
from qgis.gui import QgsLayoutComboBox
27+
from qgis.PyQt.QtCore import Qt, QModelIndex
28+
from qgis.testing import start_app, unittest
29+
from utilities import unitTestDataPath
30+
from qgis.PyQt.QtXml import QDomDocument
31+
from qgis.PyQt.QtTest import QSignalSpy
32+
33+
start_app()
34+
TEST_DATA_DIR = unitTestDataPath()
35+
36+
37+
class TestQgsLayoutComboBox(unittest.TestCase):
38+
39+
def setUp(self):
40+
"""Run before each test."""
41+
self.manager = None
42+
self.aboutFired = False
43+
44+
def tearDown(self):
45+
"""Run after each test."""
46+
pass
47+
48+
def testCombo(self):
49+
project = QgsProject()
50+
manager = QgsLayoutManager(project)
51+
layout = QgsPrintLayout(project)
52+
layout.setName('ccc')
53+
self.assertTrue(manager.addLayout(layout))
54+
layout2 = QgsPrintLayout(project)
55+
layout2.setName('bbb')
56+
self.assertTrue(manager.addLayout(layout2))
57+
r = QgsReport(project)
58+
r.setName('ddd')
59+
manager.addLayout(r)
60+
61+
combo = QgsLayoutComboBox(None, manager)
62+
spy = QSignalSpy(combo.layoutChanged)
63+
self.assertEqual(combo.count(), 3)
64+
65+
self.assertEqual(combo.itemText(0), 'bbb')
66+
self.assertEqual(combo.itemText(1), 'ccc')
67+
self.assertEqual(combo.itemText(2), 'ddd')
68+
69+
self.assertEqual(combo.layout(0), layout2)
70+
self.assertEqual(combo.layout(1), layout)
71+
self.assertEqual(combo.layout(2), r)
72+
73+
combo.setCurrentLayout(None)
74+
self.assertEqual(combo.currentLayout(), None)
75+
self.assertEqual(len(spy), 1)
76+
combo.setCurrentLayout(layout)
77+
self.assertEqual(combo.currentLayout(), layout)
78+
self.assertEqual(len(spy), 2)
79+
combo.setCurrentLayout(r)
80+
self.assertEqual(combo.currentLayout(), r)
81+
self.assertEqual(len(spy), 3)
82+
combo.setCurrentLayout(layout2)
83+
self.assertEqual(combo.currentLayout(), layout2)
84+
self.assertEqual(len(spy), 4)
85+
86+
combo.setAllowEmptyLayout(True)
87+
self.assertEqual(combo.count(), 4)
88+
self.assertEqual(combo.itemText(0), '')
89+
self.assertEqual(combo.itemText(1), 'bbb')
90+
self.assertEqual(combo.itemText(2), 'ccc')
91+
self.assertEqual(combo.itemText(3), 'ddd')
92+
combo.setCurrentLayout(None)
93+
self.assertEqual(combo.currentIndex(), 0)
94+
95+
combo.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts)
96+
self.assertEqual(combo.count(), 3)
97+
self.assertEqual(combo.itemText(0), '')
98+
self.assertEqual(combo.itemText(1), 'bbb')
99+
self.assertEqual(combo.itemText(2), 'ccc')
100+
101+
combo.setFilters(QgsLayoutManagerProxyModel.FilterReports)
102+
self.assertEqual(combo.filters(), QgsLayoutManagerProxyModel.FilterReports)
103+
self.assertEqual(combo.count(), 2)
104+
self.assertEqual(combo.itemText(0), '')
105+
self.assertEqual(combo.itemText(1), 'ddd')
106+
107+
108+
if __name__ == '__main__':
109+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.