图形绘图班组织

时间:2016-02-08 11:00:29

标签: c++ oop arduino embedded

我有一个基本的4x20字符LCD,我想用它来显示带有按钮的菜单,使用Arduino作为驱动程序(有限的标准库支持)。

Example LCD menu and buttons

我正在考虑生成一个接口类GraphicObject,然后所有图形对象都从该接口继承(如Button,MenuItem等)。似乎合乎逻辑的是,GraphicObject类应该有一个可以覆盖的draw方法。

目前,我有一个名为Lcd的课程,涵盖了文本和角色定位的低级绘图。为了绘制任何东西,我需要访问其中一个Lcd对象。如果我在我的GraphicObject或派生对象中包含指向Lcd对象的指针,我将它们耦合在一起并使它们成为仅Lcd对象。如果我改变了显示设备的类型,这些类就不再适用了。

如何组织这些类以使它们松散耦合并允许在以后更改显示类型?我是否应该使用LCDButton LCDMenuItem继承Button和MenuItem接口,然后为其他显示设备创建其他对象(OtherDisplayButton OtherDisplayMenuItem)?

任何建议的阅读?我看过很多例子,但是他们似乎都没有详细介绍draw方法的功能,以及是应该直接访问还是通过其他控制对象访问设备。

由于

编辑1

简要代码构思概述

#include "Arduino.h"

class Lcd {
public:
  struct Parameters {
    uint_fast8_t numberLines;
    uint_fast8_t numberCharacters;
//  uint_fast8_t* lineOffsets;
    Print* serial; // Can be any Serial device (all inherit from Print).
  };

protected:
  Parameters parameters_;
  const uint_fast8_t escapeCode_ = 0x1B;
  const uint_fast8_t directCode_ = 0xFE;

  void sendCommand_(uint_fast8_t command, uint_fast8_t parameter = 0);
  void sendCommandDirect_(uint_fast8_t command);

public:
  Lcd(Parameters parameters);
  void clearDisplay(void);
  void moveCursor(uint_fast8_t line, uint_fast8_t character);
  void resetDisplay(void);
  void customCharacter(const uint_fast8_t address,
      const uint_fast8_t characterMap[8]);

  void write(uint8_t character);

  // Boilerplate print forwarders.
  void print(const char character);
  void print(const String &string);
  void print(const char string[]);

  // Boilerplate println forwarders.
  void println(const char character);
  void println(const String &string);
  void println(const char string[]);
  void println(void);
};

class GraphicObject {
  virtual void draw(void)=0;
};

class Button: public GraphicObject {
public:
  typedef void (*buttonAction)(void);
  virtual void setText(const String text)=0;
  virtual const String getText() =0;
  virtual bool isActive()=0;
  virtual void setActive(bool)=0;
  virtual void setAction(buttonAction action)=0;
};

class MenuItem: public Button {
public:
  typedef void (*menuAction)(void);
  virtual MenuItem* parentItem()=0;
  virtual const MenuItem* addItem(String text, menuAction action)=0;
};

class VScrollbar: public GraphicObject {
public:
  virtual void setAtTop(bool atTop);
  virtual void setAtBottom(bool atBottom);
};

class LcdButton: public Button {
private:
  Lcd* lcd_;
  String text_;bool active_;
public:
  LcdButton(Lcd* lcd);
  void draw(void);
  void setText(String text);
  const String getText();bool isActive();
  void setActive(bool);
  void setAction(Button::buttonAction action);
};

class LcdWindow: public GraphicObject {
private:
  LcdButton* lcdButtons_ = nullptr;
public:
  enum class Position
    : uint_fast8_t {
      LEFT,
    RIGHT
  };

  bool addButton(LcdButton* lcdButton, uint_fast8_t line, uint_fast8_t offset);

  bool addVScrollbar(VScrollbar* vScrollbar, Position position);
  void draw();
};

int main(void) {
  Lcd::Parameters lcdParameters;
  lcdParameters.numberCharacters = 20;
  lcdParameters.numberLines = 4;
  lcdParameters.serial = &Serial1;
  Lcd lcd = Lcd(lcdParameters);
  LcdButton myButton(&lcd);
  myButton.setText("Select");
  myButton.setActive(true);
  LcdWindow lcdWindow;
  lcdWindow.addButton(&myButton, 1, 1);
  lcdWindow.draw();
  while (1){}
  return 0;
}

1 个答案:

答案 0 :(得分:1)

有不同的方法可以做到这一点。原则上,您应该为低级LCD驱动程序模块定义一个接口(API),并且只调用低级API的函数来绘制一些东西。然后可以交换此api的实现,而无需更改高级代码。

最简单的方法是定义一个抽象的c ++基类,其中必须派生所有lcd驱动程序实现。基类应该具有需要由派生实现重载的虚方法。

但是一个小小的信息:c ++类中的虚方法要求编译器为实例化对象时创建的每个对象生成一个方法指针表。这需要更多的记忆。此外,对这些类的对象的所有函数调用都是间接的(编译器生成的代码首先查找实际函数指针,然后使用此指针调用函数),这会使得结果代码稍慢。