Is there a way to always have one item per line in the timeline? I don't want two or more items to share the same line, whatever the dates are.
Thanks.
You have to use groups, or if you're already using those for another purpose, you have to use subgroups.
Here's the official documentation for items, groups and subgroups (I'm assuming the website isn't gonna expire anytime soon... again...).
If you can use groups
Specify a different group for every item. You can set the group's content as an empty string, if you don't want to have a label for it on the left side of the Timeline.
If you want to arrange the groups in a specific way, specify an ordering function as the Timeline's options' groupOrder property. Example:
var options = {
// other properties...
groupOrder: function (a, b) {
if(a.content > b.content)// alphabetic order
return 1;
else if(a.content < b.content)
return -1;
return 0;
}
};
If you have to use subgroups
When you create an item, put it in the group it belongs to but also specify for it the subgroup property so that it's unique, like the item's id. For example, you can use the id itself, or add a prefix or suffix to it.
In the Timeline's options, set the stack and stackSubgroups properties to true.
In each group, set the subgroupStack property to true and specify an ordering function as the group's subgroupOrder property. Example:
var group = {
id: 1,
content: 'example group',
subgroupStack: true,
subgroupOrder: function(a, b) {
var tmp = a.start.getTime() - b.start.getTime();
return tmp === 0 ? parseInt(a.id) - parseInt(b.id) : tmp;
// if the start dates are the same, I compare the items' ids.
// this is because due to a bug (I guess) the ordering of items
// that return 0 in the ordering function might "flicker" in certain
// situations. if you want to order the items alphabetically in that
// case, compare their "content" property, or whatever other
// property you want.
}
};
Related
I am using QtableView(qt5.9) with 10 columns and want to disable sorting for 2nd and 3rd (only some) columns when the user clicks on the header of these columns.
I use setsortingenabled flag to make my QtableView allow sorting
Is there any signal which I should listen to on clicking of header and then call some appropraite method or deny sorting.
You can use the header signal sortIndicatorChanged to restore the current sort indicator.
Example:
connect(m_poTableView->header(), &QHeaderView::sortIndicatorChanged,
this, &MyClass::HandleIndicatorChanged);
MyClass::HandleIndicatorChanged(int logicalIndex, Qt::SortOrder eSort)
{
if (logicalIndex != 0)
{
this->m_poTableView->horizontalHeader()->setSortIndicator(
0, this->m_poTableView->model()->sortOrder());
}
}
An easier way (for me, at least) is to subclass the filter proxy and override sorting for just the disabled columns. The below code is in Python, but it is a simple translation to C++.
def CustomSorter(QtCor.QSortFilterProxyModel):
def sort(self, column: int, order: QtCore.Qt.SortOrder) -> None:
if column == 2 or column == 3:
# Do nothing instead of sorting
return
else:
# Sort as usual
super().sort(column, order)
This is the code I use in the case I only want to allow sorting column 2 (sOrder is a private int member of MyClass):
connect(ui->tableView->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &MyClass::onSortIndicatorChanged);
void MyClass::onSortIndicatorChanged(int column, Qt::SortOrder order)
{
if (column == 2){
// Record the sort order when it is column 2
sOrder = order;
}
else{
// Restore the column 2 sort order
ui->tableView->sortByColumn(2, sOrder);
}
}
I have some json, for examle:
data = {
"name":"Bob","age":"20",
"name":"Jo","age":"21",
"name":"Jo","age":"22",
"name":"Nick","age":"23"
}
Next, I use crossfilter, create dimension and filter it:
let ndx = crossfilter(data);
let dim = ndx.dimension(d => d.name).filter(d !== "Jo");
//try to get filtered values
let filtered = dim.top(Infinity); // -> return 2 values where 'name'!='Jo'
//"name":"Bob","age":"20"
//"name":"Nick","age":"23"
let myGroup = dim.group(d => {
if(d === 'Jo') {
//Why we come here? This values must be filtered already
}
})
How can I filter my dimension and don't have these values on 'dim.group'?
Not sure what version you are using, but in the current version of Crossfilter, when a new group is created all records are first added to the group and then filtered records are removed. So the group accessor will be run at least once for all records.
Why do we do this? Because for certain types of grouping logic, it is important for the group to "see" a full picture of all records that are in scope.
It is possible that the group accessor is run over all records (even filtered ones) anyway in order to build the group index, but I don't remember.
I am trying to define a filter on rows based on a slider filter where its range is min/max of a given measure instead of the beginning and end of the data (the default behaviour).
slider widget was designed to select ranges of level members, so it doesn't support selection over measure values. You can either try to create own widget based on slider or use statical defined data as in How to use a static range and display members according a TOP(x) style query, just change:
function consumeEvent( context, event ) {
if (event.name == 'ic3-report-init') {
// Following code will replace a data provider for Slider
// with generated numbers. But to do so, you'll need UID of
// the Slider widget, in this example it's "w1"
var widget = event.value.widgetMgr().getItemById("w1");
_.assign(widget.builder().guts_, {
items:_.times(STEPS_COUNT, function(idx){
return {
name:MIN_VALUE + idx * STEP_SIZE,
uniqueName:idx
}
})})
}
}
define STEPS_COUNT, MIN_VALUE, STEP_SIZE.
After that you can try to apply event value as the filter to your MDX
I have some data stored in this format (except many cases more):
var data = [
{"name":"John", "team":"team1"},
{"name":"Megan", "team":"team2"},
{"name":"Rupert", "team":"team2"},
{"name":"Albert", "team":"team1"}
];
I want to create this:
var colourScale = d3.scale.ordinal()
.range(a)
.domain(b)
"a" being an array of all levels of "team" (i.e. ["team1","team2"] in this case).
"b" being an array of ordinal colours of the same length as "a".
colourScale() should take the "team"-value as input and return a unique colour for each team.
How do I create "a" and "b"? Is there something equivalent to R's levels(data[ ,"team"]) in javascript or d3.js?
It's not exactly your solution as you don't explicitly find your specified a or b, but I found
var colourScale = d3.scale.category10();
function colour(d) { return d.team; }
and on the object you want to colour, bound to the appropriate data, chain
.style("fill", function(d) { return colourScale(colour(d)); });
has the effect that I assume you're searching for. Hope this helps.
I think there are some extra steps you need to do. You need first to create a 'unique' set of possible domain values. Then you can create a function that maps each value to a color. Something like this perhaps:
// A function to get unique values, can be optimized further
function unique(x) {
return x.reverse().filter(function (e, i, x) {return x.indexOf(e, i+1) === -1;}).reverse();
}
var data = [
{"name":"John", "team":"team1"},
{"name":"Megan", "team":"team2"},
{"name":"Rupert", "team":"team2"},
{"name":"Albert", "team":"team1"}
];
// An arbitrary set of colors
colors = ['#005824','#1A693B','#347B53','#4F8D6B','#699F83','#83B09B','#9EC2B3','#B8D4CB','#D2E6E3','#EDF8FB','#FFFFFF','#F1EEF6','#E6D3E1','#DBB9CD','#D19EB9','#C684A4','#BB6990','#B14F7C','#A63467','#9B1A53','#91003F']
//Create the ordinary scale based on your color set above
colorScale= d3.scale.ordinal()
.domain(unique(data.map(function(d){return d.team;})))
.range(colors)
Then you can access the color by:
.style("fill",function(d){return colorScale(d.team);})
Hope this helps.
I have 6 dropdown lists (with identical options), and I am manually setting them in my codebehind. All six should have different values. When I log the values I am setting them to, I get the correct assumed values to be set to. However, when the page renders, all six of them are set to the same freaking value.
I have tried setting the values with all of the following:
// set index, find by text
dd1.SelectedIndex = dd1.Items.IndexOf(dd1.Items.FindByText(val1));
// set with selected value
dd2.SelectedValue = val2;
// set index, find by value
dd3.SelectedIndex = dd3.Items.IndexOf(dd3.Items.FindByValue(val3));
// set list item, selected = true
((ListItem)dd4.Items.FindByValue(val4)).Selected = true;
The dropdown lists' set of options are generated prior to me trying to set them:
foreach (Station st in stations) {
ListItem li = new ListItem() { Text = st.fromto, Value = st.fromto};
dd1.Items.Add(li);
dd2.Items.Add(li);
dd3.Items.Add(li);
dd4.Items.Add(li);
dd5.Items.Add(li);
dd6.Items.Add(li);
}
I then look in the database to see if any values exist for a specific reference id in my app. If so, it indicates that I need to set one or more (up to 6) dropdowns:
var existingStations = db.LOGOPS_STATIONs.Where(x => x.XREF_LOGOP_MAIN_ID == logopRefId);
if (existingStations.Count() > 0) {
int i = 1;
foreach (LOGOPS_STATION s in existingStations) {
if (i < 7) {
string text= s.FROM_STATION;
if (i == 1) dd1.SelectedIndex = dd1.Items.IndexOf(dd1.Items.FindByText(text));
// for the heck of it, set the next one manually...
else if (i == 2) dd2.SelectedIndex = 2;
// try and set one with forcing select
else if (i == 3) ((ListItem)dd3.Items.FindByText(text)).Selected = true;
// good ol normal
else if (i == 4) dd4.SelectedValue = text;
... and so on ...
}
}
}
So, the dropdowns are all populated (when I log in the codebehind they're fully populated). And when I log the actual values when they're being set, they're set to the value as expected. However, when the page loads, they're all set to the same thing
At any rate, not sure what else to do. I have turned on and off different event validation hookups. I have disabled all JS to see if that was manipulating values, and it's not. I have tried explicitly setting like this
dd1.SelectedIndex = 2;
dd2.SelectedIndex = 8;
Oddly enough, that doesn't work either. For real, when does setting SelectedIndex to a unique control with a unique id not set the item?
I had to create a separate list item for each dropdown instead of them all sharing the same li in the foreach loop. And then a step further, had to set Selected = false in the constructor of the ListItem
I assumed that you could 'reuse' code for each instance of the dropdown population, but each dropdown needed it's own unique ListItem
Maybe there's a better way, but this solution seems to have solved the problem