Home   Archive   Permalink



At as-pair problem

Hi there! I really need all the help I can get right now.
    
I am trying to set pieces on a GUI using “at” and “as-pair” based on a count that could vary, to provide a score with images instead of numbers. My first approach was to produce the final result that I want:
    
view layout [
    size 640x480
    style star image load %star.png
    at 61x307 star
    at 122x307 star
    at 183x307 star
    at 244x307 star
    at 305x307 star
]
    
So far so good. Then I notice that the x part of the coordinates have a difference of 61 on each successive iteration. If I could insert a value that changes in that amount I could solve my problem, so I try:
    
factor: 2
view layout [
    size 640x480
    style star image load %star.png
    at (as-pair 61 307) star
    at (as-pair 61 * factor 307) star
]
    
and it works! In the last line, REBOL knows to multiply 61 by the factor to produce the first component of the pair. So now all I have to do is increment the factor up to the “count”, which is the value that changes, I try:
    
view layout [
    size 640x480
    style star image load %star.png
    for factor 1 count 1 [
    at (as-pair 61 * factor 307) star
    ]
]
    
The last line has not changed at all but now the instruction doesn't work and REBOL complains with:
    
** Script Error: at expected series argument of type: series port
    
Why?! How do I fix this?
    
For a while I though that maybe “factor” needed to be “reduce”d or “compose”d, not because I know what I am doing (I am not and really just trying to learn) but because I don't know what else, so I try:
    
at (as-pair reduce [ 61 * (i) ] 307) star
    
but
    
** Script Error: as-pair expected x argument of type: number
    
so I go:
    
at (as-pair to-integer reduce [ 61 * (i) ] 307) star
    
and then it gets really discouraging:
    
** Script Error: Invalid argument: 61
    
So I try:
    
at (as-pair to-integer compose [ 61 * (i) ] 307) star
    
but:
    
** Script Error: Invalid argument: 61 * 1
    
which I though was working before. This is a bit maddening to me because I feel that instead of consistency I am always dealing with exceptions. If this works:
    
pos-x: [ 61 122 183 244 305 ]
count: 4
repeat x count [ print form as-pair (pick pos-x x) 307 ]
    
giving me the set of coordinates I need, how come this doesn't?
    
pos-x: [ 61 122 183 244 305 ]
count: 4
view layout [
    size 640x480
    style star image load %star.png
    repeat x count [ at (as-pair (pick pos-x x) 307) star ]
]
    
** Script Error: at expected series argument of type: series port
    
What am I doing wrong?
    
Thanks in advance for all insights and ideas.

posted by:   Brother Damian       13-Dec-2013/2:09:33-8:00



The view block consists of a special dialect, which is parsed using rules specific to building GUIs, so the 'for loop can't be included there. Typically, the solution is to build the GUI block first, then display it:
    
R E B O L []
factor: 2 count: 6
gui: copy [
     size 640x480
     style star image %star.png
]
repeat factor count [
     append gui compose [at (as-pair 61 * factor 307) star]
]
view layout gui
    
About 'repeat instead of 'for - here it doesn't really matter, but it's good to use 'repeat for larger loops, since it's a faster native function (also a little shorter syntactically).
    
Also about 'compose - I typically use it instead of 'reduce because it doesn't require ticks for unevaluated words (just put evaluated portions of code in parentheses).

posted by:   Nick       13-Dec-2013/7:47:56-8:00



Thanks Nick, that is very informative and it helps me a lot.

posted by:   Brother Damian       13-Dec-2013/8:46:49-8:00



Just in case it's useful, here's another way to do it:
    
R E B O L []
factor: 2 count: 6
stars: func [f c] [
     g: copy [style star image %star.png]
     repeat f c [
         append g compose [at (as-pair 61 * f 307) star]
     ] g
]
view layout compose [
     size 640x480
     (stars factor count)
]

posted by:   Nick       13-Dec-2013/13:46:15-8:00



That example uses a function which builds and returns the stars portion of the GUI block, so that the GUI layout code is a little more readable. Notice that the GUI block is composed.

posted by:   Nick       13-Dec-2013/14:32:20-8:00



Great Nick, thanks!
    
I am analyzing this and can say with pride that I can read the function, mostly. So let me ask you this:
    
The “stars” function has a “g” standing by itself at the end of the repeat block (line 7). Is this a return value for the function? If so, are we using a feature where the last value is commonly returned by functions so we don't really need to type “return” or this something else entirely?
    
Then, in line 4, what is the difference between:
    
    g: copy [style star image %star.png]
and
    g: [style star image %star.png]
    
(without the “copy”) and why do you prefer the first version?
    
I see that you now use the function “compose” twice. Why do we need to compose the GUI block as well?
    
