MythTV master
logviewer.cpp
Go to the documentation of this file.
1#include <chrono>
2#include <cstdlib>
3#include <iostream>
4#include <unistd.h>
5
6// qt
7#include <QCoreApplication>
8#include <QFile>
9#include <QKeyEvent>
10#include <QTextStream>
11
12// mythtv
21
22// mytharchive
23#include "archiveutil.h"
24#include "logviewer.h"
25
26void showLogViewer(void)
27{
29 QString logDir = getTempDirectory() + "logs";
30 QString progressLog;
31 QString fullLog;
32
33 // wait for a log file to be available
34 int tries = 10;
35 while (tries--)
36 {
37 if (QFile::exists(logDir + "/progress.log"))
38 progressLog = logDir + "/progress.log";
39
40 if (QFile::exists(logDir + "/mythburn.log"))
41 fullLog = logDir + "/mythburn.log";
42
43 // we wait for both the progress.log and mythburn.log
44 if ((!progressLog.isEmpty()) && (!fullLog.isEmpty()))
45 break;
46
47 // or we wait for a log from mytharchivehelper
48 if (progressLog.isEmpty() && fullLog.isEmpty())
49 {
50 QStringList logFiles;
51 QStringList filters;
52 filters << "*.log";
53
54 QDir d(logDir);
55 logFiles = d.entryList(filters, QDir::Files | QDir::Readable, QDir::Time);
56
57 if (!logFiles.isEmpty())
58 {
59 // the first log file should be the newest one available
60 progressLog = logDir + '/' + logFiles[0];
61 break;
62 }
63 }
64
65 sleep(1);
66 }
67
68 // do any logs exist?
69 if ((!progressLog.isEmpty()) || (!fullLog.isEmpty()))
70 {
71 auto *viewer = new LogViewer(mainStack);
72 viewer->setFilenames(progressLog, fullLog);
73 if (viewer->Create())
74 mainStack->AddScreen(viewer);
75 }
76 else
77 {
78 showWarningDialog(QCoreApplication::translate("LogViewer",
79 "Cannot find any logs to show!"));
80 }
81}
82
84 : MythScreenType(parent, "logviewer"),
85 m_autoUpdate(gCoreContext->GetBoolSetting("LogViewerAutoUpdate", true)),
86 m_updateTime(gCoreContext->GetDurSetting<std::chrono::seconds>(
87 "LogViewerUpdateTime", DEFAULT_UPDATE_TIME))
88{
89}
90
92{
93 gCoreContext->SaveDurSetting("LogViewerUpdateTime", m_updateTime);
94 gCoreContext->SaveSetting("LogViewerAutoUpdate", m_autoUpdate ? "1" : "0");
95 delete m_updateTimer;
96}
97
99{
100 // Load the theme for this screen
101 bool foundtheme = LoadWindowFromXML("mytharchive-ui.xml", "logviewer", this);
102 if (!foundtheme)
103 return false;
104
105 bool err = false;
106 UIUtilE::Assign(this, m_logList, "loglist", &err);
107 UIUtilE::Assign(this, m_logText, "logitem_text", &err);
108 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
109 UIUtilE::Assign(this, m_updateButton, "update_button", &err);
110 UIUtilE::Assign(this, m_exitButton, "exit_button", &err);
111
112 if (err)
113 {
114 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'logviewer'");
115 return false;
116 }
117
121
124
125 m_updateTimer = nullptr;
126 m_updateTimer = new QTimer(this);
128
130
132
133 return true;
134}
135
137{
139 if (m_logList->GetCount() > 0)
141}
142
143bool LogViewer::keyPressEvent(QKeyEvent *event)
144{
145 if (GetFocusWidget()->keyPressEvent(event))
146 return true;
147
148 QStringList actions;
149 bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
150
151 for (int i = 0; i < actions.size() && !handled; i++)
152 {
153 const QString& action = actions[i];
154 handled = true;
155
156 if (action == "MENU")
157 ShowMenu();
158 else
159 handled = false;
160 }
161
162 if (!handled && MythScreenType::keyPressEvent(event))
163 handled = true;
164
165 return handled;
166}
167
169{
171}
172
174{
176
177 if (m_autoUpdate)
179 else
180 m_updateTimer->stop();
181}
182
184{
185 if (item)
186 m_logText->SetText(item->GetText());
187}
188
190{
191 QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
192 QFile lockFile(tempDir + "/logs/mythburncancel.lck");
193
194 if (!lockFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
195 LOG(VB_GENERAL, LOG_ERR,
196 "LogViewer: Failed to create mythburncancel.lck file");
197
198 lockFile.write("Cancel\n\r");
199 lockFile.close();
200
201 ShowOkPopup(tr("Background creation has been asked to stop.\n"
202 "This may take a few minutes."));
203}
204
206{
207 m_updateTimer->stop();
208
209 QStringList list;
211
212 if (!list.empty())
213 {
214 bool bUpdateCurrent =
215 (m_logList->GetCount() == m_logList->GetCurrentPos() + 1) ||
216 (m_logList->GetCurrentPos() == 0);
217
218 for (const auto & label : std::as_const(list))
220
221 if (bUpdateCurrent)
223 }
224
225 bool bRunning = (getSetting("MythArchiveLastRunStatus") == "Running");
226
227 if (!bRunning)
228 {
231 }
232
233 if (m_autoUpdate)
234 {
235 if (m_logList->GetCount() > 0)
237 else
238 m_updateTimer->start(500ms);
239 }
240}
241
242QString LogViewer::getSetting(const QString &key)
243{
244 // read the setting direct from the DB rather than from the settings cache
245 // which isn't aware that the script may have changed something
247 if (query.isConnected())
248 {
249 query.prepare("SELECT data FROM settings WHERE value = :VALUE "
250 "AND hostname = :HOSTNAME ;");
251 query.bindValue(":VALUE", key);
252 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
253
254 if (query.exec() && query.next())
255 {
256 return query.value(0).toString();
257 }
258 }
259 else
260 {
261 LOG(VB_GENERAL, LOG_ERR,
262 QString("Database not open while trying to load setting: %1")
263 .arg(key));
264 }
265
266 return {""};
267}
268
269bool LogViewer::loadFile(const QString& filename, QStringList &list, int startline)
270{
271 bool strip = !(filename.endsWith("progress.log") || filename.endsWith("mythburn.log"));
272
273 list.clear();
274
275 QFile file(filename);
276
277 if (!file.exists())
278 return false;
279
280 if (file.open( QIODevice::ReadOnly ))
281 {
282 QString s;
283 QTextStream stream(&file);
284
285 // ignore the first startline lines
286 while ( !stream.atEnd() && startline > 0)
287 {
288 stream.readLine();
289 startline--;
290 }
291
292 // read rest of file
293 while ( !stream.atEnd() )
294 {
295 s = stream.readLine();
296
297 if (strip)
298 {
299 // the logging from mytharchivehelper contains a lot of junk
300 // we are not interested in so just strip it out
301 int pos = s.indexOf(" - ");
302 if (pos != -1)
303 s = s.mid(pos + 3);
304 }
305
306 list.append(s);
307 }
308 file.close();
309 }
310 else
311 {
312 return false;
313 }
314
315 return true;
316}
317
318void LogViewer::setFilenames(const QString &progressLog, const QString &fullLog)
319{
320 m_progressLog = progressLog;
321 m_fullLog = fullLog;
322 m_currentLog = progressLog;
323}
324
326{
328 m_logList->Reset();
330}
331
333{
335 m_logList->Reset();
337}
338
340{
341 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
342 auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
343
344 if (menuPopup->Create())
345 popupStack->AddScreen(menuPopup);
346
347 menuPopup->SetReturnEvent(this, "action");
348
349 if (m_autoUpdate)
350 menuPopup->AddButton(tr("Don't Auto Update"), &LogViewer::toggleAutoUpdate);
351 else
352 menuPopup->AddButton(tr("Auto Update"), &LogViewer::toggleAutoUpdate);
353
354 menuPopup->AddButton(tr("Show Progress Log"), &LogViewer::showProgressLog);
355 menuPopup->AddButton(tr("Show Full Log"), &LogViewer::showFullLog);
356}
void showWarningDialog(const QString &msg)
QString getTempDirectory(bool showError)
Definition: archiveutil.cpp:46
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
Definition: logviewer.cpp:136
void updateTimerTimeout(void)
Definition: logviewer.cpp:168
MythUIButton * m_cancelButton
Definition: logviewer.h:59
LogViewer(MythScreenStack *parent)
Definition: logviewer.cpp:83
~LogViewer(void) override
Definition: logviewer.cpp:91
void updateClicked(void)
Definition: logviewer.cpp:205
void toggleAutoUpdate(void)
Definition: logviewer.cpp:173
static void cancelClicked(void)
Definition: logviewer.cpp:189
QString m_currentLog
Definition: logviewer.h:51
MythUIButton * m_updateButton
Definition: logviewer.h:60
MythUIButtonList * m_logList
Definition: logviewer.h:55
bool Create(void) override
Definition: logviewer.cpp:98
MythUIButton * m_exitButton
Definition: logviewer.h:58
QString m_fullLog
Definition: logviewer.h:53
void setFilenames(const QString &progressLog, const QString &fullLog)
Definition: logviewer.cpp:318
QString m_progressLog
Definition: logviewer.h:52
static bool loadFile(const QString &filename, QStringList &list, int startline)
Definition: logviewer.cpp:269
QTimer * m_updateTimer
Definition: logviewer.h:49
void updateLogItem(MythUIButtonListItem *item)
Definition: logviewer.cpp:183
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
Definition: logviewer.cpp:143
void showProgressLog(void)
Definition: logviewer.cpp:325
std::chrono::seconds m_updateTime
Definition: logviewer.h:48
bool m_autoUpdate
Definition: logviewer.h:47
MythUIText * m_logText
Definition: logviewer.h:56
void ShowMenu(void) override
Definition: logviewer.cpp:339
static QString getSetting(const QString &key)
Definition: logviewer.cpp:242
void showFullLog(void)
Definition: logviewer.cpp:332
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:137
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
QString GetHostName(void)
void SaveSetting(const QString &key, int newValue)
std::enable_if_t< std::chrono::__is_duration< T >::value, void > SaveDurSetting(const QString &key, T newValue)
QString GetSetting(const QString &key, const QString &defaultval="")
Basic menu dialog, message and a list of options.
MythScreenStack * GetMainStack()
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
QString GetText(const QString &name="") const
void SetItemCurrent(MythUIButtonListItem *item)
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
int GetCurrentPos() const
void itemSelected(MythUIButtonListItem *item)
void Clicked()
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
void SetEnabled(bool enable)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
static const iso6937table * d
void showLogViewer(void)
Definition: logviewer.cpp:26
static constexpr std::chrono::seconds DEFAULT_UPDATE_TIME
Definition: logviewer.h:10
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
STL namespace.
bool exists(str path)
Definition: xbmcvfs.py:51
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:95