Skip to content

Commit b6fa94f

Browse files
committedJun 30, 2011
Merge branch 'svg_params'
2 parents eb9d610 + 4546646 commit b6fa94f

File tree

14 files changed

+938
-61
lines changed

14 files changed

+938
-61
lines changed
 

‎images/svg/geometric/Square1.svg

Lines changed: 2 additions & 2 deletions
Loading

‎images/svg/geometric/Square2.svg

Lines changed: 3 additions & 3 deletions
Loading

‎images/svg/geometric/Triangle1.svg

Lines changed: 2 additions & 2 deletions
Loading

‎images/svg/transport/amenity=airport.svg

Lines changed: 1 addition & 1 deletion
Loading

‎images/svg/transport/amenity=ferry_terminal.svg

Lines changed: 2 additions & 2 deletions
Loading

‎images/svg/transport/amenity=taxi.svg

Lines changed: 9 additions & 9 deletions
Loading

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ SET(QGIS_CORE_SRCS
3737
symbology-ng/qgsvectorcolorrampv2.cpp
3838
symbology-ng/qgsstylev2.cpp
3939
symbology-ng/qgssymbologyv2conversion.cpp
40+
symbology-ng/qgssvgcache.cpp
4041

4142
qgis.cpp
4243
qgsapplication.cpp

‎src/core/symbology-ng/qgsmarkersymbollayerv2.cpp

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "qgsapplication.h"
77
#include "qgslogger.h"
88
#include "qgsproject.h"
9+
#include "qgssvgcache.h"
910

1011
#include <QPainter>
1112
#include <QSvgRenderer>
@@ -449,6 +450,9 @@ QgsSvgMarkerSymbolLayerV2::QgsSvgMarkerSymbolLayerV2( QString name, double size,
449450
mSize = size;
450451
mAngle = angle;
451452
mOffset = QPointF( 0, 0 );
453+
mOutlineWidth = 1.0;
454+
mFillColor = QColor( Qt::black );
455+
mOutlineColor = QColor( Qt::black );
452456
}
453457

454458

@@ -466,11 +470,60 @@ QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::create( const QgsStringMap& props )
466470
angle = props["angle"].toDouble();
467471

468472
QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle );
473+
474+
//we only check the svg default parameters if necessary, since it could be expensive
475+
if( !props.contains("fill") && !props.contains("outline") && !props.contains("outline-width") )
476+
{
477+
QColor fillColor, outlineColor;
478+
double outlineWidth;
479+
bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
480+
QgsSvgCache::instance()->containsParams( name, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
481+
if( hasFillParam )
482+
{
483+
m->setFillColor( fillColor );
484+
}
485+
if( hasOutlineParam )
486+
{
487+
m->setOutlineColor( outlineColor );
488+
}
489+
if( hasOutlineWidthParam )
490+
{
491+
m->setOutlineWidth( outlineWidth );
492+
}
493+
}
494+
469495
if ( props.contains( "offset" ) )
470496
m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
497+
if ( props.contains( "fill" ) )
498+
m->setFillColor( QColor( props["fill"] ) );
499+
if ( props.contains( "outline" ) )
500+
m->setOutlineColor( QColor( props["outline"] ) );
501+
if ( props.contains( "outline-width" ) )
502+
m->setOutlineWidth( props["outline-width"].toDouble() );
471503
return m;
472504
}
473505