And in a more general note: many times I use names and REBOL knows to use their values, for example:
block: [ "alpha" "beta" "gamma" ]
n: 2
    
if I type:
    pick block n
    
I get the expected result of “beta” (which requires to use the value of two stored in the name “n”)
    
but if I try path notation:
    print block/n
    
I get an error, usually: ** Script Error: Invalid path value: n
    
I know (by reading you tutorials Nick, and many, many thanks for them) that “reduce”, “compose” and even the “:” allow me to extract the value from the name used, as if I had typed the value directly. So one can solve the issue by coding:
    
    print compose [ block/(n) ]
    
But: why is “block” understood as name but not “n”? (that is, why does only n need to be reduced but not block?) More generally: When is it necessary to reduce or compose? Under what circumstances will REBOL use the value of the name and when is it necessary to explicitly extract the value with these functions?
    
I am not sure if I should star another thread or I can ask these questions here. I will do as directed. And thanks again Nick for all your help

posted by:   Brother Damian       13-Dec-2013/15:29:43-8:00



The "g" at the end of the function is just a shorthand way of writing "return g". The last value in a function definition is always returned, unless a 'return is evaluated elsewhere in the function.
    
I always use "copy" to initialize a new (empty) block, any time I know that the block label will be repeatedly initialized. Here it's not necessary, because within the context of the function, that word is locally defined, but it's a force of habit for me. I do the same thing whenever referring to widget/text, and other properties, just to be sure I'm dealing with an isolated instance of the value. It was force of habit in this situation, but not a bad habit in general with Rebol - it'll save you from lots of potential bugs.
    
The first compose, inside the function is used to build each item in the local 'g block. That entire constructed block is then returned from the function, and needs to be evaluated before being passed to the layout function, which is what the second compose accomplishes.
    
If you need to evaluate a value within a path, it either needs to be composed or reduced, or the get-word notation needs to be used (i.e., block/:n or compose [block/(n)]). I think the get-word notation is most popular, because it's most concise. I tend to use the "pick block n" syntax a lot, because it's been the most versatile and readable syntax in many varied situations.
    
Otherwise (outside of path notation), compose and reduce work as expected.

posted by:   Nick       13-Dec-2013/17:09:03-8:00



Hello all!
    
I'm a complete noob in Rebol and came here to ask some questions, but reading this post I understood the use of copy and compose, which solved my small problems.
Thanks a lot!
    
My (first!) prog is a system-tools launcher for Win XP (in French, sorry):
    
    
R E B O L [Title: "Lanceur"]
    
data: [
["Dx Diags"
        "C:\WINDOWS\system32\dxdiag.exe"
        "Diagnostics DirectX"]
    
    [    "Services"
        "C:\WINDOWS\system32\services.msc"
        "Services Windows"]
    
    [    "Restore"
        "C:\WINDOWS\system32\Restore\rstrui.exe"
        "Restoration systθme"]
]
    
sty: stylize [
    btn: button 100x30 160.140.120
    lbl: label 220.200.180 font-size 20
]
    
win: copy [
    backdrop 140.160.180
    styles sty
    vh1 "Outils systθme" font-size 44 font-color 200.150.100
]
    
foreach elm data [
    append win compose [return across]
    append win compose [btn (elm/1)]
    append win compose/deep [[call/shell (elm/2)]]
    append win compose [lbl (elm/3)]
]
    
view center-face layout win
    
    
Now I just have to put the data as lines in a text file (so it can easily be extended/translated), and construct the block from there.
    
Happy coding!
    
––––––––––––––––––––––––––––––––––––––––––––––––––––––––
P. - S. By the way, right now I'm using Crimson editor, which is nice, but is there a free editor with syntax coloration _and_ code folding?

posted by:   Old Nick       13-Feb-2014/16:31:55-8:00



ETA: Scite folds & colors, so ditch my PS.

posted by:   Old Nick       13-Feb-2014/17:34:22-8:00



Hi Old Nick :)
    
Glad to see you got it going. Just to be sure it's clear, the second half of your code could be shortened a bit:
    
win: copy [
     backdrop 140.160.180
     styles sty
     vh1 "Outils systθme" font-size 44 font-color 200.150.100
     across
]
        
foreach elm data [
     append win compose/deep [
         return
         btn (elm/1) [call/shell (elm/2)]
         lbl (elm/3)
     ]
]

posted by:   Nick       14-Feb-2014/22:09:59-8:00



Also, there's no requirement to place the button items within separate nested blocks within the 'data block:
    
