Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRadomir 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)
commitf5fb88ffd4c9c99463d7209669175106d2a6bd27 (patch)
treee21a6d5fdcf382c6c910cdde04ea08529b2c9562
parentfb8402416007dcfba7dc6a955088e8e498108405 (diff)
Move the hatta-icon code and resources to a separate project.
-rwxr-xr-x.hgignore5
-rwxr-xr-xMANIFEST.in8
-rwxr-xr-xerror_dialog.py93
-rwxr-xr-xhatta_gtkicon.py240
-rwxr-xr-xhatta_qticon.py621
-rw-r--r--resources/HattaError.ui243
-rw-r--r--resources/error.pngbin6216 -> 0 bytes
-rwxr-xr-xresources/hatta.desktop13
-rwxr-xr-xresources/hatta.icnsbin224602 -> 0 bytes
-rwxr-xr-xresources/hatta.icobin12862 -> 0 bytes
-rwxr-xr-xsetup.py266
-rw-r--r--ui_errorDialog.py115
12 files changed, 19 insertions, 1585 deletions
diff --git a/.hgignore b/.hgignore
index 83df683..3d05baa 100755
--- a/.hgignore
+++ b/.hgignore
@@ -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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
-p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:large; font-weight:600;&quot;&gt;An error occured. Send a bug report?&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&amp;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
deleted file mode 100644
index c343e8e..0000000
--- a/resources/error.png
+++ /dev/null
Binary files differ
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
deleted file mode 100755
index 91a233e..0000000
--- a/resources/hatta.icns
+++ /dev/null
Binary files differ
diff --git a/resources/hatta.ico b/resources/hatta.ico
deleted file mode 100755
index cbe4e81..0000000
--- a/resources/hatta.ico
+++ /dev/null
Binary files differ
diff --git a/setup.py b/setup.py
index 4b41631..0927b12 100755
--- a/setup.py
+++ b/setup.py
@@ -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))