Monday, December 29, 2014

Designing the EZTable class

    I had a request for displaying changes in the data highlighted while debugging in ebe.  The idea is that in the data window, the assembly data window, the register window, the floating point register window and the stack frame window each changed item would be displayed in a highlighted color (I chose red).  The first attempt to solve this problem with to derive EbeTable from QTableWidget and derive EbeTableItem from QTableWidgetItem.  This was mostly successful, but had some issues.

    The first issue was the some data items were bigger than others.  When displaying char data in the assembly data window I could put 16 of them on a row while I could only display about 4 64 bit integers.  Fortunately Qt provided the ability to span columns in rows of a table.  This is great, but they didn't provide (AFAIK) a way to un-span some columns.  I tried to set a span of 1 for chars and a span of 4 for longs.  I used the formats dec1, dec2, dec4, dec8, hex1, hex2, hex4 and hex8 for the obvious integer formats of various sizes.  When I wanted to redisplay a hex8 value as hex1, Qt didn't see the point of setting a span of 1.

    To solve the un-spanning problem, I adopted the plan of using a span of 2 as my default size.  So hex1 used a span of 2, hex2 used a span of 4 and the larger sizes used a span of 8.  I was pleased.  I had the assembly data window working pretty well.  There was nagging issue of displaying large arrays.  I really needed to scroll the data, but I could not add a scrollbar to a few rows of a table.
 
    Then I decided to change the display of the high level language data from a QTreeWidget to an EbeTable.  I still wanted to have a tree structure, but I wanted a tree-like display in the table.  The 4 sections, globals, locals, parameters and user-defined needed to have their variables indented and ultimately class and struct objects needed their components indented as well.  I decided to indent 2 narrow columns per indent level and increment the number of columns each time I needed another level.
 
    After a while I got tired of having the limitations of QTableWidget: no un-span and only text in the table.  I started thinking about making my own table class which didn't use the QTableWidget.  I investigated using a QGridLayout, but didn't test it.  It had the ability to span and probably didn't cover un-spanning.
 
    Then I started thinking about deriving a table from the QWidget class.  As I started considering this I recalled that I used red in the various algorithm displays in the Bit Bucket to highlight things which changed and I realized that I have had a need for my own table class for a while.
 
    I decided to name the new class EZTable which I could use to transform the various tables in ebe one at a time while retaining the utility of the existing windows.  Now I am on my way.  I will try to decide the parts of the eztable.h before I start seriously working on eztable.cpp.

    The first class defined in eztable.h is really just for an enum.  I was a little tired of typing EbeTable::Highlight in my code and I definitely wanted either a namespace or a class.  Picking EZ meant I could use EZ::Highlight.  If you had ever seen me type you could understand my interest in brevity.

class EZ {
    enum Color { Default, Bold, NoChange, Highlight };
};

   I upped the game a notch adding Bold to the list of "Colors".  I plan to use Bold for creating headers in row 0 of my tables.  I don't know why a header is necessarily special.  Default will mean no highlighting.  NoChange will mean use the existing color either highlighted or not.  The way I implemented the Highlight before was to make the color black if the value has not changed and red if it has.  I was updating data item in every table for each step in debugging.  Fast CPUs allow you to make some easy, sloppy code.  For example when I change the format for one variable I can easily redraw the entire data table.  Humans operate at a much slower pace than the CPU.  Many years ago I had to worry about redoing any unnecessary work.  When a fast minicomputer was operating at maybe 1-5 MHz and instructions took more than 1 cycle, you could waste too many cycles without annoying the other people sharing the computer.

    The next class is EZCell which will be used to organize the data of the table.  EZCell is planned only for internal use within the EZTable class.  I will store a string in the cell to make that easier.  If the string is displayed I will use the widget pointer for a QLabel.  If the table needs a button or a scrollbar I will use the widget pointer for that.  Each cell will use the spanning value to indicate if it is the start of a span.  It will use spanned if it is spanned by a previous column (or row).  The hide and show functions will be used to force the widget to be hidden if the cell is spanned.

    Each cell will probably store its row, column and width values.  It may turn out that I won't need these in each cell, since the EZTable class will know these details already.

class EZCell
{
public:
    EZCell(int r=-1, c=-1);
    int row;
    int col;
    int width;
    bool spanning;

    int spannedRows;
    int spannedColumns;
    bool spanned;
    QString text;
    EZ::Color color;
    QWidget *widget;
    bool visible;
    void hide();
    void show();
};

    The next class is EZRow which uses a QVector to contain the EZCells on one row of an EZTable.

class EZRow
{
    EZRow(int r=-1);
    int row;
    int height;
    int width;
    vector<EZCell> cells;
    EZCell & operator [] ( int c );
};


    The final classes are EZTable and EZTableItem.  An EZTable has a vector of EZRows which contains the cells of the table.  EZTableItem will be used in the various register windows to maintain pointers within the tables for simplicity and to match the existing design.  I may eliminate the use of EZTableItem and store row and column numbers for the registers and simplify the design.

class EZTable: public QWidget
{
    Q_OBJECT

public:
    EZTable(QWidget *parent = 0);
    void setText ( int r, int c, QString t, EZ::Color highlight=Default );
    QWidget *widget_;
    QWidget *widget(int row, int col);
    int rows;
    int columns;
    vector<EZRow> table;
    vector<double> widthMultipliers;    // one per column
    int rowCount() {
        return rows;
    }
    int columnCount() {
        return columns;
    }
    void addWidget ( QWidget *w, int row, int col );
    EZTableItem *item(int r, int c);
    void setSpan ( int r, int c, int numRows, int numCols );
    void unSetSpan ( int r, int c );
    void setFontHeightAndWidth(int h, int w);
    QSize sizeHint();
    void hideColumn();
    EZRow & operator [] ( int r );
};


    The plan is to manage the work of each ebe table in a class derived from EZTable.  At this point I have enough practice at writing ebe to start understanding the tools I need to write ebe.  It's hard to know how to write a complicated program until you have written it.  It helps to be willing to start over with parts of the program.  I started ebe as a Python program and learned that I needed a more powerful toolkit.  Now after ebe has evolved into a fairly useful tool, I can start trying to build some tools specially adapted for ebe.

1 comment:

Carsten Hansen said...

Hello Ray,

Very interesting. Grids seems difficult to make. Commercial ERP packages has the possiblity to add icons automatically just after the textbox e.g. for selecting a new value or for more details.

It seems to me that Qt does not have an HTML control like http://docs.wxwidgets.org/trunk/overview_html.html

but I think that if you use Html you get problems when you want to copy rows or resize, hide or move columns.

Merry Christmas and happy new year!