506+
void QgsSvgMarkerSymbolLayerV2::setPath( QString path )
507+
{
508+
mPath = path;
509+
QColor fillColor, outlineColor;
510+
double outlineWidth;
511+
bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
512+
QgsSvgCache::instance()->containsParams( path, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
513+
if( hasFillParam )
514+
{
515+
setFillColor( fillColor );
516+
}
517+
if( hasOutlineParam )
518+
{
519+
setOutlineColor( outlineColor );
520+
}
521+
if( hasOutlineWidthParam )
522+
{
523+
setOutlineWidth( outlineWidth );
524+
}
525+
}
526+
474527

475528
QString QgsSvgMarkerSymbolLayerV2::layerType() const
476529
{
@@ -479,30 +532,8 @@ QString QgsSvgMarkerSymbolLayerV2::layerType() const
479532

480533
void QgsSvgMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
481534
{
482-
double pictureSize = 0;
483-
QgsRenderContext& rc = context.renderContext();
484-
485-
if ( rc.painter() && rc.painter()->device() )
486-
{
487-
//correct QPictures DPI correction
488-
pictureSize = context.outputLineWidth( mSize ) / rc.painter()->device()->logicalDpiX() * mPicture.logicalDpiX();
489-
}
490-
else
491-
{
492-
pictureSize = context.outputLineWidth( mSize );
493-
}
494-
QRectF rect( QPointF( -pictureSize / 2.0, -pictureSize / 2.0 ), QSizeF( pictureSize, pictureSize ) );
495-
QSvgRenderer renderer( mPath );
496-
QPainter painter( &mPicture );
497-
renderer.render( &painter, rect );
498-
QPainter selPainter( &mSelPicture );
499-
selPainter.setRenderHint( QPainter::Antialiasing );
500-
selPainter.setBrush( QBrush( context.selectionColor() ) );
501-
selPainter.setPen( Qt::NoPen );
502-
selPainter.drawEllipse( QPointF( 0, 0 ), pictureSize*0.6, pictureSize*0.6 );
503-
renderer.render( &selPainter, rect );
504-
505535
mOrigSize = mSize; // save in case the size would be data defined
536+
Q_UNUSED( context );
506537
}
507538

508539
void QgsSvgMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
@@ -533,8 +564,28 @@ void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2Re
533564
if ( mAngle != 0 )
534565
p->rotate( mAngle );
535566

536-
QPicture &pct = context.selected() ? mSelPicture : mPicture;
537-
p->drawPicture( 0, 0, pct );
567+
if ( doubleNear( context.renderContext().rasterScaleFactor(), 1.0, 0.1 ) )
568+
{
569+
const QImage& img = QgsSvgCache::instance()->svgAsImage( mPath, mSize, mFillColor, mOutlineColor, mOutlineWidth,
570+
context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() );
571+
p->drawImage( -img.width() / 2.0, -img.width() / 2.0, img );
572+
}
573+
else
574+
{
575+
const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( mPath, mSize, mFillColor, mOutlineColor, mOutlineWidth,
576+
context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() );
577+
p->drawPicture( 0, 0, pct );
578+
}
579+
580+
if( context.selected() )
581+
{
582+
QPen pen( context.selectionColor() );
583+
pen.setWidth( context.outputLineWidth( 1.0 ) );
584+
p->setPen( pen );
585+
p->setBrush( Qt::NoBrush );
586+
double sizePixel = context.outputLineWidth( mSize );
587+
p->drawRect( QRectF( -sizePixel / 2.0, -sizePixel / 2.0, sizePixel, sizePixel ) );
588+
}
538589

539590
p->restore();
540591
}
@@ -547,12 +598,18 @@ QgsStringMap QgsSvgMarkerSymbolLayerV2::properties() const
547598
map["size"] = QString::number( mSize );
548599
map["angle"] = QString::number( mAngle );
549600
map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
601+
map["fill"] = mFillColor.name();
602+
map["outline"] = mOutlineColor.name();
603+
map["outline-width"] = QString::number( mOutlineWidth );
550604
return map;
551605
}
552606

553607
QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const
554608
{
555609
QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( mPath, mSize, mAngle );
610+
m->setFillColor( mFillColor );
611+
m->setOutlineColor( mOutlineColor );
612+
m->setOutlineWidth( mOutlineWidth );
556613
m->setOffset( mOffset );
557614
return m;
558615
}

‎src/core/symbology-ng/qgsmarkersymbollayerv2.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,28 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayerV2 : public QgsMarkerSymbolLayerV2
112112
QgsSymbolLayerV2* clone() const;
113113

114114
QString path() const { return mPath; }
115-
void setPath( QString path ) { mPath = path; }
115+
void setPath( QString path );
116+
117+
QColor fillColor() const { return mFillColor; }
118+
void setFillColor( const QColor& c ) { mFillColor = c; }
119+
120+
QColor outlineColor() const { return mOutlineColor; }
121+
void setOutlineColor( const QColor& c ) { mOutlineColor = c; }
122+
123+
double outlineWidth() const { return mOutlineWidth; }
124+
void setOutlineWidth( double w ) { mOutlineWidth = w; }
116125

117126
protected:
118127

119128
void loadSvg();
120129

121130
QString mPath;
122-
QPicture mPicture;
123-
QPicture mSelPicture;
131+
132+
//param(fill), param(outline), param(outline-width) are going
133+
//to be replaced in memory
134+
QColor mFillColor;
135+
QColor mOutlineColor;
136+
double mOutlineWidth;
124137
double mOrigSize;
125138
};
126139

‎src/core/symbology-ng/qgssvgcache.cpp

Lines changed: 569 additions & 0 deletions
Large diffs are not rendered by default.

