The Accidental Rubyist

invalid byte sequence in UTF-8

Archive for November 2008

256 colors with ruby-ncurses

with 7 comments

My ruby-ncurses says that I have 256 colors, (the constant Ncurses.COLORS). Using some ruby scripts that use escape sequences, I am able to print 256 colors. Does anyone know how to access these 256 colors using ruby-ncurses?
(I obviously do have xterm-256color and screen-256color working on my system for ruby to say 256).

Using an escape sequence in the print command of a window is printing the escape sequences, whereas using the same escape sequence (taken from Highline, for underline), in *irb* is correctly printing underline in irb.

EDIT: I got a reply from theĀ  very helpful maintainer of ncurses (Thomas Dickey) on 256 colors. He says ncurses must be compiled with “ext” option. Also, one can refer to the colors by numbers 1..256. Without recompiling, I wrote a loop and printed colors and saw a few shades of blue. But most other colors looked like they were repeating. I am in no hurry for 256 colors, but thought I would share this.

Advertisements

Written by totalrecall

November 30, 2008 at 7:31 pm

Posted in ncurses, ruby

ncurses: friendly colors

leave a comment »

It was a lot easier than I thought. English color names such as green and red can be converted to their constant names (thanks to Sup):

 Ncurses.const_get "COLOR_#{colorstring.upcase}"

Using this simple line of code, one may maintain a hash containing color_pairs for whatever colors are in use. Whenever, a color is requested, one returns it from the hash , or creates a new one.

Thus, as may be seen in these samples, we can set or modify a widgets foreground or background color using:


color "green"
bgcolor "red"

In example 1, the password field has a red foreground and blue background set at startup. “show” means that for every character entered an asterisk will be displayed.

foreground and background color or password and radio buttons

foreground and background color or password and radio buttons


    @form.by_name["password"].show '*'
    @form.by_name["password"].color 'red'
    @form.by_name["password"].bgcolor 'blue'

Notice also that “Select a color” is cyan, and red and green radio buttons are .. red and green.

In example 2, I have checked the checkbox and selected red. The label “Select a color” has become bold and red.

change of color on selection of checkbox and radio button

change of color on selection of checkbox and radio button

In example 3, I have checked our new CheckBoxMenuItem, and doing so has made the label reverse-video.

change of attributes on selection of checkboxmenuitem

change of attributes on selection of checkboxmenuitem

Written by totalrecall

November 30, 2008 at 7:16 pm

Posted in ncurses, rbcurse, ruby

Trapping multiple keys for application

leave a comment »

This could help you if you require to map multiple keys like emacs (C-x C-d) or C-x q etc to methods. My first find was manveru’s “VER” which (I presume) means Vi Emacs in ruby.
I am using his keyboard.rb to trap keys – helpful in escape combinations and meta-key trapping.

However, his excellent keymapper was not what i required in my app, and it was too hard for a newbie like me to understand. What is more, I felt i needed key mapper objects in my application.

So I wrote a smaller mapper which takes care of modes, key combinations and regexes. It will work with both keys given as strings, as well as numeric keys. (As i wrote it, I did get back into manveru’s code and use some of it, though).

A full-fledged example using mapping defined as strings may be seen in lib/rbcurse/sqlpopup.rb.
Here is an tiny example with mappings as integers (as returned by window.getch).

Mapping methods to keys

Define a mapper object, giving self as handle. Methods to be called are in self.
(The syntax for mapping is identical to manveru’s in case you ever want to switch)


    @mapper = Mapper.new(self)

view refers to the object passed to constructor of Mapper, here it is self
:normal is the normal state of my application, any name can do. I have defined @mode = :normal in constructor.


    @mapper.let :normal do
      map(?\C-x, ?\C-d){ view.down }
      map(?\C-x, ?\C-u){ view.up }
      map(?\C-x, ?q){ view.stop }
      map(?\C-x, ?\C-s){ view.do_search_ask }
      map(?\C-x, ?\C-x){ view.do_select }

