MythTV  master
weatherSource.cpp
Go to the documentation of this file.
1 // C++
2 #include <unistd.h>
3 
4 // QT headers
5 #include <QApplication>
6 #include <QDir>
7 #include <QFile>
8 #include <QTextStream>
9 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
10 #include <QTextCodec>
11 #else
12 #include <QStringConverter>
13 #endif
14 
15 // MythTV headers
16 #include <libmyth/mythcontext.h>
17 #include <libmythbase/compat.h>
18 #include <libmythbase/exitcodes.h>
19 #include <libmythbase/mythdb.h>
20 #include <libmythbase/mythdirs.h>
22 
23 // MythWeather headers
24 #include "weatherScreen.h"
25 #include "weatherSource.h"
26 
27 QStringList WeatherSource::ProbeTypes(const QString& workingDirectory,
28  const QString& program)
29 {
30  QStringList arguments("-t");
31  const QString loc = QString("WeatherSource::ProbeTypes(%1 %2): ")
32  .arg(program, arguments.join(" "));
33  QStringList types;
34 
35  uint flags = kMSRunShell | kMSStdOut |
37  MythSystemLegacy ms(program, arguments, flags);
38  ms.SetDirectory(workingDirectory);
39  ms.Run();
40  if (ms.Wait() != GENERIC_EXIT_OK)
41  {
42  LOG(VB_GENERAL, LOG_ERR, loc + "Cannot run script");
43  return types;
44  }
45 
46  QByteArray result = ms.ReadAll();
47  QTextStream text(result);
48 
49  while (!text.atEnd())
50  {
51  QString tmp = text.readLine();
52 
53  while (tmp.endsWith('\n') || tmp.endsWith('\r'))
54  tmp.chop(1);
55 
56  if (!tmp.isEmpty())
57  types += tmp;
58  }
59 
60  if (types.empty())
61  LOG(VB_GENERAL, LOG_ERR, loc + "Invalid output from -t option");
62 
63  return types;
64 }
65 
66 bool WeatherSource::ProbeTimeouts(const QString& workingDirectory,
67  const QString& program,
68  std::chrono::seconds &updateTimeout,
69  std::chrono::seconds &scriptTimeout)
70 {
71  QStringList arguments("-T");
72  const QString loc = QString("WeatherSource::ProbeTimeouts(%1 %2): ")
73  .arg(program, arguments.join(" "));
74 
76  scriptTimeout = DEFAULT_SCRIPT_TIMEOUT;
77 
78  uint flags = kMSRunShell | kMSStdOut |
80  MythSystemLegacy ms(program, arguments, flags);
81  ms.SetDirectory(workingDirectory);
82  ms.Run();
83  if (ms.Wait() != GENERIC_EXIT_OK)
84  {
85  LOG(VB_GENERAL, LOG_ERR, loc + "Cannot run script");
86  return false;
87  }
88 
89  QByteArray result = ms.ReadAll();
90  QTextStream text(result);
91 
92  QStringList lines;
93  while (!text.atEnd())
94  {
95  QString tmp = text.readLine();
96 
97  while (tmp.endsWith('\n') || tmp.endsWith('\r'))
98  tmp.chop(1);
99 
100  if (!tmp.isEmpty())
101  lines << tmp;
102  }
103 
104  if (lines.empty())
105  {
106  LOG(VB_GENERAL, LOG_ERR, loc + "Invalid Script Output! No Lines");
107  return false;
108  }
109 
110  QStringList temp = lines[0].split(',');
111  if (temp.size() != 2)
112  {
113  LOG(VB_GENERAL, LOG_ERR, loc +
114  QString("Invalid Script Output! '%1'").arg(lines[0]));
115  return false;
116  }
117 
118  std::array<bool,2> isOK {};
119  uint ut = temp[0].toUInt(isOK.data());
120  uint st = temp[1].toUInt(&isOK[1]);
121  if (!isOK[0] || !isOK[1])
122  {
123  LOG(VB_GENERAL, LOG_ERR, loc +
124  QString("Invalid Script Output! '%1'").arg(lines[0]));
125  return false;
126  }
127 
128  updateTimeout = std::chrono::seconds(ut);
129  scriptTimeout = std::chrono::seconds(st);
130 
131  return true;
132 }
133 
135 {
136  QStringList arguments("-v");
137 
138  const QString loc = QString("WeatherSource::ProbeInfo(%1 %2): ")
139  .arg(info.program, arguments.join(" "));
140 
141  uint flags = kMSRunShell | kMSStdOut |
143  MythSystemLegacy ms(info.program, arguments, flags);
144  ms.SetDirectory(info.path);
145  ms.Run();
146  if (ms.Wait() != GENERIC_EXIT_OK)
147  {
148  LOG(VB_GENERAL, LOG_ERR, loc + "Cannot run script");
149  return false;
150  }
151 
152  QByteArray result = ms.ReadAll();
153  QTextStream text(result);
154 
155  QStringList lines;
156  while (!text.atEnd())
157  {
158  QString tmp = text.readLine();
159 
160  while (tmp.endsWith('\n') || tmp.endsWith('\r'))
161  tmp.chop(1);
162 
163  if (!tmp.isEmpty())
164  lines << tmp;
165  }
166 
167  if (lines.empty())
168  {
169  LOG(VB_GENERAL, LOG_ERR, loc + "Invalid Script Output! No Lines");
170  return false;
171  }
172 
173  QStringList temp = lines[0].split(',');
174  if (temp.size() != 4)
175  {
176  LOG(VB_GENERAL, LOG_ERR, loc +
177  QString("Invalid Script Output! '%1'").arg(lines[0]));
178  return false;
179  }
180 
181  info.name = temp[0];
182  info.version = temp[1];
183  info.author = temp[2];
184  info.email = temp[3];
185 
186  return true;
187 }
188 
189 /* Basic logic of this behemouth...
190  * run script with -v flag, this returns among other things, the version number
191  * Search the database using the name (also returned from -v).
192  * if it exists, compare versions from -v and db
193  * if the same, populate the info struct from db, and we're done
194  * if they differ, get the rest of the needed information from the script and
195  * update the database, note, it does not overwrite the existing timeout values.
196  * if the script is not in the database, we probe it for types and default
197  * timeout values, and add it to the database
198  */
200 {
201  if (!fi.isReadable() || !fi.isExecutable())
202  return nullptr;
203 
205  info.path = fi.absolutePath();
206  info.program = fi.absoluteFilePath();
207 
209  return nullptr;
210 
212  QString query =
213  "SELECT sourceid, source_name, update_timeout, retrieve_timeout, "
214  "path, author, version, email, types FROM weathersourcesettings "
215  "WHERE hostname = :HOST AND source_name = :NAME;";
216  db.prepare(query);
217  db.bindValue(":HOST", gCoreContext->GetHostName());
218  db.bindValue(":NAME", info.name);
219 
220  if (!db.exec())
221  {
222  LOG(VB_GENERAL, LOG_ERR, "Invalid response from database");
223  return nullptr;
224  }
225 
226  // the script exists in the db
227  if (db.next())
228  {
229  info.id = db.value(0).toInt();
230  info.updateTimeout = std::chrono::seconds(db.value(2).toUInt());
231  info.scriptTimeout = std::chrono::seconds(db.value(3).toUInt());
232 
233  // compare versions, if equal... be happy
234  QString dbver = db.value(6).toString();
235  if (dbver == info.version)
236  {
237  info.types = db.value(8).toString().split(",");
238  }
239  else
240  {
241  // versions differ, change db to match script output
242  LOG(VB_GENERAL, LOG_INFO, "New version of " + info.name + " found");
243  query = "UPDATE weathersourcesettings SET source_name = :NAME, "
244  "path = :PATH, author = :AUTHOR, version = :VERSION, "
245  "email = :EMAIL, types = :TYPES WHERE sourceid = :ID";
246  db.prepare(query);
247  // these info values were populated when getting the version number
248  // we leave the timeout values in
249  db.bindValue(":NAME", info.name);
250  db.bindValue(":PATH", info.program);
251  db.bindValue(":AUTHOR", info.author);
252  db.bindValue(":VERSION", info.version);
253 
254  // run the script to get supported data types
255  info.types = WeatherSource::ProbeTypes(info.path, info.program);
256 
257  db.bindValue(":TYPES", info.types.join(","));
258  db.bindValue(":ID", info.id);
259  db.bindValue(":EMAIL", info.email);
260  if (!db.exec())
261  {
262  MythDB::DBError("Updating weather source settings.", db);
263  return nullptr;
264  }
265  }
266  }
267  else
268  {
269  // Script is not in db, probe it and insert it into db
270  query = "INSERT INTO weathersourcesettings "
271  "(hostname, source_name, update_timeout, retrieve_timeout, "
272  "path, author, version, email, types) "
273  "VALUES (:HOST, :NAME, :UPDATETO, :RETTO, :PATH, :AUTHOR, "
274  ":VERSION, :EMAIL, :TYPES);";
275 
277  info.program,
278  info.updateTimeout,
279  info.scriptTimeout))
280  {
281  return nullptr;
282  }
283  db.prepare(query);
284  db.bindValue(":NAME", info.name);
285  db.bindValue(":HOST", gCoreContext->GetHostName());
286  db.bindValue(":UPDATETO", QString::number(info.updateTimeout.count()));
287  db.bindValue(":RETTO", QString::number(info.scriptTimeout.count()));
288  db.bindValue(":PATH", info.program);
289  db.bindValue(":AUTHOR", info.author);
290  db.bindValue(":VERSION", info.version);
291  db.bindValue(":EMAIL", info.email);
292  info.types = ProbeTypes(info.path, info.program);
293  db.bindValue(":TYPES", info.types.join(","));
294  if (!db.exec())
295  {
296  MythDB::DBError("Inserting weather source", db);
297  return nullptr;
298  }
299  query = "SELECT sourceid FROM weathersourcesettings "
300  "WHERE source_name = :NAME AND hostname = :HOST;";
301  // a little annoying, but look at what we just inserted to get the id
302  // number, not sure if we really need it, but better safe than sorry.
303  db.prepare(query);
304  db.bindValue(":HOST", gCoreContext->GetHostName());
305  db.bindValue(":NAME", info.name);
306  if (!db.exec())
307  {
308  MythDB::DBError("Getting weather sourceid", db);
309  return nullptr;
310  }
311  if (!db.next())
312  {
313  LOG(VB_GENERAL, LOG_ERR, "Error getting weather sourceid");
314  return nullptr;
315  }
316  info.id = db.value(0).toInt();
317  }
318 
319  return new ScriptInfo(info);
320 }
321 
329  : m_ready(info != nullptr),
330  m_inuse(info != nullptr),
331  m_info(info),
332  m_updateTimer(new QTimer(this))
333 {
334  QDir dir(GetConfDir());
335  if (!dir.exists("MythWeather"))
336  dir.mkdir("MythWeather");
337  dir.cd("MythWeather");
338  if (info != nullptr) {
339  if (!dir.exists(info->name))
340  dir.mkdir(info->name);
341  dir.cd(info->name);
342  }
343  m_dir = dir.absolutePath();
344 
346 }
347 
349 {
350  if (m_ms)
351  {
353  m_ms->Wait(5s);
354  delete m_ms;
355  }
356  delete m_updateTimer;
357 }
358 
360 {
361  connect(this, &WeatherSource::newData,
363  ++m_connectCnt;
364 
365  if (!m_data.empty())
366  {
367  emit newData(m_locale, m_units, m_data);
368  }
369 }
370 
372 {
373  disconnect(this, nullptr, ws, nullptr);
374  --m_connectCnt;
375 }
376 
377 QStringList WeatherSource::getLocationList(const QString &str)
378 {
379  QString program = m_info->program;
380  QStringList args;
381  args << "-l";
382  args << str;
383 
384  const QString loc = QString("WeatherSource::getLocationList(%1 %2): ")
385  .arg(program, args.join(" "));
386 
387  uint flags = kMSRunShell | kMSStdOut |
389  MythSystemLegacy ms(program, args, flags);
390  ms.SetDirectory(m_info->path);
391  ms.Run();
392 
393  if (ms.Wait() != GENERIC_EXIT_OK)
394  {
395  LOG(VB_GENERAL, LOG_ERR, loc + "Cannot run script");
396  return {};
397  }
398 
399  QStringList locs;
400  QByteArray result = ms.ReadAll();
401  QTextStream text(result);
402 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
403  text.setCodec("UTF-8");
404 #else
405  text.setEncoding(QStringConverter::Utf8);
406 #endif
407  while (!text.atEnd())
408  {
409  QString tmp = text.readLine().trimmed();
410  if (!tmp.isEmpty())
411  locs << tmp;
412  }
413 
414  return locs;
415 }
416 
417 void WeatherSource::startUpdate(bool forceUpdate)
418 {
419  m_buffer.clear();
420 
422  LOG(VB_GENERAL, LOG_INFO, "Starting update of " + m_info->name);
423 
424  if (m_ms)
425  {
426  LOG(VB_GENERAL, LOG_ERR, QString("%1 process exists, skipping.")
427  .arg(m_info->name));
428  return;
429  }
430 
431  if (!forceUpdate)
432  {
433  db.prepare("SELECT updated FROM weathersourcesettings "
434  "WHERE sourceid = :ID AND "
435  "TIMESTAMPADD(SECOND,update_timeout-15,updated) > NOW()");
436  db.bindValue(":ID", getId());
437  if (db.exec() && db.size() > 0)
438  {
439  LOG(VB_GENERAL, LOG_NOTICE, QString("%1 recently updated, skipping.")
440  .arg(m_info->name));
441 
442  if (m_cachefile.isEmpty())
443  {
444  QString locale_file(m_locale);
445  locale_file.replace("/", "-");
446  m_cachefile = QString("%1/cache_%2").arg(m_dir, locale_file);
447  }
448  QFile cache(m_cachefile);
449  if (cache.exists() && cache.open( QIODevice::ReadOnly ))
450  {
451  m_buffer = cache.readAll();
452  cache.close();
453 
454  processData();
455 
456  if (m_connectCnt)
457  {
458  emit newData(m_locale, m_units, m_data);
459  }
460  return;
461  }
462  LOG(VB_GENERAL, LOG_NOTICE,
463  QString("No cachefile for %1, forcing update.")
464  .arg(m_info->name));
465  }
466  }
467 
468  m_data.clear();
469  QString program = "nice";
470  QStringList args;
471  args << m_info->program;
472  args << "-u";
473  args << (m_units == SI_UNITS ? "SI" : "ENG");
474 
475  if (!m_dir.isEmpty())
476  {
477  args << "-d";
478  args << m_dir;
479  }
480  args << m_locale;
481 
484  m_ms = new MythSystemLegacy(program, args, flags);
486 
488  this, qOverload<>(&WeatherSource::processExit));
489  connect(m_ms, &MythSystemLegacy::error,
490  this, qOverload<uint>(&WeatherSource::processExit));
491 
493 }
494 
496 {
497  startUpdate();
499 }
500 
502 {
503  m_ms->disconnect(); // disconnects all signals
504 
505  if (status == GENERIC_EXIT_OK)
506  {
507  m_buffer = m_ms->ReadAll();
508  }
509 
510  delete m_ms;
511  m_ms = nullptr;
512 
513  if (status != GENERIC_EXIT_OK)
514  {
515  LOG(VB_GENERAL, LOG_ERR, QString("script exit status %1").arg(status));
516  return;
517  }
518 
519  if (m_buffer.isEmpty())
520  {
521  LOG(VB_GENERAL, LOG_ERR, "Script returned no data");
522  return;
523  }
524 
525  if (m_cachefile.isEmpty())
526  {
527  QString locale_file(m_locale);
528  locale_file.replace("/", "-");
529  m_cachefile = QString("%1/cache_%2").arg(m_dir, locale_file);
530  }
531  QFile cache(m_cachefile);
532  if (cache.open( QIODevice::WriteOnly ))
533  {
534  cache.write(m_buffer);
535  cache.close();
536  }
537  else
538  {
539  LOG(VB_GENERAL, LOG_ERR, QString("Unable to save data to cachefile: %1")
540  .arg(m_cachefile));
541  }
542 
543  processData();
544 
546 
547  db.prepare("UPDATE weathersourcesettings "
548  "SET updated = NOW() WHERE sourceid = :ID;");
549 
550  db.bindValue(":ID", getId());
551  if (!db.exec())
552  {
553  MythDB::DBError("Updating weather source's last update time", db);
554  return;
555  }
556 
557  if (m_connectCnt)
558  {
559  emit newData(m_locale, m_units, m_data);
560  }
561 }
562 
564 {
566 }
567 
569 {
570  QString unicode_buffer = QString::fromUtf8(m_buffer);
571  QStringList data = unicode_buffer.split('\n', Qt::SkipEmptyParts);
572 
573  m_data.clear();
574 
575  for (int i = 0; i < data.size(); ++i)
576  {
577  QStringList temp = data[i].split("::", Qt::SkipEmptyParts);
578  if (temp.size() > 2)
579  LOG(VB_GENERAL, LOG_ERR, "Error parsing script file, ignoring");
580  if (temp.size() < 2)
581  {
582  LOG(VB_GENERAL, LOG_ERR,
583  QString("Unrecoverable error parsing script output %1")
584  .arg(temp.size()));
585  LOG(VB_GENERAL, LOG_ERR, QString("data[%1]: '%2'")
586  .arg(i).arg(data[i]));
587  return; // we don't emit signal
588  }
589 
590  if (temp[1] != "---")
591  {
592  if (!m_data[temp[0]].isEmpty())
593  {
594  m_data[temp[0]].append("\n" + temp[1]);
595  }
596  else
597  {
598  m_data[temp[0]] = temp[1];
599  }
600  }
601  }
602 }
603 
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
WeatherSource::m_cachefile
QString m_cachefile
Definition: weatherSource.h:100
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
build_compdb.args
args
Definition: build_compdb.py:11
DEFAULT_SCRIPT_TIMEOUT
static constexpr std::chrono::seconds DEFAULT_SCRIPT_TIMEOUT
Definition: weatherUtils.h:21
WeatherSource::ProbeTypes
static QStringList ProbeTypes(const QString &workingDirectory, const QString &program)
Definition: weatherSource.cpp:27
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:214
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:102
kMSDontBlockInputDevs
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:36
mythdb.h
MythSystemLegacy
Definition: mythsystemlegacy.h:67
DEFAULT_UPDATE_TIMEOUT
static constexpr std::chrono::minutes DEFAULT_UPDATE_TIMEOUT
Definition: weatherUtils.h:20
ScriptInfo::program
QString program
Definition: weatherSource.h:29
WeatherSource::m_info
ScriptInfo * m_info
Definition: weatherSource.h:96
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
WeatherSource::m_ms
MythSystemLegacy * m_ms
Definition: weatherSource.h:97
MythSystemLegacy::finished
void finished(void)
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
WeatherSource::m_data
DataMap m_data
Definition: weatherSource.h:105
types
static const struct wl_interface * types[]
Definition: idle_inhibit_unstable_v1.c:39
WeatherSource::disconnectScreen
void disconnectScreen(WeatherScreen *ws)
Definition: weatherSource.cpp:371
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
WeatherSource::m_connectCnt
int m_connectCnt
Definition: weatherSource.h:104
MythSystemLegacy::ReadAll
QByteArray & ReadAll()
Definition: mythsystemlegacy.cpp:402
WeatherSource::startUpdateTimer
void startUpdateTimer()
Definition: weatherSource.h:72
mythdirs.h
weatherSource.h
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
mythsystemlegacy.h
WeatherSource::m_locale
QString m_locale
Definition: weatherSource.h:99
MythSystemLegacy::Signal
void Signal(MythSignal sig)
Definition: mythsystemlegacy.cpp:296
SI_UNITS
static constexpr uint8_t SI_UNITS
Definition: weatherUtils.h:18
WeatherScreen::newData
virtual void newData(const QString &, units_t, DataMap data)
Definition: weatherScreen.cpp:72
WeatherSource::ProbeInfo
static bool ProbeInfo(ScriptInfo &scriptInfo)
Definition: weatherSource.cpp:134
WeatherSource::processData
void processData()
Definition: weatherSource.cpp:568
WeatherSource::m_buffer
QByteArray m_buffer
Definition: weatherSource.h:101
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:256
WeatherSource::newData
void newData(QString, units_t, DataMap)
WeatherSource::processExit
void processExit()
Definition: weatherSource.cpp:563
GENERIC_EXIT_OK
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
MythSystemLegacy::error
void error(uint status)
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
compat.h
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
kSignalKill
@ kSignalKill
Definition: mythsystem.h:64
WeatherSource::updateTimeout
void updateTimeout()
Definition: weatherSource.cpp:495
MythSystemLegacy::Wait
uint Wait(std::chrono::seconds timeout=0s)
Definition: mythsystemlegacy.cpp:243
WeatherSource::ProbeTimeouts
static bool ProbeTimeouts(const QString &workingDirectory, const QString &program, std::chrono::seconds &updateTimeout, std::chrono::seconds &scriptTimeout)
Definition: weatherSource.cpp:66
WeatherSource::m_updateTimer
QTimer * m_updateTimer
Definition: weatherSource.h:103
MythSystemLegacy::SetDirectory
void SetDirectory(const QString &directory)
Definition: mythsystemlegacy.cpp:188
WeatherScreen
Weather screen.
Definition: weatherScreen.h:26
uint
unsigned int uint
Definition: compat.h:81
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
WeatherSource::WeatherSource
WeatherSource(ScriptInfo *info)
Watch out, we store the parameter as a member variable, don't go deleting it, that wouldn't be good.
Definition: weatherSource.cpp:328
kMSRunShell
@ kMSRunShell
run process through shell
Definition: mythsystem.h:43
WeatherSource::m_dir
QString m_dir
Definition: weatherSource.h:98
kMSRunBackground
@ kMSRunBackground
run child in the background
Definition: mythsystem.h:38
ScriptInfo::scriptTimeout
std::chrono::seconds scriptTimeout
Definition: weatherSource.h:31
WeatherSource::getId
int getId()
Definition: weatherSource.h:78
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
WeatherSource::m_units
units_t m_units
Definition: weatherSource.h:102
mythcontext.h
WeatherSource::startUpdate
void startUpdate(bool forceUpdate=false)
Definition: weatherSource.cpp:417
WeatherSource::connectScreen
void connectScreen(WeatherScreen *ws)
Definition: weatherSource.cpp:359
ScriptInfo::name
QString name
Definition: weatherSource.h:24
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:842
weatherScreen.h
WeatherSource::ProbeScript
static ScriptInfo * ProbeScript(const QFileInfo &fi)
Definition: weatherSource.cpp:199
azlyrics.info
dictionary info
Definition: azlyrics.py:7
MythSystemLegacy::Run
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
Definition: mythsystemlegacy.cpp:213
WeatherSource::~WeatherSource
~WeatherSource() override
Definition: weatherSource.cpp:348
kMSDontDisableDrawing
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
ScriptInfo
Definition: serverSideScripting.h:36
exitcodes.h
kMSStdOut
@ kMSStdOut
allow access to stdout
Definition: mythsystem.h:41
ScriptInfo::path
QString path
Definition: weatherSource.h:30
WeatherSource::getLocationList
QStringList getLocationList(const QString &str)
Definition: weatherSource.cpp:377
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837