how i can to align a widget with Gtk_Grid - ada

i've this code
Main.adb
With Gtk.Main; Use Gtk.Main;
With Gtk.Window; Use Gtk.Window;
With Gtk.Button; Use Gtk.Button;
With Gtk.Widget; Use Gtk.Widget;
With Gtk.Grid; Use Gtk.Grid;
Procedure Main is
Win : Gtk_Window;
Button : Gtk_Button;
Button2 : Gtk_Button;
Button3 : Gtk_Button;
Grid : Gtk_Grid;
begin
Init;
Gtk_New (Win);
Win.Set_Default_Size (Width => 380,
Height => 502);
Gtk_New (Button,"Button");
Gtk_New (Button2,"Button2");
Gtk_New (Button3,"Button3");
Gtk_New (Grid);
Grid.Attach (Button,0,0);
Grid.Attach (Button2,0,100);
Grid.Attach (Button3,75,20);
Win.Add (Grid);
Win.Show_All;
Gtk.Main.Main;
end Main;
here I would like my first button to be at the top on the far left, my third button to the right but at the bottom and my Second button must be at the bottom. I tried almost all the methods but still in vain I can't align my widgets correctly so does anyone have an idea on how I can align all my widgets with a Gtk_Grid.

You can just add widgets to cells. As is already stated in the comment, the Top and Left parameters of Attach represent the cell indices (row/column), not pixels. See the annotated example below.
If you want to position a widget using coordinates, then you can use a GTK Fixed container. Be aware, however, that this container is almost never a good solution for positioning widgets; widgets should, in principle, always be positioned using layout containers HBox, VBox, Table, Grid and Layout. This is to ensure proper widget composition regardless of the screen resolution and screen size of the user. This is emphasized in the description of the Fixed widget:
For most applications, you should not use this container! It keeps you from having to learn about the other GTK+ containers, but it results in broken applications.
I've added an example that shows the usage of both container widgets, Grid and Fixed. In these example, I've moved the GTK code into a separate package in order to add a Destroy_Event_Callback which in turn will call Gtk.Main.Main_Quit to stop the GTK event loop and properly exit the program when you close the window.
app.adb (using GTK Fixed, see manual and GtkAda source)
with Gtk.Main; use Gtk.Main;
with Gtk.Window; use Gtk.Window;
with Gtk.Button; use Gtk.Button;
with Gtk.Widget; use Gtk.Widget;
with Gtk.Fixed; use Gtk.Fixed;
package body App is
procedure Destroy_Event_Callback
(Widget : access Gtk.Widget.Gtk_Widget_Record'Class);
---------
-- Run --
---------
procedure Run is
Win : Gtk_Window;
Button_1 : Gtk_Button;
Button_2 : Gtk_Button;
Button_3 : Gtk_Button;
Fixed : Gtk_Fixed;
begin
Gtk.Main.Init;
Gtk_New (Win);
Win.Set_Default_Size (Width => 380, Height => 502);
Win.On_Destroy (Call => Destroy_Event_Callback'Access);
Gtk_New (Button_1, "Button 1");
Gtk_New (Button_2, "Button 2");
Gtk_New (Button_3, "Button 3");
Gtk_New (Fixed);
-- Add the buttons to the container with their
-- top-left corner at the specified coordinate.
Fixed.Put (Button_1, X => 0, Y => 0);
Fixed.Put (Button_2, X => 50, Y => 100);
Fixed.Put (Button_3, X => 200, Y => 100);
Win.Add (Fixed);
Win.Show_All;
Gtk.Main.Main;
end Run;
----------------------------
-- Destroy_Event_Callback --
----------------------------
procedure Destroy_Event_Callback
(Widget : access Gtk.Widget.Gtk_Widget_Record'Class)
is
begin
Gtk.Main.Main_Quit;
end Destroy_Event_Callback;
end App;
app.adb (using GTK Grid, see manual and GtkAda source)
with Gtk.Main; use Gtk.Main;
with Gtk.Window; use Gtk.Window;
with Gtk.Button; use Gtk.Button;
with Gtk.Widget; use Gtk.Widget;
with Gtk.Grid; use Gtk.Grid;
package body App is
procedure Destroy_Event_Callback
(Widget : access Gtk.Widget.Gtk_Widget_Record'Class);
---------
-- Run --
---------
procedure Run is
Win : Gtk_Window;
Button_1 : Gtk_Button;
Button_2 : Gtk_Button;
Button_3 : Gtk_Button;
Grid : Gtk_Grid;
begin
Gtk.Main.Init;
Gtk_New (Win);
Win.Set_Default_Size (Width => 380, Height => 502);
Win.On_Destroy (Call => Destroy_Event_Callback'Access);
Gtk_New (Button_1, "Button 1");
Gtk_New (Button_2, "Button 2");
Gtk_New (Button_3, "Button 3");
Gtk_New (Grid);
-- Stretch the grid to the size of the window.
Grid.Set_Hexpand (True);
Grid.Set_Vexpand (True);
-- Make all cells have the same width/height.
Grid.Set_Column_Homogeneous (True);
Grid.Set_Row_Homogeneous (True);
-- Insert the buttons into the grid.
--
-- Left: 0 1 2
-- Top: +------+------+------+
-- 0 | Btn1 | | |
-- +------+------+------+
-- 1 | | Btn3 | Btn2 |
-- +------+------+------+
--
Grid.Attach (Button_1, Left => 0, Top => 0);
Grid.Attach (Button_2, Left => 2, Top => 1);
Grid.Attach (Button_3, Left => 1, Top => 1);
Win.Add (Grid);
Win.Show_All;
Gtk.Main.Main;
end Run;
----------------------------
-- Destroy_Event_Callback --
----------------------------
procedure Destroy_Event_Callback
(Widget : access Gtk.Widget.Gtk_Widget_Record'Class)
is
begin
Gtk.Main.Main_Quit;
end Destroy_Event_Callback;
end App;
app.ads
package App is
procedure Run;
end App;
main.adb
with App;
procedure Main is
begin
App.Run;
end Main;

hello here I tried to find a solution so I created a gtk_alignment then I added it to my gtk_grid but a concern in all that is that gtk_alignment does not align the widget at the defined place
here the new code
Main.adb
With Gtk.Main; Use Gtk.Main;
With Gtk.Window; Use Gtk.Window;
With Gtk.Button; Use Gtk.Button;
With Gtk.Widget; Use Gtk.Widget;
With Gtk.Grid; Use Gtk.Grid;
With Test2; Use Test2;
With Gtk.Alignment; Use Gtk.Alignment;
Procedure Main is
Win : Gtk_Window;
Button_1 : Gtk_Button;
Button_2 : Gtk_Button;
Grid : Gtk_Grid;
Align : Gtk_Alignment;
begin
Init;
Gtk_New (Win);
Win.Set_Default_Size (Width => 300, Height => 502);
Win.On_Destroy (Test2.Exit_Window'Access);
Gtk_New (Grid);
-- Initialize the Gtk.Button.Gtk_Button
Gtk_New (Button_1,"Button 1");
Gtk_New (Button_2,"Button 2");
-- Add the Button_1
Grid.Attach (Button_1,0,0);
-- Initialize Gtk_Alignment and align the button_2
Gtk_New (Align,
Xalign => 0.5,
Yalign => 1.0,
Xscale => 1.0,
Yscale => 0.2);
Align.Add (Button_2);
Grid.Add (Align);
Win. Add (Grid);
Win.Show_All;
Gtk.Main.Main;
end Main;
I want my first button to be at the very top left of the window and my second button at the bottom of the window.

Related

Mecanum car with 4 motors

I am trying to rotate a mecanum car 360 degrees to right. The car uses 4 motors along with L298N driver. Every other direction works fine, only when I try to rotate the car 360 degrees it rotates opposite side. LEFT is left side wheels; RIGHT is right side wheels. Here is the code:
with MicroBit.IOs;
with MicroBit;
with movement;
procedure Main is
Speed : constant MicroBit.IOs.Analog_Value := 1023; --between 0 and 1023
Forward : constant Boolean := True; -- forward is true, backward is false
begin
-- We set the frequency by setting the period (remember f=1/t).
MicroBit.IOs.Set_Analog_Period_Us(20000); -- 50 Hz = 1/50 = 0.02s = 20 ms = 20000us
--LEFT
--front
MicroBit.IOs.Set(6, Forward); --IN1
MicroBit.IOs.Set(7, not Forward); --IN2
--back
MicroBit.IOs.Set(2, Forward); --IN3
MicroBit.IOs.Set(3, not Forward); --IN4
--RIGHT
--front
MicroBit.IOs.Set(12, not Forward); --IN1
MicroBit.IOs.Set(13, Forward); --IN2
--back
MicroBit.IOs.Set(14, not Forward); --IN3
MicroBit.IOs.Set(15, Forward); --IN4
MicroBit.IOs.Write (0, Speed); --left speed control ENA ENB
MicroBit.IOs.Write (1, Speed); --right speed control ENA ENB
loop
null;
end loop;
end Main;
I tried to do it with procedure at first, but it did not work. I tried to do it on main to find the issue did not work. The weird part is that when I make the car go front it works just fine; when I try to make one side of the car go opposite direction for some weird reason the control suddenly happens opposite way.
It's hard to help without the actual hardware to test on, but googling datasheets and tutorials for L298N suggests the max value for speed may be 255, not 1023. Could this be related to your issue?
Anyway, I would declare some types and helper procedures, and move the magic numbers out of the control logic. Something like this:
(disclaimer: It haven't compiled or tested any of this...)
type Wheel_Pins is
record
Pin1 : Microbit.IOs.Pin_ID; -- IN1 / IN3
Pin2 : Microbit.IOs.Pin_ID; -- IN2 / IN4
end record;
type Sides is (Left, Right);
type Ends is (Front, Back);
type Spin_Direction is (Off, Backward, Forward);
subtype Wheel_Speed is MicroBit.IOs.Analog_Value range 0..255;
procedure Spin_Wheel( Wheel : Wheel_Pins; Direction : Spin_Direction) is
function Low return Boolean renames False;
function High return Boolean renames True;
begin
case Direction is
when Off =>
Microbit.IOs.Set(Wheel.Pin1, Low);
Microbit.IOs.Set(Wheel.Pin2, Low);
when Backward =>
Microbit.IOs.Set(Wheel.Pin1, Low);
Microbit.IOs.Set(Wheel.Pin2, High);
when Forward =>
Microbit.IOs.Set(Wheel.Pin1, High);
Microbit.IOs.Set(Wheel.Pin2, Low);
end case;
end Spin_Wheel;
procedure Set_Speed(Speed : Wheel_Speed; Vehicle_Side : Sides) is
To_Analog_Value : constant array(Sides'Range) --'
of Microbit.IOs.Pin_ID := (Left => 0, Right => 1);
begin
MicroBit.IOs.Write (To_Analog_Value(Vehicle_Side), Speed);
end Set_Speed;
procedure Stop_All_Wheels is
begin
for Wheel of Wheels'Range loop
Spin_Wheel(Wheel, Off);
end loop;
end Stop_All_Wheels;
The pin numbers could then be "hidden" like this:
Wheels : constant array(Ends, Sides) of Wheel_Pins :=
(Front => (Left => Wheel_Pins' --'
(Pin1 => 6,
Pin2 => 7),
Right => Wheel_Pins' --'
(Pin1 => 12,
Pin2 => 13)),
Back => (Left => Wheel_Pins' --'
(Pin1 => 2,
Pin2 => 3),
Right => Wheel_Pins' --'
(Pin1 => 14,
Pin2 => 15)));
I would then test each wheel individually, before combinations of wheels, to make sure the code works as expected:
Stop_All_Wheels;
Set_Speed(Left, 60);
Set_Speed(Right, 60);
-- test all wheels, both forward, backward and off,
-- in the following order: front-left, front-right, rear-left, rear-right
for Vehicle_Side in Wheel'Range(2) loop
for Vehicle_End in Wheels'Range(1) loop
for Direction in reverse Spin_Direction'Range loop
Spin_Wheel(Wheels(Vehicle_End, Vehicle_Side), Direction);
delay 2.0;
end loop;
end loop;
end loop;
-- test two wheels at a time:
for Direction in reverse Spin_Direction'Range loop
for Vehicle_End in Wheels'Range loop
Spin_Wheel(Wheels(Vehicle_End, Left), Direction);
end loop;
delay 2.0;
end loop;
-- test rotation clockwise
Spin_Wheel(Wheels(Front, Left), Forward);
Spin_Wheel(Wheels(Back, Left), Forward);
Spin_Wheel(Wheels(Front, Right), Backward);
Spin_Wheel(Wheels(Back, Right), Backward);
delay 2.0;
Stop_All_Wheels;
-- test rotation counter-clockwise
Spin_Wheel(Wheels(Front, Left), Backward);
Spin_Wheel(Wheels(Back, Left), Backward);
Spin_Wheel(Wheels(Front, Right), Forward);
Spin_Wheel(Wheels(Back, Right), Forward);
delay 2.0;
Stop_All_Wheels;
-- test speed settings
for Wheel of Wheels'Range loop
Spin_Wheel(Wheel, Forward);
end loop;
Speed_Up: for Speed in Wheel_Speed'Range loop
Set_Speed(Left, Speed);
Set_Speed(Right, Speed);
delay 0.01;
end loop Speed_Up;
delay 1.0;
Speed_Down: for Speed in reverse Wheel_Speed'Range loop
Set_Speed(Left, Speed);
Set_Speed(Right, Speed);
end loop Speed_Down;

Drawing Bowling Pins (pyramid) with Recursion in Ada

I know this is pushing the good will of the community by presenting my least elaborate work expecting someone to come and save me but I simply have no choice with nothing to lose. I've gone through packets, files, types, flags and boxes the last few weeks but I haven't covered much of recursion. Especially not drawing with recursion. My exam is in roughly one week and I hope this is ample time to repeat and learn simple recursion tricks like that of drawing bowling pins or other patterns:
I I I I I
I I I I
I I I
I I
I
n = 5
The problem I have with recursion is that I don't quite get it. How are you supposed to approach a problem like drawing pins like this using recursion?
The closest I've come is
I I I
I I
I
n = 3
and that's using
THIS CODE NOW WORKS
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure pyramidchaser is
subtype X_Type is Integer range 0..30;
X: X_Type;
Indent : Integer := 0;
procedure Numbergrabber (X : out Integer) is
begin
Put("Enter a number: ");
Get(X);
Skip_Line;
end Numbergrabber;
procedure PrintSpaces(I : in Integer) is
begin
if I in X_Type then
Put(" ");
else
return;
end if;
PrintSpaces(I - 1);
end Printspaces;
procedure PrintPins(i, n: in Integer) is
begin
if i >= n then
return;
end if;
Put('I');
Put(' ');
PrintPins(i + 1, n);
end Printpins;
function Pins (X, Indent: in Integer) return Integer is
Printed : Integer;
begin
Printed:= 0;
if X > 0 then
PrintSpaces(Indent);
PrintPins(0, X);
New_Line;
Printed := X + Pins(X - 1, Indent + 1);
end if;
return Printed;
end Pins;
Bowlingpins : Integer;
begin
Numbergrabber(X);
Bowlingpins:= Pins(X, Indent);
end pyramidchaser;
but with that I throw in a sum I dont really need just to kick off the recursive part which I dont really know why I do except it seems to be needed to be there. I experimented with code from a completely different assignment, thats why it looks the way it does. I know mod 2 will grant me too many new lines but at least it was an approach to finding heights to the pyramid. I understand the real approach is something similar to N+1 as with each step of the growing pyramid a new line is needed, but I dont know how to implement it.
I don't expect anyone to present a complete code but I hope somebody can clue me in on how to think on the way towards finding a solution.
I can still pass the exam without knowing recursion as its typically 2 assignments where one is and one isnt recursion and you need to pass one or the other, but given that I have some time I thought Id give it a chance.
As always, immensely thankful for anyone fighting the good fight!
Seeing this post gathered some attention Id like to expand the pyramid to one a little more complex:
THE PYRAMID PROBLEM 2
hope someone looks at it. I didnt expect so many good answers, I thought why not throw all I have out there.
Level 1
|=|
Level 2
|===|
||=||
|===|
Level 3
|=====|
||===||
|||=|||
||===||
|=====|
it needs to be figured out recursively. so some way it has to build both upwards and downwards from the center.
To clarify Im studying for an exam and surely so are many others whom would be thankful for code to sink their teeth in. Maybe theres an easy way to apply what Tama built in terms of bowling pin lines in pyramid walls?
Bowling pins:
Printing ----I---- is just: (I'm going to use dashes instead of spaces throughout for readability)
Put_Line (4 * "-" & "I" & 4 * "-");
And printing the whole bowling triangle could be:
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
procedure Print_Bowling_Line (Dashes : Natural; Pins : Positive) is
Middle : constant String := (if Pins = 1 then "I"
else (Pins - 1) * "I-" & "I");
begin
Put_Line (Dashes * "-" & Middle & Dashes * "-");
end Print_Bowling_Line;
begin
Print_Bowling_Line (0, 5);
Print_Bowling_Line (1, 4);
Print_Bowling_Line (2, 3);
Print_Bowling_Line (3, 2);
Print_Bowling_Line (4, 1);
end Main;
Writing the five repeated lines as a loop is quite obvious. For recursion, there are two ways.
Tail recursion
Tail recursion is the more natural 'ask questions, then shoot' approach; first check the parameter for an end-condition, if not, do some things and finally call self.
procedure Tail_Recurse (Pins : Natural) is
begin
if Pins = 0 then
return;
end if;
Print_Bowling_Line (Total_Pins - Pins, Pins);
Tail_Recurse (Pins - 1);
end Tail_Recurse;
Head recursion
Head recursion is what mathematicians love; how do you construct a proof for N? Well, assuming you already have a proof for N-1, you just apply X and you're done. Again, we need to check the end condition before we go looking for a proof for N-1, otherwise we would endlessly recurse and get a stack overflow.
procedure Head_Recurse (Pins : Natural) is
begin
if Pins < Total_Pins then
Head_Recurse (Pins + 1); -- assuming N + 1 proof here
end if;
Print_Bowling_Line (Total_Pins - Pins, Pins);
end Head_Recurse;
For the full code, expand the following snippet:
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
Total_Pins : Natural := 5;
procedure Print_Bowling_Line (Dashes : Natural; Pins : Positive) is
Middle : constant String := (if Pins = 1 then "I"
else (Pins - 1) * "I-" & "I");
begin
Put_Line (Dashes * "-" & Middle & Dashes * "-");
end Print_Bowling_Line;
procedure Tail_Recurse (Pins : Natural) is
begin
if Pins = 0 then
return;
end if;
Print_Bowling_Line (Total_Pins - Pins, Pins);
Tail_Recurse (Pins - 1);
end Tail_Recurse;
procedure Head_Recurse (Pins : Natural) is
begin
if Pins < Total_Pins then
Head_Recurse (Pins + 1); -- assuming N + 1 proof here
end if;
Print_Bowling_Line (Total_Pins - Pins, Pins);
end Head_Recurse;
begin
Total_Pins := 8;
Head_Recurse (1);
end Main;
For simplicity, I don't pass around the second number, that indicates the stopping condition, but rather set it once before running the whole.
I always find it unfortunate to try to learn a technique by applying it where it makes the code more complicated, rather than less. So I want to show you a problem where recursion really does shine. Write a program that prints a maze with exactly one path between every two points in the maze, using the following depth-first-search pseudo code:
start by 'visiting' any field (2,2 in this example)
(recursion starts with this:)
while there are any neighbours that are unvisited,
pick one at random and
connect the current field to that field and
run this procedure for the new field
As you can see in the animation below, this should meander randomly until it gets 'stuck' in the bottom left, after which it backtracks to a node that still has an unvisited neighbour. Finally, when everything is filled, all the function calls that are still active will return because for each node there will be no neighbours left to connect to.
You can use the skeleton code in the snippet below. The answer should only modify the Depth_First_Make_Maze procedure. It need be no longer than 15 lines, calling Get_Unvisited_Neighbours, Is_Empty on the result, Get_Random_Neighbour and Connect (and itself, of course).
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Vectors;
with Ada.Numerics.Discrete_Random;
procedure Main is
N : Positive := 11; -- should be X*2 + 1 for some X >= 1
type Cell_Status is (Filled, Empty);
Maze : array (1 .. N, 1 .. N) of Cell_Status := (others => (others => Filled));
procedure Print_Maze is
begin
for Y in 1 .. N loop
for X in 1 .. N loop
declare
C : String := (case Maze (X, Y) is
--when Filled => "X", -- for legibility,
when Filled => "█", -- unicode block 0x2588 for fun
when Empty => " ");
begin
Put (C);
end;
end loop;
Put_Line ("");
end loop;
end Print_Maze;
type Cell_Address is record
X : Positive;
Y : Positive;
end record;
procedure Empty (Address : Cell_Address) is
begin
Maze (Address.X, Address.Y) := Empty;
end Empty;
procedure Connect (Address1 : Cell_Address; Address2 : Cell_Address) is
Middle_X : Positive := (Address1.X + Address2.X) / 2;
Middle_Y : Positive := (Address1.Y + Address2.Y) / 2;
begin
Empty (Address1);
Empty (Address2);
Empty ((Middle_X, Middle_Y));
end Connect;
function Cell_At (Address : Cell_Address) return Cell_Status is (Maze (Address.X, Address.Y));
function Left (Address : Cell_Address) return Cell_Address is (Address.X - 2, Address.Y);
function Right (Address : Cell_Address) return Cell_Address is (Address.X + 2, Address.Y);
function Up (Address : Cell_Address) return Cell_Address is (Address.X, Address.Y - 2);
function Down (Address : Cell_Address) return Cell_Address is (Address.X, Address.Y + 2);
type Neighbour_Count is new Integer range 0 .. 4;
package Neighbours_Package is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => Cell_Address);
use Neighbours_Package;
function Get_Unvisited_Neighbours (Address : Cell_Address) return Neighbours_Package.Vector is
NeighbourList : Neighbours_Package.Vector;
begin
NeighbourList.Reserve_Capacity (4);
if Address.X >= 4 then
declare
L : Cell_Address := Left (Address);
begin
if Cell_At (L) = Filled then
NeighbourList.Append (L);
end if;
end;
end if;
if Address.Y >= 4 then
declare
U : Cell_Address := Up (Address);
begin
if Cell_At (U) = Filled then
NeighbourList.Append (U);
end if;
end;
end if;
if Address.X <= (N - 3) then
declare
R : Cell_Address := Right (Address);
begin
if Cell_At (R) = Filled then
NeighbourList.Append (R);
end if;
end;
end if;
if Address.Y <= (N - 3) then
declare
D : Cell_Address := Down (Address);
begin
if Cell_At (D) = Filled then
NeighbourList.Append (D);
end if;
end;
end if;
return NeighbourList;
end Get_Unvisited_Neighbours;
package Rnd is new Ada.Numerics.Discrete_Random (Natural);
Gen : Rnd.Generator;
function Get_Random_Neighbour (List : Neighbours_Package.Vector) return Cell_Address is
Random : Natural := Rnd.Random (Gen);
begin
if Is_Empty (List) then
raise Program_Error with "Cannot select any item from an empty list";
end if;
declare
Index : Natural := Random mod Natural (List.Length);
begin
return List (Index);
end;
end Get_Random_Neighbour;
procedure Depth_First_Make_Maze (Address : Cell_Address) is
begin
null;
end Depth_First_Make_Maze;
begin
Rnd.Reset (Gen);
Maze (1, 2) := Empty; -- entrance
Maze (N, N - 1) := Empty; -- exit
Depth_First_Make_Maze ((2, 2));
Print_Maze;
end Main;
To see the answer, expand the following snippet.
procedure Depth_First_Make_Maze (Address : Cell_Address) is
begin
loop
declare
Neighbours : Neighbours_Package.Vector := Get_Unvisited_Neighbours (Address);
begin
exit when Is_Empty (Neighbours);
declare
Next_Node : Cell_Address := Get_Random_Neighbour (Neighbours);
begin
Connect (Address, Next_Node);
Depth_First_Make_Maze (Next_Node);
end;
end;
end loop;
end Depth_First_Make_Maze;
Converting recursion to loop
Consider how a function call works; we take the actual parameters and put them on the call stack along with the function's address. When the function completes, we take those values off the stack again and put back the return value.
We can convert a recursive function by replacing the implicit callstack containing the parameters with an explicit stack. I.e. instead of:
procedure Foo (I : Integer) is
begin
Foo (I + 1);
end Foo;
We would put I onto the stack, and as long as there are values on the stack, peek at the top value and do the body of the Foo procedure using that value. When there is a call to Foo within that body, push the value you would call the procedure with instead, and restart the loop so that we immediately start processing the new value. If there is no call to self in this case, we discard the top value on the stack.
Restructuring a recursive procedure in this way will give you an insight into how it works, especially since pushing on to the stack is now separated from 'calling' that function since you explicitly take an item from the stack and do something with it.
You will need a stack implementation, here is one that is suitable:
Bounded_Stack.ads
generic
max_stack_size : Natural;
type Element_Type is private;
package Bounded_Stack is
type Stack is private;
function Create return Stack;
procedure Push (Onto : in out Stack; Item : Element_Type);
function Pop (From : in out Stack) return Element_Type;
function Top (From : in out Stack) return Element_Type;
procedure Discard (From : in out Stack);
function Is_Empty (S : in Stack) return Boolean;
Stack_Empty_Error : exception;
Stack_Full_Error : exception;
private
type Element_List is array (1 .. max_stack_size) of Element_Type;
type Stack is
record
list : Element_List;
top_index : Natural := 0;
end record;
end Bounded_Stack;
Bounded_Stack.adb
package body Bounded_Stack is
function Create return Stack is
begin
return (top_index => 0, list => <>);
end Create;
procedure Push (Onto : in out Stack; Item : Element_Type) is
begin
if Onto.top_index = max_stack_size then
raise Stack_Full_Error;
end if;
Onto.top_index := Onto.top_index + 1;
Onto.list (Onto.top_index) := Item;
end Push;
function Pop (From : in out Stack) return Element_Type is
Top_Value : Element_Type := Top (From);
begin
From.top_index := From.top_index - 1;
return Top_Value;
end Pop;
function Top (From : in out Stack) return Element_Type is
begin
if From.top_index = 0 then
raise Stack_Empty_Error;
end if;
return From.list (From.top_index);
end Top;
procedure Discard (From : in out Stack) is
begin
if From.top_index = 0 then
raise Stack_Empty_Error;
end if;
From.top_index := From.top_index - 1;
end Discard;
function Is_Empty (S : in Stack) return Boolean is (S.top_index = 0);
end Bounded_Stack;
It can be instantiated with a maximum stack size of Width*Height, since the worst case scenario is when you happen to choose a non-forking path that visits each cell once:
N_As_Cell_Size : Natural := (N - 1) / 2;
package Cell_Stack is new Bounded_Stack(max_stack_size => N_As_Cell_Size * N_As_Cell_Size, Element_Type => Cell_Address);
Take your answer to the previous assignment, and rewrite it without recursion, using the stack above instead.
To see the answer, expand the following snippet.
procedure Depth_First_Make_Maze (Address : Cell_Address) is
Stack : Cell_Stack.Stack := Cell_Stack.Create;
use Cell_Stack;
begin
Push (Stack, Address);
loop
exit when Is_Empty (Stack);
declare
-- this shadows the parameter, which we shouldn't refer to directly anyway
Address : Cell_Address := Top (Stack);
Neighbours : Neighbours_Package.Vector := Get_Unvisited_Neighbours (Address);
begin
if Is_Empty (Neighbours) then
Discard (Stack); -- equivalent to returning from the function in the recursive version
else
declare
Next_Cell : Cell_Address := Get_Random_Neighbour (Neighbours);
begin
Connect (Address, Next_Cell);
Push (Stack, Next_Cell); -- equivalent to calling self in the recursive version
end;
end if;
end;
end loop;
end Depth_First_Make_Maze;
You’re going to print as many lines as there are pins. Each line has an indentation consisting of a number of spaces and a number of pins, each printed as "I " (OK,there's an extra space at the end of the line, but no one's going to see that).
Start off with no leading spaces and the number of pins you were asked to print.
The next line needs one more leading space and one fewer pin (unless, of course, that would mean printing no pins, in which case we're done).
I don't program in Ada (it looks like a strange Pascal to me), but there is an obvious algorithmic problem in your Pins function.
Basically, if you want to print a pyramid whose base is N down to the very bottom where base is 1, you would need to do something like this (sorry for crude pascalization of the code).
procedure PrintPins(i, n: Integer)
begin
if i >= n then return;
Ada.Text_IO.Put('I'); // Print pin
Ada.Text_IO.Put(' '); // Print space
PrintPins(i + 1, n); // Next iteration
end;
function Pins(x, indent: Integer): Integer
printed: Integer;
begin
printed := 0;
if x > 0 then
PrintSpaces(indent); // Print indentation pretty much using the same artificial approach as by printing pins
PrintPins(0, x);
(*Print new line here*)
(*Now go down the next level and print another
row*)
printed := x + Pins(x - 1, indent + 1);
end;
return printed;
end
P.S. You don't need a specific function to count the number of printed pins here. It's just a Gauss sequence sum of the range 1..N, which is given by N(N + 1)/2
A variation of this program is to use both head recursion and tail recursion in the same procedure.
The output of such a program is
Enter the number of rows in the pyramid: 5
I I I I I
I I I I
I I I
I I
I
I
I I
I I I
I I I I
I I I I I
N = 5
Tail recursion produces the upper triangle and head recursion produces the lower triangle.
The way you develop a recursive algorithm is to pretend that you already have a subprogram that does what you want, except for the first bit, and you know, or can figure out, how to do the first bit. Then your algorithm is
Do the first bit
Call the subprogram to do the rest on what remains,
taking into account the effect of the first bit, if necessary
The trick is that "Call the subprogram to do the rest" is a recursive call to the subprogram you're creating.
It's always possible that the subprogram may be called when there's nothing to do, so you have to take that into account:
if Ending Condition then
Do any final actions
return [expression];
end if;
Do the first bit
Call the subprogram to do the rest
And you're done. By repreatedly doing the first bit until the ending condition is True, you end up doing the whole thing.
As an example, the function Get_Line in Ada.Text_IO may be implemented (this is not how it is usually implemented) by thinking, "I know how to get the first Character of the line. If I have a function to return the rest of the line, then I can return the first Character concatenated with the function result." So:
function Get_Line return String is
C : Character;
begin
Get (Item => C);
return C & Get_Line;
end Get_Line;
But what if we're already at the end of a line, so there's no line to get?
function Get_Line return String is
C : Character;
begin
if End_Of_Line then
Skip_Line;
return "";
end if;
Get (Item => C);
return C & Get_Line;
end Get_Line;
For your problem the first bit is printing a row with an indent and a number of pins, and the ending condition is when there are no more rows to print.
For your pyramid problem, this tail recursion scheme doesn't work. You need to do "middle recursion":
if Level = 1 then
Print the line for Level
return
end if
Print the top line for Level
Recurse for Level - 1
Print the bottom line for Level

Refresh and execute a program in Autoit

I want to make a program in autoit with a GUI - 2 buttons: the first button must refresh a web page (Ex: Google page) every 3 seconds, the second button must stop the refresh and do some operations on the web page. I'm ok with the first button, but I cannot exit from the loop with the second button and execute the operation I want. Can someone help me? Thanks a lot! This is the code:
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
$Form1 = GUICreate("Example", 190, 133, 192, 124)
GUISetBkColor(0xFFFF00)
$Button1 = GUICtrlCreateButton("UPDATE", 3, 31 ,179, 37, 0)
$Button2 = GUICtrlCreateButton("EXECUTE", 3, 80 ,179, 37, 0)
GUICtrlSetColor(-1, 0x0000FF)
GUICtrlSetBkColor(-1, 0x00FF00)
GUICtrlSetCursor (-1, 0)
GUICtrlSetColor(-1, 0x000000)
GUISetState(#SW_SHOW)
While 1
$nMsg = GUIGetMsg()
Switch $nMsg
Case $GUI_EVENT_CLOSE
Exit
Case $button1
HotKeySet("{x}", "_Exit") ; Sets a shortcut to kill the script quickly. ESC
Opt("WinTitleMatchMode", 3) ; This is to tell the script to only refresh the EXACT window, incase there are similar windows. Such as: Window 1, Window 2.
While 1 ; This begins a While LOOP
Sleep(3000) ; Tell the script to pause for 3 seconds
$title = WinGetTitle("Google - Mozilla Firefox", "") ; Grabs the exactly title and saves it to a variable to do an If statement against.
If WinExists($title) Then ; Check to see if the desired window is running.
WinActivate($title) ; If it is running, first activate the window.
WinWaitActive($title) ; This tells the script to not send the refresh key (F5) until the window has appeared.
Send("{F5}") ; This sends the F5 key to refresh the window.
Else ; This is an else statement to tell the script to do a different operation if the window is not running.
Sleep(10) ; Just a filler item to wait until the desired window is running.
EndIf ; End the If statement to continue the script.
WEnd ; Close the While LOOP
Func _Exit() ; This is the function that is linked to the kill switch, ESC. It tells the script to perform the following actions.
ExitLoop ; Block pressing "x"
EndFunc ; Close the function up.
Case $button2
ExitLoop ; stop the loop and make operations below
Send("{TAB}")
Send("^v")
EndSwitch
WEnd
Something like this?
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
Global $Go2 = True
$Form1 = GUICreate("Example", 190, 133, 192, 124)
GUISetBkColor(0xFFFF00)
$Button1 = GUICtrlCreateButton("UPDATE", 3, 31, 179, 37, 0)
$Button2 = GUICtrlCreateButton("EXECUTE", 3, 80, 179, 37, 0)
GUICtrlSetColor(-1, 0x0000FF)
GUICtrlSetBkColor(-1, 0x00FF00)
GUICtrlSetCursor(-1, 0)
GUICtrlSetColor(-1, 0x000000)
GUISetState(#SW_SHOW)
Local $hTimer = TimerInit()
While True
$nMsg = GUIGetMsg()
Switch $nMsg
Case $GUI_EVENT_CLOSE
Exit
Case $Button1
$Go2 = True
HotKeySet("!{e}", "_Exit") ; Sets a shortcut to kill the script quickly. ESC
HotKeySet("{s}", "_ExitLoop") ; Sets a shortcut to kill the script quickly. ESC
Opt("WinTitleMatchMode", 3) ; This is to tell the script to only refresh the EXACT window, incase there are similar windows. Such as: Window 1, Window 2.
While $Go2 ; Avoid getting stuck in the while loop forever
If TimerDiff($hTimer) > 3000 Then
ConsoleWrite("Refreshing Mozilla" & #LF)
$title = WinGetTitle("Google - Mozilla Firefox", "") ; Grabs the exactly title and saves it to a variable to do an If statement against.
If WinExists($title) Then ; Check to see if the desired window is running.
WinActivate($title) ; If it is running, first activate the window.
WinWaitActive($title) ; This tells the script to not send the refresh key (F5) until the window has appeared.
Send("{F5}") ; This sends the F5 key to refresh the window.
$hTimer = TimerInit()
HotKeySet("{x}")
Else ; This is an else statement to tell the script to do a different operation if the window is not running.
;~ Sleep(10) ; Just a filler item to wait until the desired window is running.
EndIf ; End the If statement to continue the script.
EndIf
WEnd
$hTimer = TimerInit()
HotKeySet("{x}") ; Release hotkey
HotKeySet("!{e}") ; Release hotkeys
ConsoleWrite("Exited loop" & #LF)
Case $Button2
;you can't exit the loop from the $button1 here but there is a hotkey set to exit it, ALT + E
Send("{TAB}")
Send("^v")
EndSwitch
WEnd
Exit
Func _Exit()
Exit
EndFunc ;==>_Exit
Func _ExitLoop()
$Go2 = Not $Go2
EndFunc
P.S. I avoid using sleep because it pauses the script.
This is how I would do it.
Local $Refresh = 0 ; Set refresh OFF
Local $Timer = TimerInit()
Local $RefreshEvery = 3 ; seconds
Do
$msg = GUIGetMsg()
Select
Case $msg = $BtnRefresh
$Refresh = 1 ; Set refresh ON
Case $msg = $btnStop
$Refresh = 0 ; Set refresh OFF
EndSelect
If $Refresh And (TimerDiff($Timer) > $RefreshEvery*1000) Then
RefreshBrowser()
$Timer = TimerInit()
EndIf
Until $msg = $GUI_EVENT_CLOSE

Attaching button to existing application (AutoIT)

I have an existing .exe program (not mine so I can't edit it) that runs in a window of static size. What I want to do is overlay an extra button (preferably an image in the same style) on top of this window at specific coordinates within that window (and it has to move with the window if dragged elsewhere) to expand on it's functionality.
How do I go about this? I have searched for quite a bit, but wasn't able to find anything. I was able to create a Parent-Child in AutoIT itself, but cannot seem to attach a child to an existing application.
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
; NotepadAddOn
ShellExecute('notepad')
WinWaitActive("[CLASS:Notepad]", "")
$pos_A = WinGetPos("[CLASS:Notepad]", "")
$hGui = GUICreate('YOUR GUI', $pos_A[2], 50, $pos_A[0], $pos_A[1] - 50)
GUISetState(#SW_SHOW) ; will display an empty dialog box
AdlibRegister('_WinMove', 10)
; Run the GUI until the dialog is closed
While 1
$msg = GUIGetMsg()
If $msg = $GUI_EVENT_CLOSE Then ExitLoop
WEnd
GUIDelete()
Func _WinMove()
$p_A = WinGetPos("[CLASS:Notepad]", "")
WinMove($hGui, "", $p_A[0], $p_A[1] + $p_A[3])
EndFunc ;==>_WinMove

Multiple buttons

How to change this code to get it working with several (2, 3 or 4) buttons?
signal lastButtonState : std_logic := '0';
process(clk)
begin
if(rising_edge(clk)) then
if(buttonState = '1' and lastButtonState = '0') then --assuming active-high
--Rising edge - do some work...
end if;
lastButtonState <= buttonState;
end if;
end process;
I would like to get several buttons work and do some work on press 'state'. This part of code works for 1 button only.
Thanks
The std_logic_vector type can be used for multiple buttons. Code may look
like:
constant BUTTONS : positive := 4; -- Number of buttons
subtype buttons_t is std_logic_vector(1 to BUTTONS); -- Type for buttons
signal buttonsState : buttons_t; -- Input from buttons (remember to debounce)
signal lastButtonsState : buttons_t := (others => '0'); -- Last state
... -- End of declarations, and start of concurrent code (processes etc.)
process (clk)
variable buttonsRise_v : buttons_t; -- Support variable to make writing easier
begin
if rising_edge(clk) then
buttonsRise_v := buttonsState and (not lastButtonsState); -- Make '1' for each button going 0 to 1
if buttonsRise_v(1) = '1' then -- Detect button 1 going 0 to 1
-- Do some work...
end if;
-- More code reacting on buttons...
lastButtonsState <= buttonsState;
end if;
end process;

Resources