Above, we mapped some double key combinations to methods in self. To keep things simple, no more that 2 combinations may be defined.

In the next line, we switch to another mode named “:cx”.


      map(?\C-s){ view.mode = :cx }

Some single key combinations are shown. Note that a method symbol may be passed too. Of course, a single control key may be mapped, and so can a regular expression.


      map(?q) { view.stop }
      map(?j) { :down }
      map(?k) { view.up }
      map(KEY_DOWN) { view.down }
      map(KEY_UP) { view.up }

      map(?\C-e) { view.do_clear_selection }
      map(?\M-e) { view.do_clear_selection }  # using alt/meta/escape
      map(?\M-E) { view.do_something_else }   # case sensitive

      map(/^([[:print:]])$/){ view.show("Got printable: #{@arg}              ")
      }

We spoke of a mode named :cx. Whatever that is, imagine that once you are in that mode, you can only pres c, a, r and q. Pressing “i” returns you to normal. In reality, this could be linked to an object which only accepts certain keys (say a list).


    @mapper.let :cx do
      map(?c) { view.show(:c) }
      map(?a) { view.show(:a) }
      map(?r) { view.show(:r) }
      map(?q) { view.stop }
      map(?i) { view.mode = :normal }

You may (ofcourse) pass your own object/s while mapping and have them passed back.


      map(?r, @form) {|form| form.do_something(:r) }

Handling keys

After mapping keys, we can give control to the key-trapping program.

This example uses a modification of manveru’s Keyboard, to return integers and not strings. (I am just trying this out to see it’s benefits). In line 2, I allow the mapper to directly take calls.


    VER::Keyboard2.focus = self
    @keyhandler = @mapper     # i don't want a separate handler, this will do

IIRC, manveru’s VER, has a separate handler which then calls the mapper.

The keyboard programs calls press whenever a key is pressed, we pass it to the mapper to handle it. The mapper knows that a “C-x” will be followed by another key, so it takes care of combinations.


  def press(key)
      @keyhandler.press(key)
      break if stopping?
      ... exception handling etc

A couple more dependencies (check samples if interested)
Method or attribute window() that gives the keyboard program the window so as to call getch().
Accessor mode, for getting and changing mode.


 # keyboard will stop polling and return control if this returns a false.
  def stopping?
    @stop
  end

  # called by mapper if unknown key pressed
  def info(message)
    @message = message
  end

There you have it, with minimal fuss, a means to trap a large number of combinations, and map them to methods.

One advantage of binding keys is that bindings can be modified by users. Also, one may query the map object to display all mapped keys to the user. That, of course, looks better if string bindings are used as against integer bindings.

Please check keytest.rb and sqlpopup.rb for running examples. Also, see manveru’s github project for the handler and mapper he has written.

Written by totalrecall

November 26, 2008 at 2:42 pm

Posted in ncurses, ruby

Implementing a multi-line entry field in ncurses

leave a comment »

I have uploaded the code for RubyCurses onto http://github.com/rkumar/rbcurse/tree/master.

Please note that it will take a week or so for the code to be usable, I have not yet saved the form data as yet, only entered and edited.

Just some thoughts on ML fields. There are various approaches to doing this that came in mind.

1. python has a sample called textpad.py. This sample places characters that are input onto the window. At the end of editing, it takes the characters from the window and puts it into a String.

This suffers from some problems:

a. no insert mode, one is always overwriting what was written

b. we cannot exceed the size of the window.

A simple workaround for this is to use pads. However, again, a pad’s dimensions have to be defined at creation time. So when we run out, we would have to recreate a larger pad. Again the insert/overwrite issue has to be worked around.

2. Using a string: This is the first option that comes to mind. However, inserts and deletes at the start of a long string can be inefficient (I think). Line management could be tricky.

3. Using an array. I opted for this approach and its worked out as yet. Deleting till end of line, deleting lines, inserting lines, joining lines is very simple.

