IGN:Use QDesktopServices for desktop integration

This commit is contained in:
Kovid Goyal 2008-06-08 14:25:43 -07:00
parent ba2a4db044
commit 38417a67a3
8 changed files with 41 additions and 341 deletions

View File

@ -40,6 +40,8 @@ pictureflow :
pot : pot :
cd src/calibre/translations && ${PYTHON} __init__.py pot cd src/calibre/translations && ${PYTHON} __init__.py pot
egg : all egg :
${PYTHON} setup.py register bdist_egg --exclude-source-files upload ${PYTHON} setup.py bdist_egg --exclude-source-files
linux_binary:
${PYTHON} -c "import upload; upload._build_linux()"

View File

@ -16,7 +16,8 @@ from optparse import IndentedHelpFormatter
from logging import Formatter from logging import Formatter
from ttfquery import findsystem, describe from ttfquery import findsystem, describe
from PyQt4.QtCore import QSettings, QVariant from PyQt4.QtCore import QSettings, QVariant, QUrl
from PyQt4.QtGui import QDesktopServices
from calibre.translations.msgfmt import make from calibre.translations.msgfmt import make
from calibre.ebooks.chardet import detect from calibre.ebooks.chardet import detect
@ -389,13 +390,9 @@ def detect_ncpus():
def launch(path_or_url): def launch(path_or_url):
if islinux: if os.path.exists(path_or_url):
subprocess.Popen(('xdg-open', path_or_url)) path_or_url = 'file:'+path_or_url
elif isosx: QDesktopServices.openUrl(QUrl(path_or_url))
subprocess.Popen(('open', path_or_url))
elif iswindows:
win32api = __import__('win32api', globals(), locals(), [], -1)
win32api.ShellExecute(0, 'open', path_or_url, None, os.getcwd(), 1)
def relpath(target, base=os.curdir): def relpath(target, base=os.curdir):
""" """

View File

@ -1,17 +1,15 @@
from calibre.gui2 import choose_files
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import time, os import time, os
from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import SIGNAL, QUrl
from PyQt4.QtGui import QDialog, QMessageBox from PyQt4.QtGui import QDialog, QMessageBox, QDesktopServices
from calibre.web.feeds.recipes import compile_recipe from calibre.web.feeds.recipes import compile_recipe
from calibre.web.feeds.news import AutomaticNewsRecipe from calibre.web.feeds.news import AutomaticNewsRecipe
from calibre.gui2.dialogs.user_profiles_ui import Ui_Dialog from calibre.gui2.dialogs.user_profiles_ui import Ui_Dialog
from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog, choose_files
from calibre.gui2.widgets import PythonHighlighter from calibre.gui2.widgets import PythonHighlighter
from calibre.utils import sendmail
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre import isosx from calibre import isosx
@ -68,10 +66,13 @@ class UserProfiles(QDialog, Ui_Dialog):
pt = PersistentTemporaryFile(suffix='.py') pt = PersistentTemporaryFile(suffix='.py')
pt.write(src.encode('utf-8')) pt.write(src.encode('utf-8'))
pt.close() pt.close()
sendmail(subject='Recipe for '+title, body = _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title)
attachments=[pt.name], subject = _('Recipe for ')+title
body=_('Save the text below into a file named recipe.py and send the file to your friends, to allow them to use this recipe.') if isosx else _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title)) url = QUrl('mailto:')
url.addQueryItem('subject', subject)
url.addQueryItem('body', body)
url.addQueryItem('attachment', pt.name)
QDesktopServices.openUrl(url)
def edit_profile(self, current, previous): def edit_profile(self, current, previous):

View File

@ -3,9 +3,9 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, sys, textwrap, collections, traceback, shutil, time import os, sys, textwrap, collections, traceback, shutil, time
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
QVariant, QThread, QString, QSize QVariant, QThread, QString, QSize, QUrl
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \ from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
QToolButton, QDialog, QSizePolicy QToolButton, QDialog, QSizePolicy, QDesktopServices
from PyQt4.QtSvg import QSvgRenderer from PyQt4.QtSvg import QSvgRenderer
from calibre import __version__, __appname__, islinux, sanitize_file_name, launch, \ from calibre import __version__, __appname__, islinux, sanitize_file_name, launch, \
@ -858,7 +858,7 @@ class Main(MainWindow, Ui_MainWindow):
monitor=False) monitor=False)
self.viewer_job_id += 1 self.viewer_job_id += 1
else: else:
launch(name) QDesktopServices.openUrl(QUrl('file:'+name))#launch(name)
time.sleep(2) # User feedback time.sleep(2) # User feedback
def view_specific_format(self, triggered): def view_specific_format(self, triggered):