R E B O L [Title: "Lanceur"]    
data: [
     "Dx Diags"
     "C:\WINDOWS\system32\dxdiag.exe"
     "Diagnostics DirectX"
     "Services"
     "C:\WINDOWS\system32\services.msc"
     "Services Windows"
     "Restore"
     "C:\WINDOWS\system32\Restore\rstrui.exe"
     "Restoration systθme"
]    
sty: stylize [
     btn: button 100x30 160.140.120
     lbl: label 220.200.180 font-size 20
]
win: copy [
     backdrop 140.160.180
     styles sty
     vh1 "Outils systθme" font-size 44 font-color 200.150.100
     across
]    
foreach [bt prg txt] data [
     append win compose/deep [
         return
         btn (bt) [call/shell (prg)]
         lbl (txt)
     ]
]
view center-face layout win

posted by:   Nick       14-Feb-2014/22:22:47-8:00



Hello (young) Nick! ;)
    
Many thanks for having taken interest in my small problem. Of course your modification is 250% better:
– putting the "across" out of the loop (I had understood it as a kind of switch, like a gsave/grestore or pushmatrix/popmatrix pair);
– flat data structure (shows nicely how "compose" works).
    
I had tried the "foreach [x1 x2 x3]" trick, but somehow messed up. Needed the master's hand…
    
    
For my part I put the data in a text file, so it can be easily read/modified/translated:
    
    
#    –––––––– Restoration systθme ––––––––
Restore
C:\WINDOWS\system32\Restore\rstrui.exe
Restoration systθme
    
# –––––––– Lettres assignιes aux disques ––––––––
Disks
C:\WINDOWS\system32\diskmgmt.msc
Lettres disques
    
