Permalink
Browse files

initial commit

  • Loading branch information...
MicahCarrick committed Sep 6, 2011
0 parents commit 48d6aa03e061f41942e77182102ae37bb910228a
@@ -0,0 +1,2 @@
+*.pyc
+
No changes.
@@ -0,0 +1,11 @@
+[Plugin]
+Loader=python
+Module=djangoproject
+IAge=3
+Name=Django Project
+Description=Manage Django projects from Gedit.
+Authors=Micah Carrick <micah@quixotix.com>
+Copyright=Copyright © 2011 Micah Carrick
+Website=https://github.com/Quixotix/gedit-django-project
+Version=3.0.0
+
@@ -0,0 +1,2 @@
+from plugin import Plugin
+
@@ -0,0 +1,18 @@
+<ui>
+<menubar name="MenuBar">
+ <placeholder name="ExtraMenu_1">
+ <menu name="DjangoMenu" action="Django">
+ <menuitem action="NewProject"/>
+ <menuitem action="OpenProject"/>
+ <menuitem action="CloseProject"/>
+ <separator/>
+ <!-- <menuitem action="NewApp"/> -->
+ <separator/>
+ <menu action="Manage">
+ <menuitem action="RunServer"/>
+ <!-- <menuitem action="SyncDb" always-show-image="true"/> -->
+ </menu>
+ </menu>
+ </placeholder>
+</menubar>
+</ui>
No changes.
@@ -0,0 +1,160 @@
+import os
+import logging
+from gi.repository import GObject, Gtk, Gedit
+from project import DjangoProject
+from server import DjangoServer
+
+logging.basicConfig()
+LOG_LEVEL = logging.DEBUG
+logger = logging.getLogger(__name__)
+logger.setLevel(LOG_LEVEL)
+
+DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
+
+class Plugin(GObject.Object, Gedit.WindowActivatable):
+ __gtype_name__ = "GeditDjangoProjectPlugin"
+ window = GObject.property(type=Gedit.Window)
+
+ def __init__(self):
+ GObject.Object.__init__(self)
+ self._server = None
+
+ def _add_server_panel(self, cwd):
+ """ Adds a terminal panel to the bottom pane for development server. """
+ logger.debug("Adding server panel.")
+ self._server = DjangoServer()
+ self._server.cwd = cwd
+ self._server.connect("server-started", self.on_server_started)
+ self._server.connect("server-stopped", self.on_server_stopped)
+ panel = self.window.get_bottom_panel()
+ panel.add_item_with_stock_icon(self._server, "DjangoServer", "Django Server", Gtk.STOCK_NETWORK)
+
+ def _add_ui(self):
+ """ Merge the 'Django' menu into the Gedit menubar. """
+ ui_file = os.path.join(DATA_DIR, 'menu.ui')
+ manager = self.window.get_ui_manager()
+
+ # global actions are always sensitive
+ self._global_actions = Gtk.ActionGroup("DjangoGlobal")
+ self._global_actions.add_actions([
+ ('Django', None, "_Django", None, None, None),
+ ('NewProject', Gtk.STOCK_NEW, "_New Project",
+ "<Shift><Control>N", "Start a new Django project.",
+ self.on_new_project_activate),
+ ('OpenProject', Gtk.STOCK_OPEN, "_Open Project",
+ "<Shift><Control>O", "Open an existing Django project.",
+ self.on_open_project_activate),
+ ])
+ manager.insert_action_group(self._global_actions)
+
+ # project actions are sensitive when a project is open
+ self._project_actions = Gtk.ActionGroup("DjangoProject")
+ self._project_actions.add_actions([
+ ('CloseProject', Gtk.STOCK_CLOSE, "_Close Project...",
+ "<Shift><Control>W", "Close the current Django project.",
+ self.on_close_project_activate),
+ ('Manage', None, "_Manage", None, None, None),
+ #('SyncDb', gtk.STOCK_REFRESH, "_Synchronize Database",
+ # None, "Synchronize database", self.on_manage_syncdb_activate),
+ ])
+ self._project_actions.add_toggle_actions([
+ ('RunServer', Gtk.STOCK_CONNECT, "_Run Development Server",
+ None, "Start/Stop the Django development server.",
+ self.on_manage_runserver_activate, False),
+ ])
+ self._project_actions.set_sensitive(False)
+ manager.insert_action_group(self._project_actions)
+
+ self._ui_merge_id = manager.add_ui_from_file(ui_file)
+ manager.ensure_update()
+
+ def do_activate(self):
+ logger.debug("Activating plugin.")
+ self._add_ui()
+ # TEMPORARY
+ self.open_project("/home/micah/Documents/Quixotix/projects/quix.django.comments/demo")
+
+ def do_deactivate(self):
+ logger.debug("Deactivating plugin.")
+ self._remove_ui()
+ self._remove_server_panel()
+
+ def do_update_state(self):
+ pass
+
+ def error_dialog(self, message):
+ """ Display a very basic error dialog. """
+ logger.warn(message)
+ dialog = Gtk.MessageDialog(self.window,
+ Gtk.DialogFlags.MODAL |
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
+ message)
+ dialog.set_title("Error")
+ dialog.run()
+ dialog.destroy()
+
+ def on_close_project_activate(self, action, data=None):
+ pass
+
+ def on_manage_runserver_activate(self, action, data=None):
+ try:
+ if not action.get_active() and self._server.is_running():
+ self._server.stop()
+ elif action.get_active() and not self._server.is_running():
+ self._server.start()
+ except Exception as e:
+ self.error_dialog(str(e))
+ return
+
+ def on_new_project_activate(self, action, data=None):
+ pass
+
+ def on_open_project_activate(self, action, data=None):
+ """ Prompt the user for the Django project directory. """
+ path = None
+ dialog = Gtk.FileChooserDialog("Select project folder...", self.window,
+ Gtk.FileChooserAction.SELECT_FOLDER,
+ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ path = dialog.get_filename()
+ dialog.destroy()
+ if path:
+ self.open_project(path)
+
+ def on_server_started(self, server, pid, data=None):
+ self._project_actions.get_action("RunServer").set_active(True)
+
+ def on_server_stopped(self, server, pid, data=None):
+ self._project_actions.get_action("RunServer").set_active(False)
+
+ def open_project(self, path):
+ logger.debug("Opening Django project: %s" % path)
+ try:
+ self._project = DjangoProject(path)
+ except IOError as e:
+ self.error_dialog("Could not open project: %s" % str(e))
+ return
+ self._add_server_panel(self._project.get_path())
+ self._project_actions.set_sensitive(True)
+
+ def _remove_server_panel(self):
+ """ Stop and remove development server panel from the bottom panel. """
+ if self._server:
+ logger.debug("Removing server panel.")
+ self._server.stop()
+ panel = self.window.get_bottom_panel()
+ panel.remove_item(self._server)
+ self._server = None
+
+ def _remove_ui(self):
+ """ Remove the 'Django' menu from the the Gedit menubar. """
+ manager = self.window.get_ui_manager()
+ manager.remove_ui(self._ui_merge_id)
+ manager.remove_action_group(self._global_actions)
+ manager.remove_action_group(self._project_actions)
+ manager.ensure_update()
+
+
@@ -0,0 +1,33 @@
+import os
+
+class DjangoProject(object):
+
+ def __init__(self, path):
+ self.set_path(path)
+
+ def get_path(self):
+ """
+ Return the path to the django project (where settings.py and manage.py
+ are found).
+ """
+ return self._path
+
+ def set_path(self, path):
+ """
+ Set Path
+
+ Set the full filesystem path to where the Django project files are stored
+ or raise IOError if the path does not exist or if settings.py or manage.py
+ cannot be found in the path.
+ """
+ if not os.path.exists(path):
+ raise IOError("Django project directory does not exist: %s" % path)
+ settings = os.path.join(path, 'settings.py')
+ manage = os.path.join(path, 'manage.py')
+ if not os.path.isfile(settings):
+ raise IOError("Django settings file does not exist: %s" % settings)
+ if not os.path.isfile(manage):
+ raise IOError("Django manage file does not exist: %s" % manage)
+ self._path = path
+ self._settings = settings
+ self._manage = manage
@@ -0,0 +1,104 @@
+import os
+import signal
+import subprocess
+import shlex
+import logging
+from gi.repository import GObject, Gtk, Vte, GLib
+
+logging.basicConfig()
+LOG_LEVEL = logging.DEBUG
+logger = logging.getLogger(__name__)
+logger.setLevel(LOG_LEVEL)
+
+class DjangoServer(Gtk.HBox):
+ """
+ A terminal widget setup to run the Django development server management
+ command providing Start/Stop button.
+
+ Start and stop the server by calling start() and stop() methods.
+
+ Connect to the "server-started" and "server-stopped" signals to update UI as
+ the server may stop for any number of reasons, including errors in Django
+ code, pressing <CTRL+C>, or the stop button on the widget.
+ """
+ __gtype_name__ = "DjangoServer"
+ __gsignals__ = {
+ "server-started":
+ (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE,
+ (GObject.TYPE_PYOBJECT,)),
+ "server-stopped":
+ (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE,
+ (GObject.TYPE_PYOBJECT,)),
+ }
+
+ def __init__(self):
+ Gtk.HBox.__init__(self, homogeneous=False, spacing=0)
+ self.command = "python manage.py runserver"
+ self.cwd = ''
+ self._pid = None
+ self._vte = Vte.Terminal()
+ self._vte.set_size(self._vte.get_column_count(), 5)
+ self._vte.set_size_request(200, 50)
+ self._vte.set_font_from_string("monospace 10")
+ self._vte.connect("child-exited", self.on_child_exited)
+ self.pack_start(self._vte, True, True, 0)
+ scrollbar = Gtk.Scrollbar.new(Gtk.Orientation.VERTICAL, self._vte.get_vadjustment())
+ self.pack_start(scrollbar, False, False, 0)
+ self._button = Gtk.Button()
+ self._button.connect("clicked", self.on_button_clicked)
+ box = Gtk.VButtonBox()
+ box.set_border_width(5)
+ box.set_layout(Gtk.ButtonBoxStyle.START)
+ box.add(self._button)
+ self.pack_start(box, False, False, 0)
+ self._start_icon = Gtk.Image.new_from_stock(Gtk.STOCK_EXECUTE, Gtk.IconSize.BUTTON)
+ self._stop_icon = Gtk.Image.new_from_stock(Gtk.STOCK_STOP, Gtk.IconSize.BUTTON)
+ self._update_button()
+ self.show_all()
+
+ def is_running(self):
+ if self._pid is not None:
+ return True
+ else:
+ return False
+
+ def on_button_clicked(self, widget=None, data=None):
+ if self.is_running():
+ self.stop()
+ else:
+ self.start()
+
+ def on_child_exited(self, vte, data=None):
+ pid = self._pid
+ self._pid = None
+ self._update_button()
+ self.emit("server-stopped", pid)
+ logger.debug("Development server stopped (pid %s)" % pid)
+
+ def start(self):
+ if self.is_running():
+ return
+ args = shlex.split(self.command)
+ self._pid = self._vte.fork_command_full(Vte.PtyFlags.DEFAULT,
+ self.cwd,
+ args,
+ None,
+ GLib.SpawnFlags.SEARCH_PATH,
+ None,
+ None)[1]
+ self._update_button()
+ self.emit("server-started", self._pid)
+ logger.debug("Development server started (pid %s)" % self._pid)
+
+ def stop(self):
+ if self.is_running():
+ os.kill(self._pid, signal.SIGKILL)
+
+ def _update_button(self):
+ if self.is_running():
+ self._button.set_image(self._stop_icon)
+ self._button.set_label("Stop")
+ else:
+ self._button.set_image(self._start_icon)
+ self._button.set_label("Start")
+

0 comments on commit 48d6aa0

Please sign in to comment.