标签:
3周之前老板让做一个天气预报模块,目标样式:
经过一番自以为是的编程,最终完成了任务。
今天突然让我做另一个项目,里面需要用到天气预报、时钟(拆开)。同时同事手里也有一个项目,也需要用到两个模块拆开的情况。
目标样式:
突然发现之前写的模块存在很多不足,耦合度较高。常用接口未开放出来。经过一天的修改,将模块拆了开来。给同事”审批“也通过了。
目标样式:
因为最终考虑到客户的修改,所以部分参数的修改是在XML中进行的。(这部分参数是初始化时设定的数值)
经过这件事,才明白设计模式的重要性,虽然说重剑无锋,大巧不工,但是一些神妙的招式还是必须要学的。公司有实体书,是在是爽歪歪了。。。
代码以及使用范例献上:
天气预报头文件:
#ifndef GET_WEATHER_INFO #define GET_WEATHER_INFO #include "ofxTextUtilsUL2.h" #include "ofxTrueTypeFontUL2.h" #include "ofxJSON.h" #include "TextReader.h" /* * 获取天气信息,两种方式,一种是json,一种是xml * XML调整城市信息的方式与JSON不一样 */ #define IMG_INFO_FILE "weather/WeatherStateSwap.json" typedef struct WeatherStruct { // 基础信息 wstring cityName; // 中文城市名 string cityName_En; // 英文城市名 wstring time; // 当前时间 // 天气 int stateIndex; // 天气状态序号 string state1; // 天气状况详情 string state2; // 天气状况 wstring stateDetail; // 详细天气信息 // 温度 wstring maxTemp, // 最高气温 minTemp, // 最低气温 curTemp; // 当前气温 // 风 wstring windState; // 风况 wstring windDir; // 风向 wstring windPower; // 风力 // 图片 ofImage img[2]; }WeatherStruct, *pWeatherStruct; // 函数指针,默认方式是json bool GetWeatherInfoXML(string url, WeatherStruct& ws); bool GetWeatherInfoJSon(string url, WeatherStruct& ws); class GetWeatherInfo { public: GetWeatherInfo(); ~GetWeatherInfo(); void setup(string filePath); void update(); void draw(); // 查看是否有天气信息 bool GetWeatherInfoState(){ return mHasInfo; } // 设置天气图标颜色 void SetWeatherImgColor(ofColor cl){ mColorWeatherImg = cl; } // 设置天气信息的颜色 void SetWeatherInfoColor(ofColor cl){ mColorWeatherInfo = cl; } // 设置温度信息的颜色 void SetTempColor(ofColor cl){ mColorTemp = cl; } // 设置整个绘制区域 void SetErea(ofRectangle rt){ rtWholeErea = rt; } // 设置获取天气的间隔 void SetParseGap(int gap){ mParseGap = gap; curTime = gap + 1; } // 查看偶去天气的间隔 int GetParGap(){ return mParseGap; } // 获取天气信息数据,对外开放,可以随意使用 WeatherStruct& GetWeatherData(){ return mWeatherData; } private: // 获取天气信息 bool(*GetInfo)(string url, WeatherStruct& ws); // 获取设置信息 bool GetSettingInfo(string filePath); private: // 数据 WeatherStruct mWeatherData; // 天气信息 ofColor mColorWeatherImg, mColorWeatherInfo, mColorTemp; // 接口地址 string mUrl; // 区域 ofRectangle rtWholeErea, rtWeatherImg; ofPoint posWeatherInfo, posTemp; // 字体 ofxTrueTypeFontUL2 fontWeatherInfo, fontTemp; // 城市名称 string mCityName; bool mHasInfo; // 有效信息的标志位,如果没有或者信息错误,该值为 false int mParseGap; // 解析间隔 int curTime; }; #endif
天气预报Cpp文件:
#include "GetWeatherInfo.h" //-------------------------------------------------------- GetWeatherInfo::GetWeatherInfo() { curTime = mParseGap + 1; mHasInfo = false; // 默认是xml方式加载 GetInfo = GetWeatherInfoXML; } //-------------------------------------------------------- GetWeatherInfo::~GetWeatherInfo() { } //-------------------------------------------------------- void GetWeatherInfo::setup(string filePath) { // 获取设置信息 GetSettingInfo(filePath); } //-------------------------------------------------------- void GetWeatherInfo::draw() { ofSetColor(mColorWeatherImg); mWeatherData.img[0].setAnchorPercent(0.5, 0.5); mWeatherData.img[0].draw(rtWeatherImg.x + rtWholeErea.x, rtWeatherImg.y + rtWholeErea.y, rtWeatherImg.width, rtWeatherImg.height); ofSetColor( mColorWeatherInfo); fontWeatherInfo.drawString(mWeatherData.stateDetail, posWeatherInfo.x + rtWholeErea.x, posWeatherInfo.y +rtWholeErea.y); ofSetColor(mColorTemp); fontTemp.drawString(mWeatherData.curTemp + L"°C", posTemp.x + rtWholeErea.x, posTemp.y + rtWholeErea.y); } //-------------------------------------------------------- void GetWeatherInfo::update() { if (curTime >= mParseGap) curTime = 0; else { curTime += ofGetLastFrameTime(); return; } if (!GetInfo(mUrl, mWeatherData)) { mHasInfo = false; return; } mHasInfo = true; float t = (float)mWeatherData.img[0].height / (float)mWeatherData.img[0].width; rtWeatherImg.height = rtWeatherImg.width * t; } //-------------------------------------------------------- bool GetWeatherInfo::GetSettingInfo(string filePath) { // 从设置文件中加载设置信息 ofXml xml; xml.load(filePath); xml.exists("mainErea"); // 城市名字 xml.setTo("mainErea[0]"); rtWholeErea.x = ofToFloat(xml.getAttribute("x")); rtWholeErea.y = ofToFloat(xml.getAttribute("y")); rtWholeErea.width = ofToFloat(xml.getAttribute("width")); rtWholeErea.height = ofToFloat(xml.getAttribute("height")); // 天气图标 xml.setTo("weatherImg"); rtWeatherImg.x = ofToFloat(xml.getAttribute("x"))*rtWholeErea.width; rtWeatherImg.y = ofToFloat(xml.getAttribute("y"))*rtWholeErea.height; rtWeatherImg.width = ofToFloat(xml.getAttribute("width"))*rtWholeErea.width; // 天气信息字体 xml.setToParent(); xml.setTo("weatherInfo"); posWeatherInfo.x = ofToFloat(xml.getAttribute("x"))*rtWholeErea.width; posWeatherInfo.y = ofToFloat(xml.getAttribute("y"))*rtWholeErea.height; fontWeatherInfo.loadFont( xml.getAttribute("fontStyle"), ofToInt( xml.getAttribute("fontSize") ) ); // 温度字体 xml.setToParent(); xml.setTo("tempInfo"); posTemp.x = ofToFloat(xml.getAttribute("x"))*rtWholeErea.width; posTemp.y = ofToFloat(xml.getAttribute("y"))*rtWholeErea.height; fontTemp.loadFont(xml.getAttribute("fontStyle"), ofToInt(xml.getAttribute("fontSize"))); xml.setToParent(); // 城市名字 xml.setToParent(); xml.exists("city"); xml.setTo("city[0]"); mCityName = xml.getAttribute("cityName"); // 排除掉china字符串,防止解析出错 if (mCityName == "china") { ofLogError("can`t determine which city") << endl; return false; } xml.setToParent(); xml.exists("loadWay"); // 加载方式 xml.setTo("loadWay[0]"); // 链接地址 if (xml.getAttribute("wayName") == "xml") { mUrl = xml.getAttribute("urlInfo") + mCityName + ".xml"; } else if (xml.getAttribute("wayName") == "json") { mUrl = xml.getAttribute("urlInfo") + mCityName + ".xml"; } else return false; // 加载信息的时间间隔 SetParseGap( ofToInt( xml.getAttribute("parseGap") ) ); return true; } string test; //-------------------------------------------------------- bool GetWeatherInfoXML(string url, WeatherStruct& ws) { // 解析XML数据 ofHttpResponse response; response = ofLoadURL(url); ofXml mXML; // 从缓存中加载数据 if (mXML.loadFromBuffer(response.data)){ ofLogNotice("load xml succeed") << endl; } else{ ofLogNotice("failed to load xml") << endl; return false; } // 取第一个城市的天气即可 mXML.setTo("city[0]"); // 分析数据 ws.cityName = ofStringUTF8ToWide(mXML.getAttribute("cityname")); // 城市名称 ws.state1 = mXML.getAttribute("state1"); // 天气状态1 ws.state2 = mXML.getAttribute("state2"); // 天气状态2 ws.stateDetail = ofStringUTF8ToWide(mXML.getAttribute("stateDetailed"));// 天气详细信息 ws.minTemp = ofStringUTF8ToWide(mXML.getAttribute("tem2")); // 最低温度 ws.maxTemp = ofStringUTF8ToWide(mXML.getAttribute("tem1")); // 最高温度 ws.curTemp = ofStringUTF8ToWide(mXML.getAttribute("temNow")); // 当前温度 ws.windState = ofStringUTF8ToWide(mXML.getAttribute("windState")); // 风况 ws.windDir = ofStringUTF8ToWide(mXML.getAttribute("windDir")); // 风向 ws.windPower = ofStringUTF8ToWide(mXML.getAttribute("windPower")); // 风力 // 加载图片--------------------------------------------------------------------- // 从JSON文件中映射获得图片名称 ofxJSONElement imgInfos; // 图片名称集合 imgInfos.open(IMG_INFO_FILE); ws.img[0].loadImage(imgInfos["WeatherStateSwap"]["imgDir"].asString() + "/" + imgInfos["WeatherStateSwap"][ws.state1].asString()); ws.img[1].loadImage(imgInfos["WeatherStateSwap"]["imgDir"].asString() + "/" + imgInfos["WeatherStateSwap"][ws.state2].asString()); // 加载图片--------------------------------------------------------------------- return true; } //-------------------------------------------------------- bool GetWeatherInfoJSon(string url, WeatherStruct& ws) { ofxJSONElement json; if (!json.open(url)) { ofLogError("failed to open json url") << endl; return false; } ws.cityName = ofStringUTF8ToWide( json["weatherinfo"]["city"].asString() ); ws.stateDetail = ofStringUTF8ToWide(json["weatherinfo"]["weather"].asString()); return true; }
时钟头文件:
#ifndef OFX_MY_CLOCK #define OFX_MY_CLOCK #include "ofmain.h" #include <cmath> /* 在指定位置画一只表 */ #define HOUR_PCT 0.5 #define MINUTE_PCT 0.7 #define SECOND_PCT 0.85 class ofxMyClock { public: ofxMyClock(); ~ofxMyClock(); void setup(); void update(); void draw(); void SetRadius(double r); void SetCenter(ofPoint p){ mCenter = p; } void SetColor(ofColor cl){ mColor = cl; } private: // 生成表盘图片 void GenerateFaceImg(); // 根据时间计算各个指针尾点 void ComputeHourAngle(); void ComputeMinuteAngle(); void ComputeSecondAngle(); private: ofPoint mCenter; // 中心 ofColor mColor; ofPoint ereaFace; // 表盘绘制区域 ofRectangle mRtHour, mRtMinute, mRtSecond; // 表针的绘制区域 double mHourAngle, mMinuteAngle, mSecondAngle; // 三个指针的旋转角度 ofImage mClockImg; // 表盘的图片 ofImage mNeedleImg; // 指针的图片 int alphaGap; // 透明块的宽度 int mW, mH, mNumPixels; int mSize; unsigned char* mOriClockFaceData, // 表盘图片原始数据 *mCurClockFaceData; int mHour; // 小时 int mMinute; // 分钟 int mSecond; // 秒 double mCurTime; }; #endif
时钟cpp文件:
#include "ofxMyClock.h" //-------------------------------------------------------- ofxMyClock::ofxMyClock() { // 加载表盘图片 mClockImg.loadImage("clock/face.png"); mClockImg.setAnchorPercent(0.5, 0.5); mNumPixels = mClockImg.bpp / 8; mW = mClockImg.width; mH = mClockImg.height; // 图片像素数据,用于实时修改图片数据 mCurClockFaceData = mClockImg.getPixels(); // 用于生成即时数据 mSize = mW * mH * mNumPixels; mOriClockFaceData = new unsigned char[mSize]; // 保存了原始数据 memcpy( mOriClockFaceData, mCurClockFaceData, mSize); // 透明覆盖条宽度 alphaGap = 30; // 加载表针图片 mNeedleImg.loadImage("clock/needle.png"); mNeedleImg.setAnchorPercent(0.5, 1); mCenter.x = ofGetWidth() / 2; mCenter.y = ofGetHeight() / 2; // 随窗口缩放 if ( ofGetWidth()<ofGetHeight() ) ereaFace.x = ereaFace.y = ofGetWidth() / 4; else ereaFace.x = ereaFace.y = ofGetHeight() / 4; SetRadius(ereaFace.x/2); mHour = mMinute = mSecond = 0; mCurTime = 0; mColor = ofColor(255,255,255,255); } //-------------------------------------------------------- ofxMyClock::~ofxMyClock() { delete[] mOriClockFaceData; } //-------------------------------------------------------- void ofxMyClock::setup() { ComputeSecondAngle(); ComputeMinuteAngle(); ComputeHourAngle(); GenerateFaceImg(); } //-------------------------------------------------------- void ofxMyClock::draw() { ofSetColor(mColor); // 表盘 mClockImg.setAnchorPercent(0.5, 0.5); mClockImg.draw(mCenter.x, mCenter.y, ereaFace.x, ereaFace.y); ofPushMatrix(); ofTranslate(mCenter.x, mCenter.y); // 时针 ofPushMatrix(); ofRotateZ(mHourAngle); mNeedleImg.draw(mRtHour); ofPopMatrix(); // 分针 ofPushMatrix(); ofRotateZ(mMinuteAngle); mNeedleImg.draw(mRtMinute); ofPopMatrix(); // 秒针 ofPushMatrix(); ofRotateZ(mSecondAngle); mNeedleImg.draw(mRtSecond); ofPopMatrix(); ofPopMatrix(); ofSetColor(255, 255, 255, 255); } //-------------------------------------------------------- void ofxMyClock::update() { if (mCurTime > 1) { ComputeSecondAngle(); if (mCurTime > 60) { ComputeMinuteAngle(); ComputeHourAngle(); mCurTime = 0; } GenerateFaceImg(); } mCurTime += ofGetLastFrameTime(); } //-------------------------------------------------------- void ofxMyClock::SetRadius(double r) { ereaFace.x = ereaFace.y = r * 2; float t = r * mNeedleImg.getWidth() / mNeedleImg.height; // 表针的绘制范围(未随着窗口缩放前) // 时针是半径的 0.5 mRtHour.x = mRtHour.y = 0; mRtHour.width = t * 1.5; mRtHour.height = r*HOUR_PCT; // 分针是半径的0.7 mRtMinute.x = mRtMinute.y = 0; mRtMinute.width = t; mRtMinute.height = r*MINUTE_PCT; // 秒针是半径的 0.85 mRtSecond.x = mRtSecond.y = 0; mRtSecond.width = t*0.5; mRtSecond.height = r*SECOND_PCT; } //-------------------------------------------------------- void ofxMyClock::ComputeHourAngle() { mHour = ofGetHours(); mHourAngle = (mHour % 12) * 30 + mMinute / 2.0; } //-------------------------------------------------------- void ofxMyClock::ComputeMinuteAngle() { mMinute = ofGetMinutes(); mMinuteAngle = mMinute * 6.0; } //-------------------------------------------------------- void ofxMyClock::ComputeSecondAngle() { mSecond = ofGetSeconds(); mSecondAngle = mSecond * 6.0; } //-------------------------------------------------------- void ofxMyClock::GenerateFaceImg() { // 先把图片还原成原图片 memcpy(mCurClockFaceData, mOriClockFaceData, mSize); // 思路:让两个“矩形条”随着时针分针旋转,他们所经过的地方都覆盖为透明,实现举行条的位置是0度角时的位置 // 最快速的办法是利用自定义混合公式 + FBO,使用贴图实现,速度最快 double angleHour = mHourAngle*DEG_TO_RAD; double angleMinute = mMinuteAngle*DEG_TO_RAD; double angleSecond = mSecondAngle*DEG_TO_RAD; double sin1, cos1, sin2, cos2, sin3, cos3; // 时 int x1, x2, y1, y2, x3, y3; double hW = mW / 2, hH = mH /2; for (double y = 0; y < hH/4;y+=0.5) { for (double x = hW - alphaGap; x < hW+ alphaGap; x+=0.3) { double angle = atan( (x - hW) / (y - hH) ); sin1 = sin(angleHour + angle - PI/2), cos1 = cos(angleHour + angle - PI / 2); sin2 = sin(angleMinute + angle - PI / 2), cos2 = cos(angleMinute + angle - PI / 2); sin3 = sin(angleSecond + angle - PI / 2), cos3 = cos(angleSecond + angle - PI / 2); // 计算旋转后的坐标 double dist = ofDist( hW, hH, x, y); y1 = (dist*sin1 +hW); x1 = (dist*cos1 +hH); y2 = (dist*sin2 + hW); x2 = (dist*cos2 + hH); y3 = (dist*sin3 + hW); x3 = (dist*cos3 + hH); if (x1 > mW - 1 || x1 < 0 || y1<0 || y1> mH - 1) continue; if (x2 > mW - 1 || x2 < 0 || y2<0 || y2> mH - 1) continue; if (x3 > mW - 1 || x3 < 0 || y3<0 || y3> mH - 1) continue; mCurClockFaceData[y1 * mW *4 + x1 * 4 + 3] = 0; mCurClockFaceData[y2 * mW * 4 + x2 * 4 + 3] = 0; mCurClockFaceData[y3 * mW * 4 + x3 * 4 + 3] = 0; } } mClockImg.update(); }
使用范例:
头文件
#pragma once #include "ofMain.h" #include "ofxMyClock.h" #include "GetWeatherInfo.h" class ofApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void keyPressed(int key); void keyReleased(int key); void mouseMoved(int x, int y); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(int x, int y, int button); void mouseEntered(int x, int y); void mouseExited(int x, int y); void windowResized(int w, int h); void dragEvent(ofDragInfo dragInfo); void gotMessage(ofMessage msg); ofxMyClock clock; ofPoint clockCenter; GetWeatherInfo weatherCls; };
Cpp文件:
#include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup(){ ofDisableAntiAliasing(); ofSetFrameRate(60); ofSetWindowPosition(100, 100); weatherCls.setup("settings.xml"); clock.setup(); clock.SetColor(ofColor(255,255,255,255)); } string ss; //-------------------------------------------------------------- void ofApp::update(){ ofSetWindowTitle( ofToString( ofGetFrameRate() ) ); weatherCls.update(); clock.update(); } //-------------------------------------------------------------- void ofApp::draw(){ ofBackgroundGradient(ofColor(200, 200, 200), ofColor(120, 120, 120),OF_GRADIENT_CIRCULAR); ofSetColor(255, 255, 255, 255); weatherCls.draw( ); clock.draw(); } //-------------------------------------------------------------- void ofApp::keyPressed(int key){ } //-------------------------------------------------------------- void ofApp::keyReleased(int key){ } //-------------------------------------------------------------- void ofApp::mouseMoved(int x, int y){ clockCenter.x = x, clockCenter.y = y; clock.SetCenter(clockCenter); clock.SetRadius( ofDist(x,y, ofGetWidth()/2, ofGetHeight()/2)/2 ); } //-------------------------------------------------------------- void ofApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mousePressed(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseReleased(int x, int y, int button){ } //-------------------------------------------------------------- void ofApp::mouseEntered(int x, int y){ } //-------------------------------------------------------------- void ofApp::mouseExited(int x, int y){ } //-------------------------------------------------------------- void ofApp::windowResized(int w, int h){ } //-------------------------------------------------------------- void ofApp::gotMessage(ofMessage msg){ } //-------------------------------------------------------------- void ofApp::dragEvent(ofDragInfo dragInfo){ }
继续修炼。
标签:
原文地址:http://www.cnblogs.com/SecretMan001/p/4727629.html