diff options
author | Radomir Dopieralski <sheep-devel@sheep.art.pl> | 2012-02-04 19:10:41 (GMT) |
---|---|---|
committer | Radomir Dopieralski <sheep-devel@sheep.art.pl> | 2012-02-04 19:10:41 (GMT) |
commit | f5fb88ffd4c9c99463d7209669175106d2a6bd27 (patch) | |
tree | e21a6d5fdcf382c6c910cdde04ea08529b2c9562 | |
parent | fb8402416007dcfba7dc6a955088e8e498108405 (diff) |
Move the hatta-icon code and resources to a separate project.
-rwxr-xr-x | .hgignore | 5 | ||||
-rwxr-xr-x | MANIFEST.in | 8 | ||||
-rwxr-xr-x | error_dialog.py | 93 | ||||
-rwxr-xr-x | hatta_gtkicon.py | 240 | ||||
-rwxr-xr-x | hatta_qticon.py | 621 | ||||
-rw-r--r-- | resources/HattaError.ui | 243 | ||||
-rw-r--r-- | resources/error.png | bin | 6216 -> 0 bytes | |||
-rwxr-xr-x | resources/hatta.desktop | 13 | ||||
-rwxr-xr-x | resources/hatta.icns | bin | 224602 -> 0 bytes | |||
-rwxr-xr-x | resources/hatta.ico | bin | 12862 -> 0 bytes | |||
-rwxr-xr-x | setup.py | 266 | ||||
-rw-r--r-- | ui_errorDialog.py | 115 |
12 files changed, 19 insertions, 1585 deletions
@@ -7,5 +7,10 @@ build/* dist/* pygments/* werkzeug/* +bin/* +lib/* +include/* +local +*.egg-info cache/* *.DS_Store diff --git a/MANIFEST.in b/MANIFEST.in index c65f312..9bea895 100755 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,4 @@ include hatta/*.py -include hatta_qticon.py -include hatta_gtkicon.py -include resources/hatta.svg -include resources/hatta.png -include resources/hatta.ico -include resources/hatta.desktop recursive-include examples * -recursive-include locale/ar/LC_MESSAGES * +recursive-include locale * recursive-include hatta/templates *.html diff --git a/error_dialog.py b/error_dialog.py deleted file mode 100755 index 3bd94d4..0000000 --- a/error_dialog.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python - -# -*- coding: utf-8 -*- - -""" An error display and reporting dialog. - -Displays an error message whenever hatta trhows and unexpected exception. Sends -tracebacks via e-mail. -""" -import sys, os, locale -import os.path -import pprint - -from PyQt4.QtGui import QDialog, QPixmap -from PyQt4.QtCore import pyqtSlot, Qt - -from ui_errorDialog import Ui_ErrorDialog - -def pformat(object, indent=4, width=72, depth=10): - if hasattr(object, 'keys') and hasattr(object, 'values'): - return '{ ' + ('\n' + indent * ' ').join('%s:\t%s' % - (key, pformat(value, indent, width, depth)) - for key, value in object.iteritems()) - + ' }' - return pprint.pformat(object, indent, width, depth) - -class ErrorDialog(QDialog, Ui_ErrorDialog): - """ Extends the UI with connectivity. """ - def __init__(self, module_path): - QDialog.__init__(self) - self.setupUi(self) - dist_icon = os.path.join(module_path, - u'share', u'icons', u'hicolor', u'64x64', u'error.png') - debug_icon = os.path.join(module_path, u'resources', u'error.png') - self.error_icon.setPixmap(QPixmap( - dist_icon if os.path.isfile(dist_icon) else debug_icon)) - - self._caption = None - self._traceback = None - - self.details_button.toggled.connect(self._details_toggled) - - def _get_environment(self): - """ Gathers as much data as possible about the execution einvroment. - """ - env = [] - env.append(('Platform', sys.platform)) - try: - import sysconfig - env.append(('Sysconfig', sysconfig.get_platform())) - except ImportError: - try: - env.append(('Uname', os.uname())) - except AttributeError: - try: - env.append(('Uname', sys.getwindowsversion())) - except AttributeError: - env.append(('Uname', 'Unknown')) - env.append(('Version', sys.version)) - env.append(('Byteorder', sys.byteorder)) - env.append(('Encoding', sys.getfilesystemencoding())) - env.append(('Preferred encoding', locale.getpreferredencoding())) - env.append(('Current dir', os.getcwd())) - env.append(('Globals', os.environ)) - env.append(('Python path', sys.path)) - env.append(('Flags', sys.flags)) - return env - - def format_environment(self): - """ Prints the gathered environmental data in key:value form. """ - return '\n'.join('%s:\t%s' % (key, pformat(value)) - for key, value in self._get_environment()) - - @pyqtSlot(bool) - def _details_toggled(self, toggled): - if toggled: - self.error_traceback.setPlainText(self._bug_dump) - self.resize(800, 600) - else: - self.error_traceback.setPlainText(self._caption) - self.resize(400, 300) - - def get_bug_dump(self): - return self._bug_dump - - def prepare_error(self, caption, traceback): - """ Displays the error widget showing caption. """ - self._caption = caption - self._traceback = traceback - self._bug_dump = '\n'.join([ - self._traceback, self.format_environment()]) - self.error_traceback.setPlainText(caption) - diff --git a/hatta_gtkicon.py b/hatta_gtkicon.py deleted file mode 100755 index 713ddfe..0000000 --- a/hatta_gtkicon.py +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import hatta -import webbrowser -import urllib -import wsgiref.simple_server -import threading -import time - -try: - import gtk -except ImportError: - gtk = None - -try: - import dbus - import dbus.mainloop.glib - import avahi -except ImportError: - avahi = None - -class StatusIcon(object): - def __init__(self, url): - self.url = url - loader = gtk.gdk.PixbufLoader() - loader.write(hatta.Wiki.icon) - loader.close() - self.icon = gtk.StatusIcon() - self.icon.set_from_pixbuf(loader.get_pixbuf()) - self.icon.set_tooltip('Hatta Wiki') - self.icon.connect_object('activate', self.on_activate, None) - self.icon.connect_object('popup-menu', self.on_popup, None) - self.urls = {} - - - def on_activate(self, status_icon, data=None): - webbrowser.open(self.url) - - def on_popup(self, status_icon, button, activate_time): - menu = gtk.Menu() - - if self.urls: - for name, url in self.urls.iteritems(): - item = gtk.MenuItem(name) - item.connect('activate', self.url_on_activate, url) - item.set_tooltip_text(url) - menu.append(item) - item.show() - separator = gtk.SeparatorMenuItem() - menu.append(separator) - separator.show() - - browser = gtk.ImageMenuItem(gtk.STOCK_OPEN) - browser.connect('activate', self.on_activate, False) - menu.append(browser) - browser.show() - - quit = gtk.ImageMenuItem(gtk.STOCK_CLOSE) - quit.connect('activate', self.quit_on_activate, False) - menu.append(quit) - quit.show() - - menu.show() - menu.popup(None, None, None, button, activate_time) - - def quit_on_activate(self, item, data=None): - gtk.main_quit() - - def url_on_activate(self, item, data=None): - webbrowser.open(data) - -class AvahiService(object): - def __init__(self, name, host=None, port=8080, services={}): - if not host: - host = '' - self.services = services - self.service_name = name - # See http://www.dns-sd.org/ServiceTypes.html - self.service_type = "_http._tcp" - self.service_port = port - self.service_txt = "hatta-wiki" - self.domain = "" # Domain to publish on, default to .local - self.host = host # Host to publish records for, default to localhost - self.group = None #our entry group - # Counter so we only rename after collisions a sensible number of times - self.rename_count = 12 - - dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - self.bus = dbus.SystemBus() - self.server = dbus.Interface( - self.bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), - avahi.DBUS_INTERFACE_SERVER) - self.server.connect_to_signal("StateChanged", self.server_state_changed) - self.server_state_changed(self.server.GetState()) - sbrowser = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, - self.server.ServiceBrowserNew(avahi.IF_UNSPEC, - avahi.PROTO_UNSPEC, self.service_type, 'local', dbus.UInt32(0))), - avahi.DBUS_INTERFACE_SERVICE_BROWSER) - sbrowser.connect_to_signal("ItemNew", self.new_item) - sbrowser.connect_to_signal("ItemRemove", self.remove_item) - - - def add_service(self): - if self.group is None: - self.group = dbus.Interface( - self.bus.get_object(avahi.DBUS_NAME, - self.server.EntryGroupNew()), - avahi.DBUS_INTERFACE_ENTRY_GROUP) - self.group.connect_to_signal('StateChanged', - self.entry_group_state_changed) - print "Adding service '%s' of type '%s' ..." % (self.service_name, - self.service_type) - self.group.AddService( - avahi.IF_UNSPEC, #interface - avahi.PROTO_UNSPEC, #protocol - dbus.UInt32(0), #flags - self.service_name, self.service_type, - self.domain, self.host, - dbus.UInt16(self.service_port), - avahi.string_array_to_txt_array(self.service_txt)) - self.group.Commit() - - def remove_service(self): - if not self.group is None: - self.group.Reset() - - def close(self): - if not self.group is None: - self.group.Free() - - def server_state_changed(self, state): - if state == avahi.SERVER_COLLISION: - print "WARNING: Server name collision" - self.remove_service() - elif state == avahi.SERVER_RUNNING: - self.add_service() - - def entry_group_state_changed(self, state, error): - print "state change: %i" % state - - if state == avahi.ENTRY_GROUP_ESTABLISHED: - print "Service established." - elif state == avahi.ENTRY_GROUP_COLLISION: - self.rename_count = self.rename_count - 1 - if self.rename_count > 0: - self.service_name = server.GetAlternativeServiceName( - self.service_name) - print "WARNING: Service name collision, changing name to '%s' ..." % self.service_name - self.remove_service() - self.add_service() - else: - print "ERROR: No suitable service name found after %i retries, exiting." % 12 - gtk.main_quit() - elif state == avahi.ENTRY_GROUP_FAILURE: - print "Error in group state changed", error - gtk.main_quit() - - def new_item(self, interface, protocol, name, stype, domain, flags): - print "Found service '%s' type '%s' domain '%s' " % (name, stype, domain) - if flags & avahi.LOOKUP_RESULT_LOCAL: - # local service, skip - pass - self.server.ResolveService(interface, protocol, name, stype, - domain, avahi.PROTO_UNSPEC, dbus.UInt32(0), - reply_handler=self.new_service_resolved, error_handler=self.print_error) - - def remove_item(self, interface, protocol, name, stype, domain, flags): - try: - del self.services[name] - except KeyError: - pass - - def new_service_resolved(self, *args): - url = 'http://%s:%d/' % (args[7], args[8]) - self.services[args[2]] = url - - def print_error(self, *args): - print 'error_handler' - print args[0] - -class WikiServer(threading.Thread): - def __init__(self, config, host, port): - super(WikiServer, self).__init__() - self.config = config - self.port = port - self.wiki = hatta.Wiki(config) - self.server = wsgiref.simple_server.make_server( - host, port, self.wiki.application) - - def run(self): - while not self.wiki.dead: - self.server.handle_request() - - -def main(): - config = hatta.WikiConfig( - # Here you can modify the configuration: uncomment and change - # the ones you need. Note that it's better use environment - # variables or command line switches. - - # interface='' - # port=8080 - # pages_path = 'docs' - # cache_path = 'cache' - # front_page = 'Home' - # site_name = 'Hatta Wiki' - # page_charset = 'UTF-8' - ) - config.parse_args() - config.parse_files() - port = int(config.get('port', 8080)) - host = config.get('interface', '') - name = config.get('site_name', 'Hatta Wiki') - url = 'http://%s:%d' % (host or 'localhost', port) - thread = WikiServer(config, host, port) - thread.start() - if gtk: - gtk.gdk.threads_init() - status_icon = StatusIcon(url) - if avahi: - try: - service = AvahiService(name, host, port, status_icon.urls) - except dbus.exceptions.DBusException: - service = None - gtk.main() - if avahi and service: - service.close() - else: - webbrowser.open(url) - try: - while True: - time.sleep(100) - except KeyboardInterrupt: - pass - urllib.urlopen('/'.join([url, 'off-with-his-head'])).read(1) - thread.join() - -if __name__ == "__main__": - main() diff --git a/hatta_qticon.py b/hatta_qticon.py deleted file mode 100755 index c3dd52f..0000000 --- a/hatta_qticon.py +++ /dev/null @@ -1,621 +0,0 @@ -#!/usr/bin/env python - -# -*- coding: utf-8 -*- - -""" -Implements a cross-platform system tray icon for a better desktop -experience with Hatta. Allows to start, stop and interact with the wiki -without command line knowledge. - -Uses Qt and PyQt in particular for this task. -""" - -from os.path import join -from select import select -from thread import start_new_thread -from time import sleep -from traceback import format_exc -from urllib import urlopen, quote -from wsgiref import simple_server -import gettext -import os -try: - import pybonjour -except: # Deliberately so, since pybonjour is bundled, but bonjour - # itself isn't there always - pybonjour = None -import sys -import webbrowser - -from PyQt4.QtGui import (QApplication, QSystemTrayIcon, QMenu, QIcon, - QMessageBox, QAction, QKeySequence, QWidget, QVBoxLayout, QGridLayout, - QLabel, QSpinBox, QToolTip, QLineEdit, QHBoxLayout, QPushButton, - QFileDialog, QPixmap, QCheckBox, QDesktopServices, QDialog) -from PyQt4.QtCore import (QString, QThread, pyqtSignal, pyqtSlot, Qt, - QPoint, QLocale) - -from hatta import WikiConfig, Wiki, WikiRequest, project_name, project_url - -from error_dialog import ErrorDialog - -def module_path(): - """ This will get us the program's directory, - even if we are frozen using py2exe""" - - if we_are_frozen(): - return os.path.dirname(unicode( - sys.executable, sys.getfilesystemencoding())) - - return os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) - - -def we_are_frozen(): - """Returns whether we are frozen via py2exe. - This will affect how we find out where we are located.""" - - return hasattr(sys, "frozen") and sys.frozen == "windows_exe" - -class HattaThread(QThread): - """ - An instance of wiki server running in the background and handling incoming - requests. - """ - - # For passing multithread exception in Qt. - exception_signal = pyqtSignal(str) - - def __init__(self, config, error_handler): - """Create a wiki instance and a server for it.""" - super(HattaThread, self).__init__() - self.config = config - self.wiki = Wiki(config) - self.server = simple_server.make_server( - config.get('interface', ''), - int(config.get('port', 8080)), - self.application_wrapper) - - self.exception_signal.connect(error_handler) - - def run(self): - """Thread execution. Handles requests.""" - while not self.wiki.dead: - self.server.handle_request() - - def application_wrapper(self, *args, **kwargs): - try: - return self.wiki.application(*args, **kwargs) - except Exception as e: - self.exception_signal.emit(unicode(e)) - # It's very important to shut down the thread, so that the threaded - # werkzeug won't continue to run and send other exceptions. - self.quit() - - def quit(self): - self.wiki.daed = True - urlopen('http://localhost:%s/off-with-his-head' % - self.config.get('port')).close() - self.server.server_close() - super(HattaThread, self).quit() - -class ZeroconfThread(QThread): - """Handles wiki registration and searches for other wikis. - - A separate thread, as the pybonjour lib is polling.""" - - new_services = pyqtSignal(set) - timeout = 5 # seconds - - def register_callback(self, sdRef, flags, error_code, name, regtype, - domain): - """Called when a response to Zeroconf register is given.""" - if error_code != pybonjour.kDNSServiceErr_NoError: - # TODO: Should give some error - pass - - def browse_callback(self, sdRef, flags, interface_index, error_code, - service_name, reg_type, reply_domain): - """Called when a Zeroconf response for browsing is given.""" - if error_code != pybonjour.kDNSServiceErr_NoError: - return - - def resolve_callback(sdRef, flags, interface_index, - error_code, full_name, host_target, - port, txt_record): - """Called when a service name and data has been resolved. - - Needs to be embedded so that non-escaped service name can be - used. - """ - if error_code != pybonjour.kDNSServiceErr_NoError: - return - self.resolved.append(( - service_name, - host_target, - port)) - - self.resolve_sdRef = pybonjour.DNSServiceResolve( - interfaceIndex=interface_index, - name=service_name, - regtype=reg_type, - domain=reply_domain, - callBack=resolve_callback) - # Add timeout capability - try: - ready = select([self.resolve_sdRef], [], [], self.timeout) - if self.resolve_sdRef in ready[0]: - pybonjour.DNSServiceProcessResult(self.resolve_sdRef) - if flags & pybonjour.kDNSServiceFlagsAdd: - self.services.add(self.resolved.pop()) - else: - try: - self.services.remove(self.resolved.pop()) - except KeyError: - pass - if not flags & pybonjour.kDNSServiceFlagsMoreComing: - self.new_services.emit(list(self.services)) - finally: - self.resolve_sdRef.close() - - def __init__(self, config, services_handler): - super(ZeroconfThread, self).__init__() - self.name = config.get('site_name') - self.reg_type = (u'_http._tcp') - self.port = int(config.get('port', 8080)) - - self.resolved = [] - self.services = set() - - self.new_services.connect(services_handler) - - def run(self): - self.browse_sdRef = pybonjour.DNSServiceBrowse( - regtype=self.reg_type, - callBack=self.browse_callback) - try: - while True: - ready = select([self.browse_sdRef], [], [], self.timeout) - if self.browse_sdRef in ready[0]: - pybonjour.DNSServiceProcessResult(self.browse_sdRef) - finally: - self.browse_sdRef.close() - - def register_wiki(self): - # Register Hatta instance - self.service_ref = pybonjour.DNSServiceRegister( - flags=pybonjour.kDNSServiceFlagsNoAutoRename, - name=self.name, - regtype=self.reg_type, - port=self.port, - callBack=self.register_callback) - - ready = select([self.service_ref], [], [], self.timeout) - if self.service_ref in ready[0]: - pybonjour.DNSServiceProcessResult(self.service_ref) - - def quit(self): - """Close Zeroconf service connection.""" - # This is needed, as closing service_ref would emit this - # signal. - self.new_services.disconnect() - try: - self.service_ref.close() - except AttributeError: - pass - super(ZeroconfThread, self).quit() - -class HattaTrayIcon(QSystemTrayIcon): - """Extension of QSystemTrayIcon for customization towards Hatta.""" - config_filename = join( - str(QDesktopServices.storageLocation( - QDesktopServices.DataLocation)), - project_name, - project_name + u'.conf') - dist_icon = os.path.join(module_path(), - 'share/icons/hicolor/64x64/hatta.png') - debug_icon = os.path.join(module_path(), 'resources/hatta.png') - - def save_config(self): - """Saves a WikiConfig instance with custom data.""" - config_dir = os.path.dirname(self.config_filename) - try: - os.makedirs(config_dir) - except OSError, e: - if not os.path.isdir(config_dir): - raise e - self.config.save_config(self.config_filename) - - @pyqtSlot(unicode, unicode, int, bool) - def reload_config(self, site_name, pages_path, port, announce): - """Change own config and realod the wiki.""" - self.setDisabled(True) - self.menu.clear() - self.menu.addAction(_(u'Restarting wiki...')) - - self.wiki_thread.quit() - if self.zeroconf_thread is not None: - self.zeroconf_thread.quit() - # Necessary if turned off/on via preferences - self.zeroconf_thread = None - - self._modify_url(port) - self.config.set('site_name', str(site_name)) - self.config.set('pages_path', str(pages_path)) - self.config.set('port', port) - self.should_announce = announce - self.config.set('announce', str(announce)) - self.save_config() - - self.wiki_thread = HattaThread(self.config, self.on_error) - self.wiki_thread.start() - - if pybonjour is not None: - def delay_zeroconf_start(): - # This sleep is needed, as mdns server needs to change the - # port on which wiki is registered. - # TODO: This is hidious --- make some kind of wait maybe? - sleep(1) - self.zeroconf_thread = ZeroconfThread( - self.config, - self.on_new_services) - self.zeroconf_thread.start() - self.register_wiki() - - start_new_thread(delay_zeroconf_start, ()) - - # Unfreeze GUI - self.on_new_services([]) - - def setDisabled(self, disabled=True): - """Change tray icon to between disabled or normal state.""" - self.menu.setDisabled(disabled) - self.setIcon(QIcon(self.hatta_icon.pixmap(64, 64, - QIcon.Disabled if disabled else QIcon.Normal))) - - def __init__(self): - """Initialize connection data and status menu GUI.""" - super(HattaTrayIcon, self).__init__() - # First setup tray icon and display inform user about starting - self.hatta_icon = QIcon(QPixmap( - self.dist_icon if os.path.isfile(self.dist_icon) else - self.debug_icon)) - self.menu = QMenu(QString(_(u'Hatta Wiki menu'))) - self.setDisabled(True) - - self.menu.addAction(_(u'Starting wiki...')) - self.setContextMenu(self.menu) - self.setToolTip(QString(_(u'Click this icon to interact with' - u' Hatta wiki.'))) - - self.show() - - # Get config from file or create - self.config = WikiConfig() - self.config.parse_files([self.config_filename]) - if len(self.config.config) == 0: - self.config = default_config - self.save_config() - self.config.parse_args() - - # Global wiki settings - port = int(self.config.get('port')) - self._modify_url(port) - self.should_announce = bool(self.config.get_bool('announce', 1)) - - self.preferences_window = PreferenceWindow(self, self.hatta_icon, - self.config) - self.preferences_window.config_change.connect( - self.reload_config, - type=Qt.QueuedConnection) - - self.default_actions = [] - self.discovery_header = QAction(_(u'Nearby wikis:'), self) - self.discovery_header.setToolTip(_( - 'Displays a list of nearby discovered wikis. ' - u'Click one to view it.')) - self.discovery_header.setDisabled(True) - - # Start wiki thread - self.wiki_thread = HattaThread(self.config, self.on_error) - self.wiki_thread.start() - - self.showMessage(QString(_(u'Welcome to Hatta Wiki')), - QString(_(u'Click the hat icon to start or quit the ' - u'Hatta Wiki.')) - ) - - self._prepare_default_menu() - self.menu.addActions(self.default_actions) - - # Start Zeroconf thread - if pybonjour: - self.zeroconf_thread = ZeroconfThread(self.config, - self.on_new_services) - self.zeroconf_thread.start() - self.register_wiki() - else: - self.zeroconf_thread = None - - # Unfreeze the GUI - self.on_new_services([]) - - def register_wiki(self): - """Tells Zeroconf thread to register wiki in Bonjour if - appropriate.""" - if (pybonjour is not None and self.should_announce and - self.zeroconf_thread is not None): - self.zeroconf_thread.register_wiki() - - def _prepare_default_menu(self): - """Creates and populates the context menu.""" - action = QAction(_(u'&Preferences'), self) - action.setToolTip(_(u'Lets you configure the wiki.')) - action.setShortcut(QKeySequence.Preferences) - action.triggered.connect(self.preferences_window.show) - self.default_actions.append(action) - action = QAction(_(u'&Open wiki'), self) - action.setShortcuts(QKeySequence.Open) - action.setToolTip(_(u'Opens the wiki in the default browser.')) - action.triggered.connect(self.on_wiki_open) - self.default_actions.append(action) - action = QAction(QString(_(u'&Quit')), self) - action.setShortcut(Qt.CTRL + Qt.Key_Q) - action.setToolTip(_(u'Stops the wiki and quits the' - u' status icon.')) - action.triggered.connect(self.on_wiki_quit) - self.default_actions.append(action) - - def _modify_url(self, port): - """Modifies port in URL.""" - self.url = 'http://localhost:%d/' % (port,) - - def on_wiki_open(self): - """Callback for opening wiki page in a browser.""" - webbrowser.open(self.url) - - def on_wiki_quit(self): - """Callback to close running Hatta instance and unload statu - icon.""" - # Hide the icon first, so the user won't try to click anything in - # a strange long quitting scenario. - self.hide() - self.wiki_thread.quit() - if self.zeroconf_thread is not None: - self.zeroconf_thread.quit() - self.menu.destroy() - self.save_config() - global app - app.quit() - - pyqtSlot(str) - def on_error(self, strerror): - """Displays error and send bug request.""" - report_bug('bugs@hatta-wiki.org', strerror) - - def _make_discovery_action(self, name, host, port): - """Creates a menu action from a discovered wiki.""" - action = QAction(_(u' %(wiki_name)s on %(host_name)s') % dict( - wiki_name=name, host_name=host), self) - action.setToolTip(_(u'Opens %(wiki_name)s in browser.') - % dict(wiki_name=name)) - action.triggered.connect((lambda x, y: lambda: webbrowser.open( - 'http://%s:%d' % (x, y)))(host, port)) - return action - - pyqtSlot(set) - def on_new_services(self, services): - """Displays discovered services in menu.""" - self.menu.clear() - - if len(services) > 0: - # Apparently Zeroconf daemon works and returns results - self.menu.addAction(self.discovery_header) - - for name, host, port in services[:4]: - self.menu.addAction(self._make_discovery_action(name, host, - port)) - if len(services) > 4: - submenu = self.menu.addMenu(' ' + _(u'More wikis')) - submenu.setToolTip(_( - u'Shows even more wikis discovered nearby!')) - for name, host, port in services[5:]: - submenu.addAction(self._make_discovery_action(name, host, - port)) - self.menu.addSeparator() - elif len(services) == 0 and pybonjour is None: - # Seems Zeroconf returned no results. Probably no bonjour - # installed. - self.menu.addAction(self.discovery_header) - - if sys.platform.startswith('win32'): - info = QAction(_( - u' Install Bonjour to view nearby wikis'), - self) - info.triggered.connect((lambda: lambda: - webbrowser.open('http://support.apple.com/' - 'downloads/Bonjour_for_Windows'))()) - else: - info = QAction(_( - u' Install libavahi-compat-libdnssd1' - u' to view nearby wikis'), - self - ) - info.setDisabled(True) - self.menu.addAction(info) - - self.menu.addActions(self.default_actions) - self.setDisabled(False) - self.refresh_menu() - - def refresh_menu(self): - """Forces Qt to process events.""" - self.menu.repaint() - global app - if app.hasPendingEvents(): - app.processEvents() - -default_config = WikiConfig( - # Here you can modify the configuration: uncomment and change - # the ones you need. Note that it's better use environment - # variables or command line switches. - - interface='', - port=8080, - site_name='Hatta Wiki', - pages_path=join( - str(QDesktopServices.storageLocation( - QDesktopServices.DataLocation)), - project_name, - u'pages'), - cache_path=join( - str(QDesktopServices.storageLocation( - QDesktopServices.CacheLocation)), - project_name), - # front_page = 'Home' - # page_charset = 'UTF-8' -) - -class PreferenceWindow(QWidget): - """Panel with most important preferences editable to user.""" - - config_change = pyqtSignal(unicode, unicode, int, bool) - - def __init__(self, parent, icon, config): - super(PreferenceWindow, self).__init__( - None, - Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint - | Qt.WindowTitleHint) - - self.setWindowIcon(icon) - - # Preferences data - self.wiki_name = config.get('site_name', - _(u'Enter the name of your wiki')) - self.wiki_page_path = config.get('pages_path', - _(u'Choose path for wiki pages')) - self.wiki_port = int(config.get('port', 8080)) - self.should_announce = bool(config.get_bool('announce', 1)) - # Set up GUI code - self.setWindowTitle(_(u'Hatta preferences')) - self.setWindowModality(Qt.WindowModal) - - self.main_vbox = QVBoxLayout(self) - grid_layout = QGridLayout() - # Wiki name input - tooltip = _(u'Sets the name of you wiki. The name will be visible ' - u'across the wiki and on discovery.') - self.name_label = QLabel(_(u'Wiki name:'), self) - self.name_label.setToolTip(tooltip) - self.name_edit = QLineEdit(self.wiki_name, self) - self.name_edit.setToolTip(tooltip) - grid_layout.addWidget(self.name_label, 0, 0) - grid_layout.addWidget(self.name_edit, 0, 1) - - # Pages dir choosing - tooltip = _(u'Sets the pages directory, where wiki pages will be ' - u'held.') - self.pages_label = QLabel(_(u'Pages path:'), self) - self.pages_label.setToolTip(tooltip) - pages_layout = QHBoxLayout() - self.pages_edit = QLineEdit(self.wiki_page_path, self) - self.pages_edit.setToolTip(tooltip) - self.pages_chooser = QPushButton(_(u'&Open...'), self) - self.pages_chooser.clicked.connect(self.choose_page_dir) - self.pages_chooser.setToolTip(tooltip) - pages_layout.addWidget(self.pages_edit) - pages_layout.addWidget(self.pages_chooser) - grid_layout.addWidget(self.pages_label, 1, 0) - grid_layout.addLayout(pages_layout, 1, 1) - - # Listen port selection port - min_port = 1 - max_port = 65535 - tooltip = _(u'Sets the port on which wiki is listening on.' - u' Valid ports: %(min_port)d to %(max_port)d.' - ) % dict(min_port=min_port, max_port=max_port) - self.port_label = QLabel(_(u'Listen port:')) - self.port_label.setToolTip(tooltip) - self.port_spin = QSpinBox(self) - self.port_spin.setRange(min_port, max_port) - self.port_spin.setValue(self.wiki_port) - self.port_spin.setToolTip(tooltip) - grid_layout.addWidget(self.port_label, 2, 0) - grid_layout.addWidget(self.port_spin, 2, 1) - - # Announcement switch - tooltip = _(u'Should wiki announce itself to the neighbourhood' - u' network?') - self.announce_checkbox = QCheckBox(_(u'Announce hatta')) - self.announce_checkbox.setToolTip(tooltip) - self.announce_checkbox.setChecked(self.should_announce) - if pybonjour is None: - self.announce_checkbox.setVisible(False) - grid_layout.addWidget(self.announce_checkbox, 3, 1) - - self.main_vbox.addLayout(grid_layout) - - # File choosing dialog for pages path - self.page_dir_dialog = QFileDialog(self, - _(u'Choose page directory')) - self.page_dir_dialog.setAcceptMode(QFileDialog.AcceptOpen) - self.page_dir_dialog.setFileMode(QFileDialog.Directory) - self.page_dir_dialog.setOption(QFileDialog.ShowDirsOnly, True) - self.page_dir_dialog.setDirectory(config.get('pages_path')) - - def choose_page_dir(self, clicked): - """Shows a directory choosing dialog and updates preferences text - field.""" - def set_pages_dir(): - self.pages_edit.setText( - self.page_dir_dialog.directory().absolutePath()) - self.page_dir_dialog.open(set_pages_dir) - - def closeEvent(self, event): - """Notify config change to main app.""" - self.hide() - self.wiki_name = self.name_edit.text() - self.wiki_page_path = self.pages_edit.text() - self.wiki_port = self.port_spin.value - self.should_announce = self.announce_checkbox.isChecked() - self.config_change.emit( - unicode(self.name_edit.text()), - unicode(self.pages_edit.text()), - int(self.port_spin.value()), - bool(self.should_announce)) - - -error_dialog = None -def report_bug(email, caption): - error_dialog.prepare_error(unicode(caption), format_exc()) - if error_dialog.exec_() == QDialog.Accepted: - link = 'mailto:%s?subject=%s&body=%s' % ( - email, - quote(u'[Bug] ' + unicode(caption)), - quote(error_dialog.get_bug_dump())) - webbrowser.open(link) - #QApplication.exit() - -if __name__ == '__main__': - try: - from locale import getlocale, getdefaultlocale, setlocale, LC_ALL - setlocale(LC_ALL, '') - lang = str(QLocale.system().name()).split('_')[0] - - localedir = os.path.join(module_path(), 'locale') - if not os.path.isdir(localedir): - # Already installed - localedir = os.path.join(module_path(), 'share', 'locale') - translation = gettext.translation('hatta', localedir, - languages=[lang], fallback=True) - translation.install(unicode=1) - - try: - app = QApplication(sys.argv) - QApplication.setQuitOnLastWindowClosed(False) - error_dialog = ErrorDialog(module_path()) - status_icon = HattaTrayIcon() - app.exec_() - except Exception as e: - report_bug('dhubleizh@o2.pl', unicode(e)) - except KeyboardInterrupt: - pass - diff --git a/resources/HattaError.ui b/resources/HattaError.ui deleted file mode 100644 index 44c8d04..0000000 --- a/resources/HattaError.ui +++ /dev/null @@ -1,243 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>ErrorDialog</class> - <widget class="QDialog" name="ErrorDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>400</width> - <height>302</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>400</width> - <height>300</height> - </size> - </property> - <property name="sizeIncrement"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>400</width> - <height>300</height> - </size> - </property> - <property name="windowTitle"> - <string>Error Report</string> - </property> - <property name="autoFillBackground"> - <bool>true</bool> - </property> - <property name="sizeGripEnabled"> - <bool>false</bool> - </property> - <property name="modal"> - <bool>true</bool> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <property name="spacing"> - <number>24</number> - </property> - <property name="sizeConstraint"> - <enum>QLayout::SetNoConstraint</enum> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0"> - <property name="spacing"> - <number>12</number> - </property> - <property name="sizeConstraint"> - <enum>QLayout::SetNoConstraint</enum> - </property> - <item> - <widget class="QLabel" name="error_icon"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>64</width> - <height>64</height> - </size> - </property> - <property name="baseSize"> - <size> - <width>64</width> - <height>64</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="scaledContents"> - <bool>false</bool> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> - </property> - </widget> - </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="spacing"> - <number>-1</number> - </property> - <item> - <widget class="QLabel" name="error_title"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:large; font-weight:600;">An error occured. Send a bug report?</span></p></body></html></string> - </property> - <property name="textFormat"> - <enum>Qt::RichText</enum> - </property> - <property name="scaledContents"> - <bool>false</bool> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - <property name="margin"> - <number>0</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="error_descriptions"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>We're sorry, but unfortunately Hatta has encountered some difficulties. Would you like to send us a bug report, so that we might fix the issue?</string> - </property> - <property name="scaledContents"> - <bool>true</bool> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - <property name="margin"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - <item> - <widget class="QPlainTextEdit" name="error_traceback"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>40</height> - </size> - </property> - <property name="undoRedoEnabled"> - <bool>false</bool> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - <property name="plainText"> - <string>Traceback here.</string> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QPushButton" name="details_button"> - <property name="text"> - <string>&Details</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - <resources> - <include location="icons.qrc"/> - </resources> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>ErrorDialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>243</x> - <y>273</y> - </hint> - <hint type="destinationlabel"> - <x>199</x> - <y>150</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>ErrorDialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>243</x> - <y>273</y> - </hint> - <hint type="destinationlabel"> - <x>199</x> - <y>150</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/resources/error.png b/resources/error.png Binary files differdeleted file mode 100644 index c343e8e..0000000 --- a/resources/error.png +++ /dev/null diff --git a/resources/hatta.desktop b/resources/hatta.desktop deleted file mode 100755 index 3daa3c5..0000000 --- a/resources/hatta.desktop +++ /dev/null @@ -1,13 +0,0 @@ - -[Desktop Entry] -Version=1.0 -Type=Application -Name=Hatta Wiki Engine -GenericName=Wiki Engine -Comment=Start Hatta wiki engine -Exec=hatta-icon.py -d ~/hatta -t /tmp/hatta -TryExec=hatta-icon.py -Icon=hatta -Terminal=false -Categories=Network; -StartupNotify=false diff --git a/resources/hatta.icns b/resources/hatta.icns Binary files differdeleted file mode 100755 index 91a233e..0000000 --- a/resources/hatta.icns +++ /dev/null diff --git a/resources/hatta.ico b/resources/hatta.ico Binary files differdeleted file mode 100755 index cbe4e81..0000000 --- a/resources/hatta.ico +++ /dev/null @@ -1,28 +1,27 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from distutils.core import setup +import setuptools -import sys -import os - -import hatta ################### Common settings ###################### -config = dict( - name=hatta.project_name, - version=hatta.__version__, - url=hatta.project_url, - download_url='http://download.hatta-wiki.org/hatta-%s/Hatta-%s.zip' % ( - hatta.__version__, hatta.__version__), +setuptools.setup( + name='hatta', + version='1.6.0devel', license='GNU General Public License (GPL)', author='Radomir Dopieralski', author_email='hatta@sheep.art.pl', - description=hatta.project_description, - long_description=hatta.__doc__, keywords='wiki wsgi web mercurial repository', packages=['hatta'], + install_requires=[ + 'distribute', + 'werkzeug >=0.3', + 'mercurial >=1.0', + 'jinja2', +# 'pygments', + ], + tests_require=['py.test'], data_files=[ ('share/locale/ar/LC_MESSAGES', ['locale/ar/LC_MESSAGES/hatta.mo']), ('share/locale/da/LC_MESSAGES', ['locale/da/LC_MESSAGES/hatta.mo']), @@ -32,10 +31,6 @@ config = dict( ('share/locale/ja/LC_MESSAGES', ['locale/ja/LC_MESSAGES/hatta.mo']), ('share/locale/pl/LC_MESSAGES', ['locale/pl/LC_MESSAGES/hatta.mo']), ('share/locale/sv/LC_MESSAGES', ['locale/sv/LC_MESSAGES/hatta.mo']), - ('share/icons/hicolor/scalable', ['resources/hatta.svg']), - ('share/icons/hicolor/64x64', ['resources/hatta.png', - 'resources/error.png']), - ('share/applications', ['resources/hatta.desktop']), ('share/doc/hatta/examples', [ 'examples/hatta.fcg', 'examples/hatta.wsgi', @@ -43,8 +38,8 @@ config = dict( ]), ], include_package_data=True, + zip_safe=True, platforms='any', - requires=['werkzeug (>=0.3)', 'mercurial (>=1.0)'], classifiers=[ 'License :: OSI Approved :: GNU General Public License (GPL)', 'Intended Audience :: Developers', @@ -57,239 +52,4 @@ config = dict( 'Operating System :: OS Independent', 'Environment :: Web Environment', ], - options={ - 'py2exe': { - 'includes': ['sip'], - 'packages': ['werkzeug', 'dbhash', 'encodings', - 'Image', 'pygments'], - 'excludes': ['_ssl', 'tcl', 'tkinter', 'Tkconstants', - 'Tkinter'], - 'dll_excludes': ['tcl84.dll', 'tk84.dll'], - "compressed": 1, - "optimize": 2, - "bundle_files": 1, - }, - 'py2app': { - 'argv_emulation': True, -# When packaging with MacPorts PyQt add to includes: -# PyQt4._qt -# See README-MAC - 'iconfile': 'resources/hatta.icns', - 'resources': ['hatta.py'], - 'includes': ['sip', 'PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui', - 'Image', 'pygments'], - 'excludes': ['PyQt4.QtDesigner', 'PyQt4.QtNetwork', - 'PyQt4.QtOpenGL', 'PyQt4.QtScript', - 'PyQt4.QtSql', 'PyQt4.QtTest', 'PyQt4.QtWebKit', - 'PyQt4.QtXml', 'PyQt4.phonon'], - }, - }, ) - -if sys.platform == 'darwin': - from setuptools import setup - config['setup_requires'].append('py2app') - config['app'] = ['hatta_qticon.py'] - - # Add deleting Qt debug libs (like QtCore_debug) to the py2app build - # command - from py2app.build_app import py2app as _py2app - class py2app(_py2app): - """py2app extensions to delete Qt debug libs.""" - # Add dmg option - _py2app.user_options.append( - ('no-dmg', None, - 'Do not build a dmg image from the bundle'), - ) - _py2app.boolean_options.append('no-dmg') - - def initialize_options(self): - _py2app.initialize_options(self) - self.no_dmg = False - - def run(self): - """Runs original py2app and deletes all files containing - 'debug' in their name. - """ - # First normal py2app run - _py2app.run(self) - - # Then remove debuging files - print '*** removing Qt debug libs ***' - for root, dirs, files in os.walk(self.dist_dir): - for file in files: - if 'debug' in file: - print 'removing', file - os.remove(os.path.join(root, file)) - - # And run macdeployqt to copy plugins and build a dmg - print '*** running macdeployqt ***' - macdeploy_cmd = 'macdeployqt %s.app' % (self.get_appname()) - if self.no_dmg is False: - macdeploy_cmd += ' -dmg' - # The cd-ing is needed, since macdeploy with -dmg will name it - # as it's first argument and we don't like dmg names like - # dist/Hatta.app - os.system('cd %s; ' % (self.dist_dir,) + macdeploy_cmd) - - config['cmdclass'] = {'py2app': py2app} -elif sys.platform == 'win32': - ### Windows installer ### - # Hack to make py2exe import win32com - # http://www.py2exe.org/index.cgi/WinShell - # ModuleFinder can't handle runtime changes to __path__, but win32com uses them - import time - try: - # if this doesn't work, try import modulefinder - import py2exe.mf as modulefinder - import win32com - for p in win32com.__path__[1:]: - modulefinder.AddPackagePath("win32com", p) - for extra in ["win32com.shell"]: #,"win32com.mapi" - __import__(extra) - m = sys.modules[extra] - for p in m.__path__[1:]: - modulefinder.AddPackagePath(extra, p) - except ImportError: - # no build path setup, no worries. - pass - import py2exe - - class InnoScript(object): - def __init__(self, name, lib_dir, dist_dir, windows_exe_files = [], - lib_files = [], description = "", version = "1.0"): - self.lib_dir = lib_dir - self.dist_dir = dist_dir - if not self.dist_dir[-1] in "\\/": - self.dist_dir += "\\" - self.name = name.capitalize() - self.version = version - self.description = description - self.windows_exe_files = [self.chop(p) for p in windows_exe_files] - self.lib_files = [self.chop(p) for p in lib_files - if p.startswith(self.dist_dir)] - - def chop(self, pathname): - return pathname[len(self.dist_dir):] - - def create(self, pathname="dist\\hatta.iss"): - self.pathname = pathname - ofi = self.file = open(pathname, "w") - print >> ofi, "; WARNING: This script has been created by py2exe. Changes to this script" - print >> ofi, "; will be overwritten the next time py2exe is run!" - print >> ofi, r"[Setup]" - print >> ofi, r"AppName=%s" % self.name - print >> ofi, r"AppVerName=%s %s" % (self.name, self.version) - print >> ofi, r"DefaultDirName={pf}\%s" % self.name - print >> ofi, r"DefaultGroupName=%s" % self.name - print >> ofi, r"InternalCompressLevel=ultra64" - print >> ofi, r"VersionInfoVersion=%s" % '.'.join(self.version.split('.')[:2]) - print >> ofi, r"VersionInfoDescription=%s" % self.description - print >> ofi, r"OutputBaseFilename=%s" % self.name + 'Setup' - print >> ofi - - print >> ofi, r"[Files]" - for path in self.windows_exe_files + self.lib_files: - print >> ofi, r'Source: "%s"; DestDir: "{app}\%s"; Flags: ignoreversion' % (path, os.path.dirname(path)) - print >> ofi - - print >> ofi, r"[Icons]" - for path in self.windows_exe_files: - print >> ofi, r'Name: "{group}\%s"; Filename: "{app}\%s"' % \ - (self.name, path) - print >> ofi, r'Name: "{group}\Uninstall %s"; Filename: "{uninstallexe}"' % self.name - print >> ofi - - print >> ofi, r"[UninstallDelete]" - print >> ofi, r"Name: {app}; Type: filesandordirs" - - def compile(self): - try: - import ctypes - except ImportError: - try: - import win32api - except ImportError: - import os - os.startfile(self.pathname) - else: - print "Ok, using win32api." - win32api.ShellExecute(0, "compile", self.pathname, None, None, 0) - else: - print "Cool, you have ctypes installed." - res = ctypes.windll.shell32.ShellExecuteA(0, "compile", self.pathname, None, None, 0) - if res < 32: - raise RuntimeError, "ShellExecute failed, error %d" % res - # class InnnoScript - - class BuildInstaller(py2exe.build_exe.py2exe): - """ - This class first builds the exe file(s), - then creates a Windows installer. - You need InnoSetup for it. - """ - def run(self): - # First, let py2exe do it's work. - py2exe.build_exe.py2exe.run(self) - lib_dir = self.lib_dir - dist_dir = self.dist_dir - version = hatta.__version__ - if version.endswith('dev'): - from datetime import datetime - version = version[:-3] + '.' + datetime.now().strftime('%Y%m%d%H%M') - - # create the Installer, using the files py2exe has created. - script = InnoScript("hatta", lib_dir, dist_dir, - self.console_exe_files+self.windows_exe_files, - self.lib_files, - hatta.project_description, version) - print "*** creating the inno setup script***" - script.create(os.path.join(self.dist_dir, hatta.project_name + '.iss')) - print "*** compiling the inno setup script***" - script.compile() - # Note: By default the final setup.exe will be in an - # Output subdirectory. - # class BuildInstaller - - config['zipfile'] = None - config['cmdclass'] = {"py2exe": BuildInstaller} - config['windows'] = [{ - 'script': 'hatta_qticon.py', - 'icon_resources': [(1, "resources/hatta.ico")], - }] - - # Adding MS runtime C libraries - if float(sys.version.split(' ', 1)[0]) >= 2.6: - from win32com.shell import shellcon, shell - from glob import glob - windir = shell.SHGetFolderPath(0, shellcon.CSIDL_WINDOWS, 0, 0) - dlldir = glob(os.path.join(windir, u'WinSxS', '*Microsoft.VC90.CRT*'))[0] - dlls = glob(os.path.join(dlldir, '*.dll')) - dest_dir = 'Microsoft.VC90.CRT' - from tempfile import gettempdir - from shutil import copy - manifest = os.path.join(gettempdir(), 'Microsoft.VC90.CRT.manifest') - copy(glob(os.path.join( - windir, 'WinSxS', 'Manifests', '*VC90.CRT*manifest'))[0], - manifest) - config['data_files'].extend([ - (dest_dir, glob(os.path.join(dlldir, '*.dll'))), - (dest_dir, [manifest]), - ]) - else: - # For Python < 2.6 we don't need separate dir, - # so let's leave the work to py2app - origIsSystemDLL = py2exe.build_exe.isSystemDLL - def isSystemDLL(pathname): - if 'msvc' in os.path.basename(pathname).lower(): - return 0 - elif 'dwmapi' in os.path.basename(pathname).lower(): - return 0 - return origIsSystemDLL(pathname) - py2exe.build_exe.isSystemDLL = isSystemDLL - -else: # Other UNIX-like - config['scripts'] = ['hatta_qticon.py', 'hatta_gtkicon.py'] - -if __name__ == '__main__': - setup(**config) diff --git a/ui_errorDialog.py b/ui_errorDialog.py deleted file mode 100644 index 2465648..0000000 --- a/ui_errorDialog.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'resources/HattaError.ui' -# -# Created: Fri Jul 16 19:56:19 2010 -# by: PyQt4 UI code generator 4.7.3 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui - -class Ui_ErrorDialog(object): - def setupUi(self, ErrorDialog): - ErrorDialog.setObjectName("ErrorDialog") - ErrorDialog.resize(400, 302) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(ErrorDialog.sizePolicy().hasHeightForWidth()) - ErrorDialog.setSizePolicy(sizePolicy) - ErrorDialog.setMinimumSize(QtCore.QSize(400, 300)) - ErrorDialog.setSizeIncrement(QtCore.QSize(0, 0)) - ErrorDialog.setBaseSize(QtCore.QSize(400, 300)) - ErrorDialog.setAutoFillBackground(True) - ErrorDialog.setSizeGripEnabled(False) - ErrorDialog.setModal(True) - self.verticalLayout_2 = QtGui.QVBoxLayout(ErrorDialog) - self.verticalLayout_2.setSpacing(24) - self.verticalLayout_2.setSizeConstraint(QtGui.QLayout.SetNoConstraint) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setSpacing(12) - self.horizontalLayout_2.setSizeConstraint(QtGui.QLayout.SetNoConstraint) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.error_icon = QtGui.QLabel(ErrorDialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.error_icon.sizePolicy().hasHeightForWidth()) - self.error_icon.setSizePolicy(sizePolicy) - self.error_icon.setMinimumSize(QtCore.QSize(64, 64)) - self.error_icon.setBaseSize(QtCore.QSize(64, 64)) - self.error_icon.setText("") - self.error_icon.setScaledContents(False) - self.error_icon.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.error_icon.setObjectName("error_icon") - self.horizontalLayout_2.addWidget(self.error_icon) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setSpacing(-1) - self.verticalLayout.setObjectName("verticalLayout") - self.error_title = QtGui.QLabel(ErrorDialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.error_title.sizePolicy().hasHeightForWidth()) - self.error_title.setSizePolicy(sizePolicy) - self.error_title.setTextFormat(QtCore.Qt.RichText) - self.error_title.setScaledContents(False) - self.error_title.setWordWrap(True) - self.error_title.setMargin(0) - self.error_title.setObjectName("error_title") - self.verticalLayout.addWidget(self.error_title) - self.error_descriptions = QtGui.QLabel(ErrorDialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.error_descriptions.sizePolicy().hasHeightForWidth()) - self.error_descriptions.setSizePolicy(sizePolicy) - self.error_descriptions.setScaledContents(True) - self.error_descriptions.setWordWrap(True) - self.error_descriptions.setMargin(0) - self.error_descriptions.setObjectName("error_descriptions") - self.verticalLayout.addWidget(self.error_descriptions) - self.horizontalLayout_2.addLayout(self.verticalLayout) - self.horizontalLayout_2.setStretch(0, 1) - self.verticalLayout_2.addLayout(self.horizontalLayout_2) - self.error_traceback = QtGui.QPlainTextEdit(ErrorDialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.error_traceback.sizePolicy().hasHeightForWidth()) - self.error_traceback.setSizePolicy(sizePolicy) - self.error_traceback.setMinimumSize(QtCore.QSize(0, 40)) - self.error_traceback.setUndoRedoEnabled(False) - self.error_traceback.setReadOnly(True) - self.error_traceback.setObjectName("error_traceback") - self.verticalLayout_2.addWidget(self.error_traceback) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.details_button = QtGui.QPushButton(ErrorDialog) - self.details_button.setCheckable(True) - self.details_button.setChecked(False) - self.details_button.setObjectName("details_button") - self.horizontalLayout.addWidget(self.details_button) - self.buttonBox = QtGui.QDialogButtonBox(ErrorDialog) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.horizontalLayout.addWidget(self.buttonBox) - self.verticalLayout_2.addLayout(self.horizontalLayout) - - self.retranslateUi(ErrorDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), ErrorDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), ErrorDialog.reject) - QtCore.QMetaObject.connectSlotsByName(ErrorDialog) - - def retranslateUi(self, ErrorDialog): - ErrorDialog.setWindowTitle(QtGui.QApplication.translate("ErrorDialog", "Error Report", None, QtGui.QApplication.UnicodeUTF8)) - self.error_title.setText(QtGui.QApplication.translate("ErrorDialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" -"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" -"p, li { white-space: pre-wrap; }\n" -"</style></head><body style=\" font-family:\'Lucida Grande\'; font-size:13pt; font-weight:400; font-style:normal;\">\n" -"<p style=\" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:large; font-weight:600;\">An error occured. Send a bug report?</span></p></body></html>", None, QtGui.QApplication.UnicodeUTF8)) - self.error_descriptions.setText(QtGui.QApplication.translate("ErrorDialog", "We\'re sorry, but unfortunately Hatta has encountered some difficulties. Would you like to send us a bug report, so that we might fix the issue?", None, QtGui.QApplication.UnicodeUTF8)) - self.error_traceback.setPlainText(QtGui.QApplication.translate("ErrorDialog", "Traceback here.", None, QtGui.QApplication.UnicodeUTF8)) - self.details_button.setText(QtGui.QApplication.translate("ErrorDialog", "&Details", None, QtGui.QApplication.UnicodeUTF8)) |