Perl Tk: Canvas dynamic updating - perl

I'm trying to animate the results of a mathematical process in a 2D canvas with Tk. I've decided to do it with Tk and not SDL because right now i'm working with both Linux and Windows machines and Strawberry Perl doesn't compile proberly in windows, whil Tk is up on both pcs.
What i would like to do with Tk is that:
1)Popping up the canvas while my program is working out the coordinates of the points i would like to draw.
2)Drawing them instantly into the canvas without waiting for the process to reach the end
It's actually a simple animation, where a bunch of points moves around the Canvas while my script updates their coordinates.
Here you have a code snippet i've been writing so far for a single point:
use Tk;
#calcuate the coordinate of a single point
$x=10;
$y=10;
$top = MainWindow->new();
# create a canvas widget
$canvas = $top->Canvas(width => 600, height => 400) -> pack();
# For example, let's create 1 point inside the canvas
$canvas->create ('oval', $x, $y, $x+3, $y+3, -fill=>"black"); # fill color of object
MainLoop;
The problem with the above code is that i would like to add my 'math' script inside it in order to update the $x and $y coordinates above (with some sort of for/while cycle) without shutting down the original canvas, by obtaining a single point moving all around it (atually there are more points i'm supposed to display but that's a minor detail).
FYI, using a simple for cycle embedding the ''Mainloop'' directive doesn't fix the problem.
Thanks in advance guys

Quoting from Mastering Perl/Tk, chapter 15 "Anatomy of the MainLoop":
Option 1: use your own MainLoop implementation
use Tk qw(:eventtypes);
while (Tk::MainWindow->Count) {
# process events - but don't block waiting for them
DoOneEvent(ALL_EVENTS | DONT_WAIT);
# your update implementation goes here
}
Option 2: use a repeat timer event
Later in the chapter it is stated that DoOneEvent() isn't really necessary for most stuff. You could use timer events instead, e.g.
my $update = sub {
# your update implementation goes here
};
# run update every 50ms
$top->repeat(50, $update);
MainLoop;

SOLUTION:
As per Stefan Becker suggested option nr.2, here is what has finally fixed the problem:
use Tk:
$top = MainWindow->new();
# create a canvas widget
$canvas = $top->Canvas(width => 600,
height => 400,
background => 'black') -> pack();
$x=300;
$y=200;
my $update = sub {
$canvas->delete('all'); #unquote this line if you don't want the original point positions to be drawn in the canvas. Viceversa
$x=$x+(-5+rand(10)); #white noise
$y=$y-(-5+rand(10)); #white noise
$canvas->create ('oval', $x , $y , $x+5, $y+5, -fill=>"red");
};
$top->repeat(50, $update);
MainLoop;
I've just added the statement $canvas->delete('all') at the beginning of the updating loop in order to draw just actual points and not the history.

Related

plotly go Scattermapbox is not showing markers

I some how have encountered some odd bug. When i try to create a Scattermapbox the markers dont render. This bug came out of no where, It was working perfectly fine then my internet went out and now for the last 8 hours it has not been working.
Ive tried running it in different IDE's
running it in google colab to make sure its not my machine
different data sets.
i am unsure what i have done wrong
The tooltips do display however when i hover over the invisible points.
and if use the export to png button everything is shown.
but no matter what it wont show up on the actual map itself and i am at my wits end.
I will include the callback function bellow.
#app.callback(
Output('2dmap','figure'),
[Input('2dgraph', 'clickData'),
Input('checklist', 'value')])
def update_map_2d(clickData,checklist):
# =============================================================================
# P1. Render Map when no point is clicked
# =============================================================================
# If No point has been clicked
if clickData is None:
#make a map
maps2d = go.Figure(go.Scattermapbox(
lat=[], # set lat and long
lon=[],
mode='markers',
marker =({'size':5.5}) # make markers size variable
))
# set up map layout
maps2d.update_layout(
autosize=True, # Autosize
hovermode='closest', # Show info on the closest marker
showlegend=True, # show legend of colors
mapbox=dict(
accesstoken=mapbox_access_token, # token
bearing=0, # starting facing direction
# starting location
center=dict(
lat=td.cLat,
lon=td.cLong
),
#angle and zoom
pitch=0,
zoom=12
),
#height and width
width=1000,
height=1000
)
return maps2d
else:
xCoord = int(clickData['points'][0]['x'])
yCoord = int(clickData['points'][0]["y"])
solutionRow = preatoFrontier.loc[(preatoFrontier['x'] == xCoord)&(preatoFrontier['y'] == yCoord)]
solId = int(solutionRow['SolId'])
#solId = 49
solution = td.getSolution(solutions, solId)
color = []
for row in solution['upGrade']:
if row == 0:
color.append('grey')
if row == 1:
color.append('green')
if row == 2:
color.append('blue')
if row == 3:
color.append('red')
solution['color'] = color
solution2 = solution[solution['color'].isin(checklist)]
maps2d = go.Figure(go.Scattermapbox(
lat=solution2['lat'],
lon=solution2['long'],
mode='markers',
#marker =({'color':solution['color']},{'size':5.5})
marker=dict(
size=12,
color=solution2['color'], #set color equal to a variable
colorscale='Viridis', # one of plotly colorscales
showscale=True
)
))
#=============================================================================
# P3. Map Layout
#=============================================================================
#set up map layout
maps2d.update_layout(
autosize=False, # Autosize
hovermode='closest', # Show info on the closest marker
showlegend=True, # show legend of colors
mapbox=dict(
accesstoken=mapbox_access_token, # token
bearing=0, # starting facing direction
# starting location
center=dict(
lat=td.cLat,
lon=td.cLong
),
#angle and zoom
pitch=0,
zoom=10
),
#height and width
width=1000,
height=1000
)
return maps2d
After a lot of hair pulling, i thought to try creating a new venv and uploading packages one by one and running to see how and where it fails. The last package i installed before it broke was dash-tools and sure enough some how that was causing mapbox to bug out hard. So dont install dash-tools