However, for critical purposes, or large files it would always be recommended to invoke vim, nano etc. This is intended to be simple and not a word-processor by itself.

If anyone has thoughts on this topic, would like to hear.

Written by totalrecall

November 25, 2008 at 3:12 pm

Posted in ncurses, rbcurse

rubycurses – Some code samples

with 6 comments

Here’s how you might create a messagebox (image in previous post):

     @mb = RubyCurses::MessageBox.new do
        title "Color selector"
        message "Choose a color"
        type :custom
        buttons %w[red green blue yellow]
        underlines [0,0,0,0]
      end

If you want one with an input box:

        message "Enter your name"
        type :input
        default_value "rahul"

This will give an OK and Cancel button. There are other types for OK, Yes/No, Yes/No/Cancel etc.

input message box

input message box

The “underline” property will underline the given index in the name. Since ncurses underline does not work on my terminal (Highline does), I’ve changed the color. The normal key as well as Alt/Meta key is used as a hotkey. For input dialog boxes, the normal key will be processed by the edit box so the meta key was required.

Creating a form and fields:

      @form = Form.new @win
      r = 1; c = 22;
      %w[ name age company].each do |w|
        field = Field.new @form do
          name   w
          row  r
          col  c
          display_length  30
          set_buffer "abcd #{w}"
          set_label Label.new @form, {'text' => w}
        end
        r += 1
      end

A form takes a window parameter, and manages painting and navigation of its focusable fields. We’ve also tied in a label with the field, so it can put a default location for it.

All all properties of a field can be modified at any time. The given name parameter can be used to recall them.

      @form.by_name["age"].display_length = 3
      @form.by_name["age"].maxlen = 3
      @form.by_name["age"].set_buffer  "24"
      @form.by_name["age"].chars_allowed = /\d/

      @form.by_name["name"].set_buffer  "Not focusable"
     @form.by_name["name"].set_focusable(false)
      @form.by_name["company"].type(:ALPHA)

Defining variable fields and tying them to widgets

      $results = Variable.new
      $results.value = "A variable"
      var = RubyCurses::Label.new @form, {'text_variable' => $results, "row" => r, "col" => 22}

This label changes whenever the value of result is changed.

Events

The form and fields have events such as ENTER and LEAVE.

      @form.bind(:ENTER) { |f|   f.label.bgcolor = $promptcolor }
      @form.bind(:LEAVE) { |f|  f.label.bgcolor = $datacolor  }

Buttons

Notable is the text and command.

      ok_button = Button.new @form do
        text "OK"
        name "OK"
        row 18
        col 22
      end
      ok_button.command { |form| $results.value = "OK PRESS:";form.printstr(@window, 23,45, "OK CALLED") }
An OK and a variable button

An OK and a variable button

The name property of widgets is only for recalling them by name.

Radio Buttons

Here we create a label (using a hash, instead of the DSL way for a change).
Radio buttons are tied together by a Variable. On selection, the variable contains the value.

      Label.new @form, {'text' => "Select a language:", "row" => 20, "col" => 22}

      $radio = Variable.new
      radio1 = RadioButton.new @form do
        text_variable $radio
        text "ruby"
        value "ruby"
        row 21
        col 22
      end
      radio2 = RadioButton.new @form do
        text_variable $radio
        text  "java"
        value  "java"
        row 22
        col 22
      end

Listboxes

Don’t flee out of boredom. Just a minute more!

I create an array, and then set it in the listbox call. At a later point, i insert some more data into offset 5. Data may be deleted or inserted externally.

        mylist = []
        0.upto(100) { |v| mylist << "#{v} scrollable data" }

        field = Listbox.new @form do
          name   "mylist"
          row  r
          col  1
          width 40
          height 10
          list mylist
        end

        field.insert 5, "hello ruby", "so long python", "farewell java", "RIP .Net"

Checkboxes

