65.9K
CodeProject 正在变化。 阅读更多。
Home

交互式元素周期表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (21投票s)

2003年10月5日

2分钟阅读

viewsIcon

123424

downloadIcon

2564

使用 GDI 创建可缩放元素周期表的文章

PTE screenshot

引言

本文提供了一个使用 GDI 创建交互式元素周期表 (PTE) 的示例。PTE 可调整大小,对话框上的控件也会调整大小。本文旨在演示 GDI、动态调整控件大小和简单的数据库访问。

我添加了选择化学组(卤族元素、稀有气体等)并使用滑块控件为该组设置颜色的功能。双击一个元素将打开一个显示该元素详细信息的对话框。右键单击一个元素会将该元素添加到listbox中。

这个应用程序是进一步开发的良好起点,并涉及以下 MFC 功能

  • 数据库使用
  • 滑块控件
  • GDI 输出
  • 控件大小缩放

Using the Code

注意:演示项目包含一个 MS Access 文件 - 您必须配置您的 ODBC 管理器以包含到此 MDB 文件的映射 - 数据源名称必须为 PeriodicTableApp

介绍了两个封装 PTE 行为的类

  • pteTable - 元素周期表
  • pteElement - PTE 中的一个元素

该表由一个 pteElement 对象数组和定义元素单元格大小、文本偏移量以及元素对象访问方法的成员组成。

pteTable 类

#ifndef __PTETABLE_H__
#define __PTETABLE_H__
#include "pteElement.h"
#include <afxdb.h>

#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 initial 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__</afxdb.h>

pteElement 类

#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

关注点

这个项目始于上周,当时一个用户询问如何实现 PTE,并想知道如何最好地确定用户选择了哪个元素。 提出了三种选择

  • 为每个元素使用一个按钮
  • 使用一个位图来表示 PTE,并在 MemDC 中使用另一个位图,每个元素使用不同的颜色
  • 使用 GDI

我排除了使用按钮,因为它非常不美观并且会占用大量资源。 我排除了第二个,因为文本在缩放窗口时看起来会很糟糕,而且进行更改会非常耗时。 我选择了 GDI。 实际处理绘图的代码只有大约 14 行,并允许突出显示当前化学组中的所有元素

/* ******************************** */
/* 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();
}

历史

  • 版本 1.0 - 提交于 2003 年 10 月 3 日

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.