Replace rotating hourglass with less gaudy busy indicator. If you're running calibre from bzr, you will have to run python setup.py build to be able to run the GUI

This commit is contained in:
Kovid Goyal 2009-12-06 12:12:40 -07:00
parent 95f5b3c176
commit e6c6f056f2
14 changed files with 331 additions and 108 deletions

Binary file not shown.

View File

@ -112,6 +112,13 @@ extensions = [
sip_files = ['calibre/gui2/pictureflow/pictureflow.sip']
),
Extension('progress_indicator',
['calibre/gui2/progress_indicator/QProgressIndicator.cpp'],
inc_dirs = ['calibre/gui2/progress_indicator'],
headers = ['calibre/gui2/progress_indicator/QProgressIndicator.h'],
sip_files = ['calibre/gui2/progress_indicator/QProgressIndicator.sip']
),
]

View File

@ -55,7 +55,7 @@ if plugins is None:
sys.path.insert(0, plugin_path)
for plugin in ['pictureflow', 'lzx', 'msdes', 'podofo', 'cPalmdoc',
'fontconfig', 'pdfreflow'] + \
'fontconfig', 'pdfreflow', 'progress_indicator'] + \
(['winutil'] if iswindows else []) + \
(['usbobserver'] if isosx else []):
try:

View File

@ -0,0 +1,124 @@
#include "QProgressIndicator.h"
#include <QPainter>
QProgressIndicator::QProgressIndicator(QWidget* parent, int size)
: QWidget(parent),
m_angle(0),
m_timerId(-1),
m_delay(80),
m_displayedWhenStopped(true),
m_displaySize(size),
m_color(Qt::black)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setFocusPolicy(Qt::NoFocus);
}
bool QProgressIndicator::isAnimated () const
{
return (m_timerId != -1);
}
void QProgressIndicator::setDisplayedWhenStopped(bool state)
{
m_displayedWhenStopped = state;
update();
}
void QProgressIndicator::setDisplaySize(int size)
{
m_displaySize = size;
update();
}
bool QProgressIndicator::isDisplayedWhenStopped() const
{
return m_displayedWhenStopped;
}
void QProgressIndicator::startAnimation()
{
m_angle = 0;
if (m_timerId == -1)
m_timerId = startTimer(m_delay);
}
void QProgressIndicator::stopAnimation()
{
if (m_timerId != -1)
killTimer(m_timerId);
m_timerId = -1;
update();
}
void QProgressIndicator::setAnimationDelay(int delay)
{
if (m_timerId != -1)
killTimer(m_timerId);
m_delay = delay;
if (m_timerId != -1)
m_timerId = startTimer(m_delay);
}
void QProgressIndicator::setColor(const QColor & color)
{
m_color = color;
update();
}
QSize QProgressIndicator::sizeHint() const
{
return QSize(m_displaySize, m_displaySize);
}
int QProgressIndicator::heightForWidth(int w) const
{
return w;
}
void QProgressIndicator::timerEvent(QTimerEvent * /*event*/)
{
m_angle = (m_angle+30)%360;
update();
}
void QProgressIndicator::paintEvent(QPaintEvent * /*event*/)
{
if (!m_displayedWhenStopped && !isAnimated())
return;
int width = qMin(this->width(), this->height());
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
int outerRadius = (width-1)*0.5;
int innerRadius = (width-1)*0.5*0.38;
int capsuleHeight = outerRadius - innerRadius;
int capsuleWidth = (width > 32 ) ? capsuleHeight *.23 : capsuleHeight *.35;
int capsuleRadius = capsuleWidth/2;
for (int i=0; i<12; i++)
{
QColor color = m_color;
color.setAlphaF(1.0f - (i/12.0f));
p.setPen(Qt::NoPen);
p.setBrush(color);
p.save();
p.translate(rect().center());
p.rotate(m_angle - i*30.0f);
p.drawRoundedRect(-capsuleWidth*0.5, -(innerRadius+capsuleHeight), capsuleWidth, capsuleHeight, capsuleRadius, capsuleRadius);
p.restore();
}
}

