I am trying to have the auto layout manager adjust the center point of a view, based on the width of the super view. I don't understand why that is an 'Invalid Pairing' of the attributes (as told by the crash and NSInvalidArgumentException)
UIView *ac;
NSLayoutConstraint *cXloc = [NSLayoutConstraint constraintWithItem:ac
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:ac.superview
attribute:NSLayoutAttributeWidth
multiplier:.1
constant:x*ac.superview.frame.size.width*.2];
[ac.superview addConstraint:cXloc];
Could someone explain why this is an 'Invalid paring' and how I should approach this?
Thanks
It is the limitation of the current implementation of Auto Layout. However, you can easily work around it since all the constrains are linear and NSLayoutAttributes are correlated. For example, say, the constraint you want is:
subview.centerX = m * superview.width + c;
You can express it as a relationship between tow centerXs:
// Since width == 2 * centerX
subview.centerX = m * 2 * superview.centerX + c;
If you relate ac's AttributeCenterX to its superview's AttributeCenterX, AttributeLeading, or AttributeTrailing, you should be able to express your desired constraint using the multiplier and constraint. Keep in mind that the constant is evaluated only when the constraint is created, and your example's constant wouldn't update as ac.superview's width changes.
If you can express in words how you'd like ac positioned relative to its superview, we can suggest a constraint.
Edit
Here's an example with 5 NSButtons. They themselves and the space between them expand so that the spaces are 30% as wide as the buttons, all the buttons have the same width, and all the spaces have the same width. Creating 4 invisible NSViews just for spacing is pretty cumbersome, especially considering you've got it working outside of autolayout. But in case you're curious:
// Assuming these NSViews and NSButtons exist,
//NSView* superview ;
//NSButton *buttonOne, *buttonTwo, *buttonThree, *buttonFour, *buttonFive ;
[superView removeConstraints:superView.constraints] ;
// Create empty NSViews to fill the space between the 5 buttons.
NSView* spaceOne = [NSView new] ;
NSView* spaceTwo = [NSView new] ;
NSView* spaceThree = [NSView new] ;
NSView* spaceFour = [NSView new] ;
spaceOne.translatesAutoresizingMaskIntoConstraints = NO ;
spaceTwo.translatesAutoresizingMaskIntoConstraints = NO ;
spaceThree.translatesAutoresizingMaskIntoConstraints = NO ;
spaceFour.translatesAutoresizingMaskIntoConstraints = NO ;
[superView addSubview:spaceOne] ;
[superView addSubview:spaceTwo] ;
[superView addSubview:spaceThree] ;
[superView addSubview:spaceFour] ;
NSDictionary* views = NSDictionaryOfVariableBindings(superView,buttonOne,buttonTwo,buttonThree,buttonFour,buttonFive,spaceOne,spaceTwo,spaceThree,spaceFour) ;
// Vertically align buttonOne to its superview however you like.
[superView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[buttonOne]" options:0 metrics:nil views:views ] ] ;
// Make the "space" NSViews' widths equal and >= 10. Make the buttons' widths equal.
[superView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[buttonOne][spaceOne(>=10)][buttonTwo(==buttonOne)][spaceTwo(==spaceOne)][buttonThree(==buttonOne)][spaceThree(==spaceOne)][buttonFour(==buttonOne)][spaceFour(==spaceOne)][buttonFive(==buttonOne)]|" options: NSLayoutFormatAlignAllCenterY metrics:nil views:views ] ] ;
// Make the "space" NSViews' widths 30% of the NSButtons' widths.
[superView addConstraint: [NSLayoutConstraint constraintWithItem: spaceOne
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationEqual
toItem: buttonOne
attribute: NSLayoutAttributeWidth
multiplier: 0.3
constant: 0 ] ] ;
Based on an0's answer, and assuming you have an NSArray containing your buttons, the following should space the buttons equally within the superview:
NSUInteger currentButton = 1;
for (UIButton *button in self.buttons)
{
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:2.0 * (CGFloat) currentButton / (CGFloat) (self.buttons.count + 1) constant:0.0]];
currentButton++;
}
If you're looking to have your programmatically generated views to fit its superview's width. You can use the constraint pairing of NSLayoutAttributeLeading and attribute:NSLayoutAttributeCenterX
You have to make the proper calculation in order to get the right multiplier. Calculation involves the total count of the views to be laid out and the index of the current view.
//Caculate constraint multiplier from parentView CenterX.
//This sets the width of the button relative to parentView.
// A value of 2 = Full width.
CGFloat multiplier = 2/(arr.count/counter);
[parentView addConstraint:[NSLayoutConstraint constraintWithItem:btn
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual toItem:parentView
attribute:NSLayoutAttributeCenterX multiplier:multiplier constant:0]];
This will distribute the views width to fill its superview.
Related
So if I have two arrays in matlab. Let's call them locations1 and locations2
locations1
1123.44977625437 890.824688325172
1290.31273560851 5065.65794385883
1718.10632735926 2563.44895531365
1734.55379433782 4408.20631924691
2050.70084480064 1214.45353443990
2299.46239346717 3781.34694047196
4186.02801290113 4386.67818566045
5676.10649593031 4529.23023993815
locations2
7474.22619378039 3166.41503120846
8604.40241305284 5069.40744277799
9048.25231808890 2563.58997620248
9059.71923042408 4381.75034710351
9643.05902166767 3796.42822996919
11460.8617087264 4392.85930695209
And I want to make it so that any two entries of the second columns that match each other within 100.0 remain while any entry that has no match will get removed. So I want the output to look like
locations1
1290.31273560851 5065.65794385883
1718.10632735926 2563.44895531365
1734.55379433782 4408.20631924691
2299.46239346717 3781.34694047196
4186.02801290113 4386.67818566045
locations2
8604.40241305284 5069.40744277799
9048.25231808890 2563.58997620248
9059.71923042408 4381.75034710351
9643.05902166767 3796.42822996919
11460.8617087264 4392.85930695209
How would I do this? Preferably without loops. Here is what I've done, but it has loops
locround1=round(locations1/50)*50;
locround2=round(locations2/50)*50;
for i=1:size(locations1,1)
nodel1(i)=sum(locround1(i,2)== locround2(:,2))
end
nodel1=repmat(nodel1>0,[2,1]);
nodel1=nodel1';
locations1=nodel1.*locations1;
locations1( ~any(locations1,2), : ) = [];
for i=1:size(locations2,1)
nodel2(i)=sum(locround2(i,2)== locround1(:,2))
end
nodel2=repmat(nodel2>0,[2,1]);
nodel2=nodel2';
locations2=nodel2.*locations2;
locations2( ~any(locations2,2), : ) = [];
This is what I got. If your MATLAB version has set operators, you can do it with the following codes:
Li1 = ismembertol(locations1(:,2),locations2(:,2),100, 'DataScale', 1);
locations1_new = locations1 (Li1,:);
Li2 = ismembertol(locations2(:,2),locations1(:,2),100, 'DataScale', 1);
locations2_new = locations2 (Li2,:);
I tested it, it works.
Let the data be defined as
locations1 = [
1123.44977625437 890.824688325172
1290.31273560851 5065.65794385883
1718.10632735926 2563.44895531365
1734.55379433782 4408.20631924691
2050.70084480064 1214.45353443990
2299.46239346717 3781.34694047196
4186.02801290113 4386.67818566045
5676.10649593031 4529.23023993815
];
locations2 = [
7474.22619378039 3166.41503120846
8604.40241305284 5069.40744277799
9048.25231808890 2563.58997620248
9059.71923042408 4381.75034710351
9643.05902166767 3796.42822996919
11460.8617087264 4392.85930695209
];
threshold = 100;
Then:
m = abs(locations1(:,2)-locations2(:,2).')<=threshold;
result1 = locations1(any(m,2),:);
result2 = locations2(any(m,1),:);
How this works:
The first line computes a matrix with the distance between each value from the second column of locations1 and each value from the second column of locations2. The distances are then compared with threshold, so that the matrix entries become true or false.
This makes use of implicit expansion, introduced in R2016b. For Matlab versions before that, use bsxfun as follows:
m = abs(bsxfun(#minus, locations1(:,2), locations2(:,2).'))<=threshold;
Each row of the computed matrix, m, corresponds to a value from locations1; and each column corresponds to a value from locations2.
The second line uses logical indexing to select the rows of location1 that satisfy the criterion for some value of location2.
Similarly, the third line selects the rows of location2 that satisfy the criterion for some value of location1.
There are some old animations that I want to reuse, but the old animation use different axis (For example: old: Face negative Z, Y as up; new: Face Y, Negative Z as up). So I fix this by grouping the animation and rotate the group so that it faces the right axis. But when I ungroup, it's only work for current frame (I turn Auto key on).
I have search several forums:
Grouping animated objects, Scaling, Ungrouping
Need help maintaining offset during ungroup
ungrouping but keeping animation
Grouping animated objects, Scaling, Ungrouping
But nothing works.
Ok, it works for the current frame when you ungroup. So to make it work for the entire animation, you can group & un-group for all the frames. Of course I won't do it by hand but I'll do it with script.
proc GetPlaybackRange(string $bone, int $outStartEndTime[])
{
float $arrKey[] = `keyframe -q $bone`;
$arrKeyLength=size($arrKey);
$outStartEndTime[0] = floor($arrKey[0]);
$outStartEndTime[1] = ceil($arrKey[$arrKeyLength-1]);
}
proc UngroupAndGroupNextFrame(int $frame, string $groupName, float $transform[])
{
currentTime $frame ;
ungroup;
currentTime ($frame + 1) ;
group -n $groupName;
xform -worldSpace -matrix
$transform[0]
$transform[1]
$transform[2]
$transform[3]
$transform[4]
$transform[5]
$transform[6]
$transform[7]
$transform[8]
$transform[9]
$transform[10]
$transform[11]
$transform[12]
$transform[13]
$transform[14]
$transform[15]
$groupName;
}
proc UnGroupForAnimation()
{
string $sel[]= `ls -sl`;
string $groupName = $sel[0];
float $transform[];
$transform = `xform -q -worldSpace -matrix $groupName`;
string $bone[] = `listRelatives -children $groupName`;
int $startEndTime[];
GetPlaybackRange($bone[0], $startEndTime);
for($i = $startEndTime[0]; $i <= $startEndTime[1]; $i++)
{
UngroupAndGroupNextFrame($i, $groupName, $transform);
}
currentTime ($startEndTime[1] + 1) ;
ungroup;
timeSliderClearKey;
print ($bone[0] + " range "+$startEndTime[0]+" : "+$startEndTime[1]);
}
UnGroupForAnimation();
Usage of the script:
Step 1. Select the group (the children of the group should be the bone)
Step 2. Run the script.
And that's it.
I'm creating a shiny app and i'm letting the user choose what data that should be displayed in a plot and a table. This choice is done through 3 different input variables that contain 14, 4 and two choices respectivly.
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
selectInput(inputId = "DataSource", label = "Data source", choices =
c("Restoration plots", "all semi natural grasslands")),
selectInput(inputId = "Variabel", label = "Variable", choices =
choicesVariables)),
#choicesVariables definition is omitted here, because it's very long but it
#contains 14 string values
selectInput(inputId = "Factor", label = "Factor", choices = c("Company
type", "Region and type of application", "Approved or not approved
applications", "Age group" ))
),
dashboardBody(
plotOutput("thePlot"),
tableOutput("theTable")
))
This adds up to 73 choices (yes, i know the math doesn't add up there, but some choices are invalid). I would like to do this using a lookup table so a created one with every valid combination of choices like this:
rad1<-c(rep("Company type",20), rep("Region and type of application",20),
rep("Approved or not approved applications", 13), rep("Age group", 20))
rad2<-choicesVariable[c(1:14,1,4,5,9,10,11, 1:14,1,4,5,9,10,11, 1:7,9:14,
1:14,1,4,5,9,10,11)]
rad3<-c(rep("Restoration plots",14),rep("all semi natural grasslands",6),
rep("Restoration plots",14), rep("all semi natural grasslands",6),
rep("Restoration plots",27), rep("all semi natural grasslands",6))
rad4<-1:73
letaLista<-data.frame(rad1,rad2,rad3, rad4)
colnames(letaLista) <- c("Factor", "Variabel", "rest_alla", "id")
Now its easy to use subset to only get the choice that the user made. But how do i use this information to plot the plot and table without using a 73 line long ifelse statment?
I tried to create some sort of multidimensional array that could hold all the tables (and one for the plots) but i couldn't make it work. My experience with these kind of arrays is limited and this might be a simple issue, but any hints would be helpful!
My dataset that is the foundation for the plots and table consists of dataframe with 23 variables, factors and numerical. The plots and tabels are then created using the following code for all 73 combinations
s_A1 <- summarySE(Samlad_info, measurevar="Dist_brukcentrum",
groupvars="Companytype")
s_A1 <- s_A1[2:6,]
p_A1=ggplot(s_A1, aes(x=Companytype,
y=Dist_brukcentrum))+geom_bar(position=position_dodge(), stat="identity") +
geom_errorbar(aes(ymin=Dist_brukcentrum-se,
ymax=Dist_brukcentrum+se),width=.2,position=position_dodge(.9))+
scale_y_continuous(name = "") + scale_x_discrete(name = "")
where summarySE is the following function, burrowed from cookbook for R
summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=TRUE,
conf.interval=.95, .drop=TRUE) {
# New version of length which can handle NA's: if na.rm==T, don't count them
length2 <- function (x, na.rm=FALSE) {
if (na.rm) sum(!is.na(x))
else length(x)
}
# This does the summary. For each group's data frame, return a vector with
# N, mean, and sd
datac <- ddply(data, groupvars, .drop=.drop,
.fun = function(xx, col) {
c(N = length2(xx[[col]], na.rm=na.rm),
mean = mean (xx[[col]], na.rm=na.rm),
sd = sd (xx[[col]], na.rm=na.rm)
)
},
measurevar
)
# Rename the "mean" column
datac <- rename(datac, c("mean" = measurevar))
datac$se <- datac$sd / sqrt(datac$N) # Calculate standard error of the mean
# Confidence interval multiplier for standard error
# Calculate t-statistic for confidence interval:
# e.g., if conf.interval is .95, use .975 (above/below), and use df=N-1
ciMult <- qt(conf.interval/2 + .5, datac$N-1)
datac$ci <- datac$se * ciMult
return(datac)
}
The code in it's entirety is a bit to large but i hope this may clarify what i'm trying to do.
Well, thanks to florian's comment i think i might have found a solution my self. I'll present it here but leave the question open as there is probably far neater ways of doing it.
I rigged up the plots (that was created as lists by ggplot) into a list
plotList <- list(p_A1, p_A2, p_A3...)
tableList <- list(s_A1, s_A2, s_A3...)
I then used subset on my lookup table to get the matching id of the list to select the right plot and table.
output$thePlot <-renderPlot({
plotValue<-subset(letaLista, letaLista$Factor==input$Factor &
letaLista$Variabel== input$Variabel & letaLista$rest_alla==input$DataSource)
plotList[as.integer(plotValue[1,4])]
})
output$theTable <-renderTable({
plotValue<-subset(letaLista, letaLista$Factor==input$Factor &
letaLista$Variabel== input$Variabel & letaLista$rest_alla==input$DataSource)
skriva <- tableList[as.integer(plotValue[4])]
print(skriva)
})
I am trying to write an indicator originally from MT4 into NT7.
I have the following calculations in MT4:
dayi = iBarShift(Symbol(), myPeriod, Time[i], false);
Q = (iHigh(Symbol(), myPeriod,dayi+1) - iLow(Symbol(),myPeriod,dayi+1));
L = iLow(NULL,myPeriod,dayi+1);
H = iHigh(NULL,myPeriod,dayi+1);
O = iOpen(NULL,myPeriod,dayi+1);
C = iClose(NULL,myPeriod,dayi+1);
myperiod is a variable where I place the period in minutes (1440 = 1day).
What are the equivalent functions in NT7 to iBarShift, iHigh and so on?
Thanks in advance
For NinjaTrader:
iLow = Low or Lows for multi-time frame
iHigh = High or Highs
iOpen = Open or Opens
iClose = Close or Closes
So an example would be
double low = Low[0]; // Gets the low of the bar at index 0, or the last fully formed bar (If CalculateOnBarClose = true)
In order to make sure you are working on the 1440 minute time frame, you will need to add the following in the Initialize() method:
Add(PeriodType.Minute, 1440);
If there are no Add statements prior to this one, it will place it at index 1 (O being the chart default index) in a 2 dimensional array. So to access the low of the 1440 minute bar at index 0 would be:
double low = Lows[1][0];
For iBarShift look at
int barIndex = Bars.GetBar(time);
which will give you the index of the bar with the matching time. If you need to use this function on the 1440 bars (or other ones), use the BarsArray property to access the correct Bar object and then use the GetBar method on it. For example:
int barIndex = BarsArray[1].GetBar(time);
Hope that helps.
I wrote a script that returns several text boxes in a figure. The text boxes are moveable (I can drag and drop them), and their positions are predetermined by the data in an input matrix (the data from the input matrix is applied to the respective positions of the boxes by nested for loop). I want to create a matrix which is initially a copy of the input matrix, but is UPDATED as I change the positions of the boxes by dragging them around. How would I update their positions? Here's the entire script
function drag_drop=drag_drop(tsinput,infoinput)
[x,~]=size(tsinput);
dragging = [];
orPos = [];
fig = figure('Name','Docker Tool','WindowButtonUpFcn',#dropObject,...
'units','centimeters','WindowButtonMotionFcn',#moveObject,...
'OuterPosition',[0 0 25 30]);
% Setting variables to zero for the loop
plat_qty=0;
time_qty=0;
k=0;
a=0;
% Start loop
z=1:2
for idx=1:x
if tsinput(idx,4)==1
color='red';
else
color='blue';
end
a=tsinput(idx,z);
b=a/100;
c=floor(b); % hours
d=c*100;
e=a-d; % minutes
time=c*60+e; % time quantity to be used in 'position'
time_qty=time/15;
plat_qty=tsinput(idx,3)*2;
box=annotation('textbox','units','centimeters','position',...
[time_qty plat_qty 1.5 1.5],'String',infoinput(idx,z),...
'ButtonDownFcn',#dragObject,'BackgroundColor',color);
% need to new=get(box,'Position'), fill out matrix OUT of loop
end
fillmenu=uicontextmenu;
hcb1 = 'set(gco, ''BackgroundColor'', ''red'')';
hcb2 = 'set(gco, ''BackgroundColor'', ''blue'')';
item1 = uimenu(fillmenu, 'Label', 'Train Full', 'Callback', hcb1);
item2 = uimenu(fillmenu, 'Label', 'Train Empty', 'Callback', hcb2);
hbox=findall(fig,'Type','hggroup');
for jdx=1:x
set(hbox(jdx),'uicontextmenu',fillmenu);
end
end
new_arr=tsinput;
function dragObject(hObject,eventdata)
dragging = hObject;
orPos = get(gcf,'CurrentPoint');
end
function dropObject(hObject,eventdata,box)
if ~isempty(dragging)
newPos = get(gcf,'CurrentPoint');
posDiff = newPos - orPos;
set(dragging,'Position',get(dragging,'Position') + ...
[posDiff(1:2) 0 0]);
dragging = [];
end
end
function moveObject(hObject,eventdata)
if ~isempty(dragging)
newPos = get(gcf,'CurrentPoint');
posDiff = newPos - orPos;
orPos = newPos;
set(dragging,'Position',get(dragging,'Position') + [posDiff(1:2) 0 0]);
end
end
end
% Testing purpose input matrices:
% tsinput=[0345 0405 1 1 ; 0230 0300 2 0; 0540 0635 3 1; 0745 0800 4 1]
% infoinput={'AJ35 NOT' 'KL21 MAN' 'XPRES'; 'ZW31 MAN' 'KM37 NEW' 'VISTA';
% 'BC38 BIR' 'QU54 LON' 'XPRES'; 'XZ89 LEC' 'DE34 MSF' 'DERP'}
If I understand you correctly (and please post some code if I'm not), then all you need is indeed a set/get combination.
If boxHandle is a handle to the text-box object, then you get its current position by:
pos = get (boxHandle, 'position')
where pos is the output array of [x, y, width, height].
In order to set to a new position, you use:
set (boxHandle, 'position', newPos)
where newPos is the array of desired position (with the same structure as pos).
EDIT
Regarding to updating your matrix, since you have the handle of the object you move, you actually DO have access to the specific text box.
When you create each text box, set a property called 'UserData' with the associated indices of tsinput used for that box. In your nested for loop add this
set (box, 'UserData', [idx, z]);
after the box is created, and in your moveObject callback get the data by
udata = get(dragging,'UserData');
Then udata contains the indices of the elements you want to update.