I have lazily tied the box to the same Variable, you needn’t.

      checkbutton = CheckBox.new @form do
        text_variable $results
        #value = true
        onvalue "selected cb"
        offvalue "UNselected cb"
        text "Please click me"
        row 17
        col 22
      end

Now those configuration screens should be easier to build.

Multi-line fields

multi-line text area

multi-line text area

        texta = TextArea.new @form do
          name   "mytext"
          row  1
          col  52
          width 40
          height 20
        end
        texta << "hello there" << "we are testing deletes in this application"
        texta << "HELLO there" << "WE ARE testing deletes in this application"

Menubars, menuitems

Since my pickaxe had no samples of Tk Menu’s, I pulled out my old Java Swing book from my attic, and implemented menus the way those fellers do.

menu bar showing hotkeys and separator

menu bar showing hotkeys and separator

menu showing accelerator keys

menu showing accelerator keys

menu showing a sub-menu

menu showing a sub-menu

      @mb = RubyCurses::MenuBar.new
      filemenu = RubyCurses::Menu.new "File"
      filemenu.add(item = RubyCurses::MenuItem.new("Open",'O'))
      item.command(@form) {|form|  form.printstr(@window, 23,45, "Open CALLED"); }

      filemenu.insert_separator 1
      item.accelerator = "Ctrl-X"
screen shot with menu bar

screen shot with menu bar

Now to post pictures of the screen. Its not well presented, since i’ve been busy creating widgets, but still…

Written by totalrecall

November 25, 2008 at 12:04 am

Posted in ncurses, rbcurse, ruby

Tagged with

Friendlier curses with ruby

with 6 comments

I’ve been working for the last month or two, on an application builder for ncurses, using ruby. I’ve been learning both ncurses and ruby and thus have written and rewritten and refactored my work, so many times ….

Finally, the other day I thought i would just try out creating a field in pure ruby (on an ncurses window). (Putting aside my 2 months work) In about a couple hours, I HAD a working field. Along with a FORM and navigation etc. All in ruby, you can create the FIELD whenever you want, change all attributes whenever you want … none of the complicated ncurses hassles and sequencing etc.

Inactive field, integer field, char field, label linked to variable
Inactive field, integer field, char field, label linked to variable

Best of all the code is very small, and one can improve, debug etc it, whereas with ncurses you have to accept what is provided with all its limitations. Don’t get me wrong, I have enjoyed working with forms and fields and even multiline ones, but ultimately even after writing umpteen wrappers every which way, it still was NOT fun.

So then I created a label, and then a button. It was quick! All in my hands. I looked up the TK interface in the Pickaxe and made mine similar. In ONE DAY i had all this ready.

Button with focus
Button with focus, second button is based on a Variable

Day 2:
Got stuck making a menu bar. A single level was easy, but submenus really had me. Took a full day to get that done. That was the hardest.

Checkbox
Checkbox (Please click me)
clicked checkbox
clicked checkbox, results in change in button title

Day 3: Zipped through a message box, then took 5 minutes to extend the button and make a proper checkbox. Took 2 minutes to extend that and make radio buttons!!!

Message box with custom buttons
Message box with custom buttons

Day 4: I made a multiple line field (Textarea) – again tired of the limitations of the ncurses ones … spent quite some time doing word wrapping while entry, editing, deleting etc. This is one of the tough ones that needs a lot of work and testing. You should be able to dump data from outside and write it out and have it just perfect. The current one has no limitation as far as size is concerned. However, with all the situations I am trying to cover, it could be buggy yet.

Somewhere in there, i also made a scrollable list box.

scrollable list

The whole thing actually comes to two pretty small files, almost no dependencies, looks very much like the “links” browser’s menu bar and message box.

These look much cleaner and more profession that what CDK looks like.

I am going to start putting up examples, code samples and images in the next few days.

Written by totalrecall

November 24, 2008 at 10:21 pm

Posted in ncurses, rbcurse, ruby

Tagged with ,