View File

@ -0,0 +1,93 @@
#pragma once
#include <QWidget>
#include <QColor>
/*!
\class QProgressIndicator
\brief The QProgressIndicator class lets an application display a progress indicator to show that a lengthy task is under way.
Progress indicators are indeterminate and do nothing more than spin to show that the application is busy.
\sa QProgressBar
*/
class QProgressIndicator : public QWidget
{
Q_OBJECT
Q_PROPERTY(int delay READ animationDelay WRITE setAnimationDelay)
Q_PROPERTY(bool displayedWhenStopped READ isDisplayedWhenStopped WRITE setDisplayedWhenStopped)
Q_PROPERTY(QColor color READ color WRITE setColor)
Q_PROPERTY(int displaySize READ displaySize WRITE setDisplaySize)
public:
QProgressIndicator(QWidget* parent = 0, int size = 64);
/*! Returns the delay between animation steps.
\return The number of milliseconds between animation steps. By default, the animation delay is set to 80 milliseconds.
\sa setAnimationDelay
*/
int animationDelay() const { return m_delay; }
/*! Returns a Boolean value indicating whether the component is currently animated.
\return Animation state.
\sa startAnimation stopAnimation
*/
bool isAnimated () const;
/*! Returns a Boolean value indicating whether the receiver shows itself even when it is not animating.
\return Return true if the progress indicator shows itself even when it is not animating. By default, it returns false.
\sa setDisplayedWhenStopped
*/
bool isDisplayedWhenStopped() const;
/*! Returns the color of the component.
\sa setColor
*/
const QColor & color() const { return m_color; }
virtual QSize sizeHint() const;
int heightForWidth(int w) const;
int displaySize() const { return m_displaySize; }
public slots:
/*! Starts the spin animation.
\sa stopAnimation isAnimated
*/
void startAnimation();
/*! Stops the spin animation.
\sa startAnimation isAnimated
*/
void stopAnimation();
/*! Sets the delay between animation steps.
Setting the \a delay to a value larger than 40 slows the animation, while setting the \a delay to a smaller value speeds it up.
\param delay The delay, in milliseconds.
\sa animationDelay
*/
void setAnimationDelay(int delay);
/*! Sets whether the component hides itself when it is not animating.
\param state The animation state. Set false to hide the progress indicator when it is not animating; otherwise true.
\sa isDisplayedWhenStopped
*/
void setDisplayedWhenStopped(bool state);
/*! Sets the color of the components to the given color.
\sa color
*/
void setColor(const QColor & color);
/*! Set the size of this widget (used by sizeHint)
* \sa displaySize
*/
void setDisplaySize(int size);
protected:
virtual void timerEvent(QTimerEvent * event);
virtual void paintEvent(QPaintEvent * event);
private:
int m_angle;
int m_timerId;
int m_delay;
int m_displaySize;
bool m_displayedWhenStopped;
QColor m_color;
};

View File

@ -0,0 +1,52 @@
//Define the SIP wrapper to the QProgressIndicator widget
//Author - Kovid Goyal <kovid@kovidgoyal.net>
%Module progress_indicator 1
%Import QtCore/QtCoremod.sip
%Import QtGui/QtGuimod.sip
class QProgressIndicator : QWidget {
%TypeHeaderCode
#include <QProgressIndicator.h>
%End
public:
QProgressIndicator(QWidget *parent /TransferThis/ = 0, int size = 64);
int animationDelay() const;
bool isAnimated () const;
bool isDisplayedWhenStopped() const;
const QColor & color() const;
virtual QSize sizeHint() const;
int heightForWidth(int w) const;
int displaySize() const;
public slots:
void startAnimation();
void stopAnimation();
void setAnimationDelay(int delay);
void setDisplayedWhenStopped(bool state);
void setColor(const QColor & color);
void setDisplaySize(int size);
protected:
virtual void timerEvent(QTimerEvent * event);
virtual void paintEvent(QPaintEvent * event);
};

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.constants import plugins
pi, pi_error = plugins['progress_indicator']
if pi_error:
raise RuntimeError('Failed to load the Progress Indicator plugin: '+\
pi_error)
ProgressIndicator = pi.QProgressIndicator