‎src/core/symbology-ng/qgssvgcache.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/***************************************************************************
2+
qgssvgcache.h
3+
------------------------------
4+
begin : 2011
5+
copyright : (C) 2011 by Marco Hugentobler
6+
email : marco dot hugentobler at sourcepole dot ch
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSSVGCACHE_H
19+
#define QGSSVGCACHE_H
20+
21+
#include <QColor>
22+
#include <QMap>
23+
#include <QMultiHash>
24+
#include <QString>
25+
26+
class QDomElement;
27+
class QImage;
28+
class QPicture;
29+
30+
struct QgsSvgCacheEntry
31+
{
32+
QgsSvgCacheEntry();
33+
QgsSvgCacheEntry( const QString& file, double size, double outlineWidth, double widthScaleFactor, double rasterScaleFctor, const QColor& fill, const QColor& outline );
34+
~QgsSvgCacheEntry();
35+
36+
QString file;
37+
double size;
38+
double outlineWidth;
39+
double widthScaleFactor;
40+
double rasterScaleFactor;
41+
QColor fill;
42+
QColor outline;
43+
QImage* image;
44+
QPicture* picture;
45+
//content (with params replaced)
46+
QByteArray svgContent;
47+
48+
//keep entries on a least, sorted by last access
49+
QgsSvgCacheEntry* nextEntry;
50+
QgsSvgCacheEntry* previousEntry;
51+
52+
/**Don't consider image, picture, last used timestamp for comparison*/
53+
bool operator==( const QgsSvgCacheEntry& other ) const;
54+
/**Return memory usage in bytes*/
55+
int dataSize() const;
56+
};
57+
58+
/**A cache for images / pictures derived from svg files. This class supports parameter replacement in svg files
59+
according to the svg params specification (http://www.w3.org/TR/2009/WD-SVGParamPrimer-20090616/). Supported are
60+
the parameters 'fill-color', 'pen-color', 'outline-width', 'stroke-width'. E.g. <circle fill="param(fill-color red)" stroke="param(pen-color black)" stroke-width="param(outline-width 1)"*/
61+
class QgsSvgCache
62+
{
63+
public:
64+
65+
static QgsSvgCache* instance();
66+
~QgsSvgCache();
67+
68+
const QImage& svgAsImage( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
69+
double widthScaleFactor, double rasterScaleFactor );
70+
const QPicture& svgAsPicture( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
71+
double widthScaleFactor, double rasterScaleFactor );
72+
73+
/**Tests if an svg file contains parameters for fill, outline color, outline width. If yes, possible default values are returned. If there are several
74+
default values in the svg file, only the first one is considered*/
75+
void containsParams( const QString& path, bool& hasFillParam, QColor& defaultFillColor, bool& hasOutlineParam, QColor& defaultOutlineColor, bool& hasOutlineWidthParam,
76+
double& defaultOutlineWidth ) const;
77+
78+
protected:
79+
QgsSvgCache();
80+
81+
/**Creates new cache entry and returns pointer to it*/
82+
QgsSvgCacheEntry* insertSVG( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
83+
double widthScaleFactor, double rasterScaleFactor );
84+
85+
void replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry );
86+
void cacheImage( QgsSvgCacheEntry* entry );
87+
void cachePicture( QgsSvgCacheEntry* entry );
88+
/**Returns entry from cache or creates a new entry if it does not exist already*/
89+
QgsSvgCacheEntry* cacheEntry( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
90+
double widthScaleFactor, double rasterScaleFactor );
91+
92+
/**Removes the least used items until the maximum size is under the limit*/
93+
void trimToMaximumSize();
94+
95+
//Removes entry from the ordered list (but does not delete the entry itself)
96+
void takeEntryFromList( QgsSvgCacheEntry* entry );
97+
98+
private:
99+
static QgsSvgCache* mInstance;
100+
101+
/**Entry pointers accessible by file name*/
102+
QMultiHash< QString, QgsSvgCacheEntry* > mEntryLookup;
103+
/**Estimated total size of all images, pictures and svgContent*/
104+
long mTotalSize;
105+
106+
//The svg cache keeps the entries on a double connected list, moving the current entry to the front.
107+
//That way, removing entries for more space can start with the least used objects.
108+
QgsSvgCacheEntry* mLeastRecentEntry;
109+
QgsSvgCacheEntry* mMostRecentEntry;
110+
111+
//Maximum cache size
112+
static const long mMaximumSize = 20000000;
113+
114+
/**Replaces parameters in elements of a dom node and calls method for all child nodes*/
115+
void replaceElemParams( QDomElement& elem, const QColor& fill, const QColor& outline, double outlineWidth );
116+
117+
void containsElemParams( const QDomElement& elem, bool& hasFillParam, QColor& defaultFill, bool& hasOutlineParam, QColor& defaultOutline,
118+
bool& hasOutlineWidthParam, double& defaultOutlineWidth ) const;
119+
120+
/**Release memory and remove cache entry from mEntryLookup*/
121+
void removeCacheEntry( QString s, QgsSvgCacheEntry* entry );
122+
123+
/**For debugging*/
124+
void printEntryList();
125+
};
126+
127+
#endif // QGSSVGCACHE_H