View File

@ -16,7 +16,7 @@
<h1>Download $app for Linux</h1> <h1>Download $app for Linux</h1>
<p>This binary package is compatible with most recent linux distributions running on Intel 32 bit CPUs.</p> <p>This binary package is compatible with most recent linux distributions running on Intel 32 bit CPUs.</p>
<p> <p>
<img width="50" height="50" style="border:1px red solid" src="${href.chrome('/dl/images/binary_logo.png'%(name,))}" /> <img width="50" height="50" style="border:1px red solid" src="${href.chrome('/dl/images/binary_logo.png')}" />
(Version: $version <a href="/wiki/Changelog">Changelog</a>) (Version: $version <a href="/wiki/Changelog">Changelog</a>)
</p> </p>
<p>To install, copy paste the following command into a terminal and press Enter: <p>To install, copy paste the following command into a terminal and press Enter:

View File

@ -6,43 +6,4 @@ __docformat__ = 'restructuredtext en'
''' '''
Miscelleaneous utilities. Miscelleaneous utilities.
''' '''
import subprocess
from calibre import iswindows, isosx
from calibre.ptempfile import PersistentTemporaryFile
def sendmail(recipient='', subject='', attachments=[], body=''):
if not recipient:
recipient = 'someone@somewhere.net'
if isinstance(recipient, unicode):
recipient = recipient.encode('utf-8')
if isinstance(subject, unicode):
subject = subject.encode('utf-8')
if isinstance(body, unicode):
body = body.encode('utf-8')
for i, src in enumerate(attachments):
if isinstance(src, unicode):
attachments[i] = src.encode('utf-8')
if iswindows:
from calibre.utils.simplemapi import SendMail
SendMail(recipient, subject=subject, body=body, attachfiles=';'.join(attachments))
elif isosx:
pt = PersistentTemporaryFile(suffix='.txt')
pt.write(body)
if attachments:
pt.write('\n\n----\n\n')
pt.write(open(attachments[0], 'rb').read())
pt.close()
subprocess.Popen(('open', '-t', pt.name))
else:
body = '"' + body.replace('"', '\\"') + '"'
subject = '"' + subject.replace('"', '\\"') + '"'
attach = ''
if attachments:
attach = attachments[0]
attach = '"' + attach.replace('"', '\\"') + '"'
attach = '--attach '+attach
subprocess.check_call('xdg-email --utf8 --subject %s --body %s %s %s'%(subject, body, attach, recipient), shell=True)

View File