View File

@ -2,12 +2,13 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, re, collections
from PyQt4.QtGui import QStatusBar, QMovie, QLabel, QWidget, QHBoxLayout, QPixmap, \
from PyQt4.QtGui import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
QVBoxLayout, QSizePolicy, QToolButton, QIcon, QScrollArea, QFrame
from PyQt4.QtCore import Qt, QSize, SIGNAL, QCoreApplication
from calibre import fit_image, preferred_encoding, isosx
from calibre.gui2 import qstring_to_unicode, config
from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.ebooks import BOOK_EXTENSIONS
class BookInfoDisplay(QWidget):
@ -138,14 +139,12 @@ class BookInfoDisplay(QWidget):
self.setVisible(True)
class MovieButton(QFrame):
def __init__(self, movie, jobs_dialog):
def __init__(self, jobs_dialog):
QFrame.__init__(self)
movie.setCacheMode(QMovie.CacheAll)
self.setLayout(QVBoxLayout())
self.movie_widget = QLabel()
self.movie_widget.setMovie(movie)
self.movie = movie
self.layout().addWidget(self.movie_widget)
self.pi = ProgressIndicator(self)
self.layout().addWidget(self.pi)
self.jobs = QLabel('<b>'+_('Jobs:')+' 0')
self.jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)
self.layout().addWidget(self.jobs)
@ -156,11 +155,8 @@ class MovieButton(QFrame):
self.jobs_dialog = jobs_dialog
self.setCursor(Qt.PointingHandCursor)
self.setToolTip(_('Click to see list of active jobs.'))
movie.start()
movie.setPaused(True)
self.jobs_dialog.jobs_view.restore_column_widths()
def mouseReleaseEvent(self, event):
if self.jobs_dialog.isVisible():
self.jobs_dialog.jobs_view.write_settings()
@ -170,6 +166,17 @@ class MovieButton(QFrame):
self.jobs_dialog.show()
self.jobs_dialog.jobs_view.restore_column_widths()
@property
def is_running(self):
return self.pi.isAnimated()
def start(self):
self.pi.startAnimation()
def stop(self):
self.pi.stopAnimation()
class CoverFlowButton(QToolButton):
def __init__(self, parent=None):
@ -211,7 +218,7 @@ class StatusBar(QStatusBar):
def __init__(self, jobs_dialog, systray=None):
QStatusBar.__init__(self)
self.systray = systray
self.movie_button = MovieButton(QMovie(I('jobs-animated.mng')), jobs_dialog)
self.movie_button = MovieButton(jobs_dialog)
self.cover_flow_button = CoverFlowButton()
self.tag_view_button = TagViewButton()
self.addPermanentWidget(self.cover_flow_button)
@ -262,8 +269,7 @@ class StatusBar(QStatusBar):
num = self.jobs()
text = src.replace(str(num), str(nnum))
jobs.setText(text)
if self.movie_button.movie.state() == QMovie.Paused:
self.movie_button.movie.setPaused(False)
self.movie_button.start()
def job_done(self, nnum):
jobs = self.movie_button.jobs
@ -275,49 +281,8 @@ class StatusBar(QStatusBar):
self.no_more_jobs()
def no_more_jobs(self):
if self.movie_button.movie.state() == QMovie.Running:
self.movie_button.movie.jumpToFrame(0)
self.movie_button.movie.setPaused(True)
if self.movie_button.is_running:
self.movie_button.stop()
QCoreApplication.instance().alert(self, 5000)
if __name__ == '__main__':
# Used to create the animated status icon
from PyQt4.Qt import QPainter, QSvgRenderer, QColor
from subprocess import check_call
from calibre.gui2 import is_ok_to_use_qt
is_ok_to_use_qt()
def create_pixmaps(path, size=16, delta=20):
r = QSvgRenderer(path)
if not r.isValid():
raise Exception(path + ' not valid svg')
pixmaps = []
for angle in range(0, 360+delta, delta):
pm = QPixmap(size, size)
pm.fill(QColor(0,0,0,0))
p = QPainter(pm)
p.translate(size/2., size/2.)
p.rotate(angle)
p.translate(-size/2., -size/2.)
r.render(p)
p.end()
pixmaps.append(pm)
return pixmaps
def create_mng(path='', size=64, angle=5, delay=5):
pixmaps = create_pixmaps(path, size, angle)
filesl = []
for i in range(len(pixmaps)):
name = 'a%s.png'%(i,)
filesl.append(name)
pixmaps[i].save(name, 'PNG')
filesc = ' '.join(filesl)
cmd = 'convert -dispose Background -delay '+str(delay)+ ' ' + filesc + ' -loop 0 animated.gif'
try:
check_call(cmd, shell=True)
finally:
for file in filesl:
os.remove(file)
import sys
create_mng(sys.argv[1])