‎src/gui/symbology-ng/qgssymbollayerv2widget.cpp

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "characterwidget.h"
99
#include "qgsdashspacedialog.h"
1010
#include "qgssymbolv2propertiesdialog.h"
11+
#include "qgssvgcache.h"
1112

1213
#include "qgsapplication.h"
1314

@@ -537,20 +538,17 @@ class QgsSvgListModel : public QAbstractListModel
537538

538539
if ( role == Qt::DecorationRole ) // icon
539540
{
540-
541541
QPixmap pixmap;
542542
if ( !QPixmapCache::find( entry, pixmap ) )
543543
{
544544
// render SVG file
545-
QSvgRenderer renderer;
546-
QPainter painter;
547-
renderer.load( entry );
548-
pixmap = QPixmap( QSize( 24, 24 ) );
549-
pixmap.fill();
550-
painter.begin( &pixmap );
551-
renderer.render( &painter );
552-
painter.end();
545+
QColor fill, outline;
546+
double outlineWidth;
547+
bool fillParam, outlineParam, outlineWidthParam;
548+
QgsSvgCache::instance()->containsParams( entry, fillParam, fill, outlineParam, outline, outlineWidthParam, outlineWidth );
553549

550+
const QImage& img = QgsSvgCache::instance()->svgAsImage( entry, 8, fill, outline, outlineWidth, 3.5 /*appr. 88 dpi*/, 1.0 );
551+
pixmap = QPixmap::fromImage( img );
554552
QPixmapCache::insert( entry, pixmap );
555553
}
556554

@@ -575,6 +573,31 @@ void QgsSvgMarkerSymbolLayerV2Widget::populateList()
575573
viewImages->setModel( m );
576574
}
577575

576+
void QgsSvgMarkerSymbolLayerV2Widget::setGuiForSvg( const QgsSvgMarkerSymbolLayerV2* layer )
577+
{
578+
if( !layer )
579+
{
580+
return;
581+
}
582+
583+
//activate gui for svg parameters only if supported by the svg file
584+
bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
585+
QColor defaultFill, defaultOutline;
586+
double defaultOutlineWidth;
587+
QgsSvgCache::instance()->containsParams( layer->path(), hasFillParam, defaultFill, hasOutlineParam, defaultOutline, hasOutlineWidthParam, defaultOutlineWidth );
588+
mChangeColorButton->setEnabled( hasFillParam );
589+
mChangeBorderColorButton->setEnabled( hasOutlineParam );
590+
mBorderWidthSpinBox->setEnabled( hasOutlineWidthParam );
591+
592+
mFileLineEdit->blockSignals( true );
593+
mFileLineEdit->setText( layer->path() );
594+
mFileLineEdit->blockSignals( false );
595+
596+
mBorderWidthSpinBox->blockSignals( true );
597+
mBorderWidthSpinBox->setValue( layer->outlineWidth() );
598+
mBorderWidthSpinBox->blockSignals( false );
599+
}
600+
578601

579602
void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
580603
{
@@ -595,6 +618,7 @@ void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
595618
{
596619
selModel->select( idx, QItemSelectionModel::SelectCurrent );
597620
selModel->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent );
621+
setName( idx );
598622
break;
599623
}
600624
}
@@ -611,6 +635,9 @@ void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
611635
spinOffsetY->blockSignals( true );
612636
spinOffsetY->setValue( mLayer->offset().y() );
613637
spinOffsetY->blockSignals( false );
638+
639+
setGuiForSvg( mLayer );
640+
614641
}
615642

616643
QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2Widget::symbolLayer()
@@ -623,6 +650,8 @@ void QgsSvgMarkerSymbolLayerV2Widget::setName( const QModelIndex& idx )
623650
QString name = idx.data( Qt::UserRole ).toString();
624651
mLayer->setPath( name );
625652
mFileLineEdit->setText( name );
653+
654+
setGuiForSvg( mLayer );
626655
emit changed();
627656
}
628657

@@ -666,9 +695,47 @@ void QgsSvgMarkerSymbolLayerV2Widget::on_mFileLineEdit_textEdited( const QString
666695
return;
667696
}
668697
mLayer->setPath( text );
698+
setGuiForSvg( mLayer );
669699
emit changed();
670700
}
671701

