1 #include "mainwindow.h"\r
2 #include "forecastdata.h"\r
3 #include "weatherwidget.h"\r
4 #include "thermostatwidget.h"\r
5 #include "optionswidget.h"\r
6 #include "remoteaccessmanager.h"\r
7 #include "settingscreen.h"\r
8 #include "globalsettings.h"\r
9 #include "weatherdata.h"\r
10 #include "utilities.h"\r
11 \r
12 #include <QtGui>\r
13 \r
14 #include <QtDebug>\r
15 \r
16 /***********************************************************************************************************\r
17 * MainWindow\r
18 * Main Widget that holds all the child widgets and handles their layouts. Also coordinates loading of\r
19 * data from the web for the weather widget from the webdata class.\r
20 ************************************************************************************************************/\r
21 \r
22 MainWindow::MainWindow(QWidget *parent) :\r
23 QWidget(parent)\r
24 {\r
25 \r
26 QString http_proxy = QString::fromLatin1(qgetenv("qt_http_proxy"));\r
27 \r
28 // the MainWindow provides the container for the ThermostatWidget, WeatherWidget, and OptionsWidget\r
29 // it is also provides much of the interface between WebData and the widgets that require web updates\r
30 // it is set to initial hard-coded values first, and only updated from the web if internet options\r
31 // have been allowed\r
32 setObjectName("MainWindow");\r
33 \r
34 //get a handle to the global settings singleton\r
35 m_globalSettings = GlobalSettings::getInstance();\r
36 \r
37 // Initialize the proxy setting from http_proxy if defined\r
38 if(!http_proxy.isEmpty())\r
39 {\r
40 QUrl url(http_proxy);\r
41 m_globalSettings->setProxyInfo(url.host(), url.port());\r
42 }\r
43 \r
44 // sets initial weather background\r
45 setStyleSheet("mainwindow {border-image: url(:/Images/background-sunny-very-few-clouds.JPG)}");\r
46 \r
47 // remove cursor (i.e. mouse pointer) throughout application\r
48 //qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );\r
49 \r
50 //set a default temp for the thermostat\r
51 m_currentThermostatTemp = 72;\r
52 \r
53 // create web data object\r
54 webData = new WebData;\r
55 \r
56 //connect the signals for asynchronous data transfer\r
57 connect(webData, SIGNAL(dataAvailable(WeatherData*)), this, SLOT(setWebData(WeatherData*)));\r
58 connect(webData, SIGNAL(networkTimeout()), this, SLOT(webDataFailed()));\r
59 \r
60 // create weather widget\r
61 weatherWidget = new WeatherWidget(this);\r
62 connect(weatherWidget, SIGNAL(webReloadRequested()), this, SLOT(loadWebData()));\r
63 \r
64 // create thermostat widget\r
65 thermostatWidget = new ThermostatWidget;\r
66 thermostatWidget->setCurrentTempPtr(&m_currentThermostatTemp);\r
67 \r
68 // create options widget\r
69 optionsWidget = new OptionsWidget;\r
70 optionsWidget->awayScreen->setCurrentTempPtr(&m_currentThermostatTemp);\r
71 optionsWidget->settingScreen->setLicenseString(webData->licenseString());\r
72 \r
73 // connect 10 second timer that causes current temp to follow setpoint to pass signal through options widget\r
74 connect(thermostatWidget, SIGNAL(timeout()), optionsWidget, SIGNAL(currentTempChanged()));\r
75 // connect new city name signal to the MainWindow function changeCity()\r
76 connect(optionsWidget, SIGNAL(cityChanged()), this, SLOT(loadWebData()));\r
77 connect(optionsWidget, SIGNAL(valueChanged()), webData, SLOT(configureProxy()));\r
78 \r
79 // connect Celsius/Fahrenheit change signals\r
80 connect(optionsWidget, SIGNAL(valueChanged()), thermostatWidget, SLOT(updateUnit()));\r
81 connect(optionsWidget, SIGNAL(valueChanged()), weatherWidget, SIGNAL(valueChanged()));\r
82 connect(optionsWidget, SIGNAL(valueChanged()), this, SLOT(updateClock()));\r
83 \r
84 // make energy button change when setpoint is reached\r
85 connect(thermostatWidget, SIGNAL(setpointIsReached(bool)),this,SLOT(energySaving(bool)));\r
86 \r
87 // create energy button\r
88 energyButton = new QPushButton("48kW");\r
89 energyButton->setObjectName("energyButton");\r
90 energyButton->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);\r
91 energyButton->setAttribute(Qt::WA_StyledBackground,true);\r
92 energyButton->setFocusPolicy(Qt::NoFocus);\r
93 \r
94 // create time button\r
95 timeButton = new QPushButton("4:18PM");\r
96 timeButton->setObjectName("timeButton");\r
97 timeButton->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);\r
98 timeButton->setAttribute(Qt::WA_StyledBackground);\r
99 timeButton->setFocusPolicy(Qt::NoFocus);\r
100 \r
101 // create date button\r
102 dateButton = new QPushButton("Mon, Jun 17");\r
103 dateButton->setObjectName("dateButton");\r
104 dateButton->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);\r
105 dateButton->setAttribute(Qt::WA_StyledBackground);\r
106 dateButton->setFocusPolicy(Qt::NoFocus);\r
107 \r
108 // create close button\r
109 closeButton = new QPushButton();\r
110 closeButton->setIcon(QIcon(":/Images/simple-red-square-icon-x.png"));\r
111 closeButton->setIconSize(QSize(20,20));\r
112 closeButton->setObjectName("closeButton");\r
113 closeButton->setFocusPolicy(Qt::NoFocus);\r
114 connect(closeButton,SIGNAL(clicked()),this, SLOT(close()));\r
115 \r
116 //layout all of the widgets we just instatiated\r
117 createScreenLayout();\r
118 \r
119 //set clock to local system time\r
120 dateTime = QDateTime::currentDateTime();\r
121 \r
122 //start a timer to attempt to update the clock every second\r
123 clockTimer = new QTimer(this);\r
124 connect(clockTimer,SIGNAL(timeout()),this,SLOT(updateClock()));\r
125 clockTimer->start(1000);\r
126 \r
127 \r
128 //create and start the remote access manager\r
129 RemoteAccessManager *m_remoteAccessManager = new RemoteAccessManager(this);\r
130 m_remoteAccessManager->start();\r
131 //connect(m_remoteAccessManager, SIGNAL(remoteChangeReceived(QHash<QString,QVariant>)), this, SLOT(processCommand(QHash<QString,QVariant>)));\r
132 \r
133 //first set based on local cache so user sees something!\r
134 webData->loadLocalData();\r
135 \r
136 //call the changecity function which dispatches a network request for api weather data.\r
137 loadWebData();\r
138 }\r
139 \r
140 //FUNCTION: createScreenLayout\r
141 //\r
142 // all code to generate the main layout for the MainWindow is here.\r
143 //\r
144 \r
145 void MainWindow::createScreenLayout()\r
146 {\r
147 // create left portion of the layout holding the options buttons and thermostat control\r
148 QVBoxLayout *leftLayout = new QVBoxLayout;\r
149 leftLayout->addWidget(optionsWidget);\r
150 leftLayout->addWidget(thermostatWidget);\r
151 leftLayout->setStretchFactor(optionsWidget, 1);\r
152 leftLayout->setStretchFactor(thermostatWidget, 3);\r
153 \r
154 // combine the previous layout with the weatherwidget and the close button\r
155 QHBoxLayout *middleLayout = new QHBoxLayout;\r
156 middleLayout->addSpacing(6);\r
157 middleLayout->addLayout(leftLayout);\r
158 middleLayout->addSpacing(6);\r
159 middleLayout->addWidget(weatherWidget);\r
160 middleLayout->addWidget(closeButton);\r
161 middleLayout->addSpacing(6);\r
162 middleLayout->setStretchFactor(leftLayout, 1);\r
163 middleLayout->setStretchFactor(weatherWidget, 1);\r
164 middleLayout->setAlignment(closeButton,Qt::AlignTop);\r
165 \r
166 //combine the previous nested layout with the date time and energy buttons\r
167 QHBoxLayout *bottomLayout = new QHBoxLayout;\r
168 bottomLayout->addWidget(energyButton);\r
169 bottomLayout->addWidget(dateButton);\r
170 bottomLayout->addWidget(timeButton);\r
171 bottomLayout->setSpacing(0);\r
172 bottomLayout->setMargin(0);\r
173 \r
174 //it is easier to define size constraints for a widget so\r
175 //we will encapsulate everything in sizeLimiterWidget\r
176 \r
177 QWidget *sizeLimiterWidget = new QWidget;\r
178 sizeLimiterWidget->setMaximumSize(800, 450);\r
179 sizeLimiterWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\r
180 sizeLimiterWidget->setLayout(middleLayout);\r
181 QHBoxLayout* contentsBox = new QHBoxLayout;\r
182 contentsBox->addWidget(sizeLimiterWidget);\r
183 \r
184 //build the main layout\r
185 QVBoxLayout *mainLayout = new QVBoxLayout;\r
186 mainLayout->addStretch(0);\r
187 mainLayout->addLayout(contentsBox);\r
188 mainLayout->setStretchFactor(contentsBox, 1);\r
189 mainLayout->addStretch(0);\r
190 mainLayout->addLayout(bottomLayout);\r
191 mainLayout->setContentsMargins(0,0,0,0);\r
192 mainLayout->setAlignment(sizeLimiterWidget,Qt::AlignCenter);\r
193 mainLayout->setAlignment(bottomLayout,Qt::AlignBottom);\r
194 \r
195 // show layout\r
196 setLayout(mainLayout);\r
197 }\r
198 \r
199 //FUNCTION: closeEvent\r
200 //\r
201 // Reimplemented closeEvent handler just to allow the app to save the settings before exiting\r
202 //\r
203 \r
204 void MainWindow::closeEvent(QCloseEvent *e)\r
205 {\r
206 m_globalSettings->save();\r
207 delete m_globalSettings;\r
208 e->accept();\r
209 qApp->quit();\r
210 }\r
211 \r
212 void MainWindow::paintEvent(QPaintEvent *)\r
213 {\r
214 /* Applying CSS styles to custom widgets inherited from QWidget\r
215 requires reimplementing paintEvent(). Without doing it, custom\r
216 widgets will support only the background, background-clip,\r
217 and background-origin properties. */\r
218 \r
219 QStyleOption opt;\r
220 opt.init(this);\r
221 QPainter p(this);\r
222 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);\r
223 }\r
224 \r
225 void MainWindow::updateClock()\r
226 {\r
227 // make clock tick\r
228 dateTime = QDateTime::currentDateTime();\r
229 timeButton->setText(formatTimeString(dateTime.time(), m_globalSettings->timeFormat()));\r
230 dateButton->setText(dateTime.date().toString("ddd, MMM d"));\r
231 \r
232 }\r
233 \r
234 void MainWindow::energySaving(bool setpointReached)\r
235 {\r
236 // change energyButton based on whether or not setpoint is reached\r
237 if(setpointReached == true) {\r
238 energyButton->setStyleSheet("background: green;");\r
239 energyButton->setText("48kW");\r
240 }\r
241 else if(setpointReached == false) {\r
242 energyButton->setStyleSheet("background: red;");\r
243 energyButton->setText("64kW");\r
244 }\r
245 \r
246 }\r
247 \r
248 //FUNCTION: changeCity\r
249 //\r
250 // sends a web request through the webdata class to get weather data for the city\r
251 // passed. returns immediately. the webdata class invokes setWebData slot on success\r
252 // or webDataFailed otherwise.\r
253 \r
254 void MainWindow::loadWebData()\r
255 {\r
256 // get new city information and pass it to WebData to send\r
257 // a new request and get updated information for that city\r
258 weatherWidget->setStatusUpdating();\r
259 webData->changeCity(m_globalSettings->currentCity());\r
260 \r
261 }\r
262 \r
263 //FUNCTION: setWebData\r
264 //\r
265 // slot invoked by the webdata class on transfer of data across the network that handles\r
266 // setting an appropriate background for the mainwidget and passing the parsed data along\r
267 // to the weatherwidget\r
268 \r
269 void MainWindow::setWebData(WeatherData* weatherData)\r
270 {\r
271 \r
272 if(!weatherData)\r
273 {\r
274 webDataFailed();\r
275 return;\r
276 }\r
277 setBackground(weatherData->icon(), weatherData->localTime().time());\r
278 weatherWidget->setWeatherData(weatherData);\r
279 if(weatherData->cachedData())\r
280 weatherWidget->setStatusFailed();\r
281 else\r
282 weatherWidget->setStatusUpdated();\r
283 }\r
284 \r
285 //FUNCTION: webDataFailed\r
286 //\r
287 // slot invoked by the webdata class on an unsuccessful attempt at fetching weather data\r
288 // from the network. tells the weather widget to alert the user of the failure.\r
289 \r
290 void MainWindow::webDataFailed()\r
291 {\r
292 WeatherData *weatherData = weatherWidget->weatherData();\r
293 \r
294 //if the update fails, generate random data so things look nice!\r
295 //easiest way to tell if the update failed and we have no cached data is\r
296 //to compare the current configured city to the data's city\r
297 \r
298 if(weatherData->currentCity() != m_globalSettings->currentCity())\r
299 {\r
300 //generate a quick lookup for the icons\r
301 QStringList icons;\r
302 icons << "sunny" << "partlysunny" << "cloudy" << "rain" << "tstorms";\r
303 \r
304 //update the city to the current configured city\r
305 weatherData->setCurrentCity(m_globalSettings->currentCity());\r
306 \r
307 //generate temperature numbers +/- 75\r
308 weatherData->setCurrentTemp(75+(qrand()%10*qrand()%2*(-1)));\r
309 \r
310 //mod a random number by the number of icons then index into the array to pick one\r
311 weatherData->setIcon(icons[qrand()%icons.size()]);\r
312 \r
313 //set the main page background to selected icon\r
314 setBackground(weatherData->icon(), weatherData->localTime().time());\r
315 \r
316 //now iterate through the forecast days and do the same thing\r
317 int i;\r
318 for(i = 0; i < weatherData->forecastData().size(); i++)\r
319 {\r
320 ForecastData* forecast = weatherData->forecastData().at(i);\r
321 \r
322 forecast->setLowTemp(75+(qrand()%10*qrand()%2*(-1)));\r
323 forecast->setHighTemp(forecast->lowTemp()+qrand()%10);\r
324 forecast->setIcon(icons[qrand()%icons.size()]);\r
325 }\r
326 \r
327 //set the data just to trigger an update of the widgets\r
328 weatherWidget->setWeatherData(weatherData);\r
329 }\r
330 \r
331 weatherWidget->setStatusFailed();\r
332 }\r
333 \r
334 void MainWindow::setBackground(QString icon, QTime localTime)\r
335 {\r
336 // set the MainWindow background based on the current conditions\r
337 \r
338 if (icon == "partlysunny" || icon == "mostlycloudy" ) {\r
339 if(localTime.hour() < 5 || localTime.hour() > 20) {\r
340 setStyleSheet("MainWindow {border-image: url(:/Images/clear_night_sky.jpeg)}");\r
341 } else {\r
342 setStyleSheet("MainWindow {border-image: url(:/Images/mostlycloudy.jpg)}");\r
343 }\r
344 }\r
345 else if (icon == "fog") {\r
346 setStyleSheet("MainWindow {border-image: url(:/Images/fog.jpg)}");\r
347 }\r
348 else if (icon == "hazy") {\r
349 if(localTime.hour() < 5 || localTime.hour() > 20) {\r
350 setStyleSheet("MainWindow {border-image: url(:/Images/clear_night_sky.jpeg)}");\r
351 } else {\r
352 setStyleSheet("MainWindow {border-image: url(:/Images/hazy.jpg)}");\r
353 }\r
354 }\r
355 else if (icon == "cloudy") {\r
356 setStyleSheet("MainWindow {border-image: url(:/Images/overcast.jpg)}");\r
357 }\r
358 else if (icon == "rain" || icon == "chancerain") {\r
359 setStyleSheet("MainWindow {border-image: url(:/Images/rain.jpg)}");\r
360 }\r
361 else if (icon == "sleet" || icon == "chancesleet") {\r
362 setStyleSheet("MainWindow {border-image: url(:/Images/sleet.jpg)}");\r
363 }\r
364 else if (icon == "flurries" || icon == "snow" ||\r
365 icon == "chanceflurries" || icon == "chancesnow") {\r
366 setStyleSheet("MainWindow {border-image: url(:/Images/snow.jpg)}");\r
367 }\r
368 else if (icon == "clear" || icon == "sunny") {\r
369 if(localTime.hour() < 5 || localTime.hour() > 20) {\r
370 setStyleSheet("MainWindow {border-image: url(:/Images/clear_night_sky.jpeg)}");\r
371 } else {\r
372 setStyleSheet("MainWindow {border-image: url(:/Images/clear_sky.jpg)}");\r
373 }\r
374 }\r
375 else if (icon == "mostlysunny" || icon == "partlycloudy" ||\r
376 icon == "unknown") {\r
377 if(localTime.hour() < 5 || localTime.hour() > 20) {\r
378 setStyleSheet("MainWindow {border-image: url(:/Images/clear_night_sky.jpeg)}");\r
379 } else {\r
380 setStyleSheet("MainWindow {border-image: url(:/Images/background-sunny-very-few-clouds.JPG)}");\r
381 }\r
382 }\r
383 else if (icon == "tstorms" || icon == "chancetstorms") {\r
384 setStyleSheet("MainWindow {border-image: url(:/Images/storm.jpg)}");\r
385 \r
386 }\r
387 \r
388 }\r
389 \r
390 QHash<QString, QVariant> MainWindow::processCommand(QHash<QString, QVariant> command)\r
391 {\r
392 if(command["command"] == "update" || command["command"] == "update_forced")\r
393 {\r
394 QVariantHash data;\r
395 data.unite(weatherWidget->getCurrentData());\r
396 data.unite(thermostatWidget->getCurrentData());\r
397 return data;\r
398 \r
399 }\r
400 else if(command["command"] == "change_temp")\r
401 {\r
402 if(command["dt"].toInt()>0)\r
403 thermostatWidget->increaseTemp();\r
404 else if(command["dt"].toInt()<0)\r
405 thermostatWidget->decreaseTemp();\r
406 }\r
407 return QHash<QString, QVariant>();\r
408 }\r