@ -1,262 +0,0 @@
"""
Copyright (c) 2007 Ian Cook and John Popplewell
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Date : 13 August 2007
Version : 1.0.2
Contact : John Popplewell
Email : john@johnnypops.demon.co.uk
Web : http://www.johnnypops.demon.co.uk/python/
Origin : Based on the original script by Ian Cook
http://www.kirbyfooty.com/simplemapi.py
Comments: Works (and tested) with:
Outlook Express, Outlook 97 and 2000,
Eudora, Incredimail and Mozilla Thunderbird (1.5.0.2)
Thanks : Werner F. Bruhin and Michele Petrazzo on the ctypes list.
Lukas Lalinsky for patches and encouragement.
If you have any bug-fixes, enhancements or suggestions regarding this
software, please contact me at the above email address.
"""
import os
from ctypes import *
FLAGS = c_ulong
LHANDLE = c_ulong
LPLHANDLE = POINTER(LHANDLE)
# Return codes
SUCCESS_SUCCESS = 0
MAPI_USER_ABORT = 1
MAPI_E_USER_ABORT = MAPI_USER_ABORT
MAPI_E_FAILURE = 2
MAPI_E_LOGON_FAILURE = 3
MAPI_E_LOGIN_FAILURE = MAPI_E_LOGON_FAILURE
MAPI_E_DISK_FULL = 4
MAPI_E_INSUFFICIENT_MEMORY = 5
MAPI_E_ACCESS_DENIED = 6
MAPI_E_TOO_MANY_SESSIONS = 8
MAPI_E_TOO_MANY_FILES = 9
MAPI_E_TOO_MANY_RECIPIENTS = 10
MAPI_E_ATTACHMENT_NOT_FOUND = 11
MAPI_E_ATTACHMENT_OPEN_FAILURE = 12
MAPI_E_ATTACHMENT_WRITE_FAILURE = 13
MAPI_E_UNKNOWN_RECIPIENT = 14
MAPI_E_BAD_RECIPTYPE = 15
MAPI_E_NO_MESSAGES = 16
MAPI_E_INVALID_MESSAGE = 17
MAPI_E_TEXT_TOO_LARGE = 18
MAPI_E_INVALID_SESSION = 19
MAPI_E_TYPE_NOT_SUPPORTED = 20
MAPI_E_AMBIGUOUS_RECIPIENT = 21
MAPI_E_AMBIG_RECIP = MAPI_E_AMBIGUOUS_RECIPIENT
MAPI_E_MESSAGE_IN_USE = 22
MAPI_E_NETWORK_FAILURE = 23
MAPI_E_INVALID_EDITFIELDS = 24
MAPI_E_INVALID_RECIPS = 25
MAPI_E_NOT_SUPPORTED = 26
# Recipient class
MAPI_ORIG = 0
MAPI_TO = 1
# Send flags
MAPI_LOGON_UI = 1
MAPI_DIALOG = 8
class MapiRecipDesc(Structure):
_fields_ = [
('ulReserved', c_ulong),
('ulRecipClass', c_ulong),
('lpszName', c_char_p),
('lpszAddress', c_char_p),
('ulEIDSize', c_ulong),
('lpEntryID', c_void_p),
]
lpMapiRecipDesc = POINTER(MapiRecipDesc)
lppMapiRecipDesc = POINTER(lpMapiRecipDesc)
class MapiFileDesc(Structure):
_fields_ = [
('ulReserved', c_ulong),
('flFlags', c_ulong),
('nPosition', c_ulong),
('lpszPathName', c_char_p),
('lpszFileName', c_char_p),
('lpFileType', c_void_p),
]
lpMapiFileDesc = POINTER(MapiFileDesc)
class MapiMessage(Structure):
_fields_ = [
('ulReserved', c_ulong),
('lpszSubject', c_char_p),
('lpszNoteText', c_char_p),
('lpszMessageType', c_char_p),
('lpszDateReceived', c_char_p),
('lpszConversationID', c_char_p),
('flFlags', FLAGS),
('lpOriginator', lpMapiRecipDesc),
('nRecipCount', c_ulong),
('lpRecips', lpMapiRecipDesc),
('nFileCount', c_ulong),
('lpFiles', lpMapiFileDesc),
]
lpMapiMessage = POINTER(MapiMessage)
MAPI = windll.mapi32
MAPISendMail = MAPI.MAPISendMail
MAPISendMail.restype = c_ulong
MAPISendMail.argtypes = (LHANDLE, c_ulong, lpMapiMessage, FLAGS, c_ulong)
MAPIResolveName = MAPI.MAPIResolveName
MAPIResolveName.restype = c_ulong
MAPIResolveName.argtypes= (LHANDLE, c_ulong, c_char_p, FLAGS, c_ulong, lppMapiRecipDesc)
MAPIFreeBuffer = MAPI.MAPIFreeBuffer
MAPIFreeBuffer.restype = c_ulong
MAPIFreeBuffer.argtypes = (c_void_p, )
MAPILogon = MAPI.MAPILogon
MAPILogon.restype = c_ulong
MAPILogon.argtypes = (LHANDLE, c_char_p, c_char_p, FLAGS, c_ulong, LPLHANDLE)
MAPILogoff = MAPI.MAPILogoff
MAPILogoff.restype = c_ulong
MAPILogoff.argtypes = (LHANDLE, c_ulong, FLAGS, c_ulong)
class MAPIError(WindowsError):
def __init__(self, code):
WindowsError.__init__(self)
self.code = code
def __str__(self):
return 'MAPI error %d' % (self.code,)
def _logon(profileName=None, password=None):
pSession = LHANDLE()
rc = MAPILogon(0, profileName, password, MAPI_LOGON_UI, 0, byref(pSession))
if rc != SUCCESS_SUCCESS:
raise MAPIError, rc
return pSession
def _logoff(session):
rc = MAPILogoff(session, 0, 0, 0)
if rc != SUCCESS_SUCCESS:
raise MAPIError, rc
def _resolveName(session, name):
pRecipDesc = lpMapiRecipDesc()
rc = MAPIResolveName(session, 0, name, 0, 0, byref(pRecipDesc))
if rc != SUCCESS_SUCCESS:
raise MAPIError, rc
rd = pRecipDesc.contents
name, address = rd.lpszName, rd.lpszAddress
rc = MAPIFreeBuffer(pRecipDesc)
if rc != SUCCESS_SUCCESS:
raise MAPIError, rc
return name, address
def _sendMail(session, recipient, subject, body, attach, preview):
nFileCount = len(attach)
if attach:
MapiFileDesc_A = MapiFileDesc * len(attach)
fda = MapiFileDesc_A()
for fd, fa in zip(fda, attach):
fd.ulReserved = 0
fd.flFlags = 0
fd.nPosition = -1
fd.lpszPathName = fa
fd.lpszFileName = None
fd.lpFileType = None
lpFiles = fda
else:
lpFiles = lpMapiFileDesc()
RecipWork = recipient.split(';')
RecipCnt = len(RecipWork)
MapiRecipDesc_A = MapiRecipDesc * len(RecipWork)
rda = MapiRecipDesc_A()
for rd, ra in zip(rda, RecipWork):
rd.ulReserved = 0
rd.ulRecipClass = MAPI_TO
try:
rd.lpszName, rd.lpszAddress = _resolveName(session, ra)
except WindowsError:
# work-round for Mozilla Thunderbird
rd.lpszName, rd.lpszAddress = None, ra
rd.ulEIDSize = 0
rd.lpEntryID = None
recip = rda
msg = MapiMessage(0, subject, body, None, None, None, 0, lpMapiRecipDesc(),
RecipCnt, recip,
nFileCount, lpFiles)
flags = 0
if preview:
flags = MAPI_DIALOG
rc = MAPISendMail(session, 0, byref(msg), flags, 0)
if rc != SUCCESS_SUCCESS:
raise MAPIError, rc
def SendMail(recipient, subject="", body="", attachfiles="", preview=1):
"""Post an e-mail message using Simple MAPI
recipient - string: address to send to (multiple addresses separated with a semicolon)
subject - string: subject header
body - string: message text
attach - string: files to attach (multiple attachments separated with a semicolon)
preview - bool : if false, minimise user interaction. Default:true
"""
attach = []
AttachWork = attachfiles.split(';')
for filename in AttachWork:
if os.path.exists(filename):
attach.append(filename)
attach = map(os.path.abspath, attach)
restore = os.getcwd()
try:
session = _logon()
try:
_sendMail(session, recipient, subject, body, attach, preview)
finally:
_logoff(session)
finally:
os.chdir(restore)
if __name__ == '__main__':
import sys
recipient = "test@johnnypops.demon.co.uk"
subject = "Test Message Subject"
body = "Hi,\r\n\r\nthis is a quick test message,\r\n\r\ncheers,\r\nJohn."
attachment = sys.argv[0]
SendMail(recipient, subject, body, attachment)

