1

Because I don't seem to be able to find some predefined Morph that can display the contents of a Dictionary, I decided I'd better stop looking and wanted to create my own Morph. I found a nice description how to start with some nice example code to get me started, but quite soon I got to the problem that I don't seem to manage to draw text or anything like that on a canvas.

I created a class

Morph subclass: #DictionaryView
    instanceVariableNames: 'dictionary'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'StatisticsTool'

and I wanted to override drawOn as follows:

drawOn: aCanvas

    | x y |
    x := 0.
    y := 0.
    dictionary associationsDo: [ :assoc |
        aCanvas drawString: assoc key at: x@y.
        aCanvas drawString: assoc value at: x+10@y.
        y := y + 10. ].

I know this is not exactly the best piece of code (I have no idea yet how I should take into account the longest string etc, but I got to this point where I don't even really want to think about that anymore), but I just wanted to get something displayed. Unfortunately, this does not seem to work when I try

d := Dictionary new.
d at: 'test1' put: 5.
d at: 'test2' put: 23.
d at: 'test3' put: 514.

view := DictionaryView new.
view dictionary: d.
view openInWorld.

I get an Error: Instances of SmallInteger are not indexable

I don't know what to do anymore. I actually don't have time to write these long questions or to look a whole week for something like this. This all makes me very nervous and impatient and therefore I would like to excuse myself for the direct way of asking:

How can I display a dictionary in Smalltalk so that I can use it in a GUI?

PS: any tips on coping with stress are also welcome ;)

  • What about using `d inspect` or `d explore`, they both open views (inspectors) on the dictionary. And they even allow you to edit the object. – Lukas Renggli Dec 27 '15 at 10:03
  • Thanks, but I would like to be able to display it in a GUI as mentioned in my question... Did I put my question that unclear? – Mr Tsjolder from codidact Dec 27 '15 at 10:09
  • Have a look at the implementation of the `Debugger`. It is doing exactly that. It is embedding the above inspector morph into its own UI. You can do the same. – Lukas Renggli Dec 27 '15 at 10:14
  • Where should I find that in the implementation? It might be a solution, but I would like the end product to look like a simple table... The ultimate thing would be a nice looking, simple table, but I'm afraid I'll have to burry that plan... – Mr Tsjolder from codidact Dec 27 '15 at 10:52

2 Answers2

4

The source of your error is here

    aCanvas drawString: **assoc key** at: x@y.
    aCanvas drawString: **assoc value** at: x+10@y.

There's no guarantee that any of them will be string (and in your case the values are numbers), so you have to convert them manually

    aCanvas drawString: assoc key printString at: x@y. "or asString"
    aCanvas drawString: assoc value printString at: x+10@y.

You should be able to debug this kind of problem quite easily.

Regarding the width of string, you can ask a font for the length of a string.

Preferences standardDefaultTextFont heightOfString: 'hello'

Update:

You can also simply convert all the values to StringMorphs and compose them together.

DictionaryView>>dictionary: aDictionary
    | container keys values |
    (container := Morph new)
        layoutPolicy: TableLayout new;
        listDirection: #leftToRight.
    (keys := Morph new) layoutPolicy: TableLayout new.
    (values := Morph new) layoutPolicy: TableLayout new.
    aDictionary
        associationsDo:
            [ :assoc | 
            keys addMorph: assoc key printString asMorph.
            values addMorph: assoc value printString asMorph ].
    container
        addMorph: keys;
        addMorph: values.
    self addMorph: container

(of course remove the #drawOn: method as it will be no longer needed)

Obviously there's a lot of room for improvement, but that's out of the scope of this Q&A.

Alternatively you can use the MulticolumnLazyListMorph widget.

Peter Uhnak
  • 9,617
  • 5
  • 38
  • 51
  • I must admit, I put my question badly. I need it to be displayed in a morph to use it in an interface... I'm sorry. – Mr Tsjolder from codidact Dec 26 '15 at 21:55
  • @MrTsjolder I've updated the answer; if you want something else then you need to specify your question better – Peter Uhnak Dec 26 '15 at 22:29
  • In which way(s) can this be improved? This seems like a good solution to me (might be because I did not manage to display anything in a whole week). Also how can those `LazyListMorph`s be used? I've been looking into the code, but I didn't find how to attach the data in my dictionary on there. Thank you very much in any case! – Mr Tsjolder from codidact Dec 26 '15 at 23:16
  • 1
    By improvement I had in mind the look & feel of it (it looks ugly to me). As for `(Multicolumn)LazyListMorph` I have no idea --- I use Pharo and Spec and rarely use Morphic widgets, and there are no examples for this widget. It would be best if you post it as separate question (or ask on Squeak mailing list) – Peter Uhnak Dec 26 '15 at 23:33
1

working with Canvas and its drawing API is mostly about implementing your own base Morphs. If you want to build a GUI, you can try to use existing Morphs as building blocks.

Just like Debugger/Inspector don't implement their own list morphs, you can use existing classes. LazyListMorphs are used by PluggableListMorphs. You can plugin a model that provides the list and some selectors for list selection behavior.

|list listMorph|
list := Smalltalk allClasses.
listMorph := PluggableListMorph on:list list:#yourself selected:nil changeSelected:nil.
listMorph openInHand

This is a simple example. In a real world application, you would implement a model class that provides the list (see Inspector or other tools).

If you want to list dictionary contents, you can built a multicolumn listMorph for both "sublists" (keys, values), another multicolumn example:

|listOfLists listMorph|
listOfLists := { (1 to:100) asArray . (1 to:100) collect:[:x | x * x]}.
listMorph := PluggableMultiColumnListMorph on:listOfLists list:#yourself     selected:nil changeSelected:nil.
listMorph openInHand
Nicolai Hess
  • 376
  • 1
  • 2