QT += network
QT += phonon
+
OTHER_FILES += \
bundesligawidget.desktop
</valuemap>
<value key="Qt4ProjectManager.MaemoRunConfiguration.DeviceId" type="qulonglong">1</value>
<valuemap key="Qt4ProjectManager.MaemoRunConfiguration.LastDeployed" type="QVariantMap">
- <value key="192.168.2.102" type="QDateTime">2010-10-15T23:23:32</value>
+ <value key="192.168.2.102" type="QDateTime">2010-10-16T21:17:22</value>
</valuemap>
</valuemap>
<value key="ProjectExplorer.Target.RunConfigurationCount" type="int">1</value>
[Desktop Entry]
-Name=Bundesliga Widget
+Name=BuLi Scores
Comment=Live scores of the German Bundesliga.
Type=qt
X-Path=BundesligaWidget
#include "backendkicker.h"
-const int BackendKicker::INTERVAL_FAST = 100 * 1000;
-const int BackendKicker::INTERVAL_SLOW = 3600 * 1000;
+const int BackendKicker::INTERVAL_FAST = 180;
BackendKicker::BackendKicker(QObject *parent) :
MatchDayBackend(parent),
connect(&m_updateTimer, SIGNAL(timeout()),
this, SLOT(update()));
+ m_updateTimer.setSingleShot(true);
+
this->update();
}
match = new Match(hometeam, awayteam, date, this);
m_matchlist.append(match);
+ emit matchAdded(match);
return match;
}
}
}
-// qDebug() << "match state: " << match->state();
-// qDebug() << "match home: " << match->homeScore();
-// qDebug() << "match away: " << match->awayScore();
+ qDebug() << "match state: " << match->state();
+ qDebug() << "match home: " << match->homeScore();
+ qDebug() << "match away: " << match->awayScore();
}
void BackendKicker::parsePage (QString htmlstr)
}
// delete last data
+ this->m_matchlist.clear();
+ this->update();
+
return leagueIsSupported;
}
void BackendKicker::update()
{
- if (matchRunning()) {
- m_updateTimer.start(INTERVAL_FAST);
- } else {
- m_updateTimer.start(INTERVAL_SLOW);
- }
-
emit updateStarted();
- //qDebug() << "URL: " << m_URL;
+ qDebug() << "Start Update with URL: " << m_URL;
m_netaccmgr->get(QNetworkRequest(QUrl(m_URL)));
}
void BackendKicker::dlndFinished(QNetworkReply *reply)
{
QString rawdata;
+ int secstonextupdate;
if (reply->error() != QNetworkReply::NoError) {
// TODO proper user friendly error handling here!
- qDebug() << "dlnd failed: error: " << reply->error();
+ qWarning() << "Download failed with error: " << reply->error();
}
- this->m_matchlist.clear();
-
rawdata = reply->readAll();
parsePage(rawdata);
- emit matchListChanged();
+ emit updateFinished();
+
+ secstonextupdate = secsToNextGame();
+ if (secstonextupdate < INTERVAL_FAST) {
+ secstonextupdate = INTERVAL_FAST;
+ } else if (secstonextupdate > 6 * 3600) {
+ secstonextupdate = 6 * 3600;
+ }
+ m_updateTimer.start(secstonextupdate * 1000);
+ qDebug() << "Update finished, next update in: " << secstonextupdate << "seconds.";
}
-bool BackendKicker::matchRunning()
+int BackendKicker::secsToNextGame()
{
QListIterator<Match*> iter(m_matchlist);
Match* match;
- bool matchrunning = false;
int secstogame;
+ int secstonextgame = -1;
while (iter.hasNext()) {
match = iter.next();
if (match->state() == Match::FirstHalf ||
- match->state() == Match::SecondHalf) {
+ match->state() == Match::SecondHalf ||
+ match->state() == Match::HalfTime) {
- matchrunning = true;
+ secstonextgame = 0;
+ return secstonextgame;
break;
- } else {
+ } else if (match->state() == Match::NotStarted) {
secstogame = QDateTime::currentDateTime().secsTo(match->date());
- if (secstogame >= 0 && secstogame < INTERVAL_SLOW) {
- matchrunning = true;
- break;
+ if (secstonextgame == -1) {
+ secstonextgame = secstogame;
+ } else if (secstogame < secstonextgame) {
+ secstonextgame = secstogame;
}
}
}
- return matchrunning;
+ return secstonextgame;
}
class BackendKicker : public MatchDayBackend
{
Q_OBJECT
+
+ // TODO clear matchlist on match day change!
public:
explicit BackendKicker(QObject *parent = 0);
void setAutomaticUpdate(bool);
signals:
- void matchListChanged(void);
+ void updateFinished(void);
void updateStarted(void);
public slots:
QNetworkAccessManager* m_netaccmgr;
static const int INTERVAL_FAST;
- static const int INTERVAL_SLOW;
void parsePage (QString htmlstr);
- bool matchRunning (void);
+ int secsToNextGame(void);
private slots:
void dlndFinished(QNetworkReply *reply);
#include "backendkicker.h"
#include <QtGui>
+#include <QFile>
+#include <QTextStream>
#include <src/mainwidget.h>
+
+
+void messageHandler(QtMsgType type, const char *msg)
+{
+ static QFile logfile;
+ static QTextStream fw;
+ static const QString LOGFILE_PATH = "/tmp/buliscores.log";
+ static const QtMsgType LOGLEVEL = QtDebugMsg;
+ QString out;
+
+ if (type < LOGLEVEL) {
+ return;
+ }
+
+ if (logfile.isOpen() == false) {
+ logfile.setFileName(LOGFILE_PATH);
+ if (logfile.open(QIODevice::Append) == true) {
+ fw.setDevice((QIODevice*)&logfile);
+ fw << "Logfile Opened: " << QDateTime::currentDateTime().toString();
+ }
+ }
+
+ switch (type) {
+ case QtDebugMsg:
+ out = "%1 Debug: %2\n";
+ break;
+ case QtWarningMsg:
+ out = "%1 Warning: %2\n";
+ break;
+ case QtCriticalMsg:
+ out = "%1 Critical: %2\n";
+ break;
+ case QtFatalMsg:
+ out = "%1 Fatal: %2\n";
+ break;
+ }
+
+ out = out.arg(QDateTime::currentDateTime().toString(), msg);
+
+ if (logfile.isOpen()) {
+ fw << out;
+ fw.flush();
+ }
+ printf("%s", out.toAscii().constData());
+
+ if (type == QtFatalMsg) {
+ abort();
+ }
+}
+
int main(int argc, char *argv[])
{
// enforce native graphics system for now due to a bug in transparency handling
// you can remove this line if you only target PR 1.2
+ qInstallMsgHandler(messageHandler);
QApplication::setGraphicsSystem("native");
QApplication app(argc, argv);
MainWidget mw;
app.setApplicationName("Buli Scores");
app.setApplicationVersion("0.1");
app.setOrganizationName("David Solbach");
+
+
mw.resize(400,250);
QMaemo5HomescreenAdaptor *adaptor = new QMaemo5HomescreenAdaptor(&mw);
#include <QMouseEvent>
#include <QApplication>
#include <QProcess>
-#include <phonon/AudioOutput>
-#include <phonon/MediaObject>
#include "mainwidget.h"
#include "backendkicker.h"
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
+ m_mediaObject(new Phonon::MediaObject(this)),
+ m_audioOutput(new Phonon::AudioOutput(Phonon::MusicCategory, this)),
m_backend(new BackendKicker(this)),
m_datamodel(new MatchDayModel(this, m_backend)),
m_scoretbl(new ScoreTable(m_datamodel)),
QFont f;
QPalette palette;
+ this->setAttribute(Qt::WA_TranslucentBackground);
+
// label
+ m_statuslbl.hide();
m_statuslbl.setText(tr("BuLi Scores!"));
f.setPixelSize(40);
palette.setColor(QPalette::Background, QColor(0, 0, 0, 127));
palette.setColor(QPalette::Foreground, QColor(255, 255, 255, 127));
- m_statuslbl.show();
m_statuslbl.setPalette(palette);
m_statuslbl.setAttribute(Qt::WA_TransparentForMouseEvents);
m_statuslbl.setAutoFillBackground(true);
m_statuslbl.setFont(f);
// table
- m_scoretbl->hide();
-
- this->setAttribute(Qt::WA_TranslucentBackground);
+ m_statuslbl.show();
this->setLayout(&m_layout);
m_layout.addWidget(&m_statuslbl);
m_layout.addWidget(m_scoretbl);
- // sounds
- Phonon::MediaObject *m_mediaObject = new Phonon::MediaObject(this);
- Phonon::AudioOutput *m_audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
- Phonon::createPath(m_mediaObject, m_audioOutput);
-
connect(m_settingsdlg, SIGNAL(accepted()),
this, SLOT(update()));
- connect(m_backend, SIGNAL(matchListChanged()),
+ connect(m_backend, SIGNAL(updateFinished()),
this, SLOT(onBackendUpdateFinished()));
connect(m_backend, SIGNAL(updateStarted()),
this, SLOT(onBackendUpdateStarted()));
+
+ connect(m_backend, SIGNAL(matchAdded(Match*)),
+ this, SLOT(onMatchAdded(Match*)));
+
+ Phonon::Path path = Phonon::createPath(m_mediaObject, m_audioOutput);
}
// only needed for testing on desktop
void MainWidget::onBackendUpdateStarted()
{
- m_statuslbl.setText(tr("Updating..."));
- m_statuslbl.show();
- m_scoretbl->hide();
+// m_statuslbl.setText(tr("Updating..."));
+// m_statuslbl.show();
+// m_scoretbl->hide();
}
void MainWidget::onBackendUpdateFinished()
{
- //mediaObject->play();
-
m_scoretbl->show();
m_statuslbl.hide();
}
+void MainWidget::onMatchAdded(Match* match) {
+ connect(match, SIGNAL(scoreChanged(int,int,int,int)),
+ this, SLOT(onScoreChange()));
+
+ connect(match, SIGNAL(stateChanged(Match::MatchState)),
+ this, SLOT(onMatchStateChanged(Match::MatchState)));
+}
+
+void MainWidget::onScoreChange()
+{
+ m_mediaObject->setCurrentSource(Phonon::MediaSource("/usr/share/buliscores/tor.wav"));
+ m_mediaObject->play();
+}
+
+void MainWidget::onMatchStateChanged(Match::MatchState state)
+{
+ m_mediaObject->setCurrentSource(Phonon::MediaSource("/usr/share/buliscores/trillerpfeife.wav"));
+ m_mediaObject->play();
+}
+
void MainWidget::showSettingsDialog()
{
m_settingsdlg->show();
#include <QLabel>
#include "matchdaybackend.h"
+#include "match.h"
#include "scoretable.h"
#include "settingsdialog.h"
+#include <phonon/mediaobject.h>
+#include <phonon/audiooutput.h>
class MainWidget : public QWidget
void showSettingsDialog();
protected slots:
- void onBackendUpdateStarted();
+ void onBackendUpdateStarted(void);
void onBackendUpdateFinished(void);
+ void onMatchAdded(Match* match);
+ void onScoreChange();
+ void onMatchStateChanged(Match::MatchState state);
+
private:
+ Phonon::MediaObject *m_mediaObject;
+ Phonon::AudioOutput *m_audioOutput;
MatchDayBackend* m_backend;
MatchDayModel* m_datamodel;
ScoreTable* m_scoretbl;
m_homeTeam = hometeam;
m_awayTeam = awayteam;
+ m_homeScore = -1;
+ m_awayScore = -1;
m_homeEmblem = getEmblemByName(hometeam);
m_awayEmblem = getEmblemByName(awayteam);
void Match::setScore(int home, int away)
{
- bool changed;
+ bool changed = false;
+ int oldhomescore = m_homeScore;
+ int oldawayscore = m_awayScore;
if (m_homeScore != home) {
m_homeScore = home;
}
if (changed) {
- emit scoreChanged(home, away);
+ emit scoreChanged(oldhomescore, oldawayscore,
+ home, away);
}
}
void Match::setState(MatchState state) {
- m_state = state;
+ if (m_state != state) {
+ m_state = state;
+ emit stateChanged(state);
+ }
}
void setState(MatchState state);
signals:
- void scoreChanged(int homescore, int awayscore);
- void stateChanged(MatchState state);
+ void scoreChanged(int oldhomescore, int oldawayscore,
+ int homescore, int awayscore);
+ void stateChanged(Match::MatchState state);
public slots:
virtual bool selectLeague(QString league) = 0;
signals:
- void matchListChanged(void);
+ void matchAdded(Match* match);
+ void updateFinished(void);
void updateStarted(void);
public slots:
{
m_backend = backend;
- connect(m_backend, SIGNAL(matchListChanged()),
- this, SLOT(onMatchListChanged()));
+ connect(m_backend, SIGNAL(updateFinished()),
+ this, SLOT(onUpdateFinished()));
}
int MatchDayModel::rowCount(const QModelIndex&) const
int MatchDayModel::columnCount(const QModelIndex&) const
{
- return 10;
+ return 11;
}
QVariant MatchDayModel::data(const QModelIndex& index, int role) const
case Qt::BackgroundRole:
return QBrush(QColor(20, 20, 20, 100));
break;
+
case Qt::DecorationRole:
switch (index.column()) {
case AwayIcon:
s.setHeight(25);
switch (index.column()) {
case Spacer:
- s.setWidth(3);
- break;
+ case Spacer2:
+ s.setWidth(2);
+ break;
case MatchState:
s.setWidth(15);
break;
}
-// only adds for now
-void MatchDayModel::onMatchListChanged(void)
+void MatchDayModel::onUpdateFinished(void)
{
//remove all rows
- //qDebug() << "beginRemoveRows: " << 0 << ", " << rowCount(QModelIndex()) - 1;
+ qDebug() << "beginRemoveRows: " << 0 << ", " << rowCount(QModelIndex()) - 1;
beginRemoveRows(QModelIndex(),
0,
m_lastRowCount);
endRemoveRows();
//add rows
- //qDebug() << "beginInsertRows: " << 0 << ", " << m_backend->matchList().count() - 1;
+ qDebug() << "beginInsertRows: " << 0 << ", " << m_backend->matchList().count() - 1;
beginInsertRows(QModelIndex(),
0,
m_backend->matchList().count() - 1);
m_lastRowCount = m_backend->matchList().count() - 1;
// invalidate complete data
- //qDebug() << "rowCount @ emit dataChanged: " << rowCount(QModelIndex());
+ qDebug() << "rowCount @ emit dataChanged: " << rowCount(QModelIndex());
emit dataChanged(index(0, 0),
index(rowCount(QModelIndex()) - 1, columnCount(QModelIndex()) - 1));
enum {
Spacer = 0,
MatchState,
+ Spacer2,
HomeIcon,
HomeTeam,
HomeScore,
QVariant data(const QModelIndex& index, int role) const;
protected slots:
- void onMatchListChanged(void);
+ void onUpdateFinished(void);
};