交互式元素周期表






4.81/5 (21投票s)
2003年10月5日
2分钟阅读

123424

2564
使用 GDI 创建可缩放元素周期表的文章
引言
本文提供了一个使用 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 日
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。