QML Episode List Test UI

This commit is contained in:
Thomas Perl 2011-02-02 19:19:03 +01:00
parent 00a876079d
commit e0d45914d7
14 changed files with 242 additions and 41 deletions

View file

@ -74,7 +74,7 @@ test:
qmltest:
@echo -ne '\033]0;gPodder/QML console\007'
$(BINFILE) --qml --verbose
$(BINFILE) --qml --verbose -- -graphicssystem opengl
unittest:
PYTHONPATH=src/ $(PYTHON) -m gpodder.unittests

View file

@ -0,0 +1,30 @@
import Qt 4.7
Image {
width: parent.width
source: 'episodeList/bg.png'
Image {
id: icon
source: 'episodeList/' + model.episode.qfiletype + '.png'
width: 40
height: 40
opacity: model.episode.qdownloaded?1:.1
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 15
}
ShadowText {
text: model.episode.qtitle
color: model.episode.qnew?"white":"#888"
font.pointSize: 16
font.bold: false //model.episode.qnew
anchors.left: icon.right
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.leftMargin: 15
elide: Text.ElideRight
}
}

View file

@ -0,0 +1,79 @@
import Qt 4.7
Rectangle {
id: episodeList
property alias model: listView.model
property alias title: headerText.text
signal goBack
color: 'white'
Image {
anchors.fill: parent
source: 'podcastList/mask.png'
sourceSize { height: 100; width: 100 }
}
Image {
anchors.fill: parent
source: 'podcastList/noise.png'
}
ListView {
id: listView
anchors.topMargin: header.height
anchors.fill: parent
model: episodeModel
delegate: EpisodeItem {}
}
Image {
id: header
width: parent.width
source: 'episodeList/header.png'
ShadowText {
id: headerText
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
right: parent.right
leftMargin: 20
}
text: "Episodes"
font.pixelSize: 30
color: "white"
}
Item {
id: backButton
width: backButtonImage.sourceSize.width + 40
height: parent.height
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
}
Rectangle {
id: backButtonHighlight
color: "white"
opacity: (backButtonMouseArea.pressed)?(.5):(0)
anchors.fill: parent
Behavior on opacity { NumberAnimation { duration: 500 } }
}
Image {
id: backButtonImage
anchors.centerIn: parent
source: 'episodeList/back.png'
}
MouseArea {
id: backButtonMouseArea
anchors.fill: parent
onClicked: episodeList.goBack()
}
}
}
}

View file