&c.
    
    
And it is read by my small launcher (skipping void lines and #-preceded comments) with:
    
read-data: function [infile][res][
    res: exclude (read/lines infile) [""]
    forall res [if res/1/1 = (to-char "#") [remove res]]
    res
]
    
data: read-data %data.txt
    
    
I'm not so happy with the "forall" part, but don't know if it's possible (or even elegant) to get that job done by "exclude"?
    
    
One last question: how do I get rid of the permission-asking when I start my mini-prog? (I'm using Rebol 2.7.8.3.1.)
    
    
Many thanks for your kind help,
     Old Nick

posted by:   Old Nick       21-Feb-2014/12:42:49-8:00



O.N.,
    
If the purpose is simply to load your data from a file, the most basic solution is to just copy your data block to a file. For example, put these lines in %data.txt:
    
"Dx Diags"
"C:\WINDOWS\system32\dxdiag.exe"
"Diagnostics DirectX"
"Services"
"C:\WINDOWS\system32\services.msc"
"Services Windows"
"Restore"
"C:\WINDOWS\system32\Restore\rstrui.exe"
"Restoration systθme"
    
You can use a semicolon to start any comment line, and include blank lines as desired, just as in normal Rebol code:
    
; –––––––– Restoration systθme ––––––––
    
"Restore"
"C:\WINDOWS\system32\Restore\rstrui.exe"
"Restoration systθme"
    
; –––––––– Lettres assignιes aux disques ––––––––
    
"Disks"
"C:\WINDOWS\system32\diskmgmt.msc"
"Lettres disques"
    
;
    
"Dx Diags"
"C:\WINDOWS\system32\dxdiag.exe"
"Diagnostics DirectX"
    
;
    
"Services"
"C:\WINDOWS\system32\services.msc"
"Services Windows"
    
The code below will load either of the 2 data files above in the exact same way:
    
R E B O L [Title: "Lanceur"]    
data: load %data.txt
sty: stylize [
     btn: button 100x30 160.140.120
     lbl: label 220.200.180 font-size 20
]
win: copy [
     backdrop 140.160.180
     styles sty
     vh1 "Outils systθme" font-size 44 font-color 200.150.100
     across
]    
foreach [bt prg txt] data [
     append win compose/deep [
         return
         btn (bt) [call/shell (prg)]
         lbl (txt)
     ]
]
view center-face layout win
    
You could also use read/lines, if you want to avoid the extra quotes. Then your data file just needs to look like this:
    
Dx Diags
C:\WINDOWS\system32\dxdiag.exe
Diagnostics DirectX
Services
C:\WINDOWS\system32\services.msc
Services Windows
Restore
C:\WINDOWS\system32\Restore\rstrui.exe
Restoration systθme
    
If you prefer to keep your data file in the format above, my first inclination for 'read-data would have been to do something like this:
    
read-data: func [infile][    
     remove-each r read/lines infile [(#"#" = r/1) or ("" = trim r)]
]

posted by:   Nick       22-Feb-2014/21:07:24-8:00



And, unless you need to reuse that code somewhere else, you might well just use the following line to assign the label 'data to the values you want:
    
data: remove-each r read/lines %data.txt [(#"#" = r/1) or ("" = trim r)]

posted by:   Nick       22-Feb-2014/21:13:33-8:00



To eliminate the security requestor, you can use the -s option on the command line. I typically use -si when creating .exe sfx files (I typically use iexpress, which comes on all modern Windows PCs to create .exe files):
    
"C:\myfolder\rebol.exe -si merchants_village.r"

posted by:   Nick       22-Feb-2014/22:10:47-8:00



Nick, thanks for your kind help!
    
I love your one-line read-and-format: "data: remove-each r read/lines %data.txt [(#"#" = r/1) or ("" = trim r)]" – starts to look like a cross between Mathematica and Forth code ;)
I understood also why you wrote "" = trim r, and not, as I would have done spontaneously, trim r = "" (of course one can use parentheses). Well, you're an old Rebol hand…
    
I set the security with the -s switch for launching a script from the editor, but is there a way to do it from _inside_ the script? (Without making an ad hoc shortcut – I'm on Windows – or batch file.)
I tried secure [file allow], but no good. (Just thought of it: through the startfile, perhaps?)

posted by:   Old Nick       26-Feb-2014/22:50:34-8:00



The point of the security requestor is to prevent malicious scripts from performing unwanted activities, so giving a script the ability to change its own security level would defeat the purpose.
    
You can use the 'secure function to change default security levels (yes, even in your startup file), but unless you run the interpreter with -s, you will still see a requestor asking whether it's ok to change security levels:
    
http://www.rebol.com/docs/words/wsecure.html
    
It's my understanding that if you really want to remove all security requestors, the only way to do that is on the command line (in a link, batch file, as part of any other system command which can call the Rebol interpreter, etc.) with the -s option. I don't think there's another way around that, and it's the only way I've ever done things.    
    
For example, any .exe files I create with iexpress or some other SFX packager, or any other links, icons, or installations of any script created for users who I don't want to have to see or bother with a requestor, are always run with "rebol.exe -si myscript.r". Otherwise, for ad hoc scripts that I've written for myself or user familiar with Rebol, I always just press "A" on the keyboard to allow all.

posted by:   Nick       27-Feb-2014/14:54:47-8:00



Ah! Doing my little bit of programming only for myself, I'm not much into those security problems – still my question was of a nice stupidity level! *blush*
    
I started a new project, and so have a new problem. This line gives me (nearly) what I want:
    
call/output "dir F:" %test.txt
    
(Actually I only need the end of the second line, to get hardware information.)
    
I could then read the test.txt file into Rebol, but there certainly is a more direct way to capture the result of the command. Trying to redirect to a variable set to an empty string or an empty block diden't work.
    
———————————————————————————————————————————————————————————————————————————————————
    
Another, analogous question. If I use a batch file and want to see the output (or the console closes immediately), I have to write two commands:
    
dir f:
pause
    
Now, how to send _two_ commands from Rebol with call/show? Tried a lot of things, but…
    
    
Once more, thank you for your kind help!

posted by:   Old Nick       3-Mar-2014/1:01:54-8:00



This does work:
    
y: copy ""
call/show/output "dir C:" y
editor y
    
For multiple 'call commands, you can loop through a block:
    
foreach i ["dir c:" "pause"] [call/console i]

posted by:   Nick       3-Mar-2014/11:57:05-8:00



Well, I looked at the documentation, and hope I understand what the call refinements are for. Yet it's strange:
    
If I type directly into an open (Rebol) console:
y: copy "" <newline>
call/output "dir C:" y <newline> (dropping the show, as I only need the y string)
print y <newline>
all's OK.
    
If I run the same script, be it by double-clicking its icon or from the editor:
y: copy ""
call/output "dir C:" y
print y
(here possibly a "wait 5", changes nothing)
nothing happens, but Rebol stays in memory and hogs the CPU (having to kill the process).
    
Now if I insert a dummy line into the script to open the (Rebol) console before the call:
y: copy ""
print "Starting"
call/output "dir C:" y
print y
wait 5
the script executes and Rebol quits after 5 secs, but the printed string is void.
    
I checked that antivirus & firewall aren't at the root of that.
Sorry to be so verbose, but I tried to be as clear as possible.
    
And thanks for your suggestions!

posted by:   Old Nick       5-Mar-2014/3:23:59-8:00



Something we're unaware of in your local environment is interfering. This works fine for me, both in the interpreter, and in a script:
    
y: copy ""
call/output "dir C:" y
print y
    
What are in your rebol.r and user.r startup files?

posted by:   Nick       5-Mar-2014/10:12:36-8:00



You are using R2, correct?

posted by:   Nick       5-Mar-2014/11:51:06-8:00