702+
void QgsSvgMarkerSymbolLayerV2Widget::on_mChangeColorButton_clicked()
703+
{
704+
if ( !mLayer )
705+
{
706+
return;
707+
}
708+
QColor c = QColorDialog::getColor( mLayer->fillColor() );
709+
if ( c.isValid() )
710+
{
711+
mLayer->setFillColor( c );
712+
emit changed();
713+
}
714+
}
715+
716+
void QgsSvgMarkerSymbolLayerV2Widget::on_mChangeBorderColorButton_clicked()
717+
{
718+
if ( !mLayer )
719+
{
720+
return;
721+
}
722+
QColor c = QColorDialog::getColor( mLayer->outlineColor() );
723+
if ( c.isValid() )
724+
{
725+
mLayer->setOutlineColor( c );
726+
emit changed();
727+
}
728+
}
729+
730+
void QgsSvgMarkerSymbolLayerV2Widget::on_mBorderWidthSpinBox_valueChanged( double d )
731+
{
732+
if ( mLayer )
733+
{
734+
mLayer->setOutlineWidth( d );
735+
emit changed();
736+
}
737+
}
738+
672739
///////////////
673740

674741
QgsLineDecorationSymbolLayerV2Widget::QgsLineDecorationSymbolLayerV2Widget( QWidget* parent )

‎src/gui/symbology-ng/qgssymbollayerv2widget.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,15 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerV2Widget : public QgsSymbolLayerV2Widget
181181
void setOffset();
182182
void on_mFileToolButton_clicked();
183183
void on_mFileLineEdit_textEdited( const QString& text );
184+
void on_mChangeColorButton_clicked();
185+
void on_mChangeBorderColorButton_clicked();
186+
void on_mBorderWidthSpinBox_valueChanged( double d );
184187

185188
protected:
186189

187190
void populateList();
191+
//update gui for svg file (insert new path, update activation of gui elements for svg params)
192+
void setGuiForSvg( const QgsSvgMarkerSymbolLayerV2* layer );
188193

189194
QgsSvgMarkerSymbolLayerV2* mLayer;
190195
};

‎src/ui/symbollayer/widget_svgmarker.ui

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>391</width>
10-
<height>276</height>
9+
<width>302</width>
10+
<height>337</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -29,7 +29,7 @@
2929
</property>
3030
</widget>
3131
</item>
32-
<item row="0" column="2" rowspan="3">
32+
<item row="0" column="2" rowspan="6">
3333
<spacer>
3434
<property name="orientation">
3535
<enum>Qt::Horizontal</enum>
@@ -62,7 +62,7 @@
6262
</property>
6363
</widget>
6464
</item>
65-
<item row="2" column="1">
65+
<item row="5" column="1">
6666
<layout class="QHBoxLayout" name="horizontalLayout">
6767
<item>
6868
<widget class="QDoubleSpinBox" name="spinOffsetX">
@@ -92,7 +92,7 @@
9292
</item>
9393
</layout>
9494
</item>
95-
<item row="2" column="0">
95+
<item row="5" column="0">
9696
<widget class="QLabel" name="label_4">
9797
<property name="text">
9898
<string>Offset X,Y</string>
@@ -118,6 +118,44 @@
118118
</property>
119119
</widget>
120120
</item>
121+
<item row="2" column="1">
122+
<widget class="QPushButton" name="mChangeColorButton">
123+
<property name="text">
124+
<string>Change</string>
125+
</property>
126+
</widget>
127+
</item>
128+
<item row="2" column="0">
129+
<widget class="QLabel" name="mColorLabel">
130+
<property name="text">
131+
<string>Color</string>
132+
</property>
133+
</widget>
134+
</item>
135+
<item row="4" column="1">
136+
<widget class="QDoubleSpinBox" name="mBorderWidthSpinBox"/>
137+
</item>
138+
<item row="4" column="0">
139+
<widget class="QLabel" name="mBorderWidthLabel">
140+
<property name="text">
141+
<string>Border width</string>
142+
</property>
143+
</widget>
144+
</item>
145+
<item row="3" column="1">
146+
<widget class="QPushButton" name="mChangeBorderColorButton">
147+
<property name="text">
148+
<string>Change</string>
149+
</property>
150+
</widget>
151+
</item>
152+
<item row="3" column="0">
153+
<widget class="QLabel" name="mBorderColorLabel">
154+
<property name="text">
155+
<string>Border color</string>
156+
</property>
157+
</widget>
158+
</item>
121159
</layout>
122160
</item>
123161
<item row="1" column="0">

0 commit comments

Comments
 (0)
Please sign in to comment.