@ -2,6 +2,9 @@
import Qt 4.7
Image {
signal podcastSelected(variant podcast)
signal podcastContextMenu(variant podcast)
id: podcastItem
source: 'podcastList/bg.png'
width: parent.width
@ -96,10 +99,10 @@ Image {
MouseArea {
anchors.fill: parent
onPressed: highlight.opacity = .2
onClicked: descriptionText.text = "clicked"
onClicked: parent.podcastSelected(model.podcast)
onReleased: highlight.opacity = 0
onCanceled: highlight.opacity = 0
onPressAndHold: highlight.opacity = .8
onPressAndHold: parent.podcastContextMenu(model.podcast)
}
}

View file

@ -2,9 +2,15 @@
import Qt 4.7
Rectangle {
signal podcastSelected(variant podcast)
signal podcastContextMenu(variant podcast)
signal action(string action)
id: rectangle
color: "white"
property alias model: listView.model
Image {
anchors.fill: parent
source: 'podcastList/mask.png'
@ -18,12 +24,15 @@ Rectangle {
}
ListView {
id: listView
anchors.fill: parent
anchors.rightMargin: (toolbarLandscape.opacity>0)?(toolbarLandscape.width+toolbarLandscape.anchors.rightMargin):(0)
anchors.bottomMargin: (toolbar.opacity>0)?(toolbar.height+toolbar.anchors.bottomMargin):(0)
delegate: PodcastItem {}
model: podcastModel
delegate: PodcastItem {
onPodcastSelected: rectangle.podcastSelected(podcast)
onPodcastContextMenu: rectangle.podcastContextMenu(podcast)
}
snapMode: ListView.SnapToItem
}
@ -60,7 +69,7 @@ Rectangle {
anchors.bottomMargin: -height+height*opacity
Behavior on anchors.bottomMargin { NumberAnimation { duration: 300 } }
ToolbarButton { source: 'podcastList/tb_refresh.png'; onClicked: rectangle.color = "#faa" }
ToolbarButton { source: 'podcastList/tb_refresh.png'; onClicked: rectangle.action('refresh') }
ToolbarButton { source: 'podcastList/tb_add.png'; onClicked: rectangle.color = "#afa" }
ToolbarButton { source: 'podcastList/tb_search.png'; onClicked: rectangle.color = "#aaf" }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

41
data/ui/qml/main.qml Normal file
View file

@ -0,0 +1,41 @@
import Qt 4.7
Item {
function showEpisodes() {
podcastList.opacity = 0
episodeList.opacity = 1
}
function showPodcasts() {
podcastList.opacity = 1
episodeList.opacity = 0
}
PodcastList {
id: podcastList
model: podcastModel
opacity: 1
anchors.fill: parent
onPodcastSelected: { /*FIXME UGLY*/ episodeList.title = podcast.qtitle; controller.podcastSelected(podcast) }
onPodcastContextMenu: controller.podcastContextMenu(podcast)
onAction: controller.action(action)
Behavior on opacity { NumberAnimation { duration: 500 } }
}
EpisodeList {
id: episodeList
model: episodeModel
opacity: 0
anchors.fill: parent
onGoBack: parent.showPodcasts()
Behavior on opacity { NumberAnimation { duration: 500 } }
}
}

View file

@ -20,6 +20,8 @@
from PySide.QtCore import *
import gpodder
from gpodder import model
from gpodder import util
@ -28,6 +30,29 @@ class QEpisode(QObject, model.PodcastEpisode):
QObject.__init__(self)
model.PodcastEpisode.__init__(self, *args, **kwargs)
changed = Signal()
def _title(self):
return self.title.decode('utf-8')
qtitle = Property(unicode, _title, notify=changed)
def _filetype(self):
return self.file_type() or 'download' # FIXME
qfiletype = Property(unicode, _filetype, notify=changed)
def _downloaded(self):
return self.state == gpodder.STATE_DOWNLOADED
qdownloaded = Property(bool, _downloaded, notify=changed)
def _new(self):
return self.is_new
qnew = Property(bool, _new, notify=changed)
class QPodcast(QObject, model.PodcastChannel):
EpisodeClass = QEpisode

View file

@ -15,14 +15,41 @@ from gpodder import dbsqlite
from gpodder import config
from gpodder import util
class gPodderListModel(QAbstractListModel):
COLUMNS = ['podcast',]
class Controller(QObject):
def __init__(self, root):
QObject.__init__(self)
self.root = root
def __init__(self, objects):
@Slot(QObject)
def podcastSelected(self, podcast):
print 'selected:', podcast.qtitle
self.root.select_podcast(podcast)
@Slot(QObject)
def podcastContextMenu(self, podcast):
print 'context menu:', podcast.qtitle
@Slot(str)
def action(self, action):
print 'action requested:', action
if action == 'refresh':
self.root.reload_podcasts()
class gPodderListModel(QAbstractListModel):
COLUMNS = ['object',]
def __init__(self, objects=None):
QAbstractListModel.__init__(self)
if objects is None:
objects = []
self._objects = objects
self.setRoleNames(dict(enumerate(self.COLUMNS)))
def set_objects(self, objects):
self._objects = objects
self.reset()
def get_object(self, index):
return self._objects[index.row()]
@ -30,22 +57,15 @@ class gPodderListModel(QAbstractListModel):
return len(self._objects)
def data(self, index, role):
if index.isValid() and role == self.COLUMNS.index('podcast'):
if index.isValid() and role == 0:
return self.get_object(index)
return None
class gPodderListView(QListView):
def __init__(self, on_item_selected):
QListView.__init__(self)
self.setProperty('FingerScrollable', True)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self._on_item_selected = on_item_selected
QObject.connect(self, SIGNAL('activated(QModelIndex)'), self._row_cb)
class gPodderPodcastModel(gPodderListModel):
COLUMNS = ['podcast',]
def _row_cb(self, index):
if index.isValid():
model = self.model()
self._on_item_selected(model.get_object(index))
class gPodderEpisodeModel(gPodderListModel):
COLUMNS = ['episode',]
def QML(filename):
for folder in gpodder.ui_folders:
@ -56,16 +76,23 @@ def QML(filename):
class qtPodder(QApplication):
def __init__(self, args, config, db):
QApplication.__init__(self, args)
self._config = config
self._db = db
podcasts = model.Model.get_podcasts(db)
self.controller = Controller(self)
self.podcast_list = QDeclarativeView()
self.podcast_list.setResizeMode(QDeclarativeView.SizeRootObjectToView)
rc = self.podcast_list.rootContext()
self.podcast_model = gPodderListModel(podcasts)
self.podcast_model = gPodderPodcastModel(podcasts)
self.episode_model = gPodderEpisodeModel()
rc.setContextProperty('podcastModel', self.podcast_model)
self.podcast_list.setSource(QML('podcastList.qml'))
rc.setContextProperty('episodeModel', self.episode_model)
rc.setContextProperty('controller', self.controller)
self.podcast_list.setSource(QML('main.qml'))
self.podcast_window = QMainWindow()
if gpodder.ui.fremantle:
@ -75,25 +102,12 @@ class qtPodder(QApplication):
self.podcast_window.resize(800, 480)
self.podcast_window.show()
def on_podcast_selected(self, podcast):
self.episode_list = gPodderListView(self.on_episode_selected)
self.episode_list.setModel(gPodderListModel(podcast.get_all_episodes()))
self.episode_window = QMainWindow(self.podcast_window)
window_title = u'Episodes in %s' % podcast.title.decode('utf-8')
self.episode_window.setWindowTitle(window_title)
self.episode_window.setCentralWidget(self.episode_list)
self.episode_window.show()
def on_episode_selected(self, episode):
if episode.was_downloaded(and_exists=True):
util.gui_open(episode.local_filename(create=False))
else:
dialog = QMessageBox()
dialog.setWindowTitle(episode.title.decode('utf-8'))
dialog.setText('Episode not yet downloaded')
dialog.exec_()
def reload_podcasts(self):
self.podcast_model.set_objects(model.Model.get_podcasts(self._db))
def select_podcast(self, podcast):
self.episode_model.set_objects(podcast.get_all_episodes())
self.podcast_list.rootObject().showEpisodes()
def main():
cfg = config.Config(gpodder.config_file)