View File

@ -5,7 +5,7 @@ import traceback, os, sys, functools, collections, re
from functools import partial
from threading import Thread
from PyQt4.Qt import QMovie, QApplication, Qt, QIcon, QTimer, QWidget, SIGNAL, \
from PyQt4.Qt import QApplication, Qt, QIcon, QTimer, SIGNAL, \
QDesktopServices, QDoubleSpinBox, QLabel, QTextBrowser, \
QPainter, QBrush, QColor, QStandardItemModel, QPalette, \
QStandardItem, QUrl, QRegExpValidator, QRegExp, QLineEdit, \
@ -14,6 +14,7 @@ from PyQt4.Qt import QMovie, QApplication, Qt, QIcon, QTimer, QWidget, SIGNAL, \
from calibre.gui2.viewer.main_ui import Ui_EbookViewer
from calibre.gui2.viewer.printing import Printing
from calibre.gui2.viewer.bookmarkmanager import BookmarkManager
from calibre.gui2.widgets import ProgressIndicator
from calibre.gui2.main_window import MainWindow
from calibre.gui2 import Application, ORG_NAME, APP_UID, choose_files, \
info_dialog, error_dialog
@ -62,39 +63,6 @@ class Worker(Thread):
self.exception = err
self.traceback = traceback.format_exc()
class ProgressIndicator(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
self.setGeometry(0, 0, 300, 500)
self.movie = QMovie(I('jobs-animated.mng'))
self.ml = QLabel(self)
self.ml.setMovie(self.movie)
self.movie.start()
self.movie.setPaused(True)
self.status = QLabel(self)
self.status.setWordWrap(True)
self.status.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
self.setVisible(False)
def start(self, msg=''):
view = self.parent()
pwidth, pheight = view.size().width(), view.size().height()
self.resize(pwidth, min(pheight, 250))
self.move(0, (pheight-self.size().height())/2.)
self.ml.resize(self.ml.sizeHint())
self.ml.move(int((self.size().width()-self.ml.size().width())/2.), 0)
self.status.resize(self.size().width(), self.size().height()-self.ml.size().height()-10)
self.status.move(0, self.ml.size().height()+10)
self.status.setText('<h1>'+msg+'</h1>')
self.setVisible(True)
self.movie.setPaused(False)
def stop(self):
if self.movie.state() == self.movie.Running:
self.movie.setPaused(True)
self.setVisible(False)
class History(collections.deque):
def __init__(self, action_back, action_forward):

View File

@ -7,7 +7,7 @@ import re, os, traceback
from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
QListWidgetItem, QTextCharFormat, QApplication, \
QSyntaxHighlighter, QCursor, QColor, QWidget, \
QPixmap, QMovie, QPalette, QTimer, QDialog, \
QPixmap, QPalette, QTimer, QDialog, \
QAbstractListModel, QVariant, Qt, SIGNAL, \
QRegExp, QSettings, QSize, QModelIndex, \
QAbstractButton, QPainter, QLineEdit, QComboBox, \
@ -22,17 +22,14 @@ from calibre.utils.fonts import fontconfig
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
class ProgressIndicator(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
self.setGeometry(0, 0, 300, 350)
self.movie = QMovie(I('jobs-animated.mng'))
self.ml = QLabel(self)
self.ml.setMovie(self.movie)
self.movie.start()
self.movie.setPaused(True)
self.pi = _ProgressIndicator(self)
self.status = QLabel(self)
self.status.setWordWrap(True)
self.status.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
@ -43,17 +40,16 @@ class ProgressIndicator(QWidget):
pwidth, pheight = view.size().width(), view.size().height()
self.resize(pwidth, min(pheight, 250))
self.move(0, (pheight-self.size().height())/2.)
self.ml.resize(self.ml.sizeHint())
self.ml.move(int((self.size().width()-self.ml.size().width())/2.), 0)
self.status.resize(self.size().width(), self.size().height()-self.ml.size().height()-10)
self.status.move(0, self.ml.size().height()+10)
self.pi.resize(self.pi.sizeHint())
self.pi.move(int((self.size().width()-self.pi.size().width())/2.), 0)
self.status.resize(self.size().width(), self.size().height()-self.pi.size().height()-10)
self.status.move(0, self.pi.size().height()+10)
self.status.setText('<h1>'+msg+'</h1>')
self.setVisible(True)
self.movie.setPaused(False)
self.pi.startAnimation()
def stop(self):
if self.movie.state() == self.movie.Running:
self.movie.setPaused(True)
self.pi.stopAnimation()
self.setVisible(False)
class FilenamePattern(QWidget, Ui_Form):

View File

@ -433,7 +433,7 @@ settings (if any) or the defaults.
.. note::
You can see the actual settings used during any conversion by clicking the rotating hourglass
You can see the actual settings used during any conversion by clicking the rotating icon in the lower right corner
and then double clicking the individual conversion job. This will bring up a conversion log
that will contain the actual settings used, near the top.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -9,9 +9,9 @@ So you've just started |app|. What do you do now? Well, before |app| can do anyt
.. image:: images/added_books.png
Once you've admired the list of books you just added to your heart's content, you'll probably want to actually read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and it will have setup calibre for your reader device. Conversion is a breeze, just select the book you want to convert, and click the "Convert E-book" button. Ignore all the options for now and just click "OK". The little hourglass in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book.
Once you've admired the list of books you just added to your heart's content, you'll probably want to actually read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and it will have setup calibre for your reader device. Conversion is a breeze, just select the book you want to convert, and click the "Convert E-book" button. Ignore all the options for now and just click "OK". The little icon in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book.
Now if you want to read the book on your reader, just connect it to the computer, wait till calibre detects it (10-20secs) and then click the "Send to device" button. Once the hourglass stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands.
Now if you want to read the book on your reader, just connect it to the computer, wait till calibre detects it (10-20secs) and then click the "Send to device" button. Once the icon stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands.
To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`.

View File

@ -99,6 +99,7 @@ _extra_lang_codes = {
'en_AU' : _('English (AU)'),
'en_CA' : _('English (CA)'),
'en_IN' : _('English (IND)'),
'en_TH' : _('English (TH)'),
'nl' : _('Dutch (NL)'),
'nl_BE' : _('Dutch (BE)'),
'und' : _('Unknown')