How to improve perl Gtk2 performance when we have a large number of widgets

I'm writing a perl GUI using Gtk2, it has a scrolled window which contains a number of GTk2::Expander which includes different widgets.
The poroblem is when i have a large number of Expanders (~80,000) the time it takes to perform show_all() is large, and when i open a single expander it takes several seconds. Running the same script with less expanders give a very fast response.
Since my expanders exists in a scrolled window and only part of them would be really visible, is there a way to improve the performance? like showing only the visible expanders and keeping all others as hidden?
I can't use GTK3, I need to use Perl and Gtk2 only.
Example Code:
my $ItemsCount = 10000;
my $ScWin = Gtk2::ScrolledWindow->new(undef, undef);
my $Frame = Gtk2::Frame->new('Items');
my $ItemsBox = Gtk2::Table->new(2, $ItemsCount+1, FALSE);
my $View = Gtk2::Viewport->new();
$ScWin->set_policy('automatic', 'automatic');
$View->add($ItemsBox);
$ScWin->add($View);
for my $i (0 .. $ItemsCount-1) {
my $exp = Gtk2::Expander->new();
$exp ->set_label("Item #$i");
$ItemsBox->attach($exp, 0, 1, $i, $i+1, 'fill', 'fill', 1, 2);
}
$MainBox ->pack_start ($ScWin , TRUE, TRUE, 0);

Modifying pixels in an image using Perl