View File

@ -78,7 +78,7 @@ def build_osx():
return os.path.basename(installer) return os.path.basename(installer)
#return build_installer(installer, vm, 20) #return build_installer(installer, vm, 20)
def build_linux(): def _build_linux():
cwd = os.getcwd() cwd = os.getcwd()
tbz2 = os.path.join(cwd, installer_name('tar.bz2')) tbz2 = os.path.join(cwd, installer_name('tar.bz2'))
SPEC="""\ SPEC="""\
@ -125,7 +125,7 @@ open(hook, 'wb').write('hiddenimports = %%s'%%repr(temp) + '\\n')
sys.path.insert(0, CALIBRESRC) sys.path.insert(0, CALIBRESRC)
from calibre.linux import entry_points from calibre.linux import entry_points
executables, scripts = ['calibre_postinstall', 'parallel'], executables, scripts = ['calibre_postinstall', 'parallel'], \
[os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')] [os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')]
for entry in entry_points['console_scripts'] + entry_points['gui_scripts']: for entry in entry_points['console_scripts'] + entry_points['gui_scripts']:
@ -198,7 +198,7 @@ os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist'))
for f in os.listdir('.'): for f in os.listdir('.'):
tf.add(f) tf.add(f)
"""%('/home/kovid', tbz2) """%('/mnt/hgfs/giskard/', tbz2)
os.chdir(os.path.expanduser('~/build/pyinstaller')) os.chdir(os.path.expanduser('~/build/pyinstaller'))
open('calibre/calibre.spec', 'wb').write(SPEC) open('calibre/calibre.spec', 'wb').write(SPEC)
try: try:
@ -207,8 +207,17 @@ for f in os.listdir('.'):
os.chdir(cwd) os.chdir(cwd)
return os.path.basename(tbz2) return os.path.basename(tbz2)
def build_linux():
vm = '/vmware/linux/libprs500-gentoo.vmx'
vmware = ('vmware', '-q', '-x', '-n', vm)
subprocess.Popen(vmware)
print 'Waiting for linux to boot up...'
time.sleep(60)
check_call('ssh linux make -C /mnt/hgfs/giskard/work/calibre all egg linux_binary')
check_call('ssh sudo poweroff')
def build_installers(): def build_installers():
return build_windows(), build_osx() return build_linux(), build_windows(), build_osx()
def upload_demo(): def upload_demo():
check_call('''html2lrf --title='Demonstration of html2lrf' --author='Kovid Goyal' ''' check_call('''html2lrf --title='Demonstration of html2lrf' --author='Kovid Goyal' '''
@ -232,7 +241,7 @@ def upload_installers():
check_call('''ssh divok rm -f %s/calibre\*.dmg'''%(DOWNLOADS,)) check_call('''ssh divok rm -f %s/calibre\*.dmg'''%(DOWNLOADS,))
check_call('''scp %s divok:%s/'''%(dmg, DOWNLOADS)) check_call('''scp %s divok:%s/'''%(dmg, DOWNLOADS))
if tbz2 and os.path.exists(tbz2): if tbz2 and os.path.exists(tbz2):
check_call('''ssh divok rm -f %s/calibre-\*-i686.tar.bz2'''%(DOWNLOADS,)) check_call('''ssh divok rm -f %s/calibre-\*-i686.tar.bz2 %s/latest-linux-binary.tar.bz2'''%(DOWNLOADS,DOWNLOADS))
check_call('''scp %s divok:%s/'''%(tbz2, DOWNLOADS)) check_call('''scp %s divok:%s/'''%(tbz2, DOWNLOADS))
check_call('''ssh divok ln -s %s/calibre-\*-i686.tar.bz2 %s/latest-linux-binary.tar.bz2'''%(DOWNLOADS,DOWNLOADS)) check_call('''ssh divok ln -s %s/calibre-\*-i686.tar.bz2 %s/latest-linux-binary.tar.bz2'''%(DOWNLOADS,DOWNLOADS))
check_call('''ssh divok chmod a+r %s/\*'''%(DOWNLOADS,)) check_call('''ssh divok chmod a+r %s/\*'''%(DOWNLOADS,))
@ -261,16 +270,7 @@ def upload_tarball():
check_call('ssh divok rm -f %s/calibre-\*.tar.bz2'%DOWNLOADS) check_call('ssh divok rm -f %s/calibre-\*.tar.bz2'%DOWNLOADS)
check_call('scp dist/calibre-*.tar.bz2 divok:%s/'%DOWNLOADS) check_call('scp dist/calibre-*.tar.bz2 divok:%s/'%DOWNLOADS)
def pypi():
vm = '/vmware/linux/libprs500-gentoo.vmx'
vmware = ('vmware', '-q', '-x', '-n', vm)
subprocess.Popen(vmware)
print 'Waiting for linux to boot up...'
time.sleep(60)
check_call('scp ~/.pypirc linux:')
check_call('ssh linux make -C /mnt/hgfs/giskard/work/calibre egg')
check_call('ssh linux rm -f ~/.pypirc')
check_call('ssh linux sudo poweroff')
def main(): def main():
upload = len(sys.argv) < 2 upload = len(sys.argv) < 2
@ -289,10 +289,11 @@ def main():
print 'Uploading installers...' print 'Uploading installers...'
upload_installers() upload_installers()
print 'Uploading to PyPI' print 'Uploading to PyPI'
pypi()
upload_tarball() upload_tarball()
upload_docs() upload_docs()
upload_user_manual() upload_user_manual()
check_call('rm -f dist/*.bz2 dist/*.exe dist/*.dmg')
check_call('python setup.py register upload')
check_call('''rm -rf dist/* build/*''') check_call('''rm -rf dist/* build/*''')
if __name__ == '__main__': if __name__ == '__main__':