本例摘选自《Design Pattern in Modern C++》一书的外观模式的demo。作者的源码中,Window类和TextBuffer类没有给实现。这里本人使用OpenCV自己实现了写字的Window类和TextBuffer类。
本例的绘图系统的设计思路图如下。
struct TextBuffer {
std::vector texts;
double max_width;
double max_height;
double total_height;
void add_string() {
// update max_width
// update_max_height
// update total_height
}
};
struct Window {
std::vector buffers;
void add_buffer() {
}
void show() {
// 获取所有buffer的最大宽,作为窗口宽
// 获取所有buffer的高之和,作为窗口高
// 从上往下显示buffer
// 第0个: 0, 0
// 第1个: 1, buffer0.total_height
// 第2个: 2, buffer0.total_height + buffer1.total_height
}
private:
int width;
int height;
int get_width() {
// std::max buffer0.max_width, buffer1.max_width, buffer2.max_width ... buffern.max_width
return result;
}
int get_height() {
// std::accumulate buffer0.total_height, buffer1.total_height, buffer2.total_height ... buffern.total_height
return result;
}
};
本例的代码结构如下,
test/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(facade)
set(CMAKE_CXX_STANDARD 20)
add_definitions(-g)
find_package(Boost REQUIRED COMPONENTS
system
filesystem
serialization
program_options
thread
)
find_package(OpenCV REQUIRED )
find_package(glog REQUIRED)
include_directories(${Boost_INCLUDE_DIRS} /usr/local/include /usr/local/include/opencv4 /usr/local/iODBC/include /opt/snowflake/snowflakeodbc/include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_SOURCE_DIR}/../include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/)
LINK_DIRECTORIES(/usr/local/lib /usr/local/iODBC/lib /opt/snowflake/snowflakeodbc/lib/universal)
file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/death_handler/impl/*.cpp)
foreach( sourcefile ${APP_SOURCES} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
string(FIND "${filename}" "test.cpp" "TEMP")
if( NOT "${TEMP}" STREQUAL "-1" )
string(REPLACE ".cpp" "" file ${filename})
add_executable(${file} ${APP_SOURCES})
target_link_libraries(${file} ${Boost_LIBRARIES} ${OpenCV_LIBS})
target_link_libraries(${file} glog::glog ssl crypto libgtest.a libgmock.a iodbc iodbcinst libnanodbc.a pthread)
endif()
endforeach( sourcefile ${APP_SOURCES})
test/facade_test.cpp
#include "death_handler/death_handler.h" #include#include "facade.hpp" #include #include int main(int argc, char** argv) { FLAGS_log_dir = "./"; FLAGS_alsologtostderr = true; // 日志级别 INFO, WARNING, ERROR, FATAL 的值分别为0、1、2、3 FLAGS_minloglevel = 0; Debug::DeathHandler dh; google::InitGoogleLogging("./logs.log"); testing::InitGoogleTest(&argc, argv); int ret = RUN_ALL_TESTS(); return ret; } GTEST_TEST(FacadeTests, Facade) { auto window = Console::instance().multi_buffers(3); for (std::size_t i=0; i<20; ++i){ cv::HersheyFonts font = cv::HersheyFonts::FONT_HERSHEY_DUPLEX; if(i%2 == 0) { font = cv::HersheyFonts::FONT_HERSHEY_TRIPLEX; } window->buffers[0].add_string( std::string("This is line ") + std::to_string(i), font); } window->show(); window->wait_to_dispose(); }
test/include/facade.hpp
#ifndef _FREDRIC_FACADE_HPP_ #define _FREDRIC_FACADE_HPP_ #include "window.h" #includeclass Console { public: static Console& instance() { static Console console; return console; } std::shared_ptr single_buffers(int buffer_count) { auto w = std::make_shared (); w->add_buffer(TextBuffer{}); return w; } std::shared_ptr multi_buffers(int buffer_count) { auto w = std::make_shared (); for(int i=0; i add_buffer(TextBuffer{}); } return w; } }; #endif
test/include/window.h
#include "opencv2/opencv.hpp" #include#include #include #include std::string const window_name = "Draw Fonts"; int const EXIT_KEY = 27; int const line_distance = 5; int const title_bar_height = 50; struct Text { std::string text; cv::HersheyFonts font; }; struct TextBuffer { std::vector texts; int max_width{0}; int max_height{0}; int total_height{0}; void add_string(std::string const& text, cv::HersheyFonts const& font_style) { texts.emplace_back(Text{text, font_style}); int baseline; auto size = cv::getTextSize(text, font_style, 1.0f, 1, &baseline); if(size.width > max_width) { max_width = size.width; } if(size.height > max_height) { max_height = size.height; } // 加的5是行间距 total_height = texts.size() * (max_height + line_distance); } // 用于std::max_element,求最大行宽 bool operator<(TextBuffer const& other) const { return this->max_width < other.max_width; } // 用于std::accmulate,求所有buffer的高度 operator int() { return this->total_height; } }; struct Window { Window() { cv::namedWindow(window_name, cv::WINDOW_NORMAL); cv::setWindowProperty(window_name, cv::WND_PROP_TOPMOST, 1); } void add_buffer(TextBuffer const& buffer) { buffers.emplace_back(std::move(buffer)); } void show() { width = get_width(); height = get_height(); cv::resizeWindow(window_name, width, height + title_bar_height); img_ = cv::Mat::zeros(cv::Size(width, height + title_bar_height), CV_8UC1); img_.setTo(cv::Scalar(255)); int current_y_pos = title_bar_height; for(auto&& buffer_: buffers) { for(auto&& text_: buffer_.texts) { cv::putText(img_, text_.text, cv::Point(0, current_y_pos), text_.font, 1.0, cv::Scalar(0),1); current_y_pos += buffer_.max_height + line_distance; } } cv::imshow(window_name, img_); } void wait_to_dispose() { while(EXIT_KEY != cv::waitKey(1000)) { } } std::vector buffers; private: int width; int height; cv::Mat img_; int get_width() { auto buffer = *std::max_element(buffers.begin(), buffers.end()); return buffer.max_width; } int get_height() { auto result = std::accumulate(buffers.begin(), buffers.end(), 0); return result; } };
程序输出如下,