Suppose I want to take one picture, move all of its pixels one pixel to the right and one to the left, and save it. I tried this code:
my $image_file = "a.jpg";
my $im = GD::Image->newFromJpeg($image_file);
my ($width, $height) = $im->getBounds();
my $outim = new GD::Image($width, $height);
foreach my $x (1..$width)
{
foreach my $y (1..$height)
{
my $index = $im->getPixel($x-1,$y-1);
my ($r,$g,$b) = $im->rgb($index);
my $color = $outim->colorAllocate($r,$g,$b);
$outim->setPixel($x,$y,$color);
}
}
%printing the picture...
That doesn't do the trick; it draws all pixels, except those in which x=0 or y=0, in one color. Where am I going wrong?
Look in the docs:
Images created by reading JPEG images will always be truecolor. To
force the image to be palette-based, pass a value of 0 in the optional
$truecolor argument.
It's not indexed. Try adding a ,0 to your newFromJpeg call.
From the comments, it seems your next problem is the number of colors to allocate. By default, the indexed image is 8-bit, meaning a maximum number of 256 unique colors (2^8=256). The "simple" workaround is of course to use a truecolor image instead, but that depends on whether you can accept truecolor output.
If not, your next challenge will be to come up with "an optimal" set of 256 colors that will minimize the visible defects in the image itself (see http://en.wikipedia.org/wiki/Color_quantization). That used to be a whole topic in itself that we seldom have to worry about today. If you still have to worry about it, you are probably better off offloading that job to some specialized tool like Imagemagik or similar, rather than try to implement it yourself. Unless you like challenges of course.
Here's a solution using Imager because it's a very nice module and I'm more familiar with it, and it handles image transformations nicely.
use Imager;
my $image_file = "a.jpg";
my $src = Imager->new(file => $image_file) or die Imager->errstr;
my $dest = Imager->new(
xsize => $src->getwidth() + 1,
ysize => $src->getheight() + 1,
channels => $src->getchannels
);
$dest->paste(left => 1, top => 1, src => $src);
$dest->write(file => "b.jpg") or die $dest->errstr;
Try reversing the direction of x and y - not from 1 to max but from max to 1. You are not sliding the colors but copying the same again and again.
I realize that this is an old post, but this is a piece of code that I use GD:Thumb for creating resized images.
sub png {
my ($orig,$n) = (shift,shift);
my ($ox,$oy) = $orig->getBounds();
my $r = $ox>$oy ? $ox / $n : $oy / $n;
my $thumb = GD::Image->newFromPng($ox/$r,$oy/$r,[0]);
$thumb->copyResized($orig,0,0,0,0,$ox/$r,$oy/$r,$ox,$oy);
return $thumb, sprintf("%.0f",$ox/$r), sprintf("%.0f",$oy/$r);
}

alternating panel animations inside a loop

I have two panels animating (blinking on/off) fine as heartbeats. However I want them to alternate beating instead of at the same time. So panel1 beats with a 10 count and then panel2 beats with a 10 count. I then want this to loop so it continues the pattern until the user presses the back key to exit the activity. I tried a few things with no luck. Here is the two panels beating together...
Sub beats
PNL1.Initialize("PNL1")
gdpanel1.Initialize("TOP_BOTTOM", Array As Int(Colors.DarkGray, Colors.Red))
gdpanel1.CornerRadius=0
PNL1.Background=gdpanel1
mainPNL.AddView(PNL1,0,0,100%x,50%y)
SetRadialGradientRed(gdpanel1,PNL1.Height/1.5)
a.InitializeAlpha("a",0,1)
a.Duration = 200
a.RepeatCount = 10
a.Start(PNL1)
PNL2.Initialize("PNL1")
gdpanel2.Initialize("TOP_BOTTOM", Array As Int(Colors.DarkGray, Colors.Blue))
gdpanel2.CornerRadius=0
PNL2.Background=gdpanel2
mainPNL.AddView(PNL2,0,50%y,100%x,50%y)
SetRadialGradientBlue(gdpanel2,PNL2.Height/1.5)
b.InitializeAlpha("b",0,1)
b.Duration = 200
b.RepeatCount = 10
b.Start(PNL2)
End Sub
Sub a_AnimationEnd
'
End Sub
Sub b_AnimationEnd
'
End Sub
You should start animation b in Sub a_AnimationEnd and start animation a in Sub b_AnimationEnd.

Controlling window position of a Powershell console

This works:
(Get-Host).UI.RawUI
$a = (Get-Host).UI.RawUI
$a.BackgroundColor = "white"
$a.ForegroundColor = "black"
$size = (Get-Host).UI.RawUI.WindowSize
$size.Width = 80
$size.Height = 30
(Get-Host).UI.RawUI.WindowSize = $size
But this doesn't work, and I am not sure how to make it work:
$position = (Get-Host).UI.RawUI.Windowposition
$position.X = 0
$position.Y = 30
(Get-Host).UI.RawUI.Windowposition = $position
The error I get is strange. It complains about "buffer" when I am trying to set external window position:
Exception setting "WindowPosition": "Cannot use the
specified Window X (column) position because it extends
past the width of the screen buffer. Specify another X
position, starting with 0 as the left most column of
the buffer.
The error is not really strange, because WindowPosition Gets and sets the position, in characters, of the view window relative to the screen buffer.
It does not set the position of the Window, but to put it crudely, the position in the buffer that you see through the view of the window. So in your case, you are getting the error because it is outside the buffer.
http://msdn.microsoft.com/en-us/library/system.management.automation.host.pshostrawuserinterface.windowposition%28v=vs.85%29.aspx
Unfortunately, setting the position of the window is not simple. There is a snapin for it though - http://wasp.codeplex.com/ ( use Set-WindowPosition)
Take a look at this script: Resize-Console.ps1 – Resize console window/buffer using arrow keys.
It is hopefully useful itself and partially should answer the question (the size part).