YOU CAN CODE!

 

With The Case Of UCanCode.net  Release The Power OF  Visual C++ !   HomeProducts | PurchaseSupport | Downloads  
Download Evaluation
Pricing & Purchase?
E-XD++Visual C++/ MFC Products
Overview
Features Tour 
Electronic Form Solution
Visualization & HMI Solution
Power system HMI Solution
CAD Drawing and Printing Solution

Bar code labeling Solution
Workflow Solution

Coal industry HMI Solution
Instrumentation Gauge Solution

Report Printing Solution
Graphical modeling Solution
GIS mapping solution

Visio graphics solution
Industrial control SCADA &HMI Solution
BPM business process Solution

Industrial monitoring Solution
Flowchart and diagramming Solution
Organization Diagram Solution

Graphic editor Source Code
UML drawing editor Source Code
Map Diagramming Solution

Architectural Graphic Drawing Solution
Request Evaluation
Purchase
ActiveX COM Products
Overview
Download
Purchase
Technical Support
  General Q & A
Discussion Board
Contact Us

Links

Get Ready to Unleash the Power of UCanCode .NET


UCanCode Software focuses on general application software development. We provide complete solution for developers. No matter you want to develop a simple database workflow application, or an large flow/diagram based system, our product will provide a complete solution for you. Our product had been used by hundreds of top companies around the world!

"100% source code provided! Free you from not daring to use components because of unable to master the key technology of components!"


VC++ GDI Example: Draw an periodic Table shape

 
 By Terry O'Nolley. 
An article using GDI to create a scalable periodic table of the elements.

PTE screenshot

Introduction

This article presents an example of using GDI to create an interactive Periodic table of the Elements (PTE). The PTE is resizable and the controls on the dialog also resize. This article is meant to demonstrate GDI, dynamically resizing controls and simple database access.

I added the ability to select a chemical group (halogens, noble gases, etc.) and then use slider controls to set the color for that group. Double-clicking an element will open a dialog box displaying details for that element. Right clicking an element will add that element to a listbox.

This app is a good starting point for further development and touches on the following MFC capabilities:

  • Database use
  • Slider controls
  • GDI output
  • Control size scaling

Using the code

NOTE: Included with the demo project is an MS Access file - you must configure your ODBC manager to include a mapping to this MDB file - the datasource name must be PeriodicTableApp!

Two classes are presented which encapsulate the behaviors of the PTE:

  • pteTable - the Periodic table
  • pteElement - an element in the PTE

The table consists of an array of pteElement objects and members that define the size of the element cells, the offsets for the text and access methods to the element objects.

The pteTable class:

Collapse
#ifndef __PTETABLE_H__
#define __PTETABLE_H__
#include "pteElement.h"
#include 


#define NUM_ELEMENTS 118
#define NUM_GROUPS   10  

class pteTable 
{
    public:
                   pteTable::pteTable();
                   ~pteTable(); 
        // Reads in the element data from the database 
        void       pteTable::InitElements();
        // Sets the intial state of the chemical groups
        void       pteTable::InitGroups();
        // Draws the PTE
        void       pteTable::Draw(CPaintDC*, int);
        // Maps a mouse click to the element
        int        pteTable::FindElement(CPoint);
        // Returns a specified element
        pteElement pteTable::GetElement(int);
        // Returens the color for that chemical group
        COLORREF   pteTable::GetGroupBGColor(int);
        // Sets the color for a specified chemical group
        void       pteTable::SetGroupBGColor(int, COLORREF);
        // Used when drawing rectangles
        int        pteTable::GetCellWidth();
        // Used when drawing rectangles
        void       pteTable::SetCellWidth(int);
        // Sets the font to be used in the PTE
        void       pteTable::SetFontFace(CString);
        // Returns the current font used in the PTE
        CString    pteTable::GetFontFace();
        // Sets the point size for the font used in the PTE 
        void       pteTable::SetFontSize(int);
        // Returns the current font size
        int        pteTable::GetFontSize();
        // Used for positioning text during Draw
        int        pteTable::GetAtomicNumberXOffset();
        // Used for positioning text during Draw
        int        pteTable::GetAtomicNumberYOffset();
        // Used for positioning text during Draw
        int        pteTable::GetAtomicSymbolXOffset();
        // Used for positioning text during Draw
        int        pteTable::GetAtomicSymbolYOffset();
        // Get the row of an element
        int        pteTable::GetRow(int);
        // Get the column of an element
        int        pteTable::GetColumn(int);
        // Get the Atomic number of an element
        CString    pteTable::GetAtomicNumber(int);
        // Get the atomic symbol of an element
        CString    pteTable::GetAtomicSymbol(int);
        // Get the name of an element
        CString    pteTable::GetElementName(int);
        // Get the chemical group of an element
        CString    pteTable::GetGroup(int);
        // Sets the current element
        void       pteTable::SetCurrentElement(int);
        // Creates the invalidated region in prep for painting
        void       pteTable::BuildUpdateRegion(CDialog*, int, int);
        // Creates the invalidated region in prep for painting
        void       pteTable::BuildUpdateRegion(CDialog*, int);
        // Sets the pteElement CRect objects 
        void       pteTable::BuildElementRects();

    protected:
        // The array of elements
        pteElement Elements[NUM_ELEMENTS]; 
        // The width of the table cells
        int        CellWidth;
        // TextOut offset
        int        AtomicNumberXOffset;
        // TextOut offset
        int        AtomicNumberYOffset;
        // TextOut offset
        int        AtomicSymbolXOffset;
        // TextOut offset
        int        AtomicSymbolYOffset;
        // The currently active pteElement
        int        CurrentElement;
        // The background colors for each chemical group
        COLORREF   GroupBGColors[NUM_GROUPS];
        // Current font size
        int        FontSize;
        // Current font face
        CString    FontFace;
};


#endif //__PTETABLE_H__

The pteElement class:

Collapse
#ifndef CLASSX_H_INCLUDED
#define CLASSX_H_INCLUDED 

class pteElement
{
    public:
                pteElement::pteElement();
                ~pteElement();
        // Simple access methods - the set methods are
        // called during initialization and
        // are populated from the database
        void    pteElement::SetAtomicNumber(CString);
        CString pteElement::GetAtomicNumber();
        void    pteElement::SetAtomicWeight(CString);
        CString pteElement::GetAtomicWeight();
        void    pteElement::SetGroupNumber(CString);
        CString pteElement::GetGroupNumber();
        void    pteElement::SetGroupName(CString);
        CString pteElement::GetGroupName();
        void    pteElement::SetPeriod(CString);
        CString pteElement::GetPeriod();
        void    pteElement::SetBlock(CString);
        CString pteElement::GetBlock();
        void    pteElement::SetCASRegistryID(CString);
        CString pteElement::GetCASRegistryID();
        void    pteElement::SetElementName(CString);
        CString pteElement::GetElementName();
        void    pteElement::SetElementSymbol(CString);
        CString pteElement::GetElementSymbol();
        void    pteElement::SetDiscoveryDate(CString);
        CString pteElement::GetDiscoveryDate();
        void    pteElement::SetDiscovererName(CString);
        CString pteElement::GetDiscovererName();
        void    pteElement::SetRow(CString);
        int     pteElement::GetRow();
        void    pteElement::SetColumn(CString);
        int     pteElement::GetColumn();

        // sets the members of the CRect object
        // member variable. Passed the width of the cell.
        void    pteElement::BuildRect(int);
        // returns TRUE if the point passed
        // to it lies within its CRect
        BOOL    pteElement::CheckHit(CPoint);
        // returns the CRect ElementArea
        CRect*  pteElement::GetRect();
        
    protected:
        // populated from database
        CString  AtomicNumber;
        CString  AtomicWeight;
        CString  GroupNumber;
        CString  GroupName;
        CString  Period;
        CString  Block;
        CString  CASRegistryID;
        CString  ElementName;
        CString  ElementSymbol;
        CString  DiscoveryDate;
        CString  DiscovererName;
        int      Row;
        int      Column;


        // the current screen coordinates
        // that this element occupies
        CRect    ElementArea;                    
        // The color for this element
        COLORREF CellColor;
};

#endif

Points of interest

This project started last week when a user ucancode.neted about implementing a PTE and wanted to know how best to determine which element was chosen by the user. Three options were suggested:

  • Using a button for each element
  • Using a single bitmap to represent the PTE and another bitmap in a MemDC with different colors for each element
  • Using GDI

I discounted using buttons because it is very unattractive and uses a lot of resources. I discounted the second because the text would look terrible as you scaled the window and because making changes would be very time consuming. I decided on GDI. The actual code to handle the drawing is only around 14 lines and allows for highlighting all of the elements in the current chemical group:

Collapse
/* ******************************** */
/* Draw the table                   */
/*
The actual drawing of the entire table is 
accomplished in about a dozen lines.
Very simple and it allows for automatic resizing 
when the window size changes.

Because the row and column within the table 
is stored as a class member variable
in the pteElement class, computing the screen 
coordinates for that element is
very straightforward.

In drawing the elements, a few class methods are called:

COLORREF GetGroupBGColor(int);
This method takes an int representing the 
chemical group and returns 
the color for that groups background color

When selecting the brush to draw with, 
the following call is used:
GroupBrushes[atoi(Elements[x].GetGroupNumber())]
Elements[] is an array of pteElement objects. 
One of pteElement's methods is:
int GetGroupNumber();
and it returns the chemical group for that element. 
The resulting number is used as an index into the GroupBrush
array of CBrushes

Determining the location for the cell is 
accomplished with the calls
GetColumn() and GetRow()

The return value from those calls is multiplied by the current 
cellwidth - and this gives you the coordinates for
drawing the rectangle. 

Drawing the text is accomplished the same way.
*/
/* ******************************** */
void pteTable::Draw(CPaintDC* dc, int group)
{
    // declare and initialize the fonts
    CFont*  OldFont;
    CFont   NumberFont;
    CFont   SymbolFont;
    NumberFont.CreatePointFont(FontSize - 40, FontFace, NULL);
    SymbolFont.CreatePointFont(FontSize, FontFace, NULL);

    // declare and initialize the brushes
    CBrush* OldBrush;
    CBrush  GroupBrushes[NUM_GROUPS]; 
    for(int x = 0; x < NUM_GROUPS; x++)
        GroupBrushes[x].CreateSolidBrush(GetGroupBGColor(x));

    // declare and initialize the pens
    CPen*   OldPen;
    CPen    RedOutLinePen(PS_SOLID, 1, RGB(255, 0, 0));
    CPen    BlackOutLinePen(PS_SOLID, 1, RGB(0, 0, 0));

    // draw the elements
    for(x = 0; x < NUM_ELEMENTS; x++)
    {
        OldBrush = dc->SelectObject
          (&GroupBrushes[atoi(Elements[x].GetGroupNumber())]);

        // if you are drawing the active group, 
        //outline it in red, otherwise black
        if(atoi(Elements[x].GetGroupNumber()) == group)
            OldPen = dc->SelectObject(&RedOutLinePen);
        else
            OldPen = dc->SelectObject(&BlackOutLinePen);

        dc->Rectangle((
           (Elements[x].GetColumn() - 1) * CellWidth), 
           ((Elements[x].GetRow() - 1) * CellWidth), 
           ((Elements[x].GetColumn() - 1) * CellWidth) 
           + CellWidth, 
           ((Elements[x].GetRow() - 1) * CellWidth) + 
           CellWidth);
        dc->SelectObject(OldBrush);
        dc->SelectObject(OldPen);
        dc->SetBkMode(TRANSPARENT);
        
        OldFont = dc->SelectObject(&NumberFont);
        dc->TextOut((
          (Elements[x].GetColumn() - 1) * CellWidth) + 
          AtomicNumberXOffset, ((Elements[x].GetRow() - 1) 
          * CellWidth) + AtomicNumberYOffset, 
          Elements[x].GetAtomicNumber(), 
          Elements[x].GetAtomicNumber().GetLength());
        dc->SelectObject(&SymbolFont);
        dc->TextOut(((Elements[x].GetColumn() - 1) * 
          CellWidth) + AtomicSymbolXOffset, 
          ((Elements[x].GetRow()    - 1) * CellWidth) + 
          AtomicSymbolYOffset, 
          Elements[x].GetElementSymbol(), 
          Elements[x].GetElementSymbol().GetLength());
        dc->SelectObject(OldFont);
    }

    // clean-up fonts
    SymbolFont.DeleteObject();
    NumberFont.DeleteObject();
    // clean-up brushes
    for(x = 0; x < NUM_GROUPS; x++)
        GroupBrushes[x].DeleteObject();
    // clean-up pens
    RedOutLinePen.DeleteObject();
    BlackOutLinePen.DeleteObject();
}


 

Copyright ?1998-2022 UCanCode.Net Software , all rights reserved.
Other product and company names herein may be the trademarks of their respective owners.

Please direct your questions or comments to webmaster@ucancode.net