vignettes/ANCOVAs.Rmd
ANCOVAs.Rmd
Recently, Shieh (2020) demonstrated that most software uses a slightly flawed approach to estimating power for ANCOVAs, and focused on one-way ANOVA designs to compare his method to the method first mentioned by Cohen (1988).
As Shieh (2020) eloquently points out in their simulations, the method of Cohen (1988) overestimates power and the problem is exacerbated by having a high number of covariates with high proportion of the variance (\(R^2\)) explained by the covariates. The problem is worst (30% error between estimated and actual power) in the simulation when there are 10 covariates included in the model when explained variance is approximately 81% (\(\rho = 0.9\)) [Shieh (2020) see Table 3). While this may not seem like much of issue if you don’t expect to encounter this scenario, it still demonstrates the Cohen (1988) method is inconsistent in producing appropriate estimates of power. I believe this is reason enough (when provided with a sustainable and implementable alternative) to abandon the old method of Cohen (1988) for a newer method that provides exact, rather than approximate, estimates of power for ANCOVA.
Thankfully, Shieh (2020) was diligent in his work and
demonstrated, showing both the math and simulations, how a new exact
method could be utilized. The direct method described in the paper is
implemented in the power_oneway_ancova
function.
We can copy the example from Maxwell and
Delaney (2004) that Shieh also used. In this
example there are 3 groups with means (mu
) of 400, 450, 500
respectively. The error variance is 10000 (sd = 100
).
Rather than simulating dozens of examples, I will demonstrate one
scenario below where there are 3 covariates, and the \(R^2\) is equal to 0.5 (treatment effect
excluded). This is demonstrated in Shieh (2020), Table
2.
For power_oneway_ancova
we can demonstrate both the
approximate and exact methods using the type
argument. We
can leave the n
argument out in order to solve for the
sample size required to reach 80% power. Please notice that
round_up
is set to TRUE since we want have a whole number
for sample sizes (rather than a fractional sample size).
power_oneway_ancova(
mu = c(400,450,500),
n_cov = 3,
sd = 100,
r2 = .25,
alpha_level = .05,
#n = c(17,17,17),
beta_level = .2,
round_up = TRUE,
type = "approx"
)
#>
#> Power Calculation for 1-way ANCOVA
#>
#> dfs = 2, 42
#> N = 48
#> n = 16, 16, 16
#> n_cov = 3
#> mu = 400, 450, 500
#> sd = 100
#> r2 = 0.25
#> alpha_level = 0.05
#> beta_level = 0.1877374
#> power = 81.22626
#> type = approx
Notice that this method requires 3 more subjects in order to achieve a minimum of 80% power.
power_oneway_ancova(
mu = c(400,450,500),
n_cov = 3,
sd = 100,
r2 = .25,
alpha_level = .05,
#n = c(17,17,17),
beta_level = .2,
round_up = TRUE,
type = "exact"
)
#>
#> Power Calculation for 1-way ANCOVA
#>
#> dfs = 2, 45
#> N = 51
#> n = 17, 17, 17
#> n_cov = 3
#> mu = 400, 450, 500
#> sd = 100
#> r2 = 0.25
#> alpha_level = 0.05
#> beta_level = 0.1878274
#> power = 81.21726
#> type = exact
Now, Shieh (2020) mentioned something very interesting at the end of section 3.
“Although the prescribed application of general linear hypothesis is discussed only from the perspective of a one-way ANCOVA design, the number of groups G may also represent the total number of combined factor levels of a multi-factor ANCOVA design. Hence, using a contrast matrix associated with a specific designated hypothesis, the same concept and process of assessing treatment effects can be readily extended to two-way and higher-order ANCOVA designs.”
Therefore, all that is needed to extend the one-way ANOVA code
provided by Shieh (2020) is to provide the appropriate
contrast matrix for the main effect or interaction ANOVA-level effect
that is desired. Superpower
accomplishes this with the
ANCOVA_analytic
function which internally uses the
model.matrix
function to form the appropriate contrast
matrix.
This function operates similar to the ANOVA_power
and
ANOVA_exact
functions. However, the
ANCOVA_analytic
function doesn’t require the use of
ANOVA_design
first and relies upon the closed formulas from
Shieh (2020) rather than a simulation to
calculate statistical power. Please note that unlike the
power_oneway_ancova
function there is no option to apply
the approximation from Cohen (1988) for
factorial designs.
We can extend the previous scenario with 3 groups to a factorial design with 2 groups across 3 conditions.
# Run function
res1 = ANCOVA_analytic(
design = "2b*3b",
mu = c(400, 450, 500,
400, 500, 600),
n_cov = 3,
sd = 100,
r2 = .25,
alpha_level = .05,
#n = 17,
beta_level = .2,
round_up = TRUE
)
# Print main results
res1
#> Power Analysis Results for ANCOVA
#> Total N Covariates r2 Alpha Level Beta Level Power
#> a 102 3 0.25 0.05 0.1897 81.03
#> b 30 3 0.25 0.05 0.1389 86.11
#> a:b 180 3 0.25 0.05 0.1995 80.05
The results can also be printed as power.htest
objects
by accessing the individual effects in the res1
object.
res1$aov_list$a
#>
#> Power Calculation for ANCOVA
#>
#> dfs = 1, 93
#> N = 102
#> n = 17, 17, 17, 17, 17, 17
#> n_cov = 3
#> mu = 400, 450, 500, 400, 500, 600
#> sd = 100
#> r2 = 0.25
#> alpha_level = 0.05
#> beta_level = 0.1897221
#> power = 81.02779
#> type = Exact
res1$aov_list$b
#>
#> Power Calculation for ANCOVA
#>
#> dfs = 2, 21
#> N = 30
#> n = 5, 5, 5, 5, 5, 5
#> n_cov = 3
#> mu = 400, 450, 500, 400, 500, 600
#> sd = 100
#> r2 = 0.25
#> alpha_level = 0.05
#> beta_level = 0.1389288
#> power = 86.10712
#> type = Exact
res1$aov_list$ab
#>
#> Power Calculation for ANCOVA
#>
#> dfs = 2, 171
#> N = 180
#> n = 30, 30, 30, 30, 30, 30
#> n_cov = 3
#> mu = 400, 450, 500, 400, 500, 600
#> sd = 100
#> r2 = 0.25
#> alpha_level = 0.05
#> beta_level = 0.1994719
#> power = 80.05281
#> type = Exact
We can also check the design by using the plot method.
plot(res1)
However, you may want to compare the power of ANOVA to an ANCOVA. In
that case you can use the ANOVA_design
function and pass it
onto the ANCOVA_analytic
function. But, this forces you to
set the sample size.
des1 = ANOVA_design( design = "2b*3b",
mu = c(400, 450, 500,
400, 500, 600),
n = 17,
sd = 100)
res2 = ANCOVA_analytic(
design_result = des1,
n_cov = 3,
r2 = .25,
alpha_level = .05,
round_up = TRUE
)
res2
#> Power Analysis Results for ANCOVA
#> Total N Covariates r2 Alpha Level Beta Level Power
#> a 102 3 0.25 0.05 0.1897221 81.03
#> b 102 3 0.25 0.05 0.0001954 99.98
#> a:b 102 3 0.25 0.05 0.4701157 52.99
User specified contrasts can also be used for a power analysis. These
can be provided in the cmats
argument of the
ANCOVA_analytic
function or supplied directly to the
ANCOVA_contrast
function as an independent test. The
ANCOVA_analytic
function requires that the contrasts
matrices are provided as matrices (i.e., as.matrix
). The
contrasts can be accessed in the con_list
part of the
results and can be named (in this case “test”).
ANCOVA_analytic(design = "2b",
mu = c(0,1),
n = 15,
cmats = list(test = matrix(c(-1,1),
nrow = 1)),
sd = 1,
r2 = .2,
n_cov = 1)$con_list$test
#>
#> Power Calculation for ANCOVA contrast
#>
#> dfs = 1, 27
#> N = 30
#> n = 15, 15
#> n_cov = 1
#> contrast = -1, 1
#> mu = 0, 1
#> sd = 1
#> r2 = 0.2
#> alpha_level = 0.05
#> beta_level = 0.1744293
#> power = 82.55707
#> type = Exact
# Same result
ANCOVA_contrast(cmat = c(-1,1),
n = 15,
mu = c(0,1),
sd = 1,
r2 = .2,
n_cov = 1)
#>
#> Power Calculation for ANCOVA contrast
#>
#> dfs = 1, 27
#> N = 30
#> n = 15, 15
#> n_cov = 1
#> contrast = -1, 1
#> mu = 0, 1
#> sd = 1
#> r2 = 0.2
#> alpha_level = 0.05
#> beta_level = 0.1744293
#> power = 82.55707
#> type = Exact


This section is about the formatting the values of the data-*
attributes.
Is whitespace in the values of the data-*
attributes
ignored?
Yes.
The white space is handled in the same way as in most programming languages (including JavaScript). Therefore, this format is perfectly legal:
Can the values of the data-*
attributes contain comments?
Yes.
Everything between /*
and */
will be ignored,
unless it is a part of a string literal.
The format of data-ylcBind
is
someAttribute: someExpression …
. What if
"someAttribute" contains a colon?
Use a backslash to escape the colon.
The following example shows a piece of SVG where the YLC bindings are
supposed to dynammically set an xlink:href
attribute of the
SVG image
tag. Since the attribute contains a colon and
colons are also used in the binding definition to separate the attribute
from the expression, the colon in the attribute has to be escaped.
Can the YLC event handlers return true
or
false
the same way standard JavaScript handlers do?
Yes.
The meaning will be exactly the same.
Can the YLC event handlers access the JavaScript event object?
Yes, it is in context.eventObject
.
Remember the context
variable is the second parameter
automatically passed to the event handler (the first being
model
).
This section describes issues related to the data-ylcBind
attribute,
such as circumstances when certain model variables can be undefined, how to make
a binding unidirectional and how errors in expressions are handled.
On my page there is an input field whose value is populated before YLC kicks in and I need to preserve that value, but I also need to bind it to a model variable. Now, when I initialize the corresponding model variable, the original value will be obviously overwritten. But when I do not initialize it, I get the following error: Error: Invalid model variable: someVariable
Initialize the model variable to context.PREFIELD
.
The above problem is quite common. For example we have a username and a password fields which are pre-filled by the browser and the YLC controller is supposed to read the values of those values via model variables bound to those fields. If those model variables are not initialized, YLC will complain that it doesn't know about them:
This is not a bad feature –it helps detect a mistyped variable name and thus prevents mysterious bugs. But if we do initialize those model variables, we effectively overwrite the pre-filled username and password. This issue is addressed by the concept of so called PREFIELDs.
The word PREFIELD is a pun on words "pre-filled" and "field".
It is a special value to which we can initialize some model variables.
If a model variable is initialized to context.PREFIELD
(context
being the second parameter passed to the
controller method) and bound to some input field value, the variable
will be automatically initialized to whatever the value of the field was
before YLC stepped in.
Can the YLC event handlers access the JavaScript event object?
Yes, it is in context.eventObject
.
Remember the context
variable is the second parameter
automatically passed to the event handler (the first being
model
).


Code Polyglot!
Recent Snippets
→
Snippet Name
×
↓
Code Container
```
Language
Library
VM
Speed
ms.
```


stone-check
A prototype for geometric image processing of stone images in search for defects.
Select a stone image for upload:


May's portfolio
May Kuo
I am a front-end web developer.
I can do HTML/HTML5, and
(scroll down)
About
About
Works
Works


Experienced with a variety of stimulents/empathogens, with perhaps a slightly higher tolerance than average (though it might be that I push it further, ie. I'm greedy). First time with this substance, so treading lightly.
Set: Had about half a bottle of wine (15%alc) and feeling good. A few jitters of anticipation. Also consumed three pain killers approx three hours before for a tooth ache - contained paracetamol and codeine phosphate (36mg of the latter all up).
Setting: Late morning, with my brother in a comfortable environment.
I've heard good things about this substance when insufflated, so I tried this route first. Measured 86mg and separated into two roughly identical lines. Also measured a further 146mg and readied it in a gel cap.
00:00 Insufflated one line. It burned a little, but I was expecting that having read a few reports. It wasn't as bad as I'd braced myself for.
T+00:01 Insufflated second line with other nostril. My eyes watered this time. I should point out here, that my nasal absorption rate seems low with cocaine, and that might carry over to this substance.
T+00:03 The taste has hit the back of my throat and my nose is running.
T+00:05 Ever so slightly off kilter. Feel slightly zonked, may be placebo.
T+00:09 Feel a noticable increase in the speed of my thought processes.
T+00:11 Was expecting more euphoria (imagining that it would hit pretty quickly with a nasal administration). Nostril burn still present though diminishing.
T+00:12 Notice a veering sensation, as if my mind is moving sideways without a corresponding movement from my body. Also notice that my toothache, which was coming back, has disappeared. Increased awareness of smell.
T+00:14 Eyes constricted if anything at this point.
T+00:15 Slight dizziness with a noticable shift in awareness. Feel slightly distanced from my physical self.
T+00:18 Urge to document this is quite strong. Loosening of the bowels. Thoughts are becoming clearer/sharper.
T+00:20 Nasal burn almost unnoticable now. Feeling comfortable at this stage. A relaxed stimulation. Still no euphoria or empathy.
T+00:22 Doing everything faster, not in a manic way, it feels normal and easy to move fast. I go have a shower.
T+00:28 Noticed an increase in tactile sensation whilst showering. Eyes are open wider that normal but still no dilation.
T+00:29 Not happy with my dose. No indication this will get any stronger. Will redose in ten minutes if no progression before then. Heart rate is faster though, definite CNS stimulation.
T+00:39 Decide to take the oral dose. I wash the 146mg gel cap down with a beer. Feeling a little disappointed with this substance at this stage. The early promise never seemed to eventuate.
T+00:50 Finished beer. Taste was ok, and reasonably enjoyable. I'm not too inclined toward conversation at this point.
T+01:12 Over the last few minutes I have noted a very good increase in euphoria and empathy. Feeling very nice. The skin on my forearms feels hot, but not uncomfortably so.
T+01:12-2:24 At this time I became incredibly engaged in free flowing conversation with my brother (who tells me my pupils are 'f'ing enormous'). Though he hadn't taken any meph, he was as forthcoming in the conversation as I was (contact high?). The feeling was comparable to my first MDMA experience at a lower volume (I have a feeling this is only due to the low dose). Completely in control, but completely open at the same time. There was no topic too taboo.
T+02:24 The come down is swift! I could actually feel it receding. It left me feeling a bit empty. One minute I'm as open and sociable as I've ever been, and the next I'm not far from my normal self. I was mindful of the many reports out there that claim fiending is a big part of the meph experience, so I consciously avoided any thoughts of redosing.
T+03:00 (approx) Still an underlying stimulation free of any 'higher calibre' effects. The decline is enormously more gradual for the stimulative qualities, but this residual is such a paltry substitute for what has been and gone that I find it hardly worth my attention/enjoyment.
Summary: A very nice substance for me overall. My elocution was remarkably enhanced, communicative centers were gaping wide, very comfortable and relaxed stimulation, empathy was significant (even though the 'magic' has long since departed my MDMA experiences). Comedown was not painful, just so swift it left a vacuum in its wake. I overindulged in MDMA when I was first introduced to it, and now I may as well have three caffeine drinks as take a pill. This substance feels (for me) like its effects could be similarly degraded with over-use. I'll be abstaining as much as my will permits.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


Pizza cooked to perfection tossed with milk, vegetables, potatoes, poultry, 100% pure mutton, and cheese - and in creating nutritious and tasty meals to maintain good health.
Pasta cooked to perfection tossed with milk, vegetables, potatoes, poultry, 100% pure mutton, and cheese - and in creating nutritious and tasty meals to maintain good health.
Sandwizza cooked to perfection tossed with milk, vegetables, potatoes, poultry, 100% pure mutton, and cheese - and in creating nutritious and tasty meals to maintain good health.
A treat that brings to life the flavors of straight-from-the-garden-veggies replete with green chilies. An appetizing combination of panneer tikka, onions & tomatoes.
An appetizing combination of panneer tikka, onions & tomatoes.A treat that brings to life the flavors of straight-from-the-garden-veggies replete with green chilies.
An appetizing and hot combination of chicken tikka, onions & tomatoes.The ultimate non-veg fiesta of grilled chicken, chicken salami, lamb pepperoni with cheese & onions.


Google Meet receives a new update
Google Meet is getting a bunch of new features for its free and Workspace users that are designed to make it easier to control meetings. The search giant mentioned that the new features are some of the highly requested features by its users. They include the ability to add up to 25 co-hosts per meeting, new moderation and safety features, and an updated Quick access setting. It is worth noting that Google also mentions that not all features would be available for all users.
Through a post on its Workspace blog, Google announced that Google Meet users will be able to grant rights for hosting a meeting with up to 25 co-hosts. It can be done by granting these users Hosts controls in the People panel. All the co-hosts will get to access and utilise the controls available with the host.
Hosts and co-hosts now have the ability to limit who can share screens, send chat messages, mute all participants with one click, end the meeting for all, and control who can join the meeting and how through the Quick access settings.


WATCH LIVE: Intelligence Committee holds impeachment hearing featuring Gordon Sondland
by Washington ExaminerThe House Intelligence Committee will hear the next round of impeachment testimony from government officials on Wednesday at 9 a.m. EST.
Gordon Sondland, the U.S. ambassador to the European Union, will testify in the morning. Laura Cooper, a deputy assistant secretary at the Defense Department, and David Hale, the undersecretary of state for political affairs at the State Department, will testify next at 2:30 p.m.


The European Parliament has 626 MEPs who are elected in the fifteen member states of the European Union (one of which is the UK) for a five year term.
There are 87 MEPs in the UK, split across 12 regions. Each region has between 3 and 11 MEPs. Each MEP in a region represents the people living in that region at the European Parliament.
Leeds is part of the Yorkshire and Humber region, which is represented by 6 MEPs.
Conservative
UK Independence Party
Labour
UK Independence Party
Labour
Conservative


How much effort will you spend on old browsers?
The JavaScript demi-god Douglas Crockford is attributed with the statement: " the browser is the most hostile software development environment ever imagined " (I think he made that before mobile phones were around ). The problem is not only that there are different engines for HTML and JavaScript, but the fact, that older browsers are still around. Chrome and Firefox have build-in upgrade enggines and IE9++ looks quite decent. So users could do with a little reminder and encouragement (and if it is just to pick up the torches and pitchforks to march to the IT department).
For the friends of Bootstrap here's a little code snippet that nicely fits just behind the body tag in your HTML page:
Others take more drastic measures. Luckily IE6 usage is down to 0.4% in the US (and 6% worldwide), now rinse and repeat for IE7, IE8 and old versions of the others
For the friends of Bootstrap here's a little code snippet that nicely fits just behind the body tag in your HTML page:
Posted by Stephan H Wissel on 04 October 2012 | Comments (2) | categories: Software


Samples from my Ph.D. coursework can be accessed at the links below.
CSE 167: Computer Graphics I (Schulze)
CSE 169: Computer Animation--audit (Rotenberg)
CSE 274: Discrete Differential Geometry (Chern)
MUS 270A: Digital Audio Processing (Smyth)
MUS 270B: Analysis of Musical Sound (Puckette)
MUS 270D: Advanced Projects in Computer Music (Puckette)
MUS 206: Computational Acoustic Modeling (Smyth)
Samples from my Ph.D. coursework can be accessed at the links below.
CSE 167: Computer Graphics I
CSE 169: Computer Animation--audit
CSE 274: Discrete Differential Geometry
MUS 270A: Digital Audio Processing
MUS 270B: Analysis of Musical Sound
MUS 270D: Projects in Comp. Music
MUS 206: Comp. Acoustic Modeling


Basic Layouts Example
Basic Layouts shows how to use the standard layout managers that are available in Qt: QBoxLayout, QGridLayout, and QFormLayout.
The QBoxLayout class lines up widgets horizontally or vertically. QHBoxLayout and QVBoxLayout are convenience subclasses of QBoxLayout. QGridLayout lays out widgets in cells by dividing the available space into rows and columns. QFormLayout, on the other hand, sets its children in a two-column form with labels in the left column and input fields in the right column.
For more information, visit the Layout Management page.
Running the Example
To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.
Dialog Class Definition
class Dialog : public QDialog { Q_OBJECT public: Dialog(); private: void createMenu(); void createHorizontalGroupBox(); void createGridGroupBox(); void createFormGroupBox(); enum { NumGridRows = 3, NumButtons = 4 }; QMenuBar *menuBar; QGroupBox *horizontalGroupBox; QGroupBox *gridGroupBox; QGroupBox *formGroupBox; QTextEdit *smallEditor; QTextEdit *bigEditor; QLabel *labels[NumGridRows]; QLineEdit *lineEdits[NumGridRows]; QPushButton *buttons[NumButtons]; QDialogButtonBox *buttonBox; QMenu *fileMenu; QAction *exitAction; };
The Dialog
class inherits QDialog. It is a custom widget that displays its child widgets using the geometry managers: QHBoxLayout, QVBoxLayout, QGridLayout, and QFormLayout.
There are four private functions to simplify the class constructor: the createMenu()
, createHorizontalGroupBox()
, createGridGroupBox()
, and createFormGroupBox()
functions create several widgets that the example uses to demonstrate how the layout affects their appearances.
Dialog Class Implementation
Dialog::Dialog()
{
createMenu();
createHorizontalGroupBox();
createGridGroupBox();
createFormGroupBox();
In the constructor, we first use the createMenu()
function to create and populate a menu bar and the createHorizontalGroupBox()
function to create a group box containing four buttons with a horizontal layout. Next we use the createGridGroupBox()
function to create a group box containing several line edits and a small text editor which are displayed in a grid layout. Finally, we use the createFormGroupBox()
function to create a group box with three labels and three input fields: a line edit, a combo box and a spin box.
bigEditor = new QTextEdit; bigEditor->setPlainText(tr("This widget takes up all the remaining space " "in the top-level layout.")); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
We also create a big text editor and a dialog button box. The QDialogButtonBox class is a widget that presents buttons in a layout that is appropriate to the current widget style. The preferred buttons can be specified as arguments to the constructor, using the QDialogButtonBox::StandardButtons enum.
Note that we don't have to specify a parent for the widgets when we create them. The reason is that all the widgets we create here will be added to a layout, and when we add a widget to a layout, it is automatically reparented to the widget the layout is installed on.
QVBoxLayout *mainLayout = new QVBoxLayout;
The main layout is a QVBoxLayout object. QVBoxLayout is a convenience class for a box layout with vertical orientation.
In general, the QBoxLayout class takes the space it gets (from its parent layout or from the parent widget), divides it up into a series of boxes, and makes each managed widget fill one box. If the QBoxLayout's orientation is Qt::Horizontal the boxes are placed in a row. If the orientation is Qt::Vertical, the boxes are placed in a column. The corresponding convenience classes are QHBoxLayout and QVBoxLayout, respectively.
mainLayout->setMenuBar(menuBar);
When we call the QLayout::setMenuBar() function, the layout places the provided menu bar at the top of the parent widget, and outside the widget's content margins. All child widgets are placed below the bottom edge of the menu bar.
mainLayout->addWidget(horizontalGroupBox); mainLayout->addWidget(gridGroupBox); mainLayout->addWidget(formGroupBox); mainLayout->addWidget(bigEditor); mainLayout->addWidget(buttonBox);
We use the QBoxLayout::addWidget() function to add the widgets to the end of layout. Each widget will get at least its minimum size and at most its maximum size. It is possible to specify a stretch factor in the addWidget() function, and any excess space is shared according to these stretch factors. If not specified, a widget's stretch factor is 0.
setLayout(mainLayout);
setWindowTitle(tr("Basic Layouts"));
}
We install the main layout on the Dialog
widget using the QWidget::setLayout() function, and all of the layout's widgets are automatically reparented to be children of the Dialog
widget.
void Dialog::createMenu() { menuBar = new QMenuBar; fileMenu = new QMenu(tr("&File"), this); exitAction = fileMenu->addAction(tr("E&xit")); menuBar->addMenu(fileMenu); connect(exitAction, SIGNAL(triggered()), this, SLOT(accept())); }
In the private createMenu()
function we create a menu bar, and add a pull-down File menu containing an Exit option.
void Dialog::createHorizontalGroupBox() { horizontalGroupBox = new QGroupBox(tr("Horizontal layout")); QHBoxLayout *layout = new QHBoxLayout; for (int i = 0; i < NumButtons; ++i) { buttons[i] = new QPushButton(tr("Button %1").arg(i + 1)); layout->addWidget(buttons[i]); } horizontalGroupBox->setLayout(layout); }
When we create the horizontal group box, we use a QHBoxLayout as the internal layout. We create the buttons we want to put in the group box, add them to the layout and install the layout on the group box.
void Dialog::createGridGroupBox() { gridGroupBox = new QGroupBox(tr("Grid layout"));
In the createGridGroupBox()
function we use a QGridLayout which lays out widgets in a grid. It takes the space made available to it (by its parent layout or by the parent widget), divides it up into rows and columns, and puts each widget it manages into the correct cell.
for (int i = 0; i < NumGridRows; ++i) { labels[i] = new QLabel(tr("Line %1:").arg(i + 1)); lineEdits[i] = new QLineEdit; layout->addWidget(labels[i], i + 1, 0); layout->addWidget(lineEdits[i], i + 1, 1); }
For each row in the grid we create a label and an associated line edit, and add them to the layout. The QGridLayout::addWidget() function differ from the corresponding function in QBoxLayout: It needs the row and column specifying the grid cell to put the widget in.
smallEditor = new QTextEdit; smallEditor->setPlainText(tr("This widget takes up about two thirds of the " "grid layout.")); layout->addWidget(smallEditor, 0, 2, 4, 1);
QGridLayout::addWidget() can in addition take arguments specifying the number of rows and columns the cell will be spanning. In this example, we create a small editor which spans three rows and one column.
For both the QBoxLayout::addWidget() and QGridLayout::addWidget() functions it is also possible to add a last argument specifying the widget's alignment. By default it fills the whole cell. But we could, for example, align a widget with the right edge by specifying the alignment to be Qt::AlignRight.
layout->setColumnStretch(1, 10); layout->setColumnStretch(2, 20); gridGroupBox->setLayout(layout); }
Each column in a grid layout has a stretch factor. The stretch factor is set using QGridLayout::setColumnStretch() and determines how much of the available space the column will get over and above its necessary minimum.
In this example, we set the stretch factors for columns 1 and 2. The stretch factor is relative to the other columns in this grid; columns with a higher stretch factor take more of the available space. So column 2 in our grid layout will get more of the available space than column 1, and column 0 will not grow at all since its stretch factor is 0 (the default).
Columns and rows behave identically; there is an equivalent stretch factor for rows, as well as a QGridLayout::setRowStretch() function.
void Dialog::createFormGroupBox() { formGroupBox = new QGroupBox(tr("Form layout")); QFormLayout *layout = new QFormLayout; layout->addRow(new QLabel(tr("Line 1:")), new QLineEdit); layout->addRow(new QLabel(tr("Line 2, long text:")), new QComboBox); layout->addRow(new QLabel(tr("Line 3:")), new QSpinBox); formGroupBox->setLayout(layout); }
In the createFormGroupBox()
function, we use a QFormLayout to neatly arrange objects into two columns - name and field. There are three QLabel objects for names with three corresponding input widgets as fields: a QLineEdit, a QComboBox and a QSpinBox. Unlike QBoxLayout::addWidget() and QGridLayout::addWidget(), we use QFormLayout::addRow() to add widgets to the layout.
Files:


Welcome to One Minute Impress. You'll have only 60 minutes to Impress someone and that can make you or Break you. Say something meaningful.
by One Minute Impress Organisation


Some selected previews of the examples (shown in detail further down in this document), to give you a first idea of the plugin's customizability:
25 % 30 % 40 % 45 % 55 % 65 % 100 %
1:55
80
%
20
sec.
The following examples each show a JavaScript line of the kind:
$(selector).progressPie(options)
or alternatively:
$(selector).setupProgressPie(options).progressPie()
selector
is a jQuery selector string, depending on the actual HTML document. In these cases it's a selector
specific to this actual HTML page, selecting the span
elements corresponding to the respective example.options
portion of each example.
…

$(".pp.default").progressPie();
applied to elements like:
100 %
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
$(".pp.dyncolor").progressPie({mode:$.fn.progressPie.Mode.COLOR});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
$(".pp.green").progressPie({mode:$.fn.progressPie.Mode.GREEN});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
$(".pp.red").progressPie({mode:$.fn.progressPie.Mode.RED});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Add the ringWidth
option in order to draw a ring segment instead of a pie.
It's recommended to combine this with either a very small strokeWidth
(defining the
stroke of the full surrounding circle)…
$(".pr.dyncolor").progressPie({mode:$.fn.progressPie.Mode.COLOR, strokeWidth: 1, ringWidth: 3});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
… or with a strokeWidth
identical to the ringWidth
but setting a different,
constant color for the outer circle's stroke via the strokeColor
option:
$(".pr.onsilver").progressPie({mode:$.fn.progressPie.Mode.COLOR, ringWidth: 4, strokeWidth: 4, strokeColor: '#ddd'});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
$(".pp.navy").progressPie({color:"navy"});
$(".pp.yellow").progressPie({color:"#eee000"});
$(".pp.rgb").progressPie({color:"rgb(0,150,100)"});
$(".pp.rgba").progressPie({color:"rgba(0,150,100,0.6)"});
The following pies are each assigned one of the four classes ‘navy’, ‘yellow’, ‘rgb’, or ‘rgba’.
The option set is always the same (color
), the difference (beside from the color itself)
is the color notation syntax. The ‘rgba’ example demonstrates an alpha channel, setting a transparancy
level for the pie graph.
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Notice that the foreground pie overlaps the background circle.
In this case, using an alpha channel, i.e. semi-transparent strokes, this causes the
opacity of the doubly covered area to increase, the background circle remains visible behind
the foreground. If you don't want that, set the overlap
option (see below)
to false
:
$(".pp.rgbanooverlap").progressPie({color:"rgba(0,150,100,0.6)", overlap: false});
$(".pr.rgbanooverlap").progressPie({color:"rgba(0,150,100,0.6)", overlap: false, ringWidth: 3});
25 % 50 % 65 % 80 % 99 % 100 %
$(".pp.myfunc").progressPie({color:function(percent) {
return percent >= 50 ? "#3f3" : "#f33";
}});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Example 2: Function referencefunction blueGt25(percent) {
var blue = percent < 25 ? 0 : (percent-25)*3; //range from 0 to 3*75 = 225 ( = max brightness for value of 100%)
return "rgb(0,0," + blue + ")";
}
…
$(function(){
$(".pp.myblue").progressPie({color:blueGt25});
});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Example 3: A function (referenced) re-using/adapting the internal functioncolorByPercent
function colorGt50(percent) {
var p = percent <= 50 ? 0 : 2 * (percent - 50);
return $.fn.progressPie.colorByPercent(p);
}
…
$(function(){
$(".pp.gt50").progressPie({color:colorGt50});
});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Starting with V2.0.0, the ProgressPie plug-in adds class attributes to the SVG components, enabling you to define some global CSS rules to globally format some of the pie diagram's design features instead of programmatically configuring everything via plug-in options alone: The default classnames are:
progresspie-background
for the background circle andprogresspie-foreground
for the pie or ring drawn on top of the latter.progresspie-foreground
class, but
they get (both) fitted with a second class:
progresspie-outer
for the outer andprogresspie-inner
for the inner pie or ring..progresspie-foreground{...}
will apply to all foregrounds
of single pies as well as for inner and outer displays of double pies, while a selector
like .progresspie-foreground.progresspie-inner
explicitly selects all inner
parts of double pies.
progresspie-outer
,inner
) still gets marked by class progresspie-inner
,inner.inner
) gets marked by class progresspie-inner2
,inner.inner.inner
) gets marked by class progresspie-inner3
.Assume a regular plug-in call (programatically defining the colors, in this case using the predefined COLOR mode):
$(".pr.css1").progressPie({mode:$.fn.progressPie.Mode.COLOR, ringWidth: 4, strokeWidth: 4});
This example alone doesn't make any sense, since a ring segment is drawn on top of a full circle with the same
width and the same color, i.e. the ring segment will be effectively invisible.
But we can use CSS to override style properties of the chart. Here, we firstly set a stroke-dasharray
for
a dashed background circle and secondly we override the background circle's color (like we could have done locally
programatically via the strokeColor
option, see above):
.pr.css1 .progresspie-background {
stroke-dasharray: 1px 3px;
stroke: #bbb !important;
}
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Keep in mind that local styles (i.e. style
attributes inside the SVG markup generated by the plug-in)
have priority over global styles in a CSS ruleset. Since the plug-in locally sets a color via stroke
property in a style
attribute, we need to add the !important
directive in order to override
that local stroke style.
If you really want to style the color (and alignment) of your diagrams (statically) via CSS rules, you can tell the
progressPie plug-in to completely omit color and vertical-align definitions in the local styles (thus removing the need
for !important
directives).
This is done by setting the mode
option to the fourth predefined color mode:
$.fn.progressPie.Mode.CSS
.
Be aware, however, that in CSS mode…
stroke
and fill
properties for the background and the stroke
property for the foreground. (The foreground's fill
property is still set locally to none
by the ProgressPie plug-in even in CSS mode, since filling of a ring segment doesn't make sense),vertical-align
style of the SVG node, at least if the
target element is not empty, but the SVG is appended or prepended to text content: The first of the following
examples omits the vertical-align
rule with the result, as you can see, that the pies and the percent
labels are not nicely aligned. (In any other mode, this vertical alignment is set via the plug-in's
verticalAlign
option, defaulting to bottom
,
but in CSS mode, that option is ignored.$(".pp.css1").progressPie({mode:$.fn.progressPie.Mode.CSS});
.pp.css1 .progresspie-background {
stroke-dasharray: 3px 1px;
stroke: #bbb;
fill: none;
}
.pp.css1 .progresspie-foreground {
stroke: rgba(0, 100, 150, 0.5);
}
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
$(".pr.css2").progressPie({mode:$.fn.progressPie.Mode.CSS, ringWidth: 3, strokeWidth: 3});
.pr.css2 svg {
vertical-align: bottom;
}
.pr.css2 .progresspie-background {
stroke: rgb(200, 200, 200);
fill: rgba(200, 200, 200, 0.2);
}
.pr.css2 .progresspie-foreground {
stroke: rgb(0, 200, 0);
}
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
For an example if CSS formatting double pie/ring charts see the corresponding section.
The default classes progresspie-foreground
etc. have intentionally been
prefixed with "progresspie-" in order to avoid name conflicts with other CSS code.
You may globally change these defaults by overwriting the properties
jQuery.fn.progressPie.defaults.cssClassBackgroundCircle
,
jQuery.fn.progressPie.defaults.cssClassForegroundPie
,
jQuery.fn.progressPie.defaults.cssClassOuter
and jQuery.fn.progressPie.defaults.cssClassInner
.
Or you may locally set other classnames by adding the options cssClassBackgroundCircle
,
cssClassForegroundPie
, cssClassOuter
or cssClassInner
to the options argument of your plug-in call.
The following example will demonstrate this (omitting the cssClassOuter
and cssClassInner
options, since they are only used with double pies):
$(".pp.css2").progressPie({
mode: $.fn.progressPie.Mode.CSS,
strokeWidth: 0,
cssClassBackgroundCircle: "piebg",
cssClassForegroundPie: "piefg"
});
.pp.css2 svg {
vertical-align: middle;
}
.pp.css2 .piebg {
stroke-dasharray: 1,4;
fill: silver;
}
.pp.css2 .piefg {
stroke: navy
}
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
The following example shows alternating pies with and without the option verticalAlign:"middle"
(default is "bottom")
in a line with a line-height greater than the font-size (so that alignment makes a difference).
$(".pp.vc").progressPie({verticalAlign:"middle"});
applied to elements like:
0
5
…
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
By default, the pie gets prepended to the selected element's content. You may opt to appending it behind the content instead:
$(".pp.append").progressPie({prepend: false, separator: "%: "});
applied to elements like:
5
0 5 25 42 50 65 80 99 100
Just like to HTML elements you may add a title string to a chart. Most desktop browsers will display the title as popup when the mouse cursor rest on the image for some time:
$(".pp.withTitle").progressPie({globalTitle: "Demo pie chart"});
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
The globalTitle
option adds a title to the whole (rectangular) image. Alternatively, you might use the
title
option which adds a title to the chart itself. In this case, in a desktop browser, the popup title
will only show if the mouse cursor rests on the chart's foreground, not on the transparent background regions.
You may also combine both options, in which case the title
shows on the chart's foreground, while the
globalTitle
shows on the rest of the image (i.e. the background).
The title
option is mainly intended for use on diagrams with more than one value, so each value
can get its individual title. This will be demonstrated further below in examples with three rings.
By default, the percent value to display has to be the (only) content of the selected element and the SVG graphic
gets prepended (or optionally appended) to that value (see above). But you may also provide the percent value
invisibly in an attribute of the HTML element, usually (conforming to HTML) in a data-*
attribute.
The element's content may be left empty. (If not empty, the graphic still gets prepended or appended to that content.)
The following examples demonstrate a data-attribute on an empty span element, using the valueData
option.
Also, they demonstrate different uses of the strokeWidth
and strokeColor
options which
configure the look of the surrounding circle.
And a third aspect also demonstrated is the size
option: If you don't set a size
option,
the pies will be auto-sized. If the DOM node the SVG gets prepended or appended to also contains text, the pie is auto-sized to the line-height. If the pie is be be inserted into a text-less node, this does not work and the plug-in simply chooses a
default width. By setting the size
option, you may explicitly set a size (diameter) in pixels and thus
turn off auto-sizing.
$(".pp.attr:not(.silverborder):not(.navyborder):not(.noborder):not(.ring):not(.filledCircle)").progressPie({
mode:$.fn.progressPie.Mode.COLOR, valueData:"val", size:30, strokeWidth: 5});
applied to elements like:

$(".pp.attr.silverborder").progressPie({mode:$.fn.progressPie.Mode.COLOR, valueData:"val", size:30, strokeWidth: 5, strokeColor: "silver"});
strokeWidth
may also be zero:
$(".pp.attr.noborder").progressPie({mode:$.fn.progressPie.Mode.COLOR, valueData:"val", size:50, strokeWidth: 0});
If you set the strokeWidth
to the radius, i.e. half the diameter, defined by size
,
the whole background circle would effectively be filled by the background circle's strokeColor
.
This has once been recommended, but by now there are better ways for filling the background circle:
backgroundColor
.
Just like the color
option this may alternatively hold a string describing a color
or a function mapping a percent value to a color string.$(".pp.attr.filledCircle").progressPie({
mode:$.fn.progressPie.Mode.COLOR,
valueData:"val",
size:50,
strokeWidth: 0,
backgroundColor: "#ddd"
});
strokeWidth
1 combined with a ringWidth
and use of valueAttr
instead of valueData
and demonstration of a backgroundColor
function:
$(".pp.attr.ring").progressPie({
mode:$.fn.progressPie.Mode.COLOR,
valueAttr:"data-val",
size:30,
strokeWidth: 1,
ringWidth: 5,
backgroundColor: function(percent) {
return $.fn.progressPie.colorByPercent(percent, 0.1);
}
});
The backgroundColor
function in the example above uses the same color function as is used for the foreground color
in COLOR
mode, only adding an optional second parameter which specifies an alpha channel,
in this case set to 10% opacity (90% transparency).
Consider you're designing an HTML form for a user to fill, you might want to add a pie or ring chart
visualizing the value of an input (like a text input or a select box).
In that case, simply use the valueInput
option:
$("#inputSelect span").setupProgressPie({
valueInput: $("#inputSelect select"),
animate: true
}).progressPie();
applied to

<select>
<option value="0">—</option>
<option value="20">20 %</option>
<option value="40">40 %</option>
<option value="60">60 %</option>
<option value="80">80 %</option>
<option value="100">100 %</option>
</select>

If the input is a simple text input, you might want to add the optionsByRawValue
option in order to define a function showing an error icon (see separate
content plug-in examples page) if an invalid
value has been entered which cannot be visualized as percent value:
$("#inputText span").setupProgressPie({
valueInput: "#inputText input",
optionsByRawValue: raw => {
let i = parseInt(raw);
return !isNaN(i) && i >= 0 && i <= 100 ? null : {
contentPlugin: "cross",
globalTitle: "Illegal input, enter number in [0..100]!"
};
}
}).progressPie();

<input type="text" value="50" size="3">

The default event on which a chart update is triggered is the change
event.
On a text input, change
is fired only after defocussing the input element.
You may change that by specifying all events which should trigger an update in the
valueInputEvents
option.
The next example demonstrates this valueInputEvents
option by using a
jQuery-UI-Spinner element and adding support for the spinstop
event (besides the
default change
event) which is triggered when the user clicks on one of the
spin buttons for increasing or decreasing the value.
(Unlike the 'spin' event, the 'spinstop' event only triggers after the
spin has completed, i.e. when the value has been increased or decreased)
$("#inputSpinner span").setupProgressPie({
valueInput: $("#inputSpinner input").spinner({
min: 0,
max: 100
}),
valueInputEvents: "spinstop change",
verticalAlign: "middle",
size: 25
}).progressPie();

<input type="text" value="50" size="3">

Notes:
valueInput
option can only be used in setupProgressPie()
!
If you call the progressPie({…})
function directly with an options object (used only once and
not stored for updates), this option will be ignored!setupProgressPie()
function, in this case, not only stores a configuration
for the pie chart, but also registers an event handler on the specified input element
(for the events specified by valueInputEvents
). If you should change the setup
later (by calling setupProgressPie()
again), this event handler will not
be deregistered again automatically!Besides valueAttr
, valueData
and valueInput
there's
a fourth option for defining where to look up the raw value to be displayed: valueSelector
.
This is covered later on in this document.
If you set a different stroke color for the outer circle (see example pp.attr.silverborder
above)
or choose a semi-transparent foreground color, you'll notice that the foreground (pie or ring) has the same
radius as the background circle and thus overlaps it. You may optionally turn off that overlapping, so
that the radius of the pie or ring is the total radius of the graphic minus the outer circle's strokeWidth
.
To do so, set the overlap
option to false
(it defaults to true
).
$(".pp.attr.navyborder").progressPie({
mode:$.fn.progressPie.Mode.COLOR,
valueData:"val",
size:30,
strokeWidth: 2,
strokeColor: "navy",
overlap: false
});
See above for another example (using user-defined color with alpha channel)
In the previous examples showing a ring on a background circle,
we've either set ringWidth
to the same value as strokeWidth
(in which case
the ring's color has to differ from the background circle's!) or the ringWidth
has been larger
than the background circle's strokeWidth
and both strokes were aligned with their outer
sides.
You may use the ringAlign
option to change that alignment. This is only applicable,
if background circle and foreground ring are both visible (ringWidth > 0 && strokeWidth > 0
).
The meaning of the ringAlign
option depends on whether the overlap
option (see above) is true or false.
By default, overlap
is true. In this case, you can choose between three alignments
(which only make a difference if the ringWidth
is either smaller or larger than the strokeWidth
):
The default option is OUTER alignment (which has already been used in previous examples), but alternatively you may also
CENTER the smaller of the two strokes within the wider one or align both strokes on their INNER sides.
If overlap
is set to false (see above), the ringAlign
option
changes its meaning a bit: With default alignment (OUTER), the ring's OUTER side is aligned with the background circle, but
since both are not overlapping, the ring's outer side is aligned with the background circle's inner edge. Effectively
this means the ring is drawn inside of the background circle.
With the alternative INNER alignment, the ring's inner side is aligned with the outer side of the background circle.
To achieve that, the background circle is shrunk to fit inside the ring, which is effectively drawn around the background
circle on its outside. (A CENTER alignment is not defined with overlap
turned off.)
All combinations of the overlap
option and the ringAlign
option are
demonstrated in the table below. For each combination of these two options, three sets of examples
are shown, one with ringWidth < strokeWidth
, one with ringWidth === strokeWidth
and one with ringWidth > strokeWidth
.
The following code sets up all these example combinations by iterated use of the
setupProgressPie()
function: The first call sets up basic properties for all examples,
the following setups add more options to the specific examples. (Since overlap
defaults
to true
we only explicitly set the option to false in the respective examples,
and similarly, since the ringAlign
option defaults to $.fn.progressPie.RingAlign.OUTER
,
we only explicitly set that option in the examples with other alignment values.)
$(".pr.align").setupProgressPie({
mode: $.fn.progressPie.Mode.COLOR,
valueData: "val",
size: 40,
strokeColor: "#ddd",
scale: 2
});
$(".pr.align.thinner").setupProgressPie({
ringWidth: 2,
strokeWidth: 6
});
$(".pr.align.thicker").setupProgressPie({
ringWidth: 6,
strokeWidth: 2
});
$(".pr.align.same").setupProgressPie({
ringWidth: 3,
strokeWidth: 3
});
$(".pr.align.center").setupProgressPie({
ringAlign: $.fn.progressPie.RingAlign.CENTER
});
$(".pr.align.inner").setupProgressPie({
ringAlign: $.fn.progressPie.RingAlign.INNER
});
$(".pr.align.nooverlap").setupProgressPie({
overlap: false
});
$(".pr.align").progressPie();
(Hover your mouse cursor above an image in the table to see the assigned classes.)
ringAlign |
overlap: true |
overlap: false |
|---|---|---|
…OUTER (default) |
|
|
…CENTER |
|
not a valid |
…INNER |
|
|
Strokes in an SVG graphic may be dotted or dashed. The CSS section already demonstrated
the use of the stroke-dasharray
CSS property. That way you may define simple or complex
dash patterns, but you can only define the length of dashes and gaps in pixels. If you want the dashes
to be placed at certain points of the circle (e.g. draw a clock face consisting of 12 short dashes placed),
you would need to know the exact length of the circle stroke in pixels in order to calculate the correct
dasharray!
This is why the ProgressPie plug-in now also offers an option to calculate a dasharray dynamically based on the calculated circle size:
The simplest way is to just specify into how many dashes the chart's background circle should be split:
$(".pp.regulardashes").progressPie({
valueData:"val",
size:30,
strokeWidth: 2,
strokeDashes: 6
});
In this case, the dashes and the gaps between them are equally long. You may change this by additionally setting a stroke length in pixels or in percent (of the stroke's length / circumference). Only percentual settings are really independent of the pie chart's size, since they define the length in relation to the circumference. Let's demonstrate this by drawing pies of different sizes:
$(".pp.percentdashes").progressPie({
strokeWidth: 2,
strokeDashes: {
count: 8,
length: '10%'
}
});
0 5 25 33 45 50 70 80 100
To specify the length in percent, you have to
set the strokeDashes.length
option to a string literal containing a number followed
by the percent sign (see above). A single number literal would be interpreted as an abloute length in pixels!
Absolute length should only be used with manually sized pies, and you have to ensure that
strokeDashes.length * strokeDashes.count
is less than the circumference (circle stroke's length
in pixels), since the circle stroke can not be divided into parts that are (in sum) greater then the whole circle.
Also note that by default the dashes start the 12 o'clock (or 0 percent) position.
You may set the strokeDashes.centered
option to true
(it defaults to false
) in order to center the first stroke around the
12 o'clock position:
$(".pp.centereddashes").progressPie({
valueData:"val",
size:30,
strokeWidth: 2,
strokeDashes: {
count: 8,
length: '10%',
centered: true
}
});
$(".pp.clockface").progressPie({
mode: $.fn.progressPie.Mode.COLOR,
strokeColor: 'silver',
valueData: "val",
size: 30,
strokeWidth: 4,
strokeDashes: {
count: 12,
length: 2,
centered: true
}
});
You may set the strokeDashes.inverted
option to true
in order to
invert the pattern, i.e. the options count
and length
then refer to the transparent gaps, devided by (auto-adjusted) visible dashes between them:
$(".pp.clockfaceInverted").progressPie({
mode: $.fn.progressPie.Mode.COLOR,
strokeColor: 'silver',
valueData: "val",
size: 30,
strokeWidth: 4,
strokeDashes: {
count: 12,
length: 2,
centered: true,
inverted: true
}
});
sizeFactor
vs. scale
Like said above, setting the size
options disables auto-sizing and sets a new absolute size.
But you may also adjust auto-sizing by specifying the sizeFactor
option: The calculated
size (either auto-size or value of the size
option) is multiplied with this factor to get the
final size for the graphic.
The following example sets the size to 50% larger than the default size, and to emphasize the effect of relative sizing (in this case relative to font-size), each number is styled with a different font size:
$(".pp.sizefactor").progressPie({sizeFactor:1.5, verticalAlign:"middle"});
0 5 25 42 50 65 80 99 100
The sizeFactor
option is only applied to the default size (auto-size or size
) option
to calculate the diameter of the pie to draw. It has no effect on other sizes like the strokeWidth
or ringWith
options. So in the example above, the pies get larger while the strokeWidth
of the circle stays the same as it were without the sizeFactor
option.
There is a second option pretty similar, but which differs in this effect: The scale
option defines
a factor by which to resize the otherwise already rendered SVG graphic, thus resizing every aspect of the graphic
including the strokeWidth
. So if, in the example above, we replace sizeFactor
by scale
,
not only the diameter of the pies increases by 50%, but also the with of the outer circle does:
$(".pp.scale").progressPie({scale:1.5, verticalAlign:"middle"});
0 5 25 42 50 65 80 99 100
In this example of a simple pie chart, the difference between these two options is “marginal”, in more complex eamples the differences may get extremer.
If you should use “double pies” (demonstrated below), please note: Both options are “global” for outer
and inner pie and may only be set in the main options object, not inside the inner
option.
A sizeFactor
is individually applied to both graphics, outer and inner pie or ring, the
gap between an outer ring and an inner ring or pie will grow when increasing the sizeFactor
,
while scaling with the scale
option will scale outer and inner ring/pie as well the gap
between them proportionally, i.e. simply scale the whole graphic.
If the value should still be visible, but is not the only content of the selected element, but the content of some sub-element, you may provide a jQuery selector specifying the sub-element holding the value:
$(".pp.subelem").progressPie({mode:$.fn.progressPie.Mode.COLOR, valueSelector:".valueSub"});
applied to elements like:
(0 %)
(0 %) (5 %) (25 %) (42 %) (50 %) (65 %) (80 %) (99 %) (100 %)
Note: In the first examples, the “%” label was simply placed outside (behind) the span element holding
the value and (later) the SVG. But in some cases (e.g. for CSS styling reasons) you might prefer to bundle
static text labels and the value inside the same element and simply mark the value by wrapping it into a sub-element
like shown here.
The following example shows a special use-case for the valueSelector:
The following example demonstrates an application of an inner element holding the actual percent value inside the selected element. The main reason for this construct lies in the goal to display the element's content inside the ring.
Also this example demonstrates the ringEndsRounded
option.
You have two possibilities to show content inside a ring graph.
This example demonstrates the first one: Use the progressPie plug-in to simply render a ring graph
without content as SVG image and then place some HTML container with the content on top of the
image via CSS rules like those shown below.
An alternative is to use a content plug-in to render the ring content as part of the
generated SVG image. See separate content plug-in examples page
for a demonstration.
Advantages of the first alternative (HTML content on top of SVG) are easier styling of the content and that even older browsers not capable of rendering SVG images will still show the content, only the ring graph around the value would be missing.
Call of the plugin (the sequence of setupProgressPie() and progressPie() simplifies updates, see below):
$(".pr.around.percent").setupProgressPie({
size: 70,
ringWidth: 7,
strokeWidth: 0,
ringEndsRounded: true,
valueSelector: "span.value",
color: "navy",
separator: ""
}).progressPie();
and CSS:
.pr.around {
position: relative;
display: inline-block;
margin: 1em;
}
.pr.around span {
color: navy;
}
.pr.around span.outer {
position: absolute;
left: 0;
top: 0;
width: 70px;
text-align: center;
font-size: 10px;
padding: 15px 0;
}
.pr.around span.value {
font-size: 25px;
}
applied to elements like:
0
%
0
%
5
%
25
%
42
%
50
%
65
%
80
%
99
%
100
%
Note the option separator: ""
which avoids inserting a blank space text node between the inserted
SVG and the span.outer
.
A variation (using the same CSS code) for a one-minute countdown, with inverted dynamic color, a strokeColor
(see above) and a valueAdapter
for interpreting seconds (see below):
$(".pr.around.countdown").setupProgressPie({
size: 70,
ringWidth: 5,
strokeWidth: 5,
strokeColor: "#ddd",
strokeDashes: {
count: 12,
length: 2,
centered: true,
inverted: true
},
valueSelector: "span.value",
valueAdapter: function(s) {return parseInt(s)*10/6;},
color: function(p) {return $.fn.progressPie.colorByPercent(100-p);},
separator: ""
}).progressPie();
60
sec.
50
sec.
40
sec.
30
sec.
20
sec.
12
sec.
8
sec.
2
sec.
0
sec.
Note: Of course, you can use CSS code like in the above example not only to place the value inside the ring. You might also keep the label invisible (providing the value in a data attribute, see above) and put some other graphic (SVG or other format) over/inside the ring, e.g. a pause or stop icon clickable to halt or abort the running process whose value is displayed by the ring.
The following examples demonstrate dynamic updating of pies or rings, using the setupProgressPie
plug-in function
which was introduced in V1.3.0: This function once sets up the options and stores them in each selected DOM node. For updating,
now all that's needed is to update the value and call the progressPie()
again, which then re-uses the same
options on each update.
The setup is usually only done once, but the #timerbutton
example below also demonstrates, that the setup may
be updated as well: In this case, the content plugin is updated to change the displayed control icon depending on the timer's state.
One of the following examples also demonstrates updating without the setupProgressPie()
method.
In this case, all the options have to be initialized again for each redraw, and the update
option
has to be set in order to enable replacing an existent graphic (using the setupProgressPie() method, the update
option automatically defaults to true).

…

…

…

0 %

0
%

60
sec.

<button onclick="startStopTimer()" id="timerbutton" data-val="0">Click me!</button>

…

0 %
0
%
60
sec.
The control icons in the button above are a demonstration of the SVG content plug-in mechanism: You may provide special plugin modules which render additional SVG content inside a ring graph. These control plug-ins require the inclusion of an additional JavaScript file:

See separate example page for more on content plug-ins!
The following example takes a value in minutes instead of percent as a value. The valueAdapter converts any positive number of minutes into percent of an hour.
ValueAdapter functions may be used regardless of the value source: Whether the values are read from the element content (default) or from an attribute makes no difference.
$(".pp.minutes").progressPie({valueAdapter: function(valueStr) {
return parseInt(valueStr) * 10 / 6;
}})
0 5 15 20 30 35 40 45 60 80
Taking the previous example further: The progresspie component may also display two values overlapping. This might, for example, be used to depict a countdown in hours and minutes (or minutes and seconds). The following examples both show the hours-and-minutes example in different variations. In analogy to analog watch faces, the smaller, inner pie shall display an hour value (e.g. hours of a countdown still remaining) and the outer, larger pie the minutes-of-the-hour-value.
Say, you have a span element containting a time value of format "hours:minutes" like
6:15
You might now prepend a double pie chart als follows:
$(".pp.hmv").progressPie({
mode:$.fn.progressPie.Mode.GREY,
valueAdapter: function(str) {
var m = /\d+:(\d{1,2})/.exec(str)[1];
return parseInt(m) * 10 / 6;
},
inner: {
mode:$.fn.progressPie.Mode.RED,
valueAdapter: function(str) {
var hm = /(\d+):(\d{1,2})/.exec(str);
var m = 60*parseInt(hm[1]) + parseInt(hm[2]); //hours converted to minutes plus minutes of hour = total minutes
//100% = 12 hours = 720 minutes
return m * 100 / 720;
}
}
});
0:00 0:15 1:00 1:55 2:45 6:15 07:58 12:15
Note that this example uses one single value string (here: the span's content, but of course
you might also use an attribute value, see above), but two valueAdapter
functions in
order to derive two different percent values from one string value. Here, this is done
using regular expressions.
Of course, if you happen to have two separate number values “at hand” you may insert these into two separate data-attributes of the HTML element and use these directly (if they happen to be percent values) or mapped via valueAdapter functions. This example uses separate attributes for hours and minutes:
6:15
Also, this example demonstrates manual sizing of both the outer and the inner pie.
$(".pp.hma:not(.ring):not(.rings)").progressPie({
mode:$.fn.progressPie.Mode.RED,
valueData: "minutes",
valueAdapter: function(mins) { return mins * 10 / 6; },
size: 30,
inner: {
color: "navy",
valueData: "hours",
valueAdapter: function(hours) { return hours * 100 / 12; },
size: 20
},
verticalAlign: "middle",
strokeWidth: 1
});
0:00 0:15 1:00 1:55 2:45 6:15 7:60 12:15
Note however, that in this example the inner hour-pie is independent of the minutes pie: for the time "1:55" it fills exactly the same area (1/12) as for "1:00". The example above, on the other hand, takes advantage of the fact that the valueAdapter for the hour value also has access to the minutes value and displays for "1:55" a pie nearly as large as for "2:00" (1/6).
As for single graphics, you may also set the ringWidth option for the outer and/or the inner pie. This example is simply a modification of the example above, displaying the minutes value as a ring instead of a full pie. The ringWidth is less than the difference between inner and outer radius, so that the inner pie (hour value) does fit into the ring without overlap. (Outer size is 30, inner size is 20, i.e. difference in diameter is 10, difference in radius is 5; ringWidth is set to 4, which is smaller than 5, which leaves a 1 pixel gap between inner pie and outer ring.)
$(".pp.hma.ring").progressPie({
mode:$.fn.progressPie.Mode.RED,
valueData: "minutes",
valueAdapter: function(mins) { return mins * 10 / 6; },
size: 30,
inner: {
color: "navy",
valueData: "hours",
valueAdapter: function(hours) { return hours * 100 / 12; },
size: 20
},
verticalAlign: "middle",
strokeWidth: 1,
ringWidth: 4
});
0:00 0:15 1:00 1:55 2:45 6:15 7:60 12:15
Of course, you may also apply the ringWidth option to the inner pie:
$(".pp.hma.rings").progressPie({
mode:$.fn.progressPie.Mode.RED,
valueData: "minutes",
valueAdapter: function(mins) { return mins * 10 / 6; },
size: 30,
inner: {
color: "navy",
valueData: "hours",
valueAdapter: function(hours) { return hours * 100 / 12; },
size: 20,
ringWidth: 4
},
verticalAlign: "middle",
strokeWidth: 1,
ringWidth: 4
});
0:00 0:15 1:00 1:55 2:45 6:15 7:60 12:15
Starting with V2.0.0, this plug-in now supports even more than 2 pies or rings:
The inner
option may now itself contain another inner option for a third value,
that may, yet again, contain an inner option (fourth value) and so forth.
When using this feature, you should use rings instead of (overlapping) pies by setting
a ringWidth
in the main options object and in each inner object (only the innermost
might be a pie as demonstrated above for double diagrams). And you should set the size
of
each ring in order to control the spacing/gap between outer and inner ring manually for
a good result (auto-spacing doesn't work well with more than 2 values).
Also new is the support of stroke*
options even inside inner
options, i.e.
for each inner ring or pie you may draw a full circle around it or (more likely, like demonstrated below)
with strokeWidth
equal to the ringWidth
in order to draw a full
background circle overlapped by the ring.
Example with three rings, all with background circles (stroke*
), colors
configured programatically in plug-in call:
$(".triple1").progressPie({
globalTitle: "Demo with three rings",
size:50,
ringWidth: 5,
strokeWidth: 5,
color: "rgb(200, 0, 0)",
strokeColor: "rgba(200, 0, 0, 0.2)",
ringEndsRounded: true,
valueData: 'outer',
inner: {
size: 35,
ringWidth: 5,
strokeWidth: 5,
color: "rgb(0, 200, 0)",
strokeColor: "rgba(0, 200, 0, 0.2)",
ringEndsRounded: true,
valueData: 'middle',
inner: {
size: 20,
ringWidth: 5,
strokeWidth: 5,
color: 'rgb(0, 200, 200)',
strokeColor: 'rgba(0, 200, 200, 0.2)',
ringEndsRounded: true,
valueData: 'inner'
}
}
});
applied to:

As explained above in section CSS formatting, you may, if you prefer, configure the color scheme by CSS rules instead of in the plug-in call options. A variation of the above example:
$(".triple2").progressPie({
globalTitle: "Activity charts",
title: "Move",
animate: true,
size: 60,
ringWidth: 6,
strokeWidth: 6,
mode: $.fn.progressPie.Mode.CSS,
ringEndsRounded: true,
valueData: 'outer',
inner: {
title: "Exercise",
size: 45,
mode: $.fn.progressPie.Mode.CSS,
ringWidth: 6,
strokeWidth: 6,
ringEndsRounded: true,
valueData: 'middle',
inner: {
title: "Stand",
size: 30,
mode: $.fn.progressPie.Mode.CSS,
ringWidth: 6,
strokeWidth: 6,
ringEndsRounded: true,
valueData: 'inner'
}
}
});
.triple2 {
background: black;
border-radius: 5px;
padding: 5px;
margin: 1em;
display: inline-block;
box-shadow: 0 0 5px rgba(0,0,0,0.8)
}
.triple2 svg {
vertical-align: middle;
}
.triple2 .progresspie-background {fill: none}
.triple2 .progresspie-background.progresspie-outer {stroke: rgba(220, 0, 0, 0.3)}
.triple2 .progresspie-foreground.progresspie-outer {stroke: rgb(220, 0, 0)}
.triple2 .progresspie-background.progresspie-inner {stroke: rgba(0, 220, 0, 0.3)}
.triple2 .progresspie-foreground.progresspie-inner {stroke: rgb(0, 220, 0)}
.triple2 .progresspie-background.progresspie-inner2 {stroke: rgba(0, 220, 220, 0.3)}
.triple2 .progresspie-foreground.progresspie-inner2 {stroke: rgb(0, 220, 220)}

This example also demonstrates the use of the title
option, specifying a specific title for each of the ring, in combination with
the globalTitle
option, specifying a title for the whole chart. If, in a desktop browser, you point your mouse curser on the image and let
it rest for a while, a title popup should show either the local ring's title, if you point to a ring, or the global chart's title, if you point anywhere
else on the black background. (See Adding a title)
Note: This example uses SMIL animation (reload to play, not supported by Internet Explorer/Edge). See separate animation example page for more on that topic.
(Note 2: Obviously, this example is inspired by the activity display of the Apple Watch. It's not exactly a copy, though, just similar. Especially note that progresspieSVG does not support displaying values greater than 100%.)
See Example for optionsByRawValue
below for
another multiple-ring-example.
Note: This feature once used to be implemented by SMIL animation. SMIL Animations are supported by most modern browsers – except Microsoft's (IE or Edge). Since V2.5.0 the SMIL rotation animation has been replaced by a CSS animation. For now, this sadly does not improve browser support, since IE and Edge, while they do support CSS animation in general, don't support CSS transformations like rotation. This means, IE and Edge still don't support this rotation animation! But while SMIL support is not even intended for the future, at least Edge should support CSS transformations someday. Also, the CSS rotation can now be applied to inner pies/rings, while the old SMIL implementation only supported rotation for the outermost / main ring.
Update: With the new Feature Update for Windows 10 from April 2018, Microsoft finally added CSS transformation support for SVG to the Edge browser. I.e.: In the current Edge version, this rotation animation will now work! (Older Edge versions as well as Internet Explorer stillwon't play the animation.)
You may add a clockwise rotation animation to any pie or ring as shown above. For a normal value would not seem like a good idea, but if you want to indicate some waiting interval without the progress actually being measured at the time being, you might use this to build a “Busy-Indicator” like e.g. one of the following:
$('#rotate1').progressPie({
rotation: "500ms",
valueAdapter: function() {return 5;}
});
$('#rotate2').progressPie({
strokeWidth: 1,
ringWidth: 3,
color: "#333",
rotation: {
duration: "2s",
clockwise: false
},
valueAdapter: function() {return 50;}
});
$('#rotate3').progressPie({
strokeWidth: 0,
ringWidth: 1,
color: "navy",
rotation: true,
valueAdapter: function() {return 90;}
});
Note the valueAdapter
function in these examples, returning a constant value
defining the size of the rotating pie or the size of the gap in the rotating ring.
The animation by default only applies to the main / outer pie or ring. But the inner
option now (since V2.5.0) also supports a rotation
property, so you can animate each sub diagram
separately, as demonstrated below:
$('#rotate4').progressPie({
size: 40,
strokeWidth: 0,
ringWidth: 5,
ringEndsRounded: true,
color: "#900",
rotation: "1s",
valueAdapter: function() {return 85;},
inner: {
color: "navy",
valueData: "value",
rotation: {
duration: "5s",
clockwise: false
}
}
});
Normally, you specify one set of options which gets applied regardless of the value. Most options are constant, an exception is the color: As demonstrated above, you may specify a color function setting the actual color dependent upon the percent value.
Maybe that's no enough, maybe you want to change other things dependent on the percent value, too. So, for example, you might want to show a rotaion animation (see above) as long as the percent value has not been increased yet (is still zero), but switch to a pie chart for any value greater than zero.
For static values, you might simply call the plug-in with different options depending on the static value.
The “Updating” example above also already shows a way to change other options like the size by simply calling
the progressPie plug-in with different, newly calculated options on each update.
But if you want to set up
a general rule (using the setupProgressPie
plug-in function), simply define a set of default options and add an
optionsByPercent
function, mapping a percent value to either an object with some options or to null
if the default options should not be altered for this percent value. An object returned does not have to be a complete set of options, but only
to contain those options which differ from the defaults.
The following example sets some default values for a pie chart and then overrides some options only for a value of 0% in order to then show a spinning ring instead:
function greyToGreen(percent) {
var v = percent < 50 ? 0 : percent - 50; //0..50
var rb = 100 - (2 * v);
var g = 100 + (2 * v);
return "rgb(" + rb + ", " + g + ", " + rb +")";
}
…
$(".spinThenPie").setupProgressPie({
color: greyToGreen,
strokeWidth: 2,
contentPlugin: "checkComplete", //see separate examples page for details
optionsByPercent: function(percent) {
return percent > 0 ? null : { //return null for values > 0, meaning "no changes to the defaults"
//for value 0: override strokeWith and color and add some more options
strokeWidth: 0,
ringWidth: 2,
rotation: true,
valueAdapter: function() { return 85; }, //takes the 0 value and returns 85 instead.
color: greyToGreen(0) //otherwise the color would be "greenish", calculated for the value 85
}
}
}).progressPie();
0 % 5 % 25 % 42 % 50 % 65 % 80 % 99 % 100 %
Note: The following example combines several different topics, like
Double / multiple pies (see above),
content plug-ins as well as
SMIL animation.
Yet its main purpose is the demonstration of the use of the optionsByRawValue
option.
There may be cases where the calculated percent value is not suitable for deriving changes in options, but where you have to base your decision on the raw value before it gets converted into a percent value by the value adapter function.
In this case, simply use the similar optionsByRawValue
option.
But be aware that the type of the raw value (other than that of the calculated
percent value) is not guaranteed to be "number"
!
Especially when the value is read from an attribute, its type is usually
"string"
.
Just like a value adapter has to check the type and maybe convert the raw value,
so does this method!
The following example consists of two rings and one pie, all three of which deriving their individual percent values from the same raw value using different value adapters. The diagrams show a count-down. The raw value is the number of remaining seconds, which is converted into three values: hours, minutes and seconds. The inner pie's showing the total remaining time in hours (i.e. 100% filled pie represents 12 hours of remaining time or more), the ring around that shows the remaining minutes of the current hour and the outer ring the remaining seconds of the current minute—similar to an analog clock face.
In the last minute, i.e. the last 60 seconds, instead of the inner hour pie (which is already effectively empty) a content plug-in (see separate example page) is loaded which shows a numerical count-down of these 60 seconds. The decision whether to show this content plug-in or not, is easily made based on the raw value (seconds). The percent value of the outer ring (seconds) is not suitable for this decision at all, since this outer ring always shows the 100% value (60 seconds) for every full minute.
One second after the countdown reached its target (0), i.e. as soon as the raw seconds
value gets negative, the optionsByRawValue
define yet another modification
of options, removing the content plug-in again and instead showing a wait
indicator (rotating half arc). (Note that, since the raw value is allowed to be
negative, the value adapter functions for all three rings as well as the
label function for updating the hour-minute-second-label of the live pie,
explicitly test for negativeness of the raw value and return 0 in that case.)
The following code snippet not only contains the setup for the progress pie plug-in but also code for a live count-down example and the user interface to manually change the count-down value. In order not to clutter up the global scope with functions and variables specific to this example, all of them have been wrapped into an immediately invoked function expression.
(function() {
function parseSecsData(s) {
return typeof s === 'number' ? s : parseInt(s);
}
var dashes = {
count: 12,
centered: true,
length: 3,
inverted: true
};
$(".countdownHms").setupProgressPie({
globalTitle: "Countdown",
title: "Remaining seconds of current minute",
size: 60,
verticalAlign: "middle",
animate: true,
ringWidth: 2,
color: "rgb(0, 0, 200)",
strokeWidth: 2,
strokeColor: "rgba(0, 0, 200, 0.3)",
strokeDashes: dashes,
valueData: "remainingsecs",
valueAdapter: function(s) {
var totalSecs = parseSecsData(s);
var secs = totalSecs < 0 ? 0 : totalSecs % 60;
if (secs === 0 && totalSecs > 0) {
secs = 60;
}
return secs * 10 / 6; //100% = 60 secs
},
optionsByRawValue: function(s) {
var secs = parseSecsData(s);
return secs > 60 ? null : //no change if secs > 69
secs >= 0 ? { //last minute
contentPlugin: "rawValue",
contentPluginOptions: {
fontSizeFactor: 1.3
}
} : { //counter fell below zero
valueAdapter: p => 50,
rotation: true
};
},
inner: {
title: "Remaining minutes of current hour",
size: 52,
ringWidth: 5,
color: "rgb(50, 200, 150)",
animate: true,
strokeWidth: 5,
strokeColor: "rgba(50, 200, 150, 0.3)",
strokeDashes: dashes,
valueData: "remainingsecs",
valueAdapter: function(s) {
var secs = parseSecsData(s);
var p = secs < 0 ? 0 : secs % 3600 / 36; // 100% = 1h = 60min = 3600 secs
if (p === 0 && secs > 0) {
p = 100;
}
return p;
},
inner: {
title: "Remaining hours",
animate: false,
size: 38,
valueData: "remainingsecs",
valueAdapter: function(s) {
var secs = parseSecsData(s);
var mins = secs < 0 ? 0 : secs / 60;
return Math.round(mins * 10 / 72); //100% = 12 hours = 720 minutes
},
color: function(p) {
return $.fn.progressPie.colorByPercent(100-p);
}
}
}
}).progressPie();
function hms(s) {
const secs = s % 60;
const ms = s % 3600;
const mins = (ms - secs) / 60;
const hrs = (s - ms) / 3600;
return `${hrs}h${("0" + mins).slice(-2)}'${("0" + secs).slice(-2)}"`;
}
function tick() {
$(".countdownHms.live").each(function() {
const ctd = $(this);
let secs = ctd.data("remainingsecs");
if (secs >= 0) {
secs--;
ctd.data("remainingsecs", secs);
ctd.text(hms(Math.max(0, secs)));
ctd.progressPie();
}
});
}
window.setInterval(tick, 1000);
$("#livecountdownHmsUpdate").click(() => {
$(".countdownHms.live").data("remainingsecs", $("#livecountdownHmsInput").val());
});
})();
11h59'55" 8h55'11" 2h12'44" 1h00'01" 1h00'00"
0h01'00" 0h00'36" 0h00'01" 0h00'00" -1
Live Countdown:


Jess Giampietro
About
Portfolio
Contact
Portfolio
Hangman
Crystal Collector
Hamilton Trivia
Video Game Gifolofy
Train Times
Flick Pick
Burger of the Day
The Social Petwork
Book:Scrape
Gotta Remember 'Em All!
Connect with Me
©2018: Jess Giampietro