I am trying to learn Julia Language, and my current project is a "5 in a row" program. I started making an interface with Julia wrapper on Gtk for this game, but stumbled upon an interesting problem. Code is below.
The problem is: after callback function work cur_step variable is not changing, and labels of buttons are not changing too. However, if I delete the if-condition in the callback function, buttons will all get labels "x" after pressing as it is supposed to be right now.
I'm writing my code with Julia 1.0 in Jupyter Notebook.
I've tried to set up cur_step variable as global, since thought that it was a scope problem, but it didn't work out.
using Gtk
cur_step = "x"
function click_once_callback(widget)
set_gtk_property!(widget, :sensitive, false)
set_gtk_property!(widget, :label, cur_step)
if cur_step == "x"
cur_step = "o"
else
cur_step = "x"
end
end
letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o']
win = GtkWindow("GoMoku")
g = GtkGrid()
buttons = []
for i=1:15
b = []
for j=1:15
letter = letters[i]
push!(b,GtkButton("$letter:$j"))
end
push!(buttons,b)
end
for i=1:15
for j=1:15
g[i,16-j] = buttons[i][j]
id = signal_connect(click_once_callback, buttons[i][j], "clicked")
end
end
set_gtk_property!(g, :column_homogeneous, true)
set_gtk_property!(g, :column_spacing, 15) # introduce a 15-pixel gap between columns
set_gtk_property!(g, :row_spacing, 15) # introduce a 15-pixel gap between rows
push!(win, g)
showall(win)
Why is it so that global variable not changing through the callback function? I expect to change cur_step iteratively after each button was clicked.
Thank you in advance!
You need to label cur_step as global inside your function (as well as outside, for good code signposting).
A function can use a variable from its parent scope without problems, as long as there's no assignment anywhere within the function's scope. If there is an assignment somewhere (even if it's in an if block), then the function is interpreted as local; this is true even prior to the point where its assignment occurs.
In order to treat a variable that gets assigned at some point inside the function properly as a global one, you need to explicitly point this out inside the function by using global cur_step.
Related
Let's say I have a simulation set up in Simulink, which consists of three blocks: Input (From Workspace block), model calculation (S-function block) and results (Outport).
In my class, I do a lot of preprocessing on the input data, which is not shown here. Then, I point the Input block to use the inputArray, which is a property of my class.
classdef SimulationClass < handle
%SimulationClass Simulate process using model
properties
inputArray double = ones(101, 11)
modelFilename char
simOpts struct
simulationTime double
simulationResultsArray double
end
methods
function obj = SimulationClass(simulationTime)
obj.simulationTime = simulationTime;
end
function prepareSimulation(obj, modelFilename)
obj.modelFilename = modelFilename;
open_system(obj.modelFilename);
set_param([obj.modelFilename '/fromWorkspaceBlock'],...
'VariableName', 'obj.inputArray');
obj.simOpts = simset('SrcWorkspace', 'current');
end
function runSimulation(obj)
[~, ~, obj.simulationResultsArray] = sim(obj.modelFilename,...
obj.simulationTime, obj.simOpts);
end
end
end
I can easily start the simulation run from the command prompt.
mySim = SimulationClass(1:10);
mySim.prepareSimulation('mySimulinkSheet');
mySim.runSimulation;
But, if I play around with some settings (e.g., different solvers) and press Run in Simulink, it doesn't work, since the data is not in the base workspace. I.e., I lose the interactivity with Simulink.
Any ideas on how to resolve this issue? The only workaround I have figured out so far would be to write an additional method (like writePropertiesToBaseWorkspace) which would convert the classes properties into variables, using assignin().
We have a script that defines values to names similar to #define in c. For example:
script.m:
ERR_NOERROR = 0;
ERR_FATAL = 1;
This script already exists and is used for value replacement when reading data from files.
Now we have a function (or more) that does some analysis and we would like to use the same definition in this function to avoid magic numbers. But when the script is called from the function we get an error.
Attempt to add "ERR_NOERROR" to a static workspace.
See MATLAB Programming, Restrictions on Assigning to Variables for details.
And this does not help much in the understanding of the problem.
The question is how can we make these definitions visible/usable in the functions with having to copying it every time.
Example:
function foo = bar(a)
run(script.m) %also tried running it without the run command
if a == ERR_NOERROR
foo = 5;
else
foo = 6;
end
end
edit:
There was a nested function,below in the function which I was not aware of. This explains the problem.
This kind of scoping error happens when you use nested or anonymous function within a function. The solution is well documented.
To your case, you can avoid nested function, or "Convert the script to a function and pass the variable using arguments", as the documentation suggests.
EDIT: I should have made it clear that the error occurs even if the script is not called within the nested function. Similar scenario is that, in debug mode (by setting up a break point), it will be an error if one tries to create a temporal variable to test something.
This is not a direct answer, rather a recommendation to switch to another method, which will not be mixing scope and workspace.
Instead of defining your constant in a script, you could make a class containing only constant properties. ex: code for error_codes.m:
classdef error_codes
% ---------------------------------------------------------------------
% Constant error code definition
% ---------------------------------------------------------------------
properties (Constant = true)
noerror = 0 ;
fatal = 1 ;
errorlvl2 = 2 ;
errorlvl3 = 3 ;
warning = -1 ;
% etc ...
end
end
I use this style for many different type of constants. For tidiness, I groups them all in a Matlab package directory (The directories which starts with a + character.
The added benefit of using constant class properties is the safety that the values cannot be changed in the middle of the code (your variables defined in a script could easily be overwritten by a careless user).
So assuming my file error_codes.m is placed in a folder:
\...somepath...\+Constants\error_codes.m
and of course the folder +Constants is on the MATLAB path, then to use it as in your example, instead of calling the script, just initialise an instance of the class, then use the constant values when you need them:
function foo = bar(a)
ERR = Constants.error_codes ;
if a == ERR.noerror
foo = 5;
else
foo = 6;
end
or it can works in switch statement too:
switch a
case ERR.noerror
foo = 5 ;
case ERR.warning
foo = 42 ;
case ERR.fatal
foo = [] ;
end
I got an odd behaviour of my functions and since i'm not so used to matlab coding i guess is due to something really easy that i don't get.
I can't understand how this could print something different
fx(Punti(1,:),Punti(2,:))
fx(Punti(2,:),Punti(3,:))
fx(Punti(3,:),Punti(4,:))
fx(Punti(4,:),Punti(5,:))
from this
for i_unic=1:4
fx(Punti(i_unic,:),Punti(i_unic+1,:))
end
Consider fx as a generic function.
Is it possible that fx uses some variables that for some reason are erased at the end of each iteration?
EDIT
-->"Punti" is just matrix containing the points a SCARA robot should follow
-->fx is the function "Retta" and it's the following
function retta(PuntoA,PuntoB,Asse_A,q_ini,rot,contaerro,varargin)
global SCARA40
global inizio XX YY ZZ
global seg_Nsteps
npassi = seg_Nsteps;
ipuntofin = inizio + npassi;
for ipunto = inizio : ipuntofin
P4 = PuntoA + (ipunto-inizio)*(PuntoB-PuntoA)/npassi;
q = kineinversa(Asse_A,P4,q_ini,rot);
Mec = SCARA40.fkine(q);
Pec = Mec(:,4);
if (dot((P4-Pec),(P4-Pec),3)>0.0001)
fprintf(1,'\n P4 Desid. = [%9.1f %9.1f %9.1f %9.1f ] \n',P4);
fprintf(1,'\n P4 Attuato = [%9.1f %9.1f %9.1f %9.1f ] \n',Pec);
contaerro = contaerro + 1;
else
q_ini = q;
end
SCARA40.plot(q);
XX(ipunto) = Pec(1);
YY(ipunto) = Pec(2);
ZZ(ipunto) = Pec(3);
if(nargin>6)
color = varargin{1};
else
color = 'r';
end
plot3(XX,YY,ZZ,color,'LineWidth',1 );
drawnow;
hold on
end
end
the test function with the results
Punti = [ 10,10,0,1 ;10,-10,0,1 ;-10,-10,0,1 ; -10,10,0,1 ] ;
%inizio=1
%retta(Punti(1,:)',Punti(2,:)',Asse_A,q_ini,rot,contaerro)
%inizio=21
%retta(Punti(2,:)',Punti(3,:)',Asse_A,q_ini,rot,contaerro)
%inizio=41
%retta(Punti(3,:)',Punti(4,:)',Asse_A,q_ini,rot,contaerro)
%inizio=61
inizio=1
for i=1:length(Punti)-1
retta(Punti(i,:)',Punti(i+1,:)',Asse_A,q_ini,rot,contaerro)
inizio=inizio+20;
end
the two images have been generated restarting Matlab
Addressing the question in the most general sense (since there is no sample given for the function fx or the function/variable Punti) then the reason you are getting different results is likely that the state of your variables/workspace is different when you test one case versus the other. How could this happen? Here are some obvious ways...
Your functions (or possibly other functions they call) are making use of the random number generator, and the starting state of the RNG is different when you test the loop versus unrolled loop case.
Your functions are sharing global variables that aren't reset to some default value at the start of each test case. You mention in a comment that the functions use global variables, so this is likely your problem.
Your functions aren't really functions, but scripts. Scripts all share a common workspace (the base workspace), whereas a function (and specifically each call to a function) will have its own unique workspace. If fx is actually a script, each call may change any or all of the variables in the base workspace. Furthermore, any other scripts, or anything you type into the command line, can change things as well. The contents of the base workspace may therefore be different when you test the loop versus unrolled loop case.
If I were to hazard a guess, I'd say that if you were to exit and restart MATLAB before each test case (i.e. reset everything to the same default starting state) you would probably get the same exact result for the loop versus unrolled loop case.
I have a Problem with my callback functions.
I wrote a small Example For it :
function drawMeAnImrect
Numbers = [1,2,3];
h = imrect();
h.addNewPositionCallback(#(h)Randomfunc(Numbers));
h.wait();
Numbers = [1,2,3,4];
end
function Randomfunc(Numbers)
disp(Numbers)
end
everytime i move the rect i will print the Numbers 1,2 and 3, even if i change the Numbers. Ist there a way to avoid this ? It works with global variables but i dont wanna use many global variables. I hope someone can help me. Thx
I have a situation in MATLAB where I want to try to assign a struct field into a new variable, like this:
swimming = fish.carp;
BUT the field carp may or may not be defined. Is there a way to specify a default value in case carp is not a valid field? For example, in Perl I would write
my $swimming = $fish{carp} or my $swimming = 0;
where 0 is the default value and or specifies the action to be performed if the assignment fails. Seems like something similar should exist in MATLAB, but I can't seem to find any documentation of it. For the sake of code readability I'd rather not use an if statement or a try/catch block, if I can help it.
You can make your own function to handle this and keep the code rather clear. Something like:
swimming = get_struct(fish, 'carp', 0);
with
function v = get_struct(s, f, d)
if isfield(s, f)
v = s.(f); % Struct value
else
v = d; % Default value
end
Best,
From what I know, you can't do it in one line in MATLAB. MATLAB logical constructs require explicit if/else statements and can't do it in one line... like in Perl or Python.
What you can do is check to see if the fish structure contains the carp field. If it isn't, then you can set the default value to be 0.
Use isfield to help you do that. Therefore:
if isfield(fish, 'carp')
swimming = fish.carp;
else
swimming = 0;
end
Also, as what Ratbert said, you can put it into one line with commas... but again, you still need that if/else construct:
if isfield(fish,'carp'), swimming = fish.carp; else, swimming = 0;
Another possible workaround is to declare a custom function yourself that takes in a structure and a field, and allow it to return the value at the field, or 0.
function [out] = get_field(S, field)
if isfield(S, field)
out = S.(field);
else
out = 0;
end
Then, you can do this:
swimming = get_field(fish, 'carp');
swimming will either by 0, or fish.carp. This way, it doesn't sacrifice code readability, but you'll need to create a custom function to do what you want.
If you don't like to define a custom function in a separate function file - which is certainly a good option - you can define two anonymous functions at the beginning of your script instead.
helper = {#(s,f) 0, #(s,f) s.(f)}
getfieldOrDefault = #(s,f) helper{ isfield(s,f) + 1 }(s,f)
With the definition
fish.carp = 42
and the function calls
a = getfieldOrDefault(fish,'carp')
b = getfieldOrDefault(fish,'codfish')
you get for the first one
a = 42
and the previous defined default value for the second case
b = 0