From 824906c9bf883a3a1c3e9ae45d4abf26e9a26673 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Sun, 31 Jul 2011 15:43:08 +0100 Subject: [PATCH 001/141] covers my id and evt name issues, moved twisted stuff into wx.App, sperated out the events from the gui views so they can be tested and changed individually still need to sort out the config --- pythonforumide/default.cfg | 4 - pythonforumide/gui_lib/__init__.py | 0 pythonforumide/gui_lib/default.cfg | 4 + pythonforumide/gui_lib/ide_constant.py | 81 +++++++ pythonforumide/gui_lib/ide_editor.py | 201 ++++++++++++++++++ pythonforumide/gui_lib/ide_mainframe.py | 103 +++++++++ .../gui_lib/ide_mainframe_events.py | 116 ++++++++++ pythonforumide/gui_lib/ide_menu.py | 122 +++++++++++ pythonforumide/gui_lib/ide_notebook.py | 180 ++++++++++++++++ pythonforumide/gui_lib/ide_notebook_events.py | 34 +++ pythonforumide/gui_lib/ide_test_frame.py | 36 ++++ pythonforumide/gui_lib/ide_toolbar.py | 54 +++++ pythonforumide/ide_images/__init__.py | 0 pythonforumide/ide_images/menu_icons.py | 91 ++++++++ pythonforumide/wx_app.py | 78 +++++++ 15 files changed, 1100 insertions(+), 4 deletions(-) create mode 100644 pythonforumide/gui_lib/__init__.py create mode 100644 pythonforumide/gui_lib/default.cfg create mode 100644 pythonforumide/gui_lib/ide_constant.py create mode 100644 pythonforumide/gui_lib/ide_editor.py create mode 100644 pythonforumide/gui_lib/ide_mainframe.py create mode 100644 pythonforumide/gui_lib/ide_mainframe_events.py create mode 100644 pythonforumide/gui_lib/ide_menu.py create mode 100644 pythonforumide/gui_lib/ide_notebook.py create mode 100644 pythonforumide/gui_lib/ide_notebook_events.py create mode 100644 pythonforumide/gui_lib/ide_test_frame.py create mode 100644 pythonforumide/gui_lib/ide_toolbar.py create mode 100644 pythonforumide/ide_images/__init__.py create mode 100644 pythonforumide/ide_images/menu_icons.py create mode 100644 pythonforumide/wx_app.py diff --git a/pythonforumide/default.cfg b/pythonforumide/default.cfg index f0a232f..e69de29 100644 --- a/pythonforumide/default.cfg +++ b/pythonforumide/default.cfg @@ -1,4 +0,0 @@ -[ide] -indent = 4 -usetab = 0 - diff --git a/pythonforumide/gui_lib/__init__.py b/pythonforumide/gui_lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pythonforumide/gui_lib/default.cfg b/pythonforumide/gui_lib/default.cfg new file mode 100644 index 0000000..f0a232f --- /dev/null +++ b/pythonforumide/gui_lib/default.cfg @@ -0,0 +1,4 @@ +[ide] +indent = 4 +usetab = 0 + diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py new file mode 100644 index 0000000..e2acbf5 --- /dev/null +++ b/pythonforumide/gui_lib/ide_constant.py @@ -0,0 +1,81 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx +from collections import namedtuple as _nt + +_id_text= _nt("id_text", "menu toolbar status menu_kind") + +#=============================================================================== +# File ID +#=============================================================================== +ID_NEW= wx.NewId() +id_text_new= _id_text("New\tCtrl+N", "New", + "Create a new file", wx.ITEM_NORMAL) + +ID_OPEN= wx.NewId() +id_text_open= _id_text("Open\tCtrl+O", "Open", + "Open an existing file", wx.ITEM_NORMAL) + +ID_SAVE= wx.NewId() +id_text_save= _id_text("Save\tCtrl+S", "Save", + "Save the current file", wx.ITEM_NORMAL) + +ID_SAVEAS= wx.NewId() +id_text_saveas= _id_text("Save as", "Save as", + "Save as a new filename", wx.ITEM_NORMAL) + +ID_CLOSETAB= wx.NewId() +id_text_closetab= _id_text("Close tab\tCtrl+W", "Close tab", + "Close current tab", wx.ITEM_NORMAL) + +ID_EXITAPP= wx.NewId() +id_text_exitapp= _id_text("Exit\tCtrl+Q", "Exit", + "Exit the application", wx.ITEM_NORMAL) +#=============================================================================== +# Edit ID +#=============================================================================== +ID_UNDO= wx.NewId() +id_text_undo= _id_text("Undo\tCtrl+Z", "Undo", + "Undo changes", wx.ITEM_NORMAL) + +ID_REDO= wx.NewId() +id_text_redo= _id_text("Redo\tCtrl+Y", "Redo", + "Redo Changes", wx.ITEM_NORMAL) + +ID_CUT= wx.NewId() +id_text_cut= _id_text("Cut\tCtrl+X", "Cut", + "Cut the selected text", wx.ITEM_NORMAL) + +ID_COPY= wx.NewId() +id_text_copy= _id_text("Copy\tCtrl+C", "Copy", + "Copy the selected text", wx.ITEM_NORMAL) + +ID_PASTE= wx.NewId() +id_text_paste= _id_text("Paste\tCtrl+V", "Paste", + "Paste from clipboard", wx.ITEM_NORMAL) + +ID_DELETE= wx.NewId() +id_text_delete= _id_text("Delete", "Delete", + "Delete the selected text", wx.ITEM_NORMAL) + +ID_SELECTALL= wx.NewId() +id_text_selectall= _id_text("Select All\tCtrl+A", + "Select all", "Select all", wx.ITEM_NORMAL) +#=============================================================================== +# Search ID +#=============================================================================== +ID_SEARCH= wx.NewId() +id_text_search= _id_text("Search\tCtrl+H", "Search", + "Search/replace the active file", wx.ITEM_NORMAL) +#=============================================================================== +# Run ID +#=============================================================================== +ID_RUNFILE=wx.NewId() +id_text_runfile= _id_text("Run file\tF5", "Run", + "Run the active file", wx.ITEM_NORMAL) + + diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py new file mode 100644 index 0000000..6201a7d --- /dev/null +++ b/pythonforumide/gui_lib/ide_editor.py @@ -0,0 +1,201 @@ +""" +@author: Jakob, David, bunburya +@reviewer: Somelauw +""" + +#YES DIRT HACK GET OVER IT. Dont remove it might go before it goes into master +import sys +sys.path.append('..') + +from utils.textutils import split_comments +import wx.stc as stc +import wx +from config import config + +#TODO: make customisable font and sizes. Perhaps maked this named tuple? +faces = { 'times': 'Times', + 'mono' : 'Courier', + 'helv' : 'Helvetica', + 'other': 'new century schoolbook', + 'size' : 12, + 'size2': 10, + } + +class Editor(stc.StyledTextCtrl): + def __init__(self, parent): + super(Editor, self).__init__(parent) + + self.conf= config.conf + + self.filepath = '' + self.indent_level = 0 + + self.SetGenerics() + self.SetMargins() + self.SetStyles() + self.SetBindings() + + def SetBindings(self): + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + def SetGenerics(self): + """Rather than do it in the __init__ and to help debugging the styles + and settings are split into seperate SetOptions, this sets the generic + options like Tabwidth, expandtab and indentation guides + others.""" + self.SetLexer(stc.STC_LEX_PYTHON) #is this giving us trouble? + self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) #set mono spacing here! + self.SetTabWidth(4) + self.SetIndentationGuides(1) + #Indentation will only use space characters if useTabs is false + self.SetUseTabs(False) + + def SetMargins(self): + """This is specifically for the margins. Like the other Set methods it + is only really to be called in the __init__ its here more for + readability purpsoses than anything else.""" + # margin 0 for breakpoints + self.SetMarginSensitive(0, True) + self.SetMarginType(0, wx.stc.STC_MARGIN_SYMBOL) + self.SetMarginMask(0, 0x3) + self.SetMarginWidth(0, 12) + # margin 1 for current line arrow + self.SetMarginSensitive(1, False) + self.SetMarginMask(1, 0x4) + # margin 2 for line numbers + self.SetMarginType(2, stc.STC_MARGIN_NUMBER) + self.SetMarginWidth(2, 28) + + def SetStyles(self, lang='python'): + """This is different from the other Set methods thathttp://paste.pocoo.org/show/446107/ are called in the + __init__ this one is for the highlighting and syntax of the langauge, + this will eventually be callable with different langauge styles. + For the moment, leave the lang kwarg in. """ + + #INDICATOR STYLES FOR ERRORS (self.errorMark) + self.IndicatorSetStyle(2, stc.STC_INDIC_SQUIGGLE) + self.IndicatorSetForeground(2, wx.RED) + self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) + + # Python styles + + # White space + self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) + # Comment + self.StyleSetSpec(stc.STC_P_COMMENTLINE, "face:%(mono)s,fore:#007F00,back:#E8FFE8,italic,size:%(size)d" % faces) + # Number + self.StyleSetSpec(stc.STC_P_NUMBER, "face:%(mono)s,fore:#007F7F,size:%(size)d" % faces) + # String + self.StyleSetSpec(stc.STC_P_STRING, "face:%(mono)s,fore:#7F007F,size:%(size)d" % faces) + # Single quoted string + self.StyleSetSpec(stc.STC_P_CHARACTER, "face:%(mono)s,fore:#7F007F,size:%(size)d" % faces) + # Keyword + self.StyleSetSpec(stc.STC_P_WORD, "face:%(mono)s,fore:#00007F,bold,size:%(size)d" % faces) + # Triple quotes + self.StyleSetSpec(stc.STC_P_TRIPLE, "face:%(mono)s,fore:#7F0000,size:%(size)d" % faces) + # Triple double quotes + self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "face:%(mono)s,fore:#7F0000,size:%(size)d" % faces) + # Class name definition + self.StyleSetSpec(stc.STC_P_CLASSNAME, "face:%(mono)s,fore:#0000FF,bold,underline,size:%(size)d" % faces) + # Function or method name definition + self.StyleSetSpec(stc.STC_P_DEFNAME, "face:%(mono)s,fore:#007F7F,bold,size:%(size)d" % faces) + # Operators + self.StyleSetSpec(stc.STC_P_OPERATOR, "face:%(mono)s,bold,size:%(size)d" % faces) + # Identifiers + self.StyleSetSpec(stc.STC_P_IDENTIFIER, "") + # Comment-blocks + self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "face:%(mono)s,fore:#990000,back:#C0C0C0,italic,size:%(size)d" % faces) + # End of line where string is not closed + self.StyleSetSpec(stc.STC_P_STRINGEOL, "face:%(mono)s,fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) + + def SmartIndent(self): + # Read settings from the config file + indent_amount = int(self.conf["indent"]) + usetab = int(self.conf["usetab"]) + + last_line_no = self.GetCurrentLine() + last_line = split_comments(self.GetLine(last_line_no))[0] + self.NewLine() + indent_level = self.GetLineIndentation(last_line_no) // indent_amount + + if last_line.rstrip().endswith(':'): + indent_level += 1 + elif any(last_line.lstrip().startswith(token) + for token in ["return", "break", "yield"]): + indent_level = max([indent_level - 1, 0]) + + if usetab: + indent = "/t" * indent_level + else: + indent = indent_amount * " " * indent_level + + self.AddText(indent) + print self.conf + + def OnKeyDown(self, event): + key = event.GetKeyCode() + control = event.ControlDown() + alt = event.AltDown() + if key == wx.WXK_RETURN and not control and not alt: + self.SmartIndent() + else: + event.Skip() + + def on_undo(self): + """Checks if can Undo and if yes undoes""" + if self.CanUndo() == 1: + self.Undo() + + def on_redo(self): + """Checks if can Redo and if yes redoes""" + if self.CanRedo() == 1: + self.Redo() + + def on_cut(self): + """Cuts selected text""" + self.Cut() + + def on_copy(self): + """Copies selected text""" + self.Copy() + + def on_paste(self): + """Pastes selected text""" + self.Paste() + + def on_clear(self): + """Deletes selected text""" + self.Clear() + + def on_select_all(self): + """Selects all the text, this function is not necessary but makes it cleaner""" + self.SelectAll() + + def load_file(self, path): + """Loads a new file""" + self.LoadFile(path) + self.filepath= path + + def save_file(self): + """Saves the current file""" + return self.SaveFile(self.filepath) + + def save_file_as(self, filepath): + """Save the current file as a new filepath""" + self.filepath= filepath + return self.save_file() + + def on_replace(self): + """Displays a find/replace dialog""" + #I think we should create a new frame for this, to be coded yet + + +if __name__=='__main__': + import ide_test_frame + app = wx.PySimpleApp(False) + frame= ide_test_frame.TestFrame(None, title= "Testing editor") + panel= ide_test_frame.TestPanel(frame) + frame.sizer.Add(panel, 1, wx.EXPAND) + editor= Editor(panel) + panel.sizer.Add(editor, 1, wx.EXPAND) + frame.Layout() + app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py new file mode 100644 index 0000000..e9143c2 --- /dev/null +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jul 27 17:36:42 2011 + +@author: jakob, David +""" +import os +import wx +import gui_lib.ide_menu as ide_menu +import gui_lib.ide_toolbar as ide_toolbar +import gui_lib.ide_constant as ide +from ide_notebook import Notebook +from ide_notebook_events import NotebookEvents + + +class MainFrame(wx.Frame): + """Class with the GUI and GUI functions""" + + def __init__(self,*args, **kwargs): + """Creates the frame, calls some construction methods.""" + wx.Frame.__init__(self, *args, **kwargs) + self.SetInitialSize((600,600)) + self.Center(wx.BOTH) + self.CreateStatusBar() + self.frame_sizer= wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.frame_sizer) + self.frame_panel= wx.Panel(self, style= wx.BORDER_THEME) + self.frame_sizer.Add(self.frame_panel, 1, wx.EXPAND|wx.ALL, 1) + self.panel_sizer= wx.BoxSizer(wx.VERTICAL) + self.frame_panel.SetSizer(self.panel_sizer) + + self._create_menu() + self._create_toolbar() + self._create_notebook_panel() + self._start_up() + self.Layout() + self.Show() + + def _create_menu(self): + ''' + creates a menu + ''' + self.SetMenuBar(ide_menu.MenuBar(self)) + + def _create_toolbar(self): + ''' + creates a toolbar + ''' + tb= ide_toolbar.ToolBar(self, style= wx.TB_HORIZONTAL|wx.NO_BORDER|wx.TB_FLAT) + self.SetToolBar(tb) + + def _create_notebook_panel(self): + ''' + Create the notebook panel + ''' + self.notebook = Notebook(self.frame_panel) + NotebookEvents(self.notebook) + self.panel_sizer.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 0) + + def _start_up(self): + ''' + Adds a new blank editor tab + perhaps open last edited in the future, for now just open new. + ''' + self.notebook.new_editor_tab() + + + + def on_exit(self): + ''' + What to do when the application wants to exit + ''' + dial = wx.MessageDialog(self,'Do you really want to exit?', + 'Exit Python IDE', + wx.YES_NO | wx.ICON_QUESTION) + # TODO: we also need to work in a way of detecting if a file + # has changed since last save/load, and if so prompt the user + # to save before exit. + +# f = open("CONFIG", "w") +# f.write("%s\n%s\n" % (self.GetSize()[0], self.GetSize()[1])) +# f.close() + + if dial.ShowModal() == wx.ID_YES: + self.Destroy() + + def get_file(self, prompt, style): + """Abstracted method to prompt the user for a file path. + Returns a 2-tuple consisting of directory path and file name.""" + dlg = wx.FileDialog(self, prompt, '.', '', '*.*', style) + if dlg.ShowModal() == wx.ID_OK: + dirname = dlg.GetDirectory() + filename = dlg.GetFilename() + else: + # so maybe add error handling here. + raise RuntimeError("I guess something has gone wrong with the dialog") + dlg.Destroy() + return dirname, filename + +if __name__=='__main__': + app = wx.PySimpleApp(False) + MainFrame(None, title= "Testing main frame with no events") + app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py new file mode 100644 index 0000000..cccd395 --- /dev/null +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -0,0 +1,116 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx +import ide_constant as ide + +class MainFrameEvents(object): + def __init__(self, view, model= None): + self.view = view + self.model = model + self._create_binds() + + def _create_binds(self): + ''' + Create binds + ''' + self.view.Bind(wx.EVT_UPDATE_UI, self._evt_update_ui) + self.view.Bind(wx.EVT_MENU, self._on_new, id=ide.ID_NEW) + self.view.Bind(wx.EVT_MENU, self._on_open, id=ide.ID_OPEN) + self.view.Bind(wx.EVT_MENU, self._on_exit, id=ide.ID_EXITAPP) + self.view.Bind(wx.EVT_MENU, self._on_save, id=ide.ID_SAVE) + self.view.Bind(wx.EVT_MENU, self._on_save_as, id=ide.ID_SAVEAS) + self.view.Bind(wx.EVT_CLOSE, self._on_exit) + self.view.Bind(wx.EVT_MENU, self._on_editor_close, id=ide.ID_CLOSETAB) + self.view.Bind(wx.EVT_MENU, self._on_editor_undo, id=ide.ID_UNDO) + self.view.Bind(wx.EVT_MENU, self._on_editor_redo, id=ide.ID_REDO) + self.view.Bind(wx.EVT_MENU, self._on_editor_cut, id=ide.ID_CUT) + self.view.Bind(wx.EVT_MENU, self._on_editor_copy, id=ide.ID_COPY) + self.view.Bind(wx.EVT_MENU, self._on_editor_paste, id=ide.ID_PASTE) + self.view.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) + self.view.Bind(wx.EVT_MENU, self._on_editor_selectall, id=ide.ID_SELECTALL) + self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, id=ide.ID_SEARCH) + + def _on_new(self, event): + """Opens a new tab with a new editor instance""" + self.view.notebook.new_editor_tab() + + def _on_open(self, event): + """Opens a new tab and ask for a file to load""" + self.view.notebook.open_editor_tab() + + def _on_editor_close(self, event): + """Closes the current editor tab""" + self.view.notebook.close_active_editor() + + def _on_save(self, event): + """Saves the currently active file""" + self.view.notebook.save_active_editor_tab() + + def _on_save_as(self, event): + """Save as required filename""" + self.view.notebook.save_as_active_editor_tab() + + def _on_editor_undo(self, event): + """Undo for the current editor tab""" + self.view.notebook.undo_active_editor() + + def _on_editor_redo(self, event): + """Redo for the current editor tab""" + self.view.notebook.redo_active_editor() + + def _on_editor_cut(self, event): + """Cut for the current editor tab""" + self.notebook.cut_active_editor() + + def _on_editor_copy(self, event): + """Copy for the current editor tab""" + self.view.notebook.copy_active_editor() + + def _on_editor_paste(self, event): + """paste for the current editor tab""" + self.view.notebook.paste_active_editor() + + def _on_editor_delete(self, event): + """Clear for the current editor tab""" + self.view.notebook.clear_active_editor() + + def _on_editor_selectall(self, event): + """Selectall for the current editor tab""" + self.view.notebook.selectall_active_editor() + + def _on_editor_search_and_replace(self, event): + """Replace for the current editor tab""" + self.view.notebook.replace_active_editor() + + def _on_exit(self, event): + """ application wants to exit""" + self.view.on_exit() + + def _evt_update_ui(self, event): + ''' + Events to update the view + ''' + event_id = event.GetId() + if event_id== ide.ID_CUT: + event.Enable(self.view.notebook.active_editor_can_cut()) + elif event_id== ide.ID_COPY: + event.Enable(self.view.notebook.active_editor_can_copy()) + elif event_id== ide.ID_PASTE: + event.Enable(self.view.notebook.active_editor_can_paste()) + elif event_id== ide.ID_DELETE: + event.Enable(self.view.notebook.active_editor_can_delete()) + elif event_id== ide.ID_UNDO: + event.Enable(self.view.notebook.active_editor_can_undo()) + elif event_id== ide.ID_REDO: + event.Enable(self.view.notebook.active_editor_can_redo()) + elif event_id== ide.ID_SAVE: + event.Enable(self.view.notebook.active_editor_can_save()) + elif event_id== ide.ID_SAVEAS: + event.Enable(self.view.notebook.active_editor_can_saveas()) + else: + event.Skip() + \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_menu.py b/pythonforumide/gui_lib/ide_menu.py new file mode 100644 index 0000000..4dc5c22 --- /dev/null +++ b/pythonforumide/gui_lib/ide_menu.py @@ -0,0 +1,122 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx +import ide_constant as ide +from pythonforumide.ide_images import menu_icons + +class MenuBar(wx.MenuBar): + ''' + Creates a menubar + ''' + def __init__(self, parent): + super(MenuBar, self).__init__() + self._parent = parent + self._add_file_menu() + self._add_edit_menu() + self._add_search_menu() + self._add_run_menu() + +# self._parent.SetMenuBar(self) + + def _add_file_menu(self): + ''' + Adds the file menu + ''' + fileMenu = wx.Menu() + self._add_menu_item(fileMenu, ide.ID_NEW, + ide.id_text_new, menu_icons.get_icon_new()) + + fileMenu.AppendSeparator() + + self._add_menu_item(fileMenu, ide.ID_OPEN, + ide.id_text_open, menu_icons.get_icon_open()) + + fileMenu.AppendSeparator() + + self._add_menu_item(fileMenu, ide.ID_SAVE, + ide.id_text_save, menu_icons.get_icon_save()) + + self._add_menu_item(fileMenu, ide.ID_SAVEAS, + ide.id_text_saveas, menu_icons.get_icon_saveas()) + + fileMenu.AppendSeparator() + + self._add_menu_item(fileMenu, ide.ID_CLOSETAB, + ide.id_text_closetab, menu_icons.get_icon_close()) + + self._add_menu_item(fileMenu, ide.ID_EXITAPP, ide.id_text_exitapp, menu_icons.get_icon_quit()) + + self.Append(fileMenu, "&File") + + def _add_edit_menu(self): + ''' + Adds the edit menu + ''' + editMenu = wx.Menu() + + self._add_menu_item(editMenu, ide.ID_UNDO, + ide.id_text_undo, menu_icons.get_icon_undo()) + + self._add_menu_item(editMenu, ide.ID_REDO, + ide.id_text_redo, menu_icons.get_icon_redo()) + + editMenu.AppendSeparator() + + self._add_menu_item(editMenu, ide.ID_CUT, + ide.id_text_cut, menu_icons.get_icon_cut()) + + self._add_menu_item(editMenu, ide.ID_COPY, + ide.id_text_copy, menu_icons.get_icon_copy()) + + self._add_menu_item(editMenu, ide.ID_PASTE, + ide.id_text_paste, menu_icons.get_icon_paste()) + + self._add_menu_item(editMenu, ide.ID_DELETE, + ide.id_text_delete, menu_icons.get_icon_delete()) + + editMenu.AppendSeparator() + + self._add_menu_item(editMenu, ide.ID_SELECTALL, ide.id_text_selectall) + + self.Append(editMenu, "&Edit") + + def _add_search_menu(self): + ''' + Adds the search menu + ''' + searchMenu = wx.Menu() + + self._add_menu_item(searchMenu, ide.ID_SEARCH, + ide.id_text_search, menu_icons.get_find_and_replace()) + + self.Append(searchMenu, "&Search") + + def _add_run_menu(self): + ''' + Adds the run menu + ''' + runMenu = wx.Menu() + + self._add_menu_item(runMenu, ide.ID_RUNFILE, + ide.id_text_runfile) + + self.Append(runMenu, "&Run") + + def _add_menu_item(self, parent_menu, id, id_text, icon_bmp= None): + item= wx.MenuItem(parent_menu, id, id_text.menu, id_text.status, id_text.menu_kind) + if icon_bmp: + item.SetBitmap(icon_bmp) + parent_menu.AppendItem(item) + + + + + + + + + \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py new file mode 100644 index 0000000..d2f4ca5 --- /dev/null +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jul 27 17:36:42 2011 + +@author: jakob, David +""" + +import os +import wx +import wx.aui as aui +from ide_editor import Editor + +class Notebook(aui.AuiNotebook): + def __init__(self, *args, **kwargs): + super(Notebook, self).__init__(*args, **kwargs) + self._active_editor_page= None + self._active_tab_index= None + + def new_editor_tab(self, page_name= ""): + """Opens a new editor tab""" + self.Freeze() + editor= Editor(self) + self.Thaw() + self.AddPage(editor, page_name) + self._active_editor_page= editor + self.name_untitled_pages() + wx.CallAfter(self.SetSelection, self.GetPageCount()-1) + return editor + + def open_editor_tab(self): + """Loads a slected file into a new editor tab""" + dirname, filename= self.GetGrandParent().get_file('Open a file', wx.OPEN) + if dirname and filename: + editor= self.new_editor_tab(filename) + path = os.path.join(dirname, filename) + editor.load_file(path) + + def save_active_editor_tab(self): + """Saves the currently active editor file""" + if self._active_editor_page.filepath: + self._active_editor_page.save_file() + else: + self.save_as_active_editor_tab() + + def save_as_active_editor_tab(self): + """Save as for the currently active editor file""" + dirname, filename = self.GetGrandParent().get_file('Save file as', wx.SAVE) + if dirname and filename: + path = os.path.join(dirname, filename) + if path: + if self._active_editor_page.save_file_as(path): + self.set_active_tab_text(filename) + self._active_editor_page.filepath = path + + def set_active_tab_text(self, text): + """Rename the currently active tab text""" + if self._active_tab_index> -1: + self.SetPageText(self._active_tab_index, text) + + def name_untitled_pages(self): + """Renumbers the untitled pages""" + self.Freeze() + empty_page_no= 1 + for page_no in xrange(self.GetPageCount()): + page_text= self.GetPageText(page_no) + if "Untitled" in page_text or not page_text: + page= self.GetPage(page_no) + self.SetPageText(page_no, "Untitled%s.py" % (empty_page_no)) + empty_page_no+= 1 + self.Thaw() + + def get_active_editor(self): + return self._active_editor_page + + def close_active_editor(self): + """Closes the currently active editor tab""" + self.DeletePage(self._active_tab_index) + wx.CallAfter(self.name_untitled_pages) + + def undo_active_editor(self): + """Undo changes in active editor""" + self._active_editor_page.on_undo() + + def redo_active_editor(self): + """Redo changes in active editor""" + self._active_editor_page.on_redo() + + def cut_active_editor(self): + """Cut changes in active editor""" + self._active_editor_page.on_cut() + + def copy_active_editor(self): + """Copy changes in active editor""" + self._active_editor_page.on_copy() + + def paste_active_editor(self): + """Paste changes in active editor""" + self._active_editor_page.on_paste() + + def clear_active_editor(self): + """Paste changes in active editor""" + self._active_editor_page.on_clear() + + def selectall_active_editor(self): + """Sslectall changes in active editor""" + self._active_editor_page.on_select_all() + + def replace_active_editor(self): + """Replace changes in active editor""" + self._active_editor_page.on_replace() + + def active_editor_can_cut(self): + """Returns True if the active editor can cut""" + if self._active_editor_page: + return self._active_editor_page.CanCut() + else: + return False + + def active_editor_can_copy(self): + """Returns True if the active editor can copy""" + if self._active_editor_page: + return self._active_editor_page.CanCopy() + else: + return False + + def active_editor_can_paste(self): + """Returns True if the active editor can paste""" + if self._active_editor_page: + return self._active_editor_page.CanPaste() + else: + return False + + def active_editor_can_delete(self): + """Returns True if the active editor can delete""" + if self._active_editor_page: + return self._active_editor_page.HasSelection() + else: + return False + + + def active_editor_can_undo(self): + """Returns True if the active editor can undo""" + if self._active_editor_page: + return self._active_editor_page.CanUndo() + else: + return False + + def active_editor_can_redo(self): + """Returns True if the active editor can redo""" + if self._active_editor_page: + return self._active_editor_page.CanRedo() + else: + return False + + def active_editor_can_save(self): + """Returns True if the active editor can save""" + if self._active_editor_page: + return True + else: + return False + + def active_editor_can_saveas(self): + """Returns True if the active editor can saveas""" + if self._active_editor_page: + return True + else: + return False + +if __name__=='__main__': + import ide_test_frame + app = wx.PySimpleApp(False) + frame= ide_test_frame.TestFrame(None, title= "Testing notebook without events") + panel= ide_test_frame.TestPanel(frame) + frame.sizer.Add(panel, 1, wx.EXPAND) + notebook= Notebook(panel) + notebook.new_editor_tab() + panel.sizer.Add(notebook, 1, wx.EXPAND) + frame.Layout() + app.MainLoop() + \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_notebook_events.py b/pythonforumide/gui_lib/ide_notebook_events.py new file mode 100644 index 0000000..f563b22 --- /dev/null +++ b/pythonforumide/gui_lib/ide_notebook_events.py @@ -0,0 +1,34 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx +import wx.aui as aui + +class NotebookEvents(object): + def __init__(self, view, model= None): + self.view = view + self.model = model + self._create_binds() + + def _create_binds(self): + ''' + Create binds + ''' + self.view.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, + self._on_page_closed) + self.view.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, + self._on_page_changed) + + def _on_page_changed(self, event): + """sets the currently active page index and editor page""" + self.view._active_tab_index= event.Selection + self.view._active_editor_page= self.view.GetPage(self.view._active_tab_index) + + def _on_page_closed(self, event): + """Sets currently active page to none and renames the untitled pages""" + event.Skip() + self.view._active_editor_page= None + wx.CallAfter(self.view.name_untitled_pages) \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_test_frame.py b/pythonforumide/gui_lib/ide_test_frame.py new file mode 100644 index 0000000..fbb3e57 --- /dev/null +++ b/pythonforumide/gui_lib/ide_test_frame.py @@ -0,0 +1,36 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx + +class TestFrame(wx.Frame): + ''' + Frame to use for testing + ''' + def __init__(self, *args, **kwargs): + super(TestFrame, self).__init__(*args, **kwargs) + self.SetInitialSize((600,600)) + self.Center(wx.BOTH) + self.sizer= wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.sizer) + self.Show() + +class TestPanel(wx.Panel): + ''' + panel to use for testing + ''' + def __init__(self, *args, **kwargs): + super(TestPanel, self).__init__(*args, **kwargs) + self.sizer= wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.sizer) + +if __name__ == '__main__': + app = wx.PySimpleApp(False) + frame= TestFrame(None, title= "Testing TestFrame") + panel= TestPanel(frame) + frame.sizer.Add(panel, 1, wx.EXPAND) + frame.Layout() + app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_toolbar.py b/pythonforumide/gui_lib/ide_toolbar.py new file mode 100644 index 0000000..c3d7e7d --- /dev/null +++ b/pythonforumide/gui_lib/ide_toolbar.py @@ -0,0 +1,54 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx +import ide_constant as ide +from pythonforumide.ide_images import menu_icons + + +class ToolBar(wx.ToolBar): + def __init__(self, *args, **kwargs): + ''' + Create the toolbar + ''' + super(ToolBar, self).__init__( *args, **kwargs) + self.SetToolBitmapSize((24,24)) + self._add_toolbar_btn(ide.ID_NEW, ide.id_text_new, + menu_icons.get_icon_new()) + + self._add_toolbar_btn(ide.ID_OPEN, ide.id_text_open, + menu_icons.get_icon_open()) + + self._add_toolbar_btn(ide.ID_SAVE, ide.id_text_save, + menu_icons.get_icon_save()) + + self._add_toolbar_btn(ide.ID_SAVEAS, ide.id_text_saveas, + menu_icons.get_icon_saveas()) + + self.AddSeparator() + + self._add_toolbar_btn(ide.ID_CUT, ide.id_text_cut, + menu_icons.get_icon_cut()) + + self._add_toolbar_btn(ide.ID_COPY, ide.id_text_copy, + menu_icons.get_icon_copy()) + + self._add_toolbar_btn(ide.ID_PASTE, ide.id_text_paste, + menu_icons.get_icon_paste()) + + self.AddSeparator() + + self._add_toolbar_btn(ide.ID_UNDO, ide.id_text_undo, + menu_icons.get_icon_undo()) + + self._add_toolbar_btn(ide.ID_REDO, ide.id_text_redo, + menu_icons.get_icon_redo()) + + self.Realize() + + def _add_toolbar_btn(self, id, id_text, icon_bmp= None): + self.AddLabelTool(id= id, label= id_text.toolbar, bitmap= icon_bmp, + shortHelp= id_text.toolbar, longHelp= id_text.status) diff --git a/pythonforumide/ide_images/__init__.py b/pythonforumide/ide_images/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pythonforumide/ide_images/menu_icons.py b/pythonforumide/ide_images/menu_icons.py new file mode 100644 index 0000000..7330c43 --- /dev/null +++ b/pythonforumide/ide_images/menu_icons.py @@ -0,0 +1,91 @@ +''' +Created on 31 Jul 2011 + +@author: Main +''' + +import wx + +def get_icon_new(): + ''' + Returns a (24,24) new icon bmp + ''' + return _get_art_bmp(wx.ART_NEW) + +def get_icon_open(): + ''' + Returns a (24,24) open icon bmp + ''' + return _get_art_bmp(wx.ART_FILE_OPEN) + +def get_icon_save(): + ''' + Returns a (24,24) save icon bmp + ''' + return _get_art_bmp(wx.ART_FILE_SAVE) + +def get_icon_saveas(): + ''' + Returns a (24,24) saveas icon bmp + ''' + return _get_art_bmp(wx.ART_FILE_SAVE_AS) + +def get_icon_cut(): + ''' + Returns a (24,24) cut icon bmp + ''' + return _get_art_bmp(wx.ART_CUT) + +def get_icon_copy(): + ''' + Returns a (24,24) copy icon bmp + ''' + return _get_art_bmp(wx.ART_COPY) + +def get_icon_paste(): + ''' + Returns a (24,24) paste icon bmp + ''' + return _get_art_bmp(wx.ART_PASTE) + +def get_icon_undo(): + ''' + Returns a (24,24) undo icon bmp + ''' + return _get_art_bmp(wx.ART_UNDO) + +def get_icon_redo(): + ''' + Returns a (24,24) redo icon bmp + ''' + return _get_art_bmp(wx.ART_REDO) + +def get_icon_close(): + ''' + Returns a (24,24) close icon bmp + ''' + return _get_art_bmp(wx.ART_CLOSE) + +def get_icon_quit(): + ''' + Returns a (24,24) quit icon bmp + ''' + return _get_art_bmp(wx.ART_QUIT) + +def get_icon_delete(): + ''' + Returns a (24,24) delete icon bmp + ''' + return _get_art_bmp(wx.ART_DELETE) + +def get_find_and_replace(): + ''' + Returns a (24,24) find and replace icon bmp + ''' + return _get_art_bmp(wx.ART_FIND_AND_REPLACE) + +def _get_art_bmp(art_id): + ''' + Returns the passed in art_id as a (24,24) icon bmp + ''' + return wx.ArtProvider.GetBitmap(art_id, wx.ART_TOOLBAR, (24,24)) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py new file mode 100644 index 0000000..f10cba3 --- /dev/null +++ b/pythonforumide/wx_app.py @@ -0,0 +1,78 @@ +''' +Created on 31 Jul 2011 + +@author: D.W. +''' + +import wx +import gui_lib.ide_mainframe as ide_mainframe +import gui_lib.ide_mainframe_events as ide_mainframe_events + +from config import config +from twisted.internet import wxreactor +wxreactor.install() + +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol +from utils.version import get_free_port +from utils.interpreter import spawn_python + +class Wx_App(wx.App): + def __init__(self, *args, **kwargs): + ''' + Creates a wx App + ''' + super(Wx_App, self).__init__(*args, **kwargs) + self._create_port() + self._set_up_reactor() + self._create_mainframe() + + def _create_port(self): + ''' + Creates a free port + ''' + self._port = get_free_port() + + def get_port(self): + return self._port + + def _set_up_reactor(self): + ''' + Set's up the reactor + ''' + reactor.registerWxApp(self) + reactor.listenTCP(self._port, ListenFactory()) + reactor.spawnProcess(*spawn_python()) + #frame.Maximize() #Left commented to stop it getting on my nerves. + + def start_reactor(self): + ''' + Sarts the reactor + ''' + print "Port: %s" %(self.get_port()) + reactor.run() + + + def _create_mainframe(self): + ''' + Creates the mainframe + ''' + self.mainframe= ide_mainframe.MainFrame(None, title= 'PF-IDE - 0.1a') + ide_mainframe_events.MainFrameEvents(self.mainframe) + + +class ListenProtocol(Protocol): + def connectionMade(self): + print "Got connection!!!!" + + def connectionLost(self, reason): + print "Connection closed." + +class ListenFactory(Factory): + protocol = ListenProtocol + +if __name__ == '__main__': + wx_app= Wx_App(False) + wx_app.start_reactor() + + \ No newline at end of file From cb3ddd6cd12fdce8fc3ae67cc27185b6d5f982bc Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Sun, 31 Jul 2011 20:00:22 +0100 Subject: [PATCH 002/141] Got the config working again --- pythonforumide/Ide_Config.cfg | 6 + pythonforumide/config/Ide_Config.cfg | 6 + pythonforumide/config/config.py | 58 +++- pythonforumide/default.cfg | 0 pythonforumide/editor/__init__.py | 0 pythonforumide/editor/editor.py | 191 -------------- pythonforumide/editor/notebook.py | 194 -------------- pythonforumide/gui_lib/default.cfg | 4 - pythonforumide/gui_lib/ide_editor.py | 6 +- pythonforumide/gui_lib/ide_mainframe.py | 11 +- .../gui_lib/ide_mainframe_events.py | 10 +- pythonforumide/gui_lib/ide_notebook.py | 26 +- pythonforumide/gui_lib/ide_test_app.py | 26 ++ pythonforumide/wx_app.py | 23 +- pythonforumide/wx_main.py | 247 ------------------ 15 files changed, 149 insertions(+), 659 deletions(-) create mode 100644 pythonforumide/Ide_Config.cfg create mode 100644 pythonforumide/config/Ide_Config.cfg delete mode 100644 pythonforumide/default.cfg delete mode 100644 pythonforumide/editor/__init__.py delete mode 100644 pythonforumide/editor/editor.py delete mode 100644 pythonforumide/editor/notebook.py delete mode 100644 pythonforumide/gui_lib/default.cfg create mode 100644 pythonforumide/gui_lib/ide_test_app.py delete mode 100644 pythonforumide/wx_main.py diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg new file mode 100644 index 0000000..ffc8eaa --- /dev/null +++ b/pythonforumide/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = 0 +mainframe.height = 600 +mainframe.width = 600 +indent = 4 + diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg new file mode 100644 index 0000000..072431c --- /dev/null +++ b/pythonforumide/config/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = 0 +mainframe.height = 728 +mainframe.width = 1237 +indent = 4 + diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index aff0836..f934973 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -83,6 +83,7 @@ class _UglyConfig(object): rely on ConfigParser.""" def __init__(self, profile): filename = profile + ".cfg" + filename= profile self.config = ConfigParser.ConfigParser() try: @@ -124,12 +125,51 @@ def save(self): #This way the importers use the config the same way, same api but under the #hood they are different. -# Load configuration -config_file = config_file("default") -conf = load_config(config_file) - -# Set defaults -conf.set_default("indent", 4) -conf.set_default("usetab", 0) #0 means false - -conf.save() \ No newline at end of file +class Ide_config(object): + def __init__(self, filepath= "", filename= "Ide_Config"): + if not filepath: + filepath= os.path.splitext(__file__)[0] + filepath= filepath.rsplit("\\", 1)[0] + self._filepath = filepath + self._filename = filename + self._fullpath= None + self._data= {} + self._get_file_fullpath() + self._get_defaults() + + def _get_file_fullpath(self): + ext= ".cfg" + if has_yaml: + ext= ".yaml" + self._fullpath= os.path.join(self._filepath, self._filename)+ext + if not os.path.exists(self._fullpath): + a= file(self._fullpath, "w") + a.close() + print ("Config useing filepath: %s" % (self._fullpath)) + + def _get_defaults(self): + confile= Config(self._fullpath) + self._data["indent"]= confile["indent"] or 4 + self._data["usetab"]= confile["usetab"] or 0 + self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 + self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 + confile.file.close() + + def __setitem__(self, key, value): + self._data[key]= value + + def __getitem__(self, key): + return self._data[key] + + def update_configfile(self): + confile= Config(self._fullpath) + for key, value in self._data.iteritems(): + confile[key]= value + confile.save() + confile.file.close() + +if __name__ == '__main__': + ide_config= Ide_config() + print (ide_config["indent"]) + ide_config.update_configfile() + \ No newline at end of file diff --git a/pythonforumide/default.cfg b/pythonforumide/default.cfg deleted file mode 100644 index e69de29..0000000 diff --git a/pythonforumide/editor/__init__.py b/pythonforumide/editor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pythonforumide/editor/editor.py b/pythonforumide/editor/editor.py deleted file mode 100644 index e57028f..0000000 --- a/pythonforumide/editor/editor.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -@author: Jakob, David, bunburya -@reviewer: Somelauw -""" - -#YES DIRT HACK GET OVER IT. Dont remove it might go before it goes into master -import sys -sys.path.append('..') - -from utils.textutils import split_comments -import wx.stc as stc -import wx -import os -from config import config - -#TODO: make customisable font and sizes. Perhaps maked this named tuple? -faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 12, - 'size2': 10, - } - -class Editor(stc.StyledTextCtrl): - def __init__(self, parent): - super(Editor, self).__init__(parent) - - self.conf= config.conf - - self.filepath = '' - self.indent_level = 0 - - self.SetGenerics() - self.SetMargins() - self.SetStyles() - self.SetBindings() - - def SetBindings(self): - self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - - def SetGenerics(self): - """Rather than do it in the __init__ and to help debugging the styles - and settings are split into seperate SetOptions, this sets the generic - options like Tabwidth, expandtab and indentation guides + others.""" - self.SetLexer(stc.STC_LEX_PYTHON) #is this giving us trouble? - self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) #set mono spacing here! - self.SetTabWidth(4) - self.SetIndentationGuides(1) - #Indentation will only use space characters if useTabs is false - self.SetUseTabs(False) - - def SetMargins(self): - """This is specifically for the margins. Like the other Set methods it - is only really to be called in the __init__ its here more for - readability purpsoses than anything else.""" - # margin 0 for breakpoints - self.SetMarginSensitive(0, True) - self.SetMarginType(0, wx.stc.STC_MARGIN_SYMBOL) - self.SetMarginMask(0, 0x3) - self.SetMarginWidth(0, 12) - # margin 1 for current line arrow - self.SetMarginSensitive(1, False) - self.SetMarginMask(1, 0x4) - # margin 2 for line numbers - self.SetMarginType(2, stc.STC_MARGIN_NUMBER) - self.SetMarginWidth(2, 28) - - def SetStyles(self, lang='python'): - """This is different from the other Set methods thathttp://paste.pocoo.org/show/446107/ are called in the - __init__ this one is for the highlighting and syntax of the langauge, - this will eventually be callable with different langauge styles. - For the moment, leave the lang kwarg in. """ - - #INDICATOR STYLES FOR ERRORS (self.errorMark) - self.IndicatorSetStyle(2, stc.STC_INDIC_SQUIGGLE) - self.IndicatorSetForeground(2, wx.RED) - self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) - - # Python styles - - # White space - self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) - # Comment - self.StyleSetSpec(stc.STC_P_COMMENTLINE, "face:%(mono)s,fore:#007F00,back:#E8FFE8,italic,size:%(size)d" % faces) - # Number - self.StyleSetSpec(stc.STC_P_NUMBER, "face:%(mono)s,fore:#007F7F,size:%(size)d" % faces) - # String - self.StyleSetSpec(stc.STC_P_STRING, "face:%(mono)s,fore:#7F007F,size:%(size)d" % faces) - # Single quoted string - self.StyleSetSpec(stc.STC_P_CHARACTER, "face:%(mono)s,fore:#7F007F,size:%(size)d" % faces) - # Keyword - self.StyleSetSpec(stc.STC_P_WORD, "face:%(mono)s,fore:#00007F,bold,size:%(size)d" % faces) - # Triple quotes - self.StyleSetSpec(stc.STC_P_TRIPLE, "face:%(mono)s,fore:#7F0000,size:%(size)d" % faces) - # Triple double quotes - self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "face:%(mono)s,fore:#7F0000,size:%(size)d" % faces) - # Class name definition - self.StyleSetSpec(stc.STC_P_CLASSNAME, "face:%(mono)s,fore:#0000FF,bold,underline,size:%(size)d" % faces) - # Function or method name definition - self.StyleSetSpec(stc.STC_P_DEFNAME, "face:%(mono)s,fore:#007F7F,bold,size:%(size)d" % faces) - # Operators - self.StyleSetSpec(stc.STC_P_OPERATOR, "face:%(mono)s,bold,size:%(size)d" % faces) - # Identifiers - self.StyleSetSpec(stc.STC_P_IDENTIFIER, "") - # Comment-blocks - self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "face:%(mono)s,fore:#990000,back:#C0C0C0,italic,size:%(size)d" % faces) - # End of line where string is not closed - self.StyleSetSpec(stc.STC_P_STRINGEOL, "face:%(mono)s,fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) - - def SmartIndent(self): - # Read settings from the config file - indent_amount = int(self.conf["indent"]) - usetab = int(self.conf["usetab"]) - - last_line_no = self.GetCurrentLine() - last_line = split_comments(self.GetLine(last_line_no))[0] - self.NewLine() - indent_level = self.GetLineIndentation(last_line_no) // indent_amount - - if last_line.rstrip().endswith(':'): - indent_level += 1 - elif any(last_line.lstrip().startswith(token) - for token in ["return", "break", "yield"]): - indent_level = max([indent_level - 1, 0]) - - if usetab: - indent = "/t" * indent_level - else: - indent = indent_amount * " " * indent_level - - self.AddText(indent) - print self.conf - - def OnKeyDown(self, event): - key = event.GetKeyCode() - control = event.ControlDown() - alt = event.AltDown() - if key == wx.WXK_RETURN and not control and not alt: - self.SmartIndent() - else: - event.Skip() - - def on_undo(self): - """Checks if can Undo and if yes undoes""" - if self.CanUndo() == 1: - self.Undo() - - def on_redo(self): - """Checks if can Redo and if yes redoes""" - if self.CanRedo() == 1: - self.Redo() - - def on_cut(self): - """Cuts selected text""" - self.Cut() - - def on_copy(self): - """Copies selected text""" - self.Copy() - - def on_paste(self): - """Pastes selected text""" - self.Paste() - - def on_clear(self): - """Deletes selected text""" - self.Clear() - - def on_select_all(self): - """Selects all the text, this function is not necessary but makes it cleaner""" - self.SelectAll() - - def load_file(self, path): - """Loads a new file""" - self.LoadFile(path) - self.filepath= path - - def save_file(self): - """Saves the current file""" - return self.SaveFile(self.filepath) - - def save_file_as(self, filepath): - """Save the current file as a new filepath""" - self.filepath= filepath - return self.save_file() - - def on_replace(self): - """Displays a find/replace dialog""" - #I think we should create a new frame for this, to be coded yet - diff --git a/pythonforumide/editor/notebook.py b/pythonforumide/editor/notebook.py deleted file mode 100644 index eba13c3..0000000 --- a/pythonforumide/editor/notebook.py +++ /dev/null @@ -1,194 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 27 17:36:42 2011 - -@author: jakob, David -""" - -import os -import wx -import wx.aui as aui -from editor import Editor - -class Notebook(aui.AuiNotebook): - def __init__(self, *args, **kwargs): - super(Notebook, self).__init__(*args, **kwargs) - self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self._evt_page_closed) - self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self._evt_page_changed) - self._active_editor_page= None - self._active_tab_index= None - - def _evt_page_changed(self, event): - """sets the currently active page index and editor page""" - self._active_tab_index= event.Selection - self._active_editor_page= self.GetPage(self._active_tab_index) - - def _evt_page_closed(self, event): - """Sets currently active page to none and renames the untitled pages""" - event.Skip() - self._active_editor_page= None - wx.CallAfter(self.name_untitled_pages) - - def new_editor_tab(self, page_name= ""): - """Opens a new editor tab""" - self.Freeze() - editor= Editor(self) - self.Thaw() - self.AddPage(editor, page_name) - self._active_editor_page= editor - self.name_untitled_pages() - wx.CallAfter(self.SetSelection, self.GetPageCount()-1) - return editor - - def open_editor_tab(self): - """Loads a slected file into a new editor tab""" - dirname, filename= self.GetGrandParent().get_file('Open a file', wx.OPEN) - if dirname and filename: - editor= self.new_editor_tab(filename) - path = os.path.join(dirname, filename) - editor.load_file(path) - - def save_active_editor_tab(self): - """Saves the currently active editor file""" - if self._active_editor_page.filepath: - self._active_editor_page.save_file() - else: - self.save_as_active_editor_tab() - - def save_as_active_editor_tab(self): - """Save as for the currently active editor file""" - dirname, filename = self.GetGrandParent().get_file('Save file as', wx.SAVE) - if dirname and filename: - path = os.path.join(dirname, filename) - if path: - if self._active_editor_page.save_file_as(path): - self.set_active_tab_text(filename) - self._active_editor_page.filepath = path - - def set_active_tab_text(self, text): - """Rename the currently active tab text""" - if self._active_tab_index> -1: - self.SetPageText(self._active_tab_index, text) - - def name_untitled_pages(self): - """Renumbers the untitled pages""" - self.Freeze() - empty_page_no= 1 - for page_no in xrange(self.GetPageCount()): - page_text= self.GetPageText(page_no) - if "Untitled" in page_text or not page_text: - page= self.GetPage(page_no) - self.SetPageText(page_no, "Untitled%s.py" % (empty_page_no)) - empty_page_no+= 1 - self.Thaw() - - def get_active_editor(self): - return self._active_editor_page - - def close_active_editor(self): - """Closes the currently active editor tab""" - self.DeletePage(self._active_tab_index) - wx.CallAfter(self.name_untitled_pages) - - def undo_active_editor(self): - """Undo changes in active editor""" - self._active_editor_page.on_undo() - - def redo_active_editor(self): - """Redo changes in active editor""" - self._active_editor_page.on_redo() - - def cut_active_editor(self): - """Cut changes in active editor""" - self._active_editor_page.on_cut() - - def copy_active_editor(self): - """Copy changes in active editor""" - self._active_editor_page.on_copy() - - def paste_active_editor(self): - """Paste changes in active editor""" - self._active_editor_page.on_paste() - - def clear_active_editor(self): - """Paste changes in active editor""" - self._active_editor_page.on_clear() - - def selectall_active_editor(self): - """Sslectall changes in active editor""" - self._active_editor_page.on_select_all() - - def replace_active_editor(self): - """Replace changes in active editor""" - self._active_editor_page.on_replace() - - def active_editor_can_cut(self): - """Returns True if the active editor can cut""" - if self._active_editor_page: - return self._active_editor_page.CanCut() - else: - return False - - def active_editor_can_copy(self): - """Returns True if the active editor can copy""" - if self._active_editor_page: - return self._active_editor_page.CanCopy() - else: - return False - - def active_editor_can_paste(self): - """Returns True if the active editor can paste""" - if self._active_editor_page: - return self._active_editor_page.CanPaste() - else: - return False - - def active_editor_can_delete(self): - """Returns True if the active editor can delete""" - if self._active_editor_page: - return self._active_editor_page.HasSelection() - else: - return False - - - def active_editor_can_undo(self): - """Returns True if the active editor can undo""" - if self._active_editor_page: - return self._active_editor_page.CanUndo() - else: - return False - - def active_editor_can_redo(self): - """Returns True if the active editor can redo""" - if self._active_editor_page: - return self._active_editor_page.CanRedo() - else: - return False - - def active_editor_can_save(self): - """Returns True if the active editor can save""" - if self._active_editor_page: - return True - else: - return False - - def active_editor_can_saveas(self): - """Returns True if the active editor can saveas""" - if self._active_editor_page: - return True - else: - return False - - - - - - - - - - - - - - diff --git a/pythonforumide/gui_lib/default.cfg b/pythonforumide/gui_lib/default.cfg deleted file mode 100644 index f0a232f..0000000 --- a/pythonforumide/gui_lib/default.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[ide] -indent = 4 -usetab = 0 - diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 6201a7d..fba4095 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -10,7 +10,6 @@ from utils.textutils import split_comments import wx.stc as stc import wx -from config import config #TODO: make customisable font and sizes. Perhaps maked this named tuple? faces = { 'times': 'Times', @@ -25,7 +24,7 @@ class Editor(stc.StyledTextCtrl): def __init__(self, parent): super(Editor, self).__init__(parent) - self.conf= config.conf + self.conf= wx.GetApp().config self.filepath = '' self.indent_level = 0 @@ -190,8 +189,9 @@ def on_replace(self): if __name__=='__main__': + import ide_test_app as wx_app import ide_test_frame - app = wx.PySimpleApp(False) + app = wx_app.Wx_App(False) frame= ide_test_frame.TestFrame(None, title= "Testing editor") panel= ide_test_frame.TestPanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index e9143c2..195d8c3 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -19,7 +19,10 @@ class MainFrame(wx.Frame): def __init__(self,*args, **kwargs): """Creates the frame, calls some construction methods.""" wx.Frame.__init__(self, *args, **kwargs) + self.config= wx.GetApp().config self.SetInitialSize((600,600)) + self.SetSize((int(self.config["MainFrame.Width"]), + int(self.config["MainFrame.Height"]))) self.Center(wx.BOTH) self.CreateStatusBar() self.frame_sizer= wx.BoxSizer(wx.VERTICAL) @@ -76,7 +79,10 @@ def on_exit(self): # TODO: we also need to work in a way of detecting if a file # has changed since last save/load, and if so prompt the user # to save before exit. - + + self.config["MainFrame.Width"]= self.GetSize()[0] + self.config["MainFrame.Height"]= self.GetSize()[1] + # f = open("CONFIG", "w") # f.write("%s\n%s\n" % (self.GetSize()[0], self.GetSize()[1])) # f.close() @@ -98,6 +104,7 @@ def get_file(self, prompt, style): return dirname, filename if __name__=='__main__': - app = wx.PySimpleApp(False) + import ide_test_app as wx_app + app = wx_app.Wx_App(False) MainFrame(None, title= "Testing main frame with no events") app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index cccd395..da852f8 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -12,7 +12,7 @@ def __init__(self, view, model= None): self.view = view self.model = model self._create_binds() - + def _create_binds(self): ''' Create binds @@ -64,7 +64,7 @@ def _on_editor_redo(self, event): def _on_editor_cut(self, event): """Cut for the current editor tab""" - self.notebook.cut_active_editor() + self.view.notebook.cut_active_editor() def _on_editor_copy(self, event): """Copy for the current editor tab""" @@ -111,6 +111,12 @@ def _evt_update_ui(self, event): event.Enable(self.view.notebook.active_editor_can_save()) elif event_id== ide.ID_SAVEAS: event.Enable(self.view.notebook.active_editor_can_saveas()) + elif event_id== ide.ID_CLOSETAB: + event.Enable(self.view.notebook.active_editor_can_close_tab()) + elif event_id== ide.ID_SEARCH: + event.Enable(self.view.notebook.active_editor_can_search()) + elif event_id== ide.ID_RUNFILE: + event.Enable(self.view.notebook.active_editor_can_run()) else: event.Skip() \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index d2f4ca5..35aaac4 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -165,10 +165,34 @@ def active_editor_can_saveas(self): return True else: return False + + def active_editor_can_close_tab(self): + """Returns True if the active editor can close""" + if self._active_editor_page: + return True + else: + return False + + def active_editor_can_search(self): + """Returns True if the active editor can search""" + if self._active_editor_page: + return True + else: + return False + + def active_editor_can_run(self): + """Returns True if the active editor can search""" + if self._active_editor_page: + return True + else: + return False + + if __name__=='__main__': + import ide_test_app as wx_app import ide_test_frame - app = wx.PySimpleApp(False) + app = wx_app.Wx_App(False) frame= ide_test_frame.TestFrame(None, title= "Testing notebook without events") panel= ide_test_frame.TestPanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py new file mode 100644 index 0000000..689cfe8 --- /dev/null +++ b/pythonforumide/gui_lib/ide_test_app.py @@ -0,0 +1,26 @@ +''' +Created on 31 Jul 2011 + +@author: Main +''' + +import wx +from pythonforumide.config.config import Ide_config + +class Wx_App(wx.App): + def __init__(self, *args, **kwargs): + ''' + Creates a test wx App + ''' + super(Wx_App, self).__init__(*args, **kwargs) + self._create_config() + + def _create_config(self): + ''' + Set up config + ''' + self.config= Ide_config() + + def OnExit(self): + print ("App closing") + self.config.update_configfile() \ No newline at end of file diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index f10cba3..a298dfb 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -7,8 +7,9 @@ import wx import gui_lib.ide_mainframe as ide_mainframe import gui_lib.ide_mainframe_events as ide_mainframe_events +from pythonforumide.config.config import Ide_config -from config import config +#import config.config.Ide_config as Ide_config from twisted.internet import wxreactor wxreactor.install() @@ -23,10 +24,17 @@ def __init__(self, *args, **kwargs): Creates a wx App ''' super(Wx_App, self).__init__(*args, **kwargs) + self._create_config() self._create_port() self._set_up_reactor() self._create_mainframe() + def _create_config(self): + ''' + Set up config + ''' + self.config = Ide_config() + def _create_port(self): ''' Creates a free port @@ -49,17 +57,20 @@ def start_reactor(self): ''' Sarts the reactor ''' - print "Port: %s" %(self.get_port()) + print "Port: %s" % (self.get_port()) reactor.run() - def _create_mainframe(self): ''' Creates the mainframe ''' - self.mainframe= ide_mainframe.MainFrame(None, title= 'PF-IDE - 0.1a') + self.mainframe = ide_mainframe.MainFrame(None, title='PF-IDE - 0.1a') ide_mainframe_events.MainFrameEvents(self.mainframe) + def OnExit(self): + print ("App closing") + self.config.update_configfile() + class ListenProtocol(Protocol): def connectionMade(self): @@ -72,7 +83,7 @@ class ListenFactory(Factory): protocol = ListenProtocol if __name__ == '__main__': - wx_app= Wx_App(False) + wx_app = Wx_App(False) wx_app.start_reactor() - \ No newline at end of file + diff --git a/pythonforumide/wx_main.py b/pythonforumide/wx_main.py deleted file mode 100644 index 5116ef6..0000000 --- a/pythonforumide/wx_main.py +++ /dev/null @@ -1,247 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 27 17:36:42 2011 - -@author: jakob, David -""" -import os -import wx -from config import config -from twisted.internet import wxreactor -wxreactor.install() - -from twisted.internet import reactor -from twisted.internet.protocol import Factory, Protocol -from editor.editor import Editor -from editor.notebook import Notebook -from utils.version import get_free_port -from utils.interpreter import spawn_python - -class MainFrame(wx.Frame): - """Class with the GUI and GUI functions""" - - def __init__(self, parent, id): - """Creates the frame, calls some construction methods.""" - wx.Frame.__init__(self, parent, - id, 'PF-IDE - 0.1a') - - self.conf = config.conf - - self.port = get_free_port() - - sizer= wx.BoxSizer(wx.VERTICAL) - self.SetSizer(sizer) - panel= wx.Panel(self, style= wx.BORDER_THEME) - sizer.Add(panel, 1, wx.EXPAND|wx.ALL, 1) - panel_sizer= wx.BoxSizer(wx.VERTICAL) - panel.SetSizer(panel_sizer) - - self.notebook = Notebook(panel) - panel_sizer.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 0) - - #perhaps open last edited in the future, for now just open new. - self.notebook.new_editor_tab() - self.spawn_menus() - self.CreateStatusBar() - self.Bind(wx.EVT_UPDATE_UI, self._evt_update_ui) - - def _evt_update_ui(self, event): - event_id = event.GetId() - if event_id== wx.ID_CUT: - event.Enable(self.notebook.active_editor_can_cut()) - elif event_id== wx.ID_COPY: - event.Enable(self.notebook.active_editor_can_copy()) - elif event_id== wx.ID_PASTE: - event.Enable(self.notebook.active_editor_can_paste()) - elif event_id== wx.ID_DELETE: - event.Enable(self.notebook.active_editor_can_delete()) - elif event_id== wx.ID_UNDO: - event.Enable(self.notebook.active_editor_can_undo()) - elif event_id== wx.ID_REDO: - event.Enable(self.notebook.active_editor_can_redo()) - elif event_id== wx.ID_SAVE: - event.Enable(self.notebook.active_editor_can_save()) - elif event_id== wx.ID_SAVEAS: - event.Enable(self.notebook.active_editor_can_saveas()) - else: - event.Skip() - - def spawn_menus(self): - """Spawns the menus and sets the bindings to keep __init__ short""" - menuBar = wx.MenuBar() - fileMenu = wx.Menu() - menuBar.Append(fileMenu, "&File") - fileMenu.Append(wx.ID_NEW, "New\tCtrl+N") - fileMenu.AppendSeparator() - fileMenu.Append(wx.ID_OPEN, "Open\tCtrl+O") - fileMenu.AppendSeparator() - fileMenu.Append(wx.ID_SAVE, "Save\tCtrl+S") - fileMenu.Append(wx.ID_SAVEAS, "Save as") - fileMenu.AppendSeparator() - fileMenu.Append(wx.ID_CLOSE, "Close\tCtrl+W") - fileMenu.Append(wx.ID_CLOSE_ALL, "Exit\tCtrl+Q") - - editMenu = wx.Menu() - menuBar.Append(editMenu, "&Edit") - editMenu.Append(wx.ID_UNDO, "Undo\tCtrl+Z") - editMenu.Append(wx.ID_REDO, "Redo\tCtrl+Y") - editMenu.AppendSeparator() - editMenu.Append(wx.ID_CUT, "Cut\tCtrl+X") - editMenu.Append(wx.ID_COPY, "Copy\tCtrl+C") - editMenu.Append(wx.ID_PASTE, "Paste\tCtrl+V") - editMenu.Append(wx.ID_DELETE, "Delete") - editMenu.AppendSeparator() - editMenu.Append(wx.ID_SELECTALL, "Select All\tCtrl+A") - - searchMenu = wx.Menu() - searchMenu.Append(wx.ID_FIND, "Replace\tCtrl+H") - menuBar.Append(searchMenu, "&Search") - - runMenu = wx.Menu() - menuBar.Append(runMenu, "&Run") - runMenu.Append(wx.ID_EXECUTE, "Run file\tF5") - - self.SetMenuBar(menuBar) - - tb= self.CreateToolBar(wx.TB_HORIZONTAL|wx.NO_BORDER|wx.TB_FLAT) - tsize = (24,24) - new_bmp = wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR, tsize) - open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize) - save_bmp= wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize) - save_as_bmp= wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR, tsize) - cut_bmp= wx.ArtProvider.GetBitmap(wx.ART_CUT, wx.ART_TOOLBAR, tsize) - copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR, tsize) - paste_bmp= wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR, tsize) - undo_bmp= wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR, tsize) - redo_bmp= wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_TOOLBAR, tsize) - tb.SetToolBitmapSize(tsize) - tb.AddLabelTool(wx.ID_NEW, "New", new_bmp, shortHelp="New", longHelp="Create a new file") - tb.AddLabelTool(wx.ID_OPEN, "Open", open_bmp, shortHelp="Open", longHelp="Open an exisiting file") - tb.AddLabelTool(wx.ID_SAVE, "Save", save_bmp, shortHelp="Save", longHelp="Save the currently active file") - tb.AddLabelTool(wx.ID_SAVEAS, "Save as", save_as_bmp, shortHelp="Save as", longHelp="Save the currently active file as something else") - tb.AddSeparator() - tb.AddSimpleTool(wx.ID_CUT, cut_bmp, "Cut", "Cut selected") - tb.AddSimpleTool(wx.ID_COPY, copy_bmp, "Copy", "Copy selected") - tb.AddSimpleTool(wx.ID_PASTE, paste_bmp, "Paste", "Paste text") - tb.AddSeparator() - tb.AddSimpleTool(wx.ID_UNDO, undo_bmp, "Undo", "Undo") - tb.AddSimpleTool(wx.ID_REDO, redo_bmp, "Redo", "Redo") - tb.Realize() - - self.Bind(wx.EVT_MENU, self._evt_new, id=wx.ID_NEW) - self.Bind(wx.EVT_MENU, self._evt_open, id=wx.ID_OPEN) - self.Bind(wx.EVT_MENU, self._evt_exit, id=wx.ID_CLOSE_ALL) - self.Bind(wx.EVT_MENU, self._evt_save, id=wx.ID_SAVE) - self.Bind(wx.EVT_MENU, self._evt_save_as, id=wx.ID_SAVEAS) - self.Bind(wx.EVT_MENU, self._evt_exit, id=wx.ID_CLOSE_ALL) - self.Bind(wx.EVT_CLOSE, self._evt_exit) - self.Bind(wx.EVT_MENU, self._evt_close_current_editor_tab, id=wx.ID_CLOSE) - self.Bind(wx.EVT_MENU, self._evt_undo_current_editor_tab, id=wx.ID_UNDO) - self.Bind(wx.EVT_MENU, self._evt_redo_current_editor_tab, id=wx.ID_REDO) - self.Bind(wx.EVT_MENU, self._evt_cut_current_editor_tab, id=wx.ID_CUT) - self.Bind(wx.EVT_MENU, self._evt_copy_current_editor_tab, id=wx.ID_COPY) - self.Bind(wx.EVT_MENU, self._evt_paste_current_editor_tab, id=wx.ID_PASTE) - self.Bind(wx.EVT_MENU, self._evt_clear_current_editor_tab, id=wx.ID_DELETE) - self.Bind(wx.EVT_MENU, self._evt_selectall_current_editor_tab, id=wx.ID_SELECTALL) - self.Bind(wx.EVT_MENU, self._evt_replace_current_editor_tab, id=wx.ID_FIND) - - def _evt_new(self, event): - """Opens a new tab with a new editor instance""" - self.notebook.new_editor_tab() - - def _evt_open(self, event): - """Opens a new tab and ask for a file to load""" - self.notebook.open_editor_tab() - - def _evt_close_current_editor_tab(self, event): - """Closes the current editor tab""" - self.notebook.close_active_editor() - - def _evt_save(self, event): - """Saves the currently active file""" - self.notebook.save_active_editor_tab() - - def _evt_save_as(self, event): - """Save as required filename""" - self.notebook.save_as_active_editor_tab() - - def _evt_undo_current_editor_tab(self, event): - """Undo for the current editor tab""" - self.notebook.undo_active_editor() - - def _evt_redo_current_editor_tab(self, event): - """Redo for the current editor tab""" - self.notebook.redo_active_editor() - - def _evt_cut_current_editor_tab(self, event): - """Cut for the current editor tab""" - self.notebook.cut_active_editor() - - def _evt_copy_current_editor_tab(self, event): - """Copy for the current editor tab""" - self.notebook.copy_active_editor() - - def _evt_paste_current_editor_tab(self, event): - """paste for the current editor tab""" - self.notebook.paste_active_editor() - - def _evt_clear_current_editor_tab(self, event): - """Clear for the current editor tab""" - self.notebook.clear_active_editor() - - def _evt_selectall_current_editor_tab(self, event): - """Selectall for the current editor tab""" - self.notebook.selectall_active_editor() - - def _evt_replace_current_editor_tab(self, event): - """Replace for the current editor tab""" - self.notebook.replace_active_editor() - - def _evt_exit(self, event): - dial = wx.MessageDialog(None,'Do you really want to exit?', - 'Exit Python IDE', - wx.YES_NO | wx.ICON_QUESTION) - # TODO: we also need to work in a way of detecting if a file - # has changed since last save/load, and if so prompt the user - # to save before exit. - -# f = open("CONFIG", "w") -# f.write("%s\n%s\n" % (self.GetSize()[0], self.GetSize()[1])) -# f.close() - - if dial.ShowModal() == wx.ID_YES: - self.Destroy() - - def get_file(self, prompt, style): - """Abstracted method to prompt the user for a file path. - Returns a 2-tuple consisting of directory path and file name.""" - dlg = wx.FileDialog(self, prompt, '.', '', '*.*', style) - if dlg.ShowModal() == wx.ID_OK: - dirname = dlg.GetDirectory() - filename = dlg.GetFilename() - else: - # so maybe add error handling here. - raise RuntimeError("I guess something has gone wrong with the dialog") - dlg.Destroy() - return dirname, filename - -class ListenProtocol(Protocol): - def connectionMade(self): - print "Got connection!!!!" - - def connectionLost(self, reason): - print "Connection closed." - -class ListenFactory(Factory): - protocol = ListenProtocol - -if __name__=='__main__': - app = wx.PySimpleApp(False) - frame = MainFrame(parent=None, id=-1) - print frame.port - frame.Show() - reactor.registerWxApp(app) - reactor.listenTCP(frame.port, ListenFactory()) - reactor.spawnProcess(*spawn_python()) - #frame.Maximize() #Left commented to stop it getting on my nerves. - reactor.run() From 97bdbe7c598c8531b2c6ec1556cdaf14bfc7d500 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Sun, 31 Jul 2011 20:36:19 +0100 Subject: [PATCH 003/141] Fixed some comments and whitespace --- pythonforumide/wx_app.py | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index a298dfb..554a799 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -1,8 +1,9 @@ -''' +""" Created on 31 Jul 2011 -@author: D.W. -''' +@author: D.W., david +@reviewr: david +""" import wx import gui_lib.ide_mainframe as ide_mainframe @@ -19,10 +20,9 @@ from utils.interpreter import spawn_python class Wx_App(wx.App): + """Sets the editor up and the ports""" #Not sure if correct description def __init__(self, *args, **kwargs): - ''' - Creates a wx App - ''' + """Creates a wx App""" super(Wx_App, self).__init__(*args, **kwargs) self._create_config() self._create_port() @@ -30,24 +30,18 @@ def __init__(self, *args, **kwargs): self._create_mainframe() def _create_config(self): - ''' - Set up config - ''' + """Set up config""" self.config = Ide_config() def _create_port(self): - ''' - Creates a free port - ''' + """Creates a free port""" self._port = get_free_port() def get_port(self): return self._port def _set_up_reactor(self): - ''' - Set's up the reactor - ''' + """Set's up the reactor""" reactor.registerWxApp(self) reactor.listenTCP(self._port, ListenFactory()) reactor.spawnProcess(*spawn_python()) @@ -61,9 +55,7 @@ def start_reactor(self): reactor.run() def _create_mainframe(self): - ''' - Creates the mainframe - ''' + """Creates the mainframe""" self.mainframe = ide_mainframe.MainFrame(None, title='PF-IDE - 0.1a') ide_mainframe_events.MainFrameEvents(self.mainframe) @@ -85,5 +77,3 @@ class ListenFactory(Factory): if __name__ == '__main__': wx_app = Wx_App(False) wx_app.start_reactor() - - From b229d77a9523c6af79042615c0298cc57f618c64 Mon Sep 17 00:00:00 2001 From: confab Date: Sun, 31 Jul 2011 13:43:31 -0700 Subject: [PATCH 004/141] fixed .gitignore to actually ignore files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c9b568f..c11bd69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc -*.swp +.*.swp +*~ From e778223e4b254fb0ed19f92810b3d41aa3c18cbd Mon Sep 17 00:00:00 2001 From: confab Date: Sun, 31 Jul 2011 13:51:39 -0700 Subject: [PATCH 005/141] removed files --- pythonforumide/.wx_main.py.swp | Bin 20480 -> 0 bytes pythonforumide/CONFIG | 2 -- pythonforumide/config/.config.py.swp | Bin 20480 -> 0 bytes 3 files changed, 2 deletions(-) delete mode 100644 pythonforumide/.wx_main.py.swp delete mode 100644 pythonforumide/CONFIG delete mode 100644 pythonforumide/config/.config.py.swp diff --git a/pythonforumide/.wx_main.py.swp b/pythonforumide/.wx_main.py.swp deleted file mode 100644 index eca86b7d9ca49276c61c10ae61920761a5ca6fa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI3ZHy#E8Gu{vKoCwmO!SA*6tlq2a=UZ8cZZM-8}4p*_E>ZK0dspNA7@|hOwDd{ zv)z;Ko||11QTV4uArgWTHJD(6qKTggks!t&0x>}hF(I0eC=n7g@WVffKYXgXdb($B zSmv@OVA7L3JKbGxz4g{x)o*=F?aKJ^xp{GIX}`tiLzeZErzRGTPg>SJhb^n&v;yfx zDrL)$ksE6`&B$-Qz3AtS;NGRpXwCO#{h+lzH&fo*)VFnY>(=Dn_J+OgxL&Eb`Sz;v zl-vWk2hPO_hA zd*J`v15tB_bvJc5&(uRN4!C}wxlg`#n)~a`_r9L=9p=7lzSn!w&29dbdm#5f?t$C` zxd(C&F%la!k3=!1e2KWc|{{dbARhO@Tgfd(KL-0rH`5kx=HsK�O!N2 z7g9dl3u|yA6yWdgx2&JTkKyZZ8g{`Y@cIShffYCjH^5uxTh{O4o3IR*!kcWKyaCU{ zmtX_hFc14+0zLxIv5E2n_$mZ24OhVqc!3R?pTYfb3J$_q}uR9X-CeA?bgJUXm6AjWP7fbk?2uU44pI5c0%MwMWMot zaKb{WFik!Obsb%|N7GTz*i*?U>?yxrC3KmC+VA6v*!V@ROo5Aq5z+C<03{kFgH_R! z?YKZ(Ysy>5R4`87T2-8?+A`BJ>)al8f;5erOmd-6s5G6n7m8?23MCv0$E%4@M)9K+ z*Q>eSDmT73EoD>Kw(Gf(ZHsWt4<~og1WzBET`!%Og91&Bxm(9)Cx0EXxE%lP_ikZyyQaf-XSu9)`-tH+1;!2@Q zFCCFl#l2G&M{XNGIZA~nJbg5AawP4Jb-be*t5}#`T$r6ZQlQheael}8Ays?Tfy9dB zY2kXp5p8Ev_;^c3vKrASN}(fEPwXfWJs;jxTxc&WiqEO{ zc8&_#H!YVd$L1CnQU~;862oQcC*p%wfSh`h@(jnu=qt*IPBFTal#UNdl(=O&n6cGF zs~&UT3#OiJXOEF*xwN~|G-S7TrF#}X@oVMH2!<|;5{nAz%Pusy{ulk;P1>;uX zx>L4E*MU?uP3Fb#wtLRwP11d&*s*F0>tekWS=RQ)@glJ{HqW%Z4hyQRw4ZjYb80oy z#fpWN7r9YG)~*aWiKt?bwl8@~$gIhYwBAI`MdA&imbbhu7+FQzjn>SqvCj3v$RU(u zLo9C&8P0BG+WBo?oVs$I^fKqw{1c+v#$u!==^^HD$2oG($b0#iNjNG(!nP zh#KSTt%!rVqk?fvcz#>l(PE%d-CT9W($?*lZN9EL&^QYwSnJN2+S6@+lPLqTt1`0d zft0qoXSg??j%<$=ri-Yrj)=M`F-x&&VyD%JiUswKc@bmxfwc4?LFiGvK#7E@+Kfhb zwWLO9TRn1Di}9DOdQqw-^APLfJKb^JRV`U8m?E+bA&#Qf2)CG0GeV^XBvtPIR#V*J zrP(otTrnobOB2p;Y6QdgY``#9N@idt3@~4(-suQUyqIPHj$}N6tE)VtqRNOIGW}SW z$|UAsEj?y3<>Pd9Ei7fDO#cFeTT9*;RRj~9FpgTPR~=cN(^DMvU2l0>cc^q$wcI>M zw6*cYq!vn5otcnmMQ$T3xn3lLX28rQ5bnC}Ch^-CANS6Ro{>R_FB7q@z7)%u$dUx~ zL^A22T|y>&nKH|IJctFNwj1)^#HM*NDlu`3{Hotb^vya|rWc!|V#y@*&zFePCQ6>w z+=jVCH6gyigm>$loeZ&9-2W1_@ttKV(B80=*AF@^nl+ddcQ~j0l~FO{oN;Tc5p}<) zF#!`NhzOr*HN?Ja#l*Ff`#(8(V4v7GJ~5%Q4~em>$LJ?&=08abaTr)VvDEti&K;KZ zeb%39{a-eF5YMuve;hswH^Bk;DEy6e{Il>7wBQ!F8g{{-S<62KUxsy1>-$f@Tddok zf(M`lOK=&ewfr;iINSxxZ~*qeKUuFo0bhV4a3MU;I{gu-z{T(z*5`M@HSjv?@kd}D z{>?i4U+@zA5*~uDfeW)xhGDn}E`&E&qdyOi!hLWzoPsgf0Z-x^<)6otkMd9Mf!qV{ zf(O*Nnnc;4#nRAHPlohhSekb>+;zP*Acl_0dPJmKXWK$elGHk%e@hZ9e;HWxBAZ~j zzRBb$I3vS&&7UaNBNV~ED#NgNb#}Zen%#v&vZ00&@7VNGF}6SraHHa=erkr1-YkJ! zRms7L4L0VAPO}+TAwmPe?P0Sa(-{t@Ohr1wU_9n`*Ur>3nL()5 zh%GNn`<@ps;Dz4Zsg=?#B?dQ>cGM4(4FjAS`W~8;67{k4U;Y@4M|{N6FC1PcQ}TeF zzI0x9ZYq(+SvIo zY&03o#+lA*uFo}lns?AwW;+I}Ww0($T_{sCv1O~*lrmX7>zsUd)v*(sbbeN&^YMm! zQSFnn7oWC*d0>8rDKcs)QMRJ5y1C_L7M3OoZ-GMT%C`y1M$zu%8hJtD;;rAIPTVv;scVZ|iw zqG?G5V^f9W8HI_+U5T@V)I1gT7&Hp5P22R5Dup55LdjCgo9~_0t;dR#g z--AbLy+VFeC? zuKT;NqRETQJ&=1K_dxD}_oxSwO^BW`wck7?JIoF?FsI8WhUH3rpz58Epv4O@yv3r& zOkwf^Rr!Ic{6JNHpo(LJ-6zC!bX9TN?0=|ZBWC~V?6WrAJ38H(2kwR3-BY-Lbk)l>N^SRK?wuUR>7wRyS1Vu;qyLUtl&Z A2><{9 diff --git a/pythonforumide/CONFIG b/pythonforumide/CONFIG deleted file mode 100644 index 491a6a0..0000000 --- a/pythonforumide/CONFIG +++ /dev/null @@ -1,2 +0,0 @@ -838 -1029 diff --git a/pythonforumide/config/.config.py.swp b/pythonforumide/config/.config.py.swp deleted file mode 100644 index a6e58919dfeea53c4a45c19992529ca92ba98639..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeHPON<;x8Lq^T@Nx)20tE@FvJLB6$5^&3f0~bx25pI<+&^GfjHB zo9gZzPl9<}Kmu+EaRA{%A|y~iLR?4`1qli9Iv_4VG6xGGAcw#K!Y#n}S5?0zyTJh@ zB2*)PYIoP`ukU}<)81YA@cLQ)-tJutzndAm{h8k8eSOA0dz`UQvrM&FV=35R$ zlEulvSwCqr9yvG5#&L8iR{3Q8#K|Lxe)Z`!7|J7qIP!y$eRPxA!4Gst<90QI4$ADwNG2j?*3^)cH1C9a5fMdWh z;28KHFd&AEeG7~KL4dBm*8T$k9d*Hjkr-2RN9l))?Q*UDI+kgV@1I_?FU$id<9Uz$AR;}+kh8dk96RxKnf^80zKeX;JMc^_6y+Wz+=ES zfE>6JxDog}HZ`sQUj{w_Yyq3V?Z68+F!opADsUOt19ISg;AY@2uVw64z;}QrfJcD{ zI1RiA&VK@Y9rzkB0Z0$$flI&+a2U9X&6Q_??*rJbVFREC+yMLux_ch@5%47-1U%qF zz#?!xa2-%?@Vvyvxc#Lv^t5xCEZj?J++^fg%qc?`-*uj$ z^m!`wOxdM=S&qs#Ua0N|;v@^==%aj_A6$fG4Mu2QAjese!NpGfSd=%`-2%S)MQR_r|9$JGkCZ5;m zaEh)Yr*AJMc%98+u+41zBktHlMW(CBqp<&*}}7P7L*57zZiGA2P>* z_68pBehk{=19T~qLk=BIktU+WjOQTwBOf@s!9_IWW4J#Ih7CeegLls;oS9bIriQv7 z&cr0_4)aN3VvT93V;H1fRm4Ij9e5fdiIh_fJkDXP#&@i+RJiQVd{8R&USqxsrzoSx zK5p>}1Iz)Dp{SUGPSrP)In|-4@NY6C{Qb1dOpk3$@KrpiDWyYdsD%t@i~=XVC==93 z?0+*%92lJ;7DWdU6@d!W3lqY4Ei@F$9LCZ}r3aOUQal8R{Q_7KkW9F2s!JRDDw>v2 z=~$h`>0-@0h`X9<;UteS;EjjX8|r@D1UXDn)CzFKeKlP*|9AVyeb? zF^Q`63$^+p2ve>pB<5J{V;OF6*jvo8?zIBm$fGA=VqmIZhmDPAzCMJ&`bn(}R%sY9ttpgT^xiO9y?Gzl?$Ud-BtBVxxW%aWK@!w03|D4T^1O=+6bE@Uc&#DHeg?YM9X`q>hN0bYK*{zQI!L) zA-vwD%Fy;HqRB92imDE_&xVz2UD+uK%pr{tLj3%ZM`kdPQ81vMh`}J9z_`;f;y{42 zSR$Ovm!3N`d6T}6(Y((u$PjUbh7g#PBBhuiU0Xpl`WK{;8&D?Lg*CY}PbdJu~2GZ>FF}B?_vFNknNetNfHe zz|b<-khE5t#q$=;LyD=4?nS@Nig}+7VYT^vt=0?5u1jihjCNQgZ@E#m$mfH=2T$Y2Rk2T#V%E<4>GDv z51p9O4lQ!xRD%8{l7R2xxB+`h*e;>GddtD=HDHsdww=lJP8bf8wvbIt=`;!_CP|FF zzT&hZr%j3C0J5rMZj+N?!=>1g=(84fyikumW}!_a5+c%)mRYBV;8;}A02 zO$&1vqu2kx#TonkIESb8e{sM6A6W1I8h93X0vG{(pam1D^pN1Rel%JFgP^aAG(H90QI4$ADwNG2j?*3^)cH1CD|J76UkI*T32| sA$PNu{%<#TvljaX|N3Sv-DJi^Hhn|j72ca;*7T~Ylyre*pF4B^1agA(2><{9 From cc5367b28542ab0b986dee95244bea95a51cd0f0 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Sun, 31 Jul 2011 21:58:24 +0100 Subject: [PATCH 006/141] Minimal changes, test commit --- pythonforumide/wx_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 554a799..23121ab 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -2,7 +2,7 @@ Created on 31 Jul 2011 @author: D.W., david -@reviewr: david +@reviewer: david """ import wx From e14d3bf5906d76e82e075b606fbf9a455bbd97b9 Mon Sep 17 00:00:00 2001 From: bunburya Date: Sun, 31 Jul 2011 23:04:53 +0100 Subject: [PATCH 007/141] fixed some imports and config filepath finding --- pythonforumide/config/Ide_Config.cfg | 6 ------ pythonforumide/config/config.py | 9 +++++---- pythonforumide/gui_lib/ide_menu.py | 4 ++-- pythonforumide/gui_lib/ide_toolbar.py | 2 +- pythonforumide/wx_app.py | 2 +- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 072431c..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +0,0 @@ -[ide] -usetab = 0 -mainframe.height = 728 -mainframe.width = 1237 -indent = 4 - diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index f934973..91413da 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -128,8 +128,8 @@ def save(self): class Ide_config(object): def __init__(self, filepath= "", filename= "Ide_Config"): if not filepath: - filepath= os.path.splitext(__file__)[0] - filepath= filepath.rsplit("\\", 1)[0] + filepath= os.path.dirname(__file__) + #filepath= filepath.rsplit("\\", 1)[0] self._filepath = filepath self._filename = filename self._fullpath= None @@ -138,9 +138,10 @@ def __init__(self, filepath= "", filename= "Ide_Config"): self._get_defaults() def _get_file_fullpath(self): - ext= ".cfg" if has_yaml: ext= ".yaml" + else: + ext= ".cfg" self._fullpath= os.path.join(self._filepath, self._filename)+ext if not os.path.exists(self._fullpath): a= file(self._fullpath, "w") @@ -172,4 +173,4 @@ def update_configfile(self): ide_config= Ide_config() print (ide_config["indent"]) ide_config.update_configfile() - \ No newline at end of file + diff --git a/pythonforumide/gui_lib/ide_menu.py b/pythonforumide/gui_lib/ide_menu.py index 4dc5c22..1c4d80e 100644 --- a/pythonforumide/gui_lib/ide_menu.py +++ b/pythonforumide/gui_lib/ide_menu.py @@ -6,7 +6,7 @@ import wx import ide_constant as ide -from pythonforumide.ide_images import menu_icons +from ide_images import menu_icons class MenuBar(wx.MenuBar): ''' @@ -119,4 +119,4 @@ def _add_menu_item(self, parent_menu, id, id_text, icon_bmp= None): - \ No newline at end of file + diff --git a/pythonforumide/gui_lib/ide_toolbar.py b/pythonforumide/gui_lib/ide_toolbar.py index c3d7e7d..506441d 100644 --- a/pythonforumide/gui_lib/ide_toolbar.py +++ b/pythonforumide/gui_lib/ide_toolbar.py @@ -6,7 +6,7 @@ import wx import ide_constant as ide -from pythonforumide.ide_images import menu_icons +from ide_images import menu_icons class ToolBar(wx.ToolBar): diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 23121ab..0444978 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -8,7 +8,7 @@ import wx import gui_lib.ide_mainframe as ide_mainframe import gui_lib.ide_mainframe_events as ide_mainframe_events -from pythonforumide.config.config import Ide_config +from config.config import Ide_config #import config.config.Ide_config as Ide_config from twisted.internet import wxreactor From 2b1c75481811cbac5eae41fa35b2b3392e6bea02 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Sun, 31 Jul 2011 23:07:48 +0100 Subject: [PATCH 008/141] Changes to the way a missing attribute is handled --- pythonforumide/.wx_main.py.swp | Bin 20480 -> 0 bytes pythonforumide/CONFIG | 2 -- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/ide_images/menu_icons.py | 11 +++++++++++ 4 files changed, 13 insertions(+), 4 deletions(-) delete mode 100644 pythonforumide/.wx_main.py.swp delete mode 100644 pythonforumide/CONFIG diff --git a/pythonforumide/.wx_main.py.swp b/pythonforumide/.wx_main.py.swp deleted file mode 100644 index eca86b7d9ca49276c61c10ae61920761a5ca6fa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI3ZHy#E8Gu{vKoCwmO!SA*6tlq2a=UZ8cZZM-8}4p*_E>ZK0dspNA7@|hOwDd{ zv)z;Ko||11QTV4uArgWTHJD(6qKTggks!t&0x>}hF(I0eC=n7g@WVffKYXgXdb($B zSmv@OVA7L3JKbGxz4g{x)o*=F?aKJ^xp{GIX}`tiLzeZErzRGTPg>SJhb^n&v;yfx zDrL)$ksE6`&B$-Qz3AtS;NGRpXwCO#{h+lzH&fo*)VFnY>(=Dn_J+OgxL&Eb`Sz;v zl-vWk2hPO_hA zd*J`v15tB_bvJc5&(uRN4!C}wxlg`#n)~a`_r9L=9p=7lzSn!w&29dbdm#5f?t$C` zxd(C&F%la!k3=!1e2KWc|{{dbARhO@Tgfd(KL-0rH`5kx=HsK�O!N2 z7g9dl3u|yA6yWdgx2&JTkKyZZ8g{`Y@cIShffYCjH^5uxTh{O4o3IR*!kcWKyaCU{ zmtX_hFc14+0zLxIv5E2n_$mZ24OhVqc!3R?pTYfb3J$_q}uR9X-CeA?bgJUXm6AjWP7fbk?2uU44pI5c0%MwMWMot zaKb{WFik!Obsb%|N7GTz*i*?U>?yxrC3KmC+VA6v*!V@ROo5Aq5z+C<03{kFgH_R! z?YKZ(Ysy>5R4`87T2-8?+A`BJ>)al8f;5erOmd-6s5G6n7m8?23MCv0$E%4@M)9K+ z*Q>eSDmT73EoD>Kw(Gf(ZHsWt4<~og1WzBET`!%Og91&Bxm(9)Cx0EXxE%lP_ikZyyQaf-XSu9)`-tH+1;!2@Q zFCCFl#l2G&M{XNGIZA~nJbg5AawP4Jb-be*t5}#`T$r6ZQlQheael}8Ays?Tfy9dB zY2kXp5p8Ev_;^c3vKrASN}(fEPwXfWJs;jxTxc&WiqEO{ zc8&_#H!YVd$L1CnQU~;862oQcC*p%wfSh`h@(jnu=qt*IPBFTal#UNdl(=O&n6cGF zs~&UT3#OiJXOEF*xwN~|G-S7TrF#}X@oVMH2!<|;5{nAz%Pusy{ulk;P1>;uX zx>L4E*MU?uP3Fb#wtLRwP11d&*s*F0>tekWS=RQ)@glJ{HqW%Z4hyQRw4ZjYb80oy z#fpWN7r9YG)~*aWiKt?bwl8@~$gIhYwBAI`MdA&imbbhu7+FQzjn>SqvCj3v$RU(u zLo9C&8P0BG+WBo?oVs$I^fKqw{1c+v#$u!==^^HD$2oG($b0#iNjNG(!nP zh#KSTt%!rVqk?fvcz#>l(PE%d-CT9W($?*lZN9EL&^QYwSnJN2+S6@+lPLqTt1`0d zft0qoXSg??j%<$=ri-Yrj)=M`F-x&&VyD%JiUswKc@bmxfwc4?LFiGvK#7E@+Kfhb zwWLO9TRn1Di}9DOdQqw-^APLfJKb^JRV`U8m?E+bA&#Qf2)CG0GeV^XBvtPIR#V*J zrP(otTrnobOB2p;Y6QdgY``#9N@idt3@~4(-suQUyqIPHj$}N6tE)VtqRNOIGW}SW z$|UAsEj?y3<>Pd9Ei7fDO#cFeTT9*;RRj~9FpgTPR~=cN(^DMvU2l0>cc^q$wcI>M zw6*cYq!vn5otcnmMQ$T3xn3lLX28rQ5bnC}Ch^-CANS6Ro{>R_FB7q@z7)%u$dUx~ zL^A22T|y>&nKH|IJctFNwj1)^#HM*NDlu`3{Hotb^vya|rWc!|V#y@*&zFePCQ6>w z+=jVCH6gyigm>$loeZ&9-2W1_@ttKV(B80=*AF@^nl+ddcQ~j0l~FO{oN;Tc5p}<) zF#!`NhzOr*HN?Ja#l*Ff`#(8(V4v7GJ~5%Q4~em>$LJ?&=08abaTr)VvDEti&K;KZ zeb%39{a-eF5YMuve;hswH^Bk;DEy6e{Il>7wBQ!F8g{{-S<62KUxsy1>-$f@Tddok zf(M`lOK=&ewfr;iINSxxZ~*qeKUuFo0bhV4a3MU;I{gu-z{T(z*5`M@HSjv?@kd}D z{>?i4U+@zA5*~uDfeW)xhGDn}E`&E&qdyOi!hLWzoPsgf0Z-x^<)6otkMd9Mf!qV{ zf(O*Nnnc;4#nRAHPlohhSekb>+;zP*Acl_0dPJmKXWK$elGHk%e@hZ9e;HWxBAZ~j zzRBb$I3vS&&7UaNBNV~ED#NgNb#}Zen%#v&vZ00&@7VNGF}6SraHHa=erkr1-YkJ! zRms7L4L0VAPO}+TAwmPe?P0Sa(-{t@Ohr1wU_9n`*Ur>3nL()5 zh%GNn`<@ps;Dz4Zsg=?#B?dQ>cGM4(4FjAS`W~8;67{k4U;Y@4M|{N6FC1PcQ}TeF zzI0x9ZYq(+SvIo zY&03o#+lA*uFo}lns?AwW;+I}Ww0($T_{sCv1O~*lrmX7>zsUd)v*(sbbeN&^YMm! zQSFnn7oWC*d0>8rDKcs)QMRJ5y1C_L7M3OoZ-GMT%C`y1M$zu%8hJtD;;rAIPTVv;scVZ|iw zqG?G5V^f9W8HI_+U5T@V)I1gT7&Hp5P22R5Dup55LdjCgo9~_0t;dR#g z--AbLy+VFeC? zuKT;NqRETQJ&=1K_dxD}_oxSwO^BW`wck7?JIoF?FsI8WhUH3rpz58Epv4O@yv3r& zOkwf^Rr!Ic{6JNHpo(LJ-6zC!bX9TN?0=|ZBWC~V?6WrAJ38H(2kwR3-BY-Lbk)l>N^SRK?wuUR>7wRyS1Vu;qyLUtl&Z A2><{9 diff --git a/pythonforumide/CONFIG b/pythonforumide/CONFIG deleted file mode 100644 index 491a6a0..0000000 --- a/pythonforumide/CONFIG +++ /dev/null @@ -1,2 +0,0 @@ -838 -1029 diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 072431c..ffc8eaa 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = 0 -mainframe.height = 728 -mainframe.width = 1237 +mainframe.height = 600 +mainframe.width = 600 indent = 4 diff --git a/pythonforumide/ide_images/menu_icons.py b/pythonforumide/ide_images/menu_icons.py index 7330c43..7cbec24 100644 --- a/pythonforumide/ide_images/menu_icons.py +++ b/pythonforumide/ide_images/menu_icons.py @@ -6,6 +6,16 @@ import wx +class MissingArt(object): + def __init__(self, f): + self.f = f + + def __call__(self): + try: + return self.f() + except AttributeError: + return wx.NullBitmap + def get_icon_new(): ''' Returns a (24,24) new icon bmp @@ -60,6 +70,7 @@ def get_icon_redo(): ''' return _get_art_bmp(wx.ART_REDO) +@MissingArt def get_icon_close(): ''' Returns a (24,24) close icon bmp From b7b8e3823385458fd2704c0b5815548b152be4c1 Mon Sep 17 00:00:00 2001 From: bunburya Date: Sun, 31 Jul 2011 23:11:17 +0100 Subject: [PATCH 009/141] Cleaned up Ide_Config.py, as it was confusing ConfigParser. --- pythonforumide/config/Ide_Config.cfg | 9 --------- pythonforumide/config/config.py | 1 - 2 files changed, 10 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 6937237..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +0,0 @@ -<<<<<<< HEAD -[ide] -usetab = 0 -mainframe.height = 600 -mainframe.width = 600 -indent = 4 - -======= ->>>>>>> e14d3bf5906d76e82e075b606fbf9a455bbd97b9 diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index 91413da..8c76ea1 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -129,7 +129,6 @@ class Ide_config(object): def __init__(self, filepath= "", filename= "Ide_Config"): if not filepath: filepath= os.path.dirname(__file__) - #filepath= filepath.rsplit("\\", 1)[0] self._filepath = filepath self._filename = filename self._fullpath= None From 0d5dbeca7054ab0965e4982d2a8b61875fdb75ef Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Sun, 31 Jul 2011 23:21:27 +0100 Subject: [PATCH 010/141] Pushed some 2.8 \ 2.9 fixes --- pythonforumide/config/Ide_Config.cfg | 6 ++++++ pythonforumide/gui_lib/ide_editor.py | 1 - pythonforumide/gui_lib/ide_notebook.py | 22 ++++++++++++++-------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index e69de29..ffc8eaa 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = 0 +mainframe.height = 600 +mainframe.width = 600 +indent = 4 + diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index fba4095..d38f331 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -187,7 +187,6 @@ def on_replace(self): """Displays a find/replace dialog""" #I think we should create a new frame for this, to be coded yet - if __name__=='__main__': import ide_test_app as wx_app import ide_test_frame diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 35aaac4..f728219 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -111,17 +111,23 @@ def replace_active_editor(self): def active_editor_can_cut(self): """Returns True if the active editor can cut""" - if self._active_editor_page: - return self._active_editor_page.CanCut() - else: - return False + try: + if self._active_editor_page: + return self._active_editor_page.CanCut() + else: + return False + except AttributeError: + return True def active_editor_can_copy(self): """Returns True if the active editor can copy""" - if self._active_editor_page: - return self._active_editor_page.CanCopy() - else: - return False + try: + if self._active_editor_page: + return self._active_editor_page.CanCopy() + else: + return False + except AttributeError: + return True def active_editor_can_paste(self): """Returns True if the active editor can paste""" From 4d137a6c2386145c153b351a6e47be6b8931e110 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Sun, 31 Jul 2011 23:51:58 +0100 Subject: [PATCH 011/141] Compat changes --- pythonforumide/gui_lib/ide_notebook.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index f728219..c6630fb 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -138,10 +138,13 @@ def active_editor_can_paste(self): def active_editor_can_delete(self): """Returns True if the active editor can delete""" - if self._active_editor_page: - return self._active_editor_page.HasSelection() - else: - return False + try: + if self._active_editor_page: + return self._active_editor_page.HasSelection() + else: + return False + except AttributeError: + return True def active_editor_can_undo(self): From 369a303fe79436e699976a79c01334a8c478f9da Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 00:28:23 +0100 Subject: [PATCH 012/141] Added an OutputFrame, error displays when trying to open it for now --- pythonforumide/gui_lib/ide_mainframe.py | 17 +++++++++-------- pythonforumide/gui_lib/ide_mainframe_events.py | 12 +++++++----- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 195d8c3..56c95aa 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -16,7 +16,7 @@ class MainFrame(wx.Frame): """Class with the GUI and GUI functions""" - def __init__(self,*args, **kwargs): + def __init__(self, *args, **kwargs): """Creates the frame, calls some construction methods.""" wx.Frame.__init__(self, *args, **kwargs) self.config= wx.GetApp().config @@ -66,8 +66,6 @@ def _start_up(self): perhaps open last edited in the future, for now just open new. ''' self.notebook.new_editor_tab() - - def on_exit(self): ''' @@ -83,12 +81,8 @@ def on_exit(self): self.config["MainFrame.Width"]= self.GetSize()[0] self.config["MainFrame.Height"]= self.GetSize()[1] -# f = open("CONFIG", "w") -# f.write("%s\n%s\n" % (self.GetSize()[0], self.GetSize()[1])) -# f.close() - if dial.ShowModal() == wx.ID_YES: - self.Destroy() + self.Destroy() def get_file(self, prompt, style): """Abstracted method to prompt the user for a file path. @@ -103,6 +97,13 @@ def get_file(self, prompt, style): dlg.Destroy() return dirname, filename + def on_run(self, event): + from ide_outputframe import OutputFrame + output_app = wx.PySimpleApp() + output_frame = OutputFrame(parent = None, title="") + output_frame.Show() + output_app.MainLoop() + if __name__=='__main__': import ide_test_app as wx_app app = wx_app.Wx_App(False) diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index da852f8..39ac3f6 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -1,11 +1,12 @@ -''' +""" Created on 31 Jul 2011 @author: D.W. -''' +""" import wx import ide_constant as ide +from ide_mainframe import MainFrame class MainFrameEvents(object): def __init__(self, view, model= None): @@ -33,7 +34,8 @@ def _create_binds(self): self.view.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) self.view.Bind(wx.EVT_MENU, self._on_editor_selectall, id=ide.ID_SELECTALL) self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, id=ide.ID_SEARCH) - + self.view.Bind(wx.EVT_MENU, MainFrame.on_run, id=ide.ID_RUNFILE) + def _on_new(self, event): """Opens a new tab with a new editor instance""" self.view.notebook.new_editor_tab() @@ -85,7 +87,7 @@ def _on_editor_selectall(self, event): def _on_editor_search_and_replace(self, event): """Replace for the current editor tab""" self.view.notebook.replace_active_editor() - + def _on_exit(self, event): """ application wants to exit""" self.view.on_exit() @@ -119,4 +121,4 @@ def _evt_update_ui(self, event): event.Enable(self.view.notebook.active_editor_can_run()) else: event.Skip() - \ No newline at end of file + From 841431da8b2e8f780fcd1fd3d0b43ab757c6744f Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Mon, 1 Aug 2011 00:29:17 +0100 Subject: [PATCH 013/141] reoreder creation --- pythonforumide/gui_lib/ide_mainframe.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 195d8c3..6257cb3 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -29,12 +29,16 @@ def __init__(self,*args, **kwargs): self.SetSizer(self.frame_sizer) self.frame_panel= wx.Panel(self, style= wx.BORDER_THEME) self.frame_sizer.Add(self.frame_panel, 1, wx.EXPAND|wx.ALL, 1) + self.panel_sizer= wx.BoxSizer(wx.VERTICAL) self.frame_panel.SetSizer(self.panel_sizer) - + + + self._create_notebook_panel() + self._create_menu() self._create_toolbar() - self._create_notebook_panel() +# self._create_notebook_panel() self._start_up() self.Layout() self.Show() From 1fcfe67e1a33803ac9526fd40ab7bc74bca59965 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Mon, 1 Aug 2011 00:50:53 +0100 Subject: [PATCH 014/141] reorder creation --- pythonforumide/config/Ide_Config.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index ffc8eaa..7a831b9 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = 0 -mainframe.height = 600 -mainframe.width = 600 +mainframe.height = 626 +mainframe.width = 722 indent = 4 From 1810af5617660686fcebbe2d551ce48c330b7e44 Mon Sep 17 00:00:00 2001 From: confab Date: Sun, 31 Jul 2011 17:54:49 -0700 Subject: [PATCH 015/141] run command opens a skeleton, other minor fixes --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/gui_lib/ide_editor.py | 13 ++++++++++++- pythonforumide/gui_lib/ide_mainframe_events.py | 6 +++++- pythonforumide/gui_lib/ide_notebook.py | 6 +++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 7a831b9..ffc8eaa 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = 0 -mainframe.height = 626 -mainframe.width = 722 +mainframe.height = 600 +mainframe.width = 600 indent = 4 diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index d38f331..dd13b1c 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -11,6 +11,8 @@ import wx.stc as stc import wx +from ide_test_frame import TestFrame + #TODO: make customisable font and sizes. Perhaps maked this named tuple? faces = { 'times': 'Times', 'mono' : 'Courier', @@ -123,7 +125,7 @@ def SmartIndent(self): indent_level = max([indent_level - 1, 0]) if usetab: - indent = "/t" * indent_level + indent = "\t" * indent_level else: indent = indent_amount * " " * indent_level @@ -186,6 +188,15 @@ def save_file_as(self, filepath): def on_replace(self): """Displays a find/replace dialog""" #I think we should create a new frame for this, to be coded yet + pass + + def on_run(self): + """Runs selected code in a new window.""" + + # Create a test frame and hook into the caller. + # Allows this frame to be destroyed by the main window on close. + self.editor_run = TestFrame(wx.GetApp().TopWindow, title="") + return self.editor_run if __name__=='__main__': import ide_test_app as wx_app diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 39ac3f6..74ea99d 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -34,7 +34,7 @@ def _create_binds(self): self.view.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) self.view.Bind(wx.EVT_MENU, self._on_editor_selectall, id=ide.ID_SELECTALL) self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, id=ide.ID_SEARCH) - self.view.Bind(wx.EVT_MENU, MainFrame.on_run, id=ide.ID_RUNFILE) + self.view.Bind(wx.EVT_MENU, self._on_editor_run, id=ide.ID_RUNFILE) def _on_new(self, event): """Opens a new tab with a new editor instance""" @@ -91,6 +91,10 @@ def _on_editor_search_and_replace(self, event): def _on_exit(self, event): """ application wants to exit""" self.view.on_exit() + + def _on_editor_run(self, event): + """Runs selected code in a new window.""" + self.view.notebook.run_active_editor() def _evt_update_ui(self, event): ''' diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index c6630fb..a2ec41a 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -109,6 +109,10 @@ def replace_active_editor(self): """Replace changes in active editor""" self._active_editor_page.on_replace() + def run_active_editor(self): + """Runs selected code in a new window.""" + self._active_editor_page.on_run() + def active_editor_can_cut(self): """Returns True if the active editor can cut""" try: @@ -210,4 +214,4 @@ def active_editor_can_run(self): panel.sizer.Add(notebook, 1, wx.EXPAND) frame.Layout() app.MainLoop() - \ No newline at end of file + From 82ad04114c1568c2335799bc6e36566740ee287d Mon Sep 17 00:00:00 2001 From: confab Date: Sun, 31 Jul 2011 18:36:19 -0700 Subject: [PATCH 016/141] run command opens skeleton, other minor logic changes --- pythonforumide/gui_lib/ide_editor.py | 4 ++-- pythonforumide/gui_lib/ide_mainframe_events.py | 3 ++- pythonforumide/gui_lib/ide_notebook.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index dd13b1c..4065c16 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -1,6 +1,6 @@ """ -@author: Jakob, David, bunburya -@reviewer: Somelauw +@author: Jakob, David, bunburya, confab +@reviewer: Somelauw, ghoulmaster """ #YES DIRT HACK GET OVER IT. Dont remove it might go before it goes into master diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 74ea99d..6677826 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -1,7 +1,8 @@ """ Created on 31 Jul 2011 -@author: D.W. +@author: D.W., confab +@reviewer: ghoulmaster """ import wx diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index a2ec41a..4cc7989 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -2,7 +2,8 @@ """ Created on Wed Jul 27 17:36:42 2011 -@author: jakob, David +@author: jakob, David, confab +@reviewer: ghoulmaster """ import os From b0bf71b10f0ce72233c5826185208cc3e3505140 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:15:35 +0100 Subject: [PATCH 017/141] Fixed PEP-8 Related Things --- pythonforumide/gui_lib/ide_menu.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_menu.py b/pythonforumide/gui_lib/ide_menu.py index 1c4d80e..b9634e0 100644 --- a/pythonforumide/gui_lib/ide_menu.py +++ b/pythonforumide/gui_lib/ide_menu.py @@ -1,7 +1,8 @@ ''' Created on 31 Jul 2011 -@author: D.W. +@author: D.W., David +@reviewer: david ''' import wx @@ -48,7 +49,8 @@ def _add_file_menu(self): self._add_menu_item(fileMenu, ide.ID_CLOSETAB, ide.id_text_closetab, menu_icons.get_icon_close()) - self._add_menu_item(fileMenu, ide.ID_EXITAPP, ide.id_text_exitapp, menu_icons.get_icon_quit()) + self._add_menu_item(fileMenu, ide.ID_EXITAPP, ide.id_text_exitapp, + menu_icons.get_icon_quit()) self.Append(fileMenu, "&File") @@ -91,7 +93,8 @@ def _add_search_menu(self): searchMenu = wx.Menu() self._add_menu_item(searchMenu, ide.ID_SEARCH, - ide.id_text_search, menu_icons.get_find_and_replace()) + ide.id_text_search, + menu_icons.get_find_and_replace()) self.Append(searchMenu, "&Search") @@ -107,7 +110,8 @@ def _add_run_menu(self): self.Append(runMenu, "&Run") def _add_menu_item(self, parent_menu, id, id_text, icon_bmp= None): - item= wx.MenuItem(parent_menu, id, id_text.menu, id_text.status, id_text.menu_kind) + item= wx.MenuItem(parent_menu, id, id_text.menu, id_text.status, + id_text.menu_kind) if icon_bmp: item.SetBitmap(icon_bmp) parent_menu.AppendItem(item) From cfc1d54ab0be40e89e04154553a7d55328ae3ba3 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:22:38 +0100 Subject: [PATCH 018/141] More PEP-8-Related Fixes --- pythonforumide/gui_lib/ide_editor.py | 5 +-- pythonforumide/gui_lib/ide_mainframe.py | 5 +-- .../gui_lib/ide_mainframe_events.py | 7 ++-- pythonforumide/gui_lib/ide_menu.py | 32 ++++--------------- pythonforumide/gui_lib/ide_notebook.py | 11 +++---- pythonforumide/gui_lib/ide_notebook_events.py | 18 +++++------ 6 files changed, 30 insertions(+), 48 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 4065c16..00926a6 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -67,7 +67,8 @@ def SetMargins(self): self.SetMarginWidth(2, 28) def SetStyles(self, lang='python'): - """This is different from the other Set methods thathttp://paste.pocoo.org/show/446107/ are called in the + """This is different from the other Set methods that + http://paste.pocoo.org/show/446107/ are called in the __init__ this one is for the highlighting and syntax of the langauge, this will eventually be callable with different langauge styles. For the moment, leave the lang kwarg in. """ @@ -168,7 +169,7 @@ def on_clear(self): self.Clear() def on_select_all(self): - """Selects all the text, this function is not necessary but makes it cleaner""" + """Selects all the text""" self.SelectAll() def load_file(self, path): diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 1486ae2..4b452ec 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -53,7 +53,8 @@ def _create_toolbar(self): ''' creates a toolbar ''' - tb= ide_toolbar.ToolBar(self, style= wx.TB_HORIZONTAL|wx.NO_BORDER|wx.TB_FLAT) + tb= ide_toolbar.ToolBar(self, style= wx.TB_HORIZONTAL|wx.NO_BORDER| + wx.TB_FLAT) self.SetToolBar(tb) def _create_notebook_panel(self): @@ -97,7 +98,7 @@ def get_file(self, prompt, style): filename = dlg.GetFilename() else: # so maybe add error handling here. - raise RuntimeError("I guess something has gone wrong with the dialog") + raise RuntimeError("Something has gone wrong with the dialog") dlg.Destroy() return dirname, filename diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 6677826..8d94074 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -33,8 +33,10 @@ def _create_binds(self): self.view.Bind(wx.EVT_MENU, self._on_editor_copy, id=ide.ID_COPY) self.view.Bind(wx.EVT_MENU, self._on_editor_paste, id=ide.ID_PASTE) self.view.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) - self.view.Bind(wx.EVT_MENU, self._on_editor_selectall, id=ide.ID_SELECTALL) - self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, id=ide.ID_SEARCH) + self.view.Bind(wx.EVT_MENU, self._on_editor_selectall, + id=ide.ID_SELECTALL) + self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, + id=ide.ID_SEARCH) self.view.Bind(wx.EVT_MENU, self._on_editor_run, id=ide.ID_RUNFILE) def _on_new(self, event): @@ -126,4 +128,3 @@ def _evt_update_ui(self, event): event.Enable(self.view.notebook.active_editor_can_run()) else: event.Skip() - diff --git a/pythonforumide/gui_lib/ide_menu.py b/pythonforumide/gui_lib/ide_menu.py index b9634e0..70dc975 100644 --- a/pythonforumide/gui_lib/ide_menu.py +++ b/pythonforumide/gui_lib/ide_menu.py @@ -10,9 +10,7 @@ from ide_images import menu_icons class MenuBar(wx.MenuBar): - ''' - Creates a menubar - ''' + """Creates a menubar""" def __init__(self, parent): super(MenuBar, self).__init__() self._parent = parent @@ -21,12 +19,10 @@ def __init__(self, parent): self._add_search_menu() self._add_run_menu() -# self._parent.SetMenuBar(self) + #self._parent.SetMenuBar(self) def _add_file_menu(self): - ''' - Adds the file menu - ''' + """Adds the file menu""" fileMenu = wx.Menu() self._add_menu_item(fileMenu, ide.ID_NEW, ide.id_text_new, menu_icons.get_icon_new()) @@ -55,9 +51,7 @@ def _add_file_menu(self): self.Append(fileMenu, "&File") def _add_edit_menu(self): - ''' - Adds the edit menu - ''' + """Adds the edit menu""" editMenu = wx.Menu() self._add_menu_item(editMenu, ide.ID_UNDO, @@ -87,9 +81,7 @@ def _add_edit_menu(self): self.Append(editMenu, "&Edit") def _add_search_menu(self): - ''' - Adds the search menu - ''' + """Adds the search menu""" searchMenu = wx.Menu() self._add_menu_item(searchMenu, ide.ID_SEARCH, @@ -99,9 +91,7 @@ def _add_search_menu(self): self.Append(searchMenu, "&Search") def _add_run_menu(self): - ''' - Adds the run menu - ''' + """Adds the run menu""" runMenu = wx.Menu() self._add_menu_item(runMenu, ide.ID_RUNFILE, @@ -110,17 +100,9 @@ def _add_run_menu(self): self.Append(runMenu, "&Run") def _add_menu_item(self, parent_menu, id, id_text, icon_bmp= None): + """Adds a menu item to the parent_menu""" item= wx.MenuItem(parent_menu, id, id_text.menu, id_text.status, id_text.menu_kind) if icon_bmp: item.SetBitmap(icon_bmp) parent_menu.AppendItem(item) - - - - - - - - - diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 4cc7989..964d982 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -30,7 +30,8 @@ def new_editor_tab(self, page_name= ""): def open_editor_tab(self): """Loads a slected file into a new editor tab""" - dirname, filename= self.GetGrandParent().get_file('Open a file', wx.OPEN) + dirname, filename= self.GetGrandParent().get_file('Open a file', + wx.OPEN) if dirname and filename: editor= self.new_editor_tab(filename) path = os.path.join(dirname, filename) @@ -45,7 +46,8 @@ def save_active_editor_tab(self): def save_as_active_editor_tab(self): """Save as for the currently active editor file""" - dirname, filename = self.GetGrandParent().get_file('Save file as', wx.SAVE) + dirname, filename = self.GetGrandParent().get_file('Save file as', + wx.SAVE) if dirname and filename: path = os.path.join(dirname, filename) if path: @@ -193,15 +195,13 @@ def active_editor_can_search(self): return True else: return False - + def active_editor_can_run(self): """Returns True if the active editor can search""" if self._active_editor_page: return True else: return False - - if __name__=='__main__': import ide_test_app as wx_app @@ -215,4 +215,3 @@ def active_editor_can_run(self): panel.sizer.Add(notebook, 1, wx.EXPAND) frame.Layout() app.MainLoop() - diff --git a/pythonforumide/gui_lib/ide_notebook_events.py b/pythonforumide/gui_lib/ide_notebook_events.py index f563b22..b9d6fab 100644 --- a/pythonforumide/gui_lib/ide_notebook_events.py +++ b/pythonforumide/gui_lib/ide_notebook_events.py @@ -1,8 +1,8 @@ -''' +""" Created on 31 Jul 2011 -@author: D.W. -''' +@author: D.W., David +""" import wx import wx.aui as aui @@ -14,9 +14,7 @@ def __init__(self, view, model= None): self._create_binds() def _create_binds(self): - ''' - Create binds - ''' + """Create binds""" self.view.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self._on_page_closed) self.view.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, @@ -24,11 +22,11 @@ def _create_binds(self): def _on_page_changed(self, event): """sets the currently active page index and editor page""" - self.view._active_tab_index= event.Selection - self.view._active_editor_page= self.view.GetPage(self.view._active_tab_index) + self.view._active_tab_index = event.Selection + self.view._active_editor_page = self.view.GetPage(self.view._active_tab_index) def _on_page_closed(self, event): """Sets currently active page to none and renames the untitled pages""" event.Skip() - self.view._active_editor_page= None - wx.CallAfter(self.view.name_untitled_pages) \ No newline at end of file + self.view._active_editor_page = None + wx.CallAfter(self.view.name_untitled_pages) From c48f897b08bfe62f2e9b9ca19941a6c5fbaed538 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:28:14 +0100 Subject: [PATCH 019/141] Even more PEP-8 related fixes --- pythonforumide/gui_lib/ide_mainframe_events.py | 10 ++++------ pythonforumide/gui_lib/ide_notebook.py | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 8d94074..3809bc9 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -10,15 +10,15 @@ from ide_mainframe import MainFrame class MainFrameEvents(object): + """Handles the MainFrame eventss""" def __init__(self, view, model= None): + """Sets the binds and does other stuff""" self.view = view self.model = model self._create_binds() def _create_binds(self): - ''' - Create binds - ''' + """Create binds""" self.view.Bind(wx.EVT_UPDATE_UI, self._evt_update_ui) self.view.Bind(wx.EVT_MENU, self._on_new, id=ide.ID_NEW) self.view.Bind(wx.EVT_MENU, self._on_open, id=ide.ID_OPEN) @@ -100,9 +100,7 @@ def _on_editor_run(self, event): self.view.notebook.run_active_editor() def _evt_update_ui(self, event): - ''' - Events to update the view - ''' + """Events to update the view""" event_id = event.GetId() if event_id== ide.ID_CUT: event.Enable(self.view.notebook.active_editor_can_cut()) diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 964d982..4af71ea 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -207,7 +207,8 @@ def active_editor_can_run(self): import ide_test_app as wx_app import ide_test_frame app = wx_app.Wx_App(False) - frame= ide_test_frame.TestFrame(None, title= "Testing notebook without events") + frame = ide_test_frame.TestFrame(None, + title="Testing notebook without events") panel= ide_test_frame.TestPanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) notebook= Notebook(panel) From 7cd8a9015479dd1a22388087a4982b487177b963 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:35:10 +0100 Subject: [PATCH 020/141] Some more PEP-8 Related Fixes --- pythonforumide/gui_lib/ide_constant.py | 6 +-- pythonforumide/gui_lib/ide_menu.py | 4 +- pythonforumide/gui_lib/ide_test_app.py | 14 ++---- pythonforumide/gui_lib/ide_test_frame.py | 12 ++--- pythonforumide/gui_lib/ide_toolbar.py | 8 ++- pythonforumide/ide_images/menu_icons.py | 63 +++++++----------------- 6 files changed, 34 insertions(+), 73 deletions(-) diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py index e2acbf5..939c8c7 100644 --- a/pythonforumide/gui_lib/ide_constant.py +++ b/pythonforumide/gui_lib/ide_constant.py @@ -1,8 +1,8 @@ -''' +""" Created on 31 Jul 2011 @author: D.W. -''' +""" import wx from collections import namedtuple as _nt @@ -77,5 +77,3 @@ ID_RUNFILE=wx.NewId() id_text_runfile= _id_text("Run file\tF5", "Run", "Run the active file", wx.ITEM_NORMAL) - - diff --git a/pythonforumide/gui_lib/ide_menu.py b/pythonforumide/gui_lib/ide_menu.py index 70dc975..9c0f367 100644 --- a/pythonforumide/gui_lib/ide_menu.py +++ b/pythonforumide/gui_lib/ide_menu.py @@ -1,9 +1,9 @@ -''' +""" Created on 31 Jul 2011 @author: D.W., David @reviewer: david -''' +""" import wx import ide_constant as ide diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py index 689cfe8..f2a9e44 100644 --- a/pythonforumide/gui_lib/ide_test_app.py +++ b/pythonforumide/gui_lib/ide_test_app.py @@ -1,26 +1,22 @@ -''' +""" Created on 31 Jul 2011 @author: Main -''' +""" import wx from pythonforumide.config.config import Ide_config class Wx_App(wx.App): def __init__(self, *args, **kwargs): - ''' - Creates a test wx App - ''' + """Creates a test wx App""" super(Wx_App, self).__init__(*args, **kwargs) self._create_config() def _create_config(self): - ''' - Set up config - ''' + """Set up config""" self.config= Ide_config() def OnExit(self): print ("App closing") - self.config.update_configfile() \ No newline at end of file + self.config.update_configfile() diff --git a/pythonforumide/gui_lib/ide_test_frame.py b/pythonforumide/gui_lib/ide_test_frame.py index fbb3e57..9c970c0 100644 --- a/pythonforumide/gui_lib/ide_test_frame.py +++ b/pythonforumide/gui_lib/ide_test_frame.py @@ -1,15 +1,13 @@ -''' +""" Created on 31 Jul 2011 @author: D.W. -''' +""" import wx class TestFrame(wx.Frame): - ''' - Frame to use for testing - ''' + """Frame to use for testing""" def __init__(self, *args, **kwargs): super(TestFrame, self).__init__(*args, **kwargs) self.SetInitialSize((600,600)) @@ -19,9 +17,7 @@ def __init__(self, *args, **kwargs): self.Show() class TestPanel(wx.Panel): - ''' - panel to use for testing - ''' + """panel to use for testing""" def __init__(self, *args, **kwargs): super(TestPanel, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.VERTICAL) diff --git a/pythonforumide/gui_lib/ide_toolbar.py b/pythonforumide/gui_lib/ide_toolbar.py index 506441d..3cb0d7b 100644 --- a/pythonforumide/gui_lib/ide_toolbar.py +++ b/pythonforumide/gui_lib/ide_toolbar.py @@ -1,8 +1,8 @@ -''' +""" Created on 31 Jul 2011 @author: D.W. -''' +""" import wx import ide_constant as ide @@ -11,9 +11,7 @@ class ToolBar(wx.ToolBar): def __init__(self, *args, **kwargs): - ''' - Create the toolbar - ''' + """Create the toolbar""" super(ToolBar, self).__init__( *args, **kwargs) self.SetToolBitmapSize((24,24)) self._add_toolbar_btn(ide.ID_NEW, ide.id_text_new, diff --git a/pythonforumide/ide_images/menu_icons.py b/pythonforumide/ide_images/menu_icons.py index 7cbec24..9e50927 100644 --- a/pythonforumide/ide_images/menu_icons.py +++ b/pythonforumide/ide_images/menu_icons.py @@ -1,12 +1,13 @@ -''' +""" Created on 31 Jul 2011 -@author: Main -''' +@author: Main, David +""" import wx class MissingArt(object): + """Handles missing icons""" def __init__(self, f): self.f = f @@ -17,86 +18,58 @@ def __call__(self): return wx.NullBitmap def get_icon_new(): - ''' - Returns a (24,24) new icon bmp - ''' + """Returns a (24,24) new icon bmp""" return _get_art_bmp(wx.ART_NEW) def get_icon_open(): - ''' - Returns a (24,24) open icon bmp - ''' + """Returns a (24,24) open icon bmp""" return _get_art_bmp(wx.ART_FILE_OPEN) def get_icon_save(): - ''' - Returns a (24,24) save icon bmp - ''' + """"Returns a (24,24) save icon bmp""" return _get_art_bmp(wx.ART_FILE_SAVE) def get_icon_saveas(): - ''' - Returns a (24,24) saveas icon bmp - ''' + """Returns a (24,24) saveas icon bmp""" return _get_art_bmp(wx.ART_FILE_SAVE_AS) def get_icon_cut(): - ''' - Returns a (24,24) cut icon bmp - ''' + """Returns a (24,24) cut icon bmp""" return _get_art_bmp(wx.ART_CUT) def get_icon_copy(): - ''' - Returns a (24,24) copy icon bmp - ''' + """Returns a (24,24) copy icon bmp""" return _get_art_bmp(wx.ART_COPY) def get_icon_paste(): - ''' - Returns a (24,24) paste icon bmp - ''' + """Returns a (24,24) paste icon bmp""" return _get_art_bmp(wx.ART_PASTE) def get_icon_undo(): - ''' - Returns a (24,24) undo icon bmp - ''' + """Returns a (24,24) undo icon bmp""" return _get_art_bmp(wx.ART_UNDO) def get_icon_redo(): - ''' - Returns a (24,24) redo icon bmp - ''' + """Returns a (24,24) redo icon bmp""" return _get_art_bmp(wx.ART_REDO) @MissingArt def get_icon_close(): - ''' - Returns a (24,24) close icon bmp - ''' + """Returns a (24,24) close icon bmp""" return _get_art_bmp(wx.ART_CLOSE) def get_icon_quit(): - ''' - Returns a (24,24) quit icon bmp - ''' + """Returns a (24,24) quit icon bmp""" return _get_art_bmp(wx.ART_QUIT) def get_icon_delete(): - ''' - Returns a (24,24) delete icon bmp - ''' + """Returns a (24,24) delete icon bmp""" return _get_art_bmp(wx.ART_DELETE) def get_find_and_replace(): - ''' - Returns a (24,24) find and replace icon bmp - ''' + """Returns a (24,24) find and replace icon bmp""" return _get_art_bmp(wx.ART_FIND_AND_REPLACE) def _get_art_bmp(art_id): - ''' - Returns the passed in art_id as a (24,24) icon bmp - ''' + """Returns the passed in art_id as a (24,24) icon bmp""" return wx.ArtProvider.GetBitmap(art_id, wx.ART_TOOLBAR, (24,24)) From 305a8b5867721117f9dd903655b81d525606c05e Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:39:24 +0100 Subject: [PATCH 021/141] Some more PEP-8 Related Fixes, somehow fixed Paste toolbar button? --- pythonforumide/wx_app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 0444978..0b0d632 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -60,11 +60,14 @@ def _create_mainframe(self): ide_mainframe_events.MainFrameEvents(self.mainframe) def OnExit(self): + """Handles the event that closes the IDE""" print ("App closing") self.config.update_configfile() class ListenProtocol(Protocol): + """Handles connections""" + #The name of the following functions is not ok according to PEP-8 def connectionMade(self): print "Got connection!!!!" From f93ccff807c49ea5ecd482bcab74998c8b309ead Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:45:05 +0100 Subject: [PATCH 022/141] Sigh, even more PEP-8-Related Fixes --- pythonforumide/gui_lib/ide_editor.py | 10 ++++++++-- pythonforumide/gui_lib/ide_mainframe.py | 24 ++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 00926a6..4e308d2 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -1,6 +1,6 @@ """ @author: Jakob, David, bunburya, confab -@reviewer: Somelauw, ghoulmaster +@reviewer: Somelauw, ghoulmaster, David """ #YES DIRT HACK GET OVER IT. Dont remove it might go before it goes into master @@ -23,7 +23,9 @@ } class Editor(stc.StyledTextCtrl): + """Inherits wxStyledTextCtrl and handles all editor functions""" def __init__(self, parent): + """Starts the editor and calls some editor-related functions""" super(Editor, self).__init__(parent) self.conf= wx.GetApp().config @@ -37,6 +39,7 @@ def __init__(self, parent): self.SetBindings() def SetBindings(self): + """Sets the key events bindings""" self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) def SetGenerics(self): @@ -109,7 +112,8 @@ def SetStyles(self, lang='python'): # End of line where string is not closed self.StyleSetSpec(stc.STC_P_STRINGEOL, "face:%(mono)s,fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) - def SmartIndent(self): + def SmartIndent(self): + """Handles smart indentation for the editor""" # Read settings from the config file indent_amount = int(self.conf["indent"]) usetab = int(self.conf["usetab"]) @@ -134,6 +138,7 @@ def SmartIndent(self): print self.conf def OnKeyDown(self, event): + """Defines events for when the user presses a key""" key = event.GetKeyCode() control = event.ControlDown() alt = event.AltDown() @@ -200,6 +205,7 @@ def on_run(self): return self.editor_run if __name__=='__main__': + """Adds the editor to the frame""" import ide_test_app as wx_app import ide_test_frame app = wx_app.Wx_App(False) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 4b452ec..fb8dae8 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -12,7 +12,6 @@ from ide_notebook import Notebook from ide_notebook_events import NotebookEvents - class MainFrame(wx.Frame): """Class with the GUI and GUI functions""" @@ -33,34 +32,27 @@ def __init__(self, *args, **kwargs): self.panel_sizer= wx.BoxSizer(wx.VERTICAL) self.frame_panel.SetSizer(self.panel_sizer) - self._create_notebook_panel() self._create_menu() self._create_toolbar() -# self._create_notebook_panel() + #self._create_notebook_panel() self._start_up() self.Layout() self.Show() def _create_menu(self): - ''' - creates a menu - ''' + """Creates a menu""" self.SetMenuBar(ide_menu.MenuBar(self)) def _create_toolbar(self): - ''' - creates a toolbar - ''' + """Creates a toolbar""" tb= ide_toolbar.ToolBar(self, style= wx.TB_HORIZONTAL|wx.NO_BORDER| wx.TB_FLAT) self.SetToolBar(tb) def _create_notebook_panel(self): - ''' - Create the notebook panel - ''' + '''Create the notebook panel''' self.notebook = Notebook(self.frame_panel) NotebookEvents(self.notebook) self.panel_sizer.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 0) @@ -73,9 +65,7 @@ def _start_up(self): self.notebook.new_editor_tab() def on_exit(self): - ''' - What to do when the application wants to exit - ''' + '''Handles the event triggered by the user to exit''' dial = wx.MessageDialog(self,'Do you really want to exit?', 'Exit Python IDE', wx.YES_NO | wx.ICON_QUESTION) @@ -102,7 +92,9 @@ def get_file(self, prompt, style): dlg.Destroy() return dirname, filename - def on_run(self, event): + def on_run(self, event): + """Supposedly handles the Run Event""" + #Not working, this is being done somewhere else (confab made it) from ide_outputframe import OutputFrame output_app = wx.PySimpleApp() output_frame = OutputFrame(parent = None, title="") From 4c6fe68f0b1f1b57c4f51df4036bb4c87a3a97dd Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:46:56 +0100 Subject: [PATCH 023/141] PEP-8 Fixes --- pythonforumide/gui_lib/ide_test_app.py | 1 + pythonforumide/gui_lib/ide_test_frame.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py index f2a9e44..e30f2a7 100644 --- a/pythonforumide/gui_lib/ide_test_app.py +++ b/pythonforumide/gui_lib/ide_test_app.py @@ -18,5 +18,6 @@ def _create_config(self): self.config= Ide_config() def OnExit(self): + """Handles the frame closing function""" print ("App closing") self.config.update_configfile() diff --git a/pythonforumide/gui_lib/ide_test_frame.py b/pythonforumide/gui_lib/ide_test_frame.py index 9c970c0..d34776f 100644 --- a/pythonforumide/gui_lib/ide_test_frame.py +++ b/pythonforumide/gui_lib/ide_test_frame.py @@ -1,7 +1,7 @@ """ Created on 31 Jul 2011 -@author: D.W. +@author: D.W., David """ import wx @@ -9,6 +9,7 @@ class TestFrame(wx.Frame): """Frame to use for testing""" def __init__(self, *args, **kwargs): + """Initiates the frame and the GUI""" super(TestFrame, self).__init__(*args, **kwargs) self.SetInitialSize((600,600)) self.Center(wx.BOTH) @@ -17,13 +18,15 @@ def __init__(self, *args, **kwargs): self.Show() class TestPanel(wx.Panel): - """panel to use for testing""" + """Panel to use for testing""" def __init__(self, *args, **kwargs): + """Creates the GUI for the test panel""" super(TestPanel, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) if __name__ == '__main__': + """Adds the test panel to the test frame""" app = wx.PySimpleApp(False) frame= TestFrame(None, title= "Testing TestFrame") panel= TestPanel(frame) From 88f4dbd7496b67f0b3fa53f5c8fd99a0ab691f53 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:56:13 +0100 Subject: [PATCH 024/141] Added View Menu and Show Toolbar option (no event yet), some more pep-8 fixes too --- pythonforumide/gui_lib/ide_constant.py | 6 ++++++ pythonforumide/gui_lib/ide_menu.py | 12 +++++++++++- pythonforumide/wx_app.py | 1 - 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py index 939c8c7..fac361d 100644 --- a/pythonforumide/gui_lib/ide_constant.py +++ b/pythonforumide/gui_lib/ide_constant.py @@ -66,6 +66,12 @@ id_text_selectall= _id_text("Select All\tCtrl+A", "Select all", "Select all", wx.ITEM_NORMAL) #=============================================================================== +# View ID +#=============================================================================== +ID_SHOW_TOOLBAR = wx.NewId() +id_show_toolbar = _id_text("Show Toolbar", "Show Toolbar", "Show Toolbar", + wx.ITEM_CHECK) +#=============================================================================== # Search ID #=============================================================================== ID_SEARCH= wx.NewId() diff --git a/pythonforumide/gui_lib/ide_menu.py b/pythonforumide/gui_lib/ide_menu.py index 9c0f367..8264f53 100644 --- a/pythonforumide/gui_lib/ide_menu.py +++ b/pythonforumide/gui_lib/ide_menu.py @@ -12,13 +12,14 @@ class MenuBar(wx.MenuBar): """Creates a menubar""" def __init__(self, parent): + """Initiates the menu bar and menubar-related functions""" super(MenuBar, self).__init__() self._parent = parent self._add_file_menu() self._add_edit_menu() + self._add_view_menu() self._add_search_menu() self._add_run_menu() - #self._parent.SetMenuBar(self) def _add_file_menu(self): @@ -79,6 +80,15 @@ def _add_edit_menu(self): self._add_menu_item(editMenu, ide.ID_SELECTALL, ide.id_text_selectall) self.Append(editMenu, "&Edit") + + def _add_view_menu(self): + """Adds the view menu""" + view_menu = wx.Menu() + + self._add_menu_item(view_menu, ide.ID_SHOW_TOOLBAR, + ide.id_show_toolbar) + + self.Append(view_menu, "&View") def _add_search_menu(self): """Adds the search menu""" diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 0b0d632..02b63f9 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -67,7 +67,6 @@ def OnExit(self): class ListenProtocol(Protocol): """Handles connections""" - #The name of the following functions is not ok according to PEP-8 def connectionMade(self): print "Got connection!!!!" From 228b16c06d15e174503a76e2337bb6ba63b475be Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 10:59:06 +0100 Subject: [PATCH 025/141] Minimum changes, I'm done for now --- pythonforumide/gui_lib/ide_mainframe_events.py | 2 +- pythonforumide/wx_app.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 3809bc9..89fc570 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -37,7 +37,7 @@ def _create_binds(self): id=ide.ID_SELECTALL) self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, id=ide.ID_SEARCH) - self.view.Bind(wx.EVT_MENU, self._on_editor_run, id=ide.ID_RUNFILE) + self.view.Bind(wx.EVT_MENU, self._on_editor_run, id=ide.ID_RUNFILE) def _on_new(self, event): """Opens a new tab with a new editor instance""" diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 02b63f9..2b6021f 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -74,6 +74,7 @@ def connectionLost(self, reason): print "Connection closed." class ListenFactory(Factory): + """Handles Twisted listen protocols""" protocol = ListenProtocol if __name__ == '__main__': From 35e936d2044d96ef4854ec5acd3bffcb0af97ad4 Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 05:56:38 -0700 Subject: [PATCH 026/141] don't blame me --- pythonforumide/gui_lib/ide_mainframe.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index fb8dae8..8bb9210 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -2,7 +2,7 @@ """ Created on Wed Jul 27 17:36:42 2011 -@author: jakob, David +@author: jakob, David, confab """ import os import wx @@ -95,6 +95,12 @@ def get_file(self, prompt, style): def on_run(self, event): """Supposedly handles the Run Event""" #Not working, this is being done somewhere else (confab made it) + + # Sorry bro, but if you actually read the history of the file + # you'd see that this comment is my [confab] first commit. + # Look at commit 63ebce0f6a9f331167e4 before you blame me for + # something else again. + from ide_outputframe import OutputFrame output_app = wx.PySimpleApp() output_frame = OutputFrame(parent = None, title="") From 75110f49e506bd8690495f999b3ec5acb3b03214 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 14:16:14 +0100 Subject: [PATCH 027/141] Added HACKING file --- HACKING | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 HACKING diff --git a/HACKING b/HACKING new file mode 100644 index 0000000..8a49508 --- /dev/null +++ b/HACKING @@ -0,0 +1,17 @@ +If you want to hack PythonForumIDE you need the following software: + - Python 2.7.1 + - wxPython 2.8 + - Twisted 10.2.0-1 + +PythonForumIDE uses the PEP-8 Coding Style, which can be found here: +http://www.python.org/dev/peps/pep-0008/ + +When in doubt copy the coding style of the file you are editing. + +To clone the latest PythonForumIDE version simpy use git to clone it: +git clone git@github.com:Python-Forum/PythonForumIDE.git + +To run it, try the following from the pythonforumide/ directory: +python wx_app.py + +For more information see the README file. From 47474561ee6422baafc8cdcc80918077c88f2442 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 15:16:53 +0200 Subject: [PATCH 028/141] Edited HACKING file. --- HACKING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING b/HACKING index 8a49508..b3a300b 100644 --- a/HACKING +++ b/HACKING @@ -1,4 +1,4 @@ -If you want to hack PythonForumIDE you need the following software: +If you want to hack on PythonForumIDE you need the following software: - Python 2.7.1 - wxPython 2.8 - Twisted 10.2.0-1 From 17e72bacb2097db2563a9e37afee3fe5b3e3094e Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 15:26:03 +0100 Subject: [PATCH 029/141] Applied the decorator to menu_icons.py --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/ide_images/menu_icons.py | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index ffc8eaa..bc1ab57 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = 0 -mainframe.height = 600 -mainframe.width = 600 +mainframe.height = 784 +mainframe.width = 1275 indent = 4 diff --git a/pythonforumide/ide_images/menu_icons.py b/pythonforumide/ide_images/menu_icons.py index 9e50927..44afcbf 100644 --- a/pythonforumide/ide_images/menu_icons.py +++ b/pythonforumide/ide_images/menu_icons.py @@ -7,7 +7,9 @@ import wx class MissingArt(object): - """Handles missing icons""" + """Sometimes wx doesn't have some attributes, this is caused with different + wx versions, this decorator wraps each function call in a try/except and + returns NullBitmap for everything that is not an attribute of wx.""" def __init__(self, f): self.f = f @@ -17,34 +19,42 @@ def __call__(self): except AttributeError: return wx.NullBitmap +@MissingArt def get_icon_new(): """Returns a (24,24) new icon bmp""" return _get_art_bmp(wx.ART_NEW) +@MissingArt def get_icon_open(): """Returns a (24,24) open icon bmp""" return _get_art_bmp(wx.ART_FILE_OPEN) +@MissingArt def get_icon_save(): """"Returns a (24,24) save icon bmp""" return _get_art_bmp(wx.ART_FILE_SAVE) +@MissingArt def get_icon_saveas(): """Returns a (24,24) saveas icon bmp""" return _get_art_bmp(wx.ART_FILE_SAVE_AS) +@MissingArt def get_icon_cut(): """Returns a (24,24) cut icon bmp""" return _get_art_bmp(wx.ART_CUT) +@MissingArt def get_icon_copy(): """Returns a (24,24) copy icon bmp""" return _get_art_bmp(wx.ART_COPY) +@MissingArt def get_icon_paste(): """Returns a (24,24) paste icon bmp""" return _get_art_bmp(wx.ART_PASTE) +@MissingArt def get_icon_undo(): """Returns a (24,24) undo icon bmp""" return _get_art_bmp(wx.ART_UNDO) @@ -58,14 +68,17 @@ def get_icon_close(): """Returns a (24,24) close icon bmp""" return _get_art_bmp(wx.ART_CLOSE) +@MissingArt def get_icon_quit(): """Returns a (24,24) quit icon bmp""" return _get_art_bmp(wx.ART_QUIT) +@MissingArt def get_icon_delete(): """Returns a (24,24) delete icon bmp""" return _get_art_bmp(wx.ART_DELETE) +@MissingArt def get_find_and_replace(): """Returns a (24,24) find and replace icon bmp""" return _get_art_bmp(wx.ART_FIND_AND_REPLACE) From c1040b463b542a6580396d2a52731f15cc3dfdad Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 07:35:41 -0700 Subject: [PATCH 030/141] testing run some more --- pythonforumide/gui_lib/ide_editor.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 4e308d2..f69c6fa 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -11,7 +11,7 @@ import wx.stc as stc import wx -from ide_test_frame import TestFrame +from ide_test_frame import TestFrame, TestPanel #TODO: make customisable font and sizes. Perhaps maked this named tuple? faces = { 'times': 'Times', @@ -201,8 +201,12 @@ def on_run(self): # Create a test frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. - self.editor_run = TestFrame(wx.GetApp().TopWindow, title="") - return self.editor_run + run_editor = TestFrame(wx.GetApp().TopWindow, title="") + run_panel = wx.TextCtrl(run_editor) + run_editor.sizer.Add(run_panel, 1, wx.EXPAND) + run_editor.Layout() + + return run_panel if __name__=='__main__': """Adds the editor to the frame""" From d0c24be0296ca04c664a873971f9c4a3d1943f79 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 17:11:57 +0100 Subject: [PATCH 031/141] Pushing a working run feature. --- pythonforumide/config/Ide_Config.cfg | 6 ----- pythonforumide/gui_lib/ide_editor.py | 30 ++++++++++++++++++++++--- pythonforumide/gui_lib/ide_mainframe.py | 19 +--------------- pythonforumide/utils/interpreter.py | 6 +---- pythonforumide/wx_app.py | 8 +++---- 5 files changed, 32 insertions(+), 37 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index bc1ab57..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +0,0 @@ -[ide] -usetab = 0 -mainframe.height = 784 -mainframe.width = 1275 -indent = 4 - diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index f69c6fa..7d0fa47 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -8,9 +8,17 @@ sys.path.append('..') from utils.textutils import split_comments +from utils.interpreter import PythonProcessProtocol +from utils.version import get_python_exe import wx.stc as stc +import os.path import wx +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + from ide_test_frame import TestFrame, TestPanel #TODO: make customisable font and sizes. Perhaps maked this named tuple? @@ -28,7 +36,7 @@ def __init__(self, parent): """Starts the editor and calls some editor-related functions""" super(Editor, self).__init__(parent) - self.conf= wx.GetApp().config + self.conf = wx.GetApp().config self.filepath = '' self.indent_level = 0 @@ -198,14 +206,30 @@ def on_replace(self): def on_run(self): """Runs selected code in a new window.""" - # Create a test frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. + reactor = wx.GetApp().this_reactor + run_editor = TestFrame(wx.GetApp().TopWindow, title="") run_panel = wx.TextCtrl(run_editor) run_editor.sizer.Add(run_panel, 1, wx.EXPAND) run_editor.Layout() - + + if self.filepath: + run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1] + reactor.spawnProcess(PythonProcessProtocol(run_panel), + get_python_exe(), + ["python", str(self.filepath)]) + else: + run_panel.WriteText("Running unsaved script.") + script = StringIO() + script.write(self.GetText()) + script.seek(0) + reactor.spawnProcess(PythonProcessProtocol(run_panel), + get_python_exe(), + ["python", "-c", script.read()]) + + return run_panel if __name__=='__main__': diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 8bb9210..d27d546 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -4,11 +4,9 @@ @author: jakob, David, confab """ -import os import wx import gui_lib.ide_menu as ide_menu import gui_lib.ide_toolbar as ide_toolbar -import gui_lib.ide_constant as ide from ide_notebook import Notebook from ide_notebook_events import NotebookEvents @@ -56,7 +54,7 @@ def _create_notebook_panel(self): self.notebook = Notebook(self.frame_panel) NotebookEvents(self.notebook) self.panel_sizer.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 0) - + def _start_up(self): ''' Adds a new blank editor tab @@ -92,21 +90,6 @@ def get_file(self, prompt, style): dlg.Destroy() return dirname, filename - def on_run(self, event): - """Supposedly handles the Run Event""" - #Not working, this is being done somewhere else (confab made it) - - # Sorry bro, but if you actually read the history of the file - # you'd see that this comment is my [confab] first commit. - # Look at commit 63ebce0f6a9f331167e4 before you blame me for - # something else again. - - from ide_outputframe import OutputFrame - output_app = wx.PySimpleApp() - output_frame = OutputFrame(parent = None, title="") - output_frame.Show() - output_app.MainLoop() - if __name__=='__main__': import ide_test_app as wx_app app = wx_app.Wx_App(False) diff --git a/pythonforumide/utils/interpreter.py b/pythonforumide/utils/interpreter.py index a3bf463..4698de3 100644 --- a/pythonforumide/utils/interpreter.py +++ b/pythonforumide/utils/interpreter.py @@ -8,7 +8,6 @@ sys.path.append('..') from twisted.internet.protocol import ProcessProtocol -from utils.version import get_python_exe class PythonProcessProtocol(ProcessProtocol): def __init__(self, frame): @@ -16,13 +15,10 @@ def __init__(self, frame): def connectionMade(self): print "subprocess open.!" - self.transport.write("2+2") def outReceived(self, data): + self.frame.WriteText(data) print "Got stdout." def errRecieved(self, data): print "Got stderr!" - -def spawn_python(): - return [PythonProcessProtocol(None), get_python_exe(), ["python"]] diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 2b6021f..b9db488 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -17,7 +17,6 @@ from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol from utils.version import get_free_port -from utils.interpreter import spawn_python class Wx_App(wx.App): """Sets the editor up and the ports""" #Not sure if correct description @@ -44,14 +43,13 @@ def _set_up_reactor(self): """Set's up the reactor""" reactor.registerWxApp(self) reactor.listenTCP(self._port, ListenFactory()) - reactor.spawnProcess(*spawn_python()) #frame.Maximize() #Left commented to stop it getting on my nerves. def start_reactor(self): - ''' - Sarts the reactor - ''' + """ + Starts the reactor, bind a reference to it locally.""" print "Port: %s" % (self.get_port()) + self.this_reactor = reactor reactor.run() def _create_mainframe(self): From 7675d780309733679e9676d150ace424671b24b5 Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 09:22:52 -0700 Subject: [PATCH 032/141] fixed SyntaxError --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 7d0fa47..0fd073c 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -216,7 +216,7 @@ def on_run(self): run_editor.Layout() if self.filepath: - run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1] + run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1]) reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), ["python", str(self.filepath)]) From 90267c5d2c7fd1f49460e77abe831ed2f268bbc0 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 18:23:14 +0100 Subject: [PATCH 033/141] Fixed Save Bug --- pythonforumide/config/Ide_Config.cfg | 6 ++++++ pythonforumide/gui_lib/ide_mainframe.py | 7 +++---- pythonforumide/gui_lib/ide_notebook.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index e69de29..ffc8eaa 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = 0 +mainframe.height = 600 +mainframe.width = 600 +indent = 4 + diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index d27d546..30b2c7a 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -84,11 +84,10 @@ def get_file(self, prompt, style): if dlg.ShowModal() == wx.ID_OK: dirname = dlg.GetDirectory() filename = dlg.GetFilename() + dlg.Destroy() + return dirname, filename else: - # so maybe add error handling here. - raise RuntimeError("Something has gone wrong with the dialog") - dlg.Destroy() - return dirname, filename + return "", "" #Cancels the action if __name__=='__main__': import ide_test_app as wx_app diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 4af71ea..e6d47fe 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -54,7 +54,7 @@ def save_as_active_editor_tab(self): if self._active_editor_page.save_file_as(path): self.set_active_tab_text(filename) self._active_editor_page.filepath = path - + def set_active_tab_text(self, text): """Rename the currently active tab text""" if self._active_tab_index> -1: From a4828d38e27fb6d9b1074c9f54efefb775579816 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 18:24:30 +0100 Subject: [PATCH 034/141] Fixing bugs in on_run --- pythonforumide/gui_lib/ide_editor.py | 8 ++++++-- pythonforumide/utils/interpreter.py | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 7d0fa47..490a531 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -10,6 +10,7 @@ from utils.textutils import split_comments from utils.interpreter import PythonProcessProtocol from utils.version import get_python_exe +import wx.richtext import wx.stc as stc import os.path import wx @@ -211,17 +212,20 @@ def on_run(self): reactor = wx.GetApp().this_reactor run_editor = TestFrame(wx.GetApp().TopWindow, title="") - run_panel = wx.TextCtrl(run_editor) + run_panel = wx.richtext.RichTextCtrl(run_editor) run_editor.sizer.Add(run_panel, 1, wx.EXPAND) run_editor.Layout() if self.filepath: - run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1] + filename = os.path.split(self.filepath)[1] + run_panel.WriteText("Running %s" % filename) + run_panel.Newline() reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), ["python", str(self.filepath)]) else: run_panel.WriteText("Running unsaved script.") + run_panel.Newline() script = StringIO() script.write(self.GetText()) script.seek(0) diff --git a/pythonforumide/utils/interpreter.py b/pythonforumide/utils/interpreter.py index 4698de3..7cb4d6f 100644 --- a/pythonforumide/utils/interpreter.py +++ b/pythonforumide/utils/interpreter.py @@ -15,10 +15,23 @@ def __init__(self, frame): def connectionMade(self): print "subprocess open.!" - + + def connectionLost(self, reason): + self.frame.Newline() + self.frame.WriteText("\n\nExited with code 0") + def outReceived(self, data): self.frame.WriteText(data) print "Got stdout." def errRecieved(self, data): print "Got stderr!" + self.frame.Newline() + self.frame.BeginTextColour("Red") + self.frame.WriteText(data) + + def errConnectionLost(self): + print "errConnectionLost! The child closed their stderr." + + def processEnded(self, reason): + print "processEnded, status %d" % (reason.value.exitCode,) From d5e1d3991c4e8a71ed0e5a7c14221a4f0f500f2f Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 18:34:18 +0100 Subject: [PATCH 035/141] Made python write stderr properly. --- pythonforumide/gui_lib/ide_editor.py | 6 +++--- pythonforumide/utils/interpreter.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 0f4766f..bc1fd3d 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -217,13 +217,13 @@ def on_run(self): run_editor.Layout() if self.filepath: -<<<<<<< HEAD + filename = os.path.split(self.filepath)[1] run_panel.WriteText("Running %s" % filename) run_panel.Newline() -======= + run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1]) ->>>>>>> 90267c5d2c7fd1f49460e77abe831ed2f268bbc0 + reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), ["python", str(self.filepath)]) diff --git a/pythonforumide/utils/interpreter.py b/pythonforumide/utils/interpreter.py index 7cb4d6f..1016d0d 100644 --- a/pythonforumide/utils/interpreter.py +++ b/pythonforumide/utils/interpreter.py @@ -24,7 +24,7 @@ def outReceived(self, data): self.frame.WriteText(data) print "Got stdout." - def errRecieved(self, data): + def errReceived(self, data): print "Got stderr!" self.frame.Newline() self.frame.BeginTextColour("Red") From c43d7c8132b4ec34b53c55a31a31c5a92a19caae Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 18:39:13 +0100 Subject: [PATCH 036/141] Made run frame textbox read only --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index bc1fd3d..076302a 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -212,7 +212,7 @@ def on_run(self): reactor = wx.GetApp().this_reactor run_editor = TestFrame(wx.GetApp().TopWindow, title="") - run_panel = wx.richtext.RichTextCtrl(run_editor) + run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) run_editor.sizer.Add(run_panel, 1, wx.EXPAND) run_editor.Layout() From 9da63bfec246b708565b9a13f68b948451b7fb8c Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 10:33:10 -0700 Subject: [PATCH 037/141] revamped config/config.py --- TOREVIEW | 5 + latest_diff | 347 +++++++++++++++++++++++++ pythonforumide/Ide_Config.cfg | 2 +- pythonforumide/config/Ide_Config.cfg | 2 +- pythonforumide/config/config.py | 157 ++++++----- pythonforumide/gui_lib/ide_test_app.py | 6 +- pythonforumide/wx_app.py | 7 +- 7 files changed, 455 insertions(+), 71 deletions(-) create mode 100644 latest_diff diff --git a/TOREVIEW b/TOREVIEW index 769bbcf..f9e1382 100644 --- a/TOREVIEW +++ b/TOREVIEW @@ -18,3 +18,8 @@ To get your file reviewed; Files to be reviewed; - +Ide_Config.cfg +config/Ide_Config.cfg +config/config.py +gui_lib/ide_test_app.py +wx_app.py diff --git a/latest_diff b/latest_diff new file mode 100644 index 0000000..3070603 --- /dev/null +++ b/latest_diff @@ -0,0 +1,347 @@ +t eaf1ce1fe147c53035368f7bafcb759a9ba3c17e +Author: confab +Date: Mon Aug 1 10:33:10 2011 -0700 + + revamped config/config.py + +diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg +index ffc8eaa..2697499 100644 +--- a/pythonforumide/Ide_Config.cfg ++++ b/pythonforumide/Ide_Config.cfg +@@ -1,5 +1,5 @@ + [ide] +-usetab = 0 ++usetab = False + mainframe.height = 600 + mainframe.width = 600 + indent = 4 +diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg +index ffc8eaa..2697499 100644 +--- a/pythonforumide/config/Ide_Config.cfg ++++ b/pythonforumide/config/Ide_Config.cfg +@@ -1,5 +1,5 @@ + [ide] +-usetab = 0 ++usetab = False + mainframe.height = 600 + mainframe.width = 600 + indent = 4 +diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py +index 8c76ea1..e5c508f 100644 +--- a/pythonforumide/config/config.py ++++ b/pythonforumide/config/config.py +@@ -15,17 +15,21 @@ except ImportError: + import ConfigParser #Ugly config file :( + has_yaml = False + +-def config_file(profile): +- """Returns the name of the configuration file. It does not guarantee existence. +- Side effect: Tells the user if can't load the configuration file if it is not supported. ++def file_config(profile): ++ """ ++ Returns the name of the configuration file. ++ It does not guarantee existence. ++ Side effect: ++ Tells the user if can't load the configuration file if it is not supported. + """ + + # We will need some fuzzy logic to accomplish this +- (profile, ext) = os.path.splitext(profile) +- yaml_exists = os.path.exists(profile + ".yaml") +- cfg_exists = os.path.exists(profile + ".cfg") ++ profile, ext = os.path.splitext(profile) ++ yaml_exists = os.path.exists(''.join((profile, ".yaml"))) ++ cfg_exists = os.path.exists(''.join((profile, ".cfg"))) + +- # If no extension could be found, guess what configuration type is likely to be preferred ++ # If no extension could be found, ++ # guess what configuration type is likely to be preferred. + if not ext: + if yaml_exists or (has_yaml and not cfg_exists): + ext = ".yaml" +@@ -34,14 +38,16 @@ def config_file(profile): + + # We can't load yaml if it doesn't exist + if ext == ".yaml" and not has_yaml: +- print("yaml configuration could not be loaded because python-yaml is not installed.") ++ print("yaml configuration could not be loaded", ++ "because python-yaml is not installed.") + ext = ".cfg" + +- return profile + ext ++ return ''.join((profile, ext)) + + def load_config(configfile): + """Loads a config file""" +- (profile, ext) = os.path.splitext(configfile) ++ ++ profile, ext = os.path.splitext(configfile) + if ext == ".yaml": + return _BeautifulConfig(profile) + else: +@@ -49,10 +55,14 @@ def load_config(configfile): + + class _BeautifulConfig(object): + """This is the config object if we can import yaml.""" ++ + def __init__(self, profile): +- """Load the yaml config from file, +- if this fails write an empty new one.""" +- filename = profile + ".yaml" ++ """ ++ Load the yaml config from file, ++ if this fails write an empty new one. ++ """ ++ ++ filename = ''.join((profile, ".yaml")) + + try: + self.config = yaml.load(open(filename)) +@@ -60,116 +70,139 @@ class _BeautifulConfig(object): + #Raise a config warning error here? + self.config = {} + +- self.file = open(filename,'w') +- ++ self.file_config = open(filename, 'w') ++ + def __setitem__(self, option, value): + """Set an option to a value inside the config, does not write.""" ++ + self.config[option] = value +- ++ + def __getitem__(self, option): +- """Gets the option from the config, if the option is not there +- returns None""" ++ """ ++ Gets the option from the config. ++ Return None, if the option is not there ++ """ ++ + return self.config.get(option, None) + + def set_default(self, option, value): ++ + self.config.set_default(option, value) + + def save(self): + """Writes the config as yaml out to the config file.""" +- yaml.dump(self.config, self.file) ++ ++ yaml.dump(self.config, self.file_config) + + class _UglyConfig(object): +- """This is the config object created if we can't use YAML and have to +- rely on ConfigParser.""" ++ """ ++ This is the config object created if we can't use YAML and have to ++ rely on ConfigParser. ++ """ ++ + def __init__(self, profile): +- filename = profile + ".cfg" +- filename= profile + ++ filename = ''.join((profile, ".cfg")) ++ filename = profile + self.config = ConfigParser.ConfigParser() +- try: +- self.config.read(filename) +- except IOError: # <<< Exception will never be trown (RTFM, noob!) +- pass #Raise a config warning error here? +- self.config.add_section('ide') ++ self.config.read(filename) + + if not self.config.has_section('ide'): + self.config.add_section('ide') + +- self.file = open(filename,'w') ++ self.file_config = open(filename,'w') + + def __setitem__(self, option, value): + """Set the value to the option inside the default section.""" +- self.config.set('ide',option, value) +- ++ ++ self.config.set('ide', option, value) ++ + def __getitem__(self, option): + """Return the values to the option given, or return None""" ++ + try: + return self.config.get('ide', option) + except ConfigParser.NoOptionError: + return None + + def set_default(self, option, value): ++ + if not self.config.has_option('ide', option): + self[option] = value +- ++ + def save(self): + """Write config to file.""" +- self.config.write(self.file) ++ ++ self.config.write(self.file_config) + + + #If we have yaml then the ConfigObject will point to the cooler config object + if has_yaml: + Config = _BeautifulConfig +-else: # Otherwise we point to the ugly one ++# Otherwise we point to the ugly one ++else: + Config = _UglyConfig + #This way the importers use the config the same way, same api but under the + #hood they are different. + +-class Ide_config(object): +- def __init__(self, filepath= "", filename= "Ide_Config"): ++ ++class IdeConfig(object): ++ ++ def __init__(self, filepath="", filename="Ide_Config"): ++ + if not filepath: +- filepath= os.path.dirname(__file__) ++ filepath = os.path.dirname(__file__) + self._filepath = filepath + self._filename = filename +- self._fullpath= None +- self._data= {} ++ self._fullpath = None ++ self._data = {} + self._get_file_fullpath() + self._get_defaults() +- ++ + def _get_file_fullpath(self): ++ + if has_yaml: +- ext= ".yaml" ++ ext = ".yaml" + else: +- ext= ".cfg" +- self._fullpath= os.path.join(self._filepath, self._filename)+ext +- if not os.path.exists(self._fullpath): +- a= file(self._fullpath, "w") +- a.close() +- print ("Config useing filepath: %s" % (self._fullpath)) +- ++ ext = ".cfg" ++ ++ _temp_path = os.path.join(self._filepath, self._filename) ++ self._fullpath = ''.join((_temp_path, ext)) ++ ++ with open(self._fullpath, "a") as _: ++ # Opens file as append to ensure safe creation of the file. ++ pass ++ ++ print ("Config using filepath: %s" % (self._fullpath)) ++ + def _get_defaults(self): +- confile= Config(self._fullpath) +- self._data["indent"]= confile["indent"] or 4 +- self._data["usetab"]= confile["usetab"] or 0 ++ ++ confile = Config(self._fullpath) ++ self._data["indent"] = confile["indent"] or 4 ++ self._data["usetab"] = confile["usetab"] or False + self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 + self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 +- confile.file.close() +- ++ confile.file_config.close() ++ + def __setitem__(self, key, value): +- self._data[key]= value +- ++ ++ self._data[key] = value ++ + def __getitem__(self, key): ++ + return self._data[key] +- ++ + def update_configfile(self): +- confile= Config(self._fullpath) ++ ++ confile = Config(self._fullpath) + for key, value in self._data.iteritems(): +- confile[key]= value ++ confile[key] = value ++ + confile.save() +- confile.file.close() ++ confile.file_config.close() + + if __name__ == '__main__': +- ide_config= Ide_config() +- print (ide_config["indent"]) ++ ide_config = IdeConfig(config_style=Config) ++ print(ide_config["indent"]) + ide_config.update_configfile() +- ++ +diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py +index e30f2a7..405b1ff 100644 +--- a/pythonforumide/gui_lib/ide_test_app.py ++++ b/pythonforumide/gui_lib/ide_test_app.py +@@ -1,11 +1,11 @@ + """ + Created on 31 Jul 2011 + +-@author: Main ++@author: Main, confab + """ + + import wx +-from pythonforumide.config.config import Ide_config ++from pythonforumide.config.config import IdeConfig + + class Wx_App(wx.App): + def __init__(self, *args, **kwargs): +@@ -15,7 +15,7 @@ class Wx_App(wx.App): + + def _create_config(self): + """Set up config""" +- self.config= Ide_config() ++ self.config= IdeConfig() + + def OnExit(self): + """Handles the frame closing function""" +diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py +index 2b6021f..a87361a 100644 +--- a/pythonforumide/wx_app.py ++++ b/pythonforumide/wx_app.py +@@ -1,16 +1,15 @@ + """ + Created on 31 Jul 2011 + +-@author: D.W., david ++@author: D.W., david, confab + @reviewer: david + """ + + import wx + import gui_lib.ide_mainframe as ide_mainframe + import gui_lib.ide_mainframe_events as ide_mainframe_events +-from config.config import Ide_config ++from config.config import IdeConfig + +-#import config.config.Ide_config as Ide_config + from twisted.internet import wxreactor + wxreactor.install() + +@@ -31,7 +30,7 @@ class Wx_App(wx.App): + + def _create_config(self): + """Set up config""" +- self.config = Ide_config() ++ self.config = IdeConfig() + + def _create_port(self): + """Creates a free port""" diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg index ffc8eaa..2697499 100644 --- a/pythonforumide/Ide_Config.cfg +++ b/pythonforumide/Ide_Config.cfg @@ -1,5 +1,5 @@ [ide] -usetab = 0 +usetab = False mainframe.height = 600 mainframe.width = 600 indent = 4 diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index ffc8eaa..2697499 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,5 +1,5 @@ [ide] -usetab = 0 +usetab = False mainframe.height = 600 mainframe.width = 600 indent = 4 diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index 8c76ea1..e5c508f 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -15,17 +15,21 @@ import ConfigParser #Ugly config file :( has_yaml = False -def config_file(profile): - """Returns the name of the configuration file. It does not guarantee existence. - Side effect: Tells the user if can't load the configuration file if it is not supported. +def file_config(profile): + """ + Returns the name of the configuration file. + It does not guarantee existence. + Side effect: + Tells the user if can't load the configuration file if it is not supported. """ # We will need some fuzzy logic to accomplish this - (profile, ext) = os.path.splitext(profile) - yaml_exists = os.path.exists(profile + ".yaml") - cfg_exists = os.path.exists(profile + ".cfg") + profile, ext = os.path.splitext(profile) + yaml_exists = os.path.exists(''.join((profile, ".yaml"))) + cfg_exists = os.path.exists(''.join((profile, ".cfg"))) - # If no extension could be found, guess what configuration type is likely to be preferred + # If no extension could be found, + # guess what configuration type is likely to be preferred. if not ext: if yaml_exists or (has_yaml and not cfg_exists): ext = ".yaml" @@ -34,14 +38,16 @@ def config_file(profile): # We can't load yaml if it doesn't exist if ext == ".yaml" and not has_yaml: - print("yaml configuration could not be loaded because python-yaml is not installed.") + print("yaml configuration could not be loaded", + "because python-yaml is not installed.") ext = ".cfg" - return profile + ext + return ''.join((profile, ext)) def load_config(configfile): """Loads a config file""" - (profile, ext) = os.path.splitext(configfile) + + profile, ext = os.path.splitext(configfile) if ext == ".yaml": return _BeautifulConfig(profile) else: @@ -49,10 +55,14 @@ def load_config(configfile): class _BeautifulConfig(object): """This is the config object if we can import yaml.""" + def __init__(self, profile): - """Load the yaml config from file, - if this fails write an empty new one.""" - filename = profile + ".yaml" + """ + Load the yaml config from file, + if this fails write an empty new one. + """ + + filename = ''.join((profile, ".yaml")) try: self.config = yaml.load(open(filename)) @@ -60,116 +70,139 @@ def __init__(self, profile): #Raise a config warning error here? self.config = {} - self.file = open(filename,'w') - + self.file_config = open(filename, 'w') + def __setitem__(self, option, value): """Set an option to a value inside the config, does not write.""" + self.config[option] = value - + def __getitem__(self, option): - """Gets the option from the config, if the option is not there - returns None""" + """ + Gets the option from the config. + Return None, if the option is not there + """ + return self.config.get(option, None) def set_default(self, option, value): + self.config.set_default(option, value) def save(self): """Writes the config as yaml out to the config file.""" - yaml.dump(self.config, self.file) + + yaml.dump(self.config, self.file_config) class _UglyConfig(object): - """This is the config object created if we can't use YAML and have to - rely on ConfigParser.""" + """ + This is the config object created if we can't use YAML and have to + rely on ConfigParser. + """ + def __init__(self, profile): - filename = profile + ".cfg" - filename= profile + filename = ''.join((profile, ".cfg")) + filename = profile self.config = ConfigParser.ConfigParser() - try: - self.config.read(filename) - except IOError: # <<< Exception will never be trown (RTFM, noob!) - pass #Raise a config warning error here? - self.config.add_section('ide') + self.config.read(filename) if not self.config.has_section('ide'): self.config.add_section('ide') - self.file = open(filename,'w') + self.file_config = open(filename,'w') def __setitem__(self, option, value): """Set the value to the option inside the default section.""" - self.config.set('ide',option, value) - + + self.config.set('ide', option, value) + def __getitem__(self, option): """Return the values to the option given, or return None""" + try: return self.config.get('ide', option) except ConfigParser.NoOptionError: return None def set_default(self, option, value): + if not self.config.has_option('ide', option): self[option] = value - + def save(self): """Write config to file.""" - self.config.write(self.file) + + self.config.write(self.file_config) #If we have yaml then the ConfigObject will point to the cooler config object if has_yaml: Config = _BeautifulConfig -else: # Otherwise we point to the ugly one +# Otherwise we point to the ugly one +else: Config = _UglyConfig #This way the importers use the config the same way, same api but under the #hood they are different. -class Ide_config(object): - def __init__(self, filepath= "", filename= "Ide_Config"): + +class IdeConfig(object): + + def __init__(self, filepath="", filename="Ide_Config"): + if not filepath: - filepath= os.path.dirname(__file__) + filepath = os.path.dirname(__file__) self._filepath = filepath self._filename = filename - self._fullpath= None - self._data= {} + self._fullpath = None + self._data = {} self._get_file_fullpath() self._get_defaults() - + def _get_file_fullpath(self): + if has_yaml: - ext= ".yaml" + ext = ".yaml" else: - ext= ".cfg" - self._fullpath= os.path.join(self._filepath, self._filename)+ext - if not os.path.exists(self._fullpath): - a= file(self._fullpath, "w") - a.close() - print ("Config useing filepath: %s" % (self._fullpath)) - + ext = ".cfg" + + _temp_path = os.path.join(self._filepath, self._filename) + self._fullpath = ''.join((_temp_path, ext)) + + with open(self._fullpath, "a") as _: + # Opens file as append to ensure safe creation of the file. + pass + + print ("Config using filepath: %s" % (self._fullpath)) + def _get_defaults(self): - confile= Config(self._fullpath) - self._data["indent"]= confile["indent"] or 4 - self._data["usetab"]= confile["usetab"] or 0 + + confile = Config(self._fullpath) + self._data["indent"] = confile["indent"] or 4 + self._data["usetab"] = confile["usetab"] or False self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 - confile.file.close() - + confile.file_config.close() + def __setitem__(self, key, value): - self._data[key]= value - + + self._data[key] = value + def __getitem__(self, key): + return self._data[key] - + def update_configfile(self): - confile= Config(self._fullpath) + + confile = Config(self._fullpath) for key, value in self._data.iteritems(): - confile[key]= value + confile[key] = value + confile.save() - confile.file.close() + confile.file_config.close() if __name__ == '__main__': - ide_config= Ide_config() - print (ide_config["indent"]) + ide_config = IdeConfig(config_style=Config) + print(ide_config["indent"]) ide_config.update_configfile() - + diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py index e30f2a7..405b1ff 100644 --- a/pythonforumide/gui_lib/ide_test_app.py +++ b/pythonforumide/gui_lib/ide_test_app.py @@ -1,11 +1,11 @@ """ Created on 31 Jul 2011 -@author: Main +@author: Main, confab """ import wx -from pythonforumide.config.config import Ide_config +from pythonforumide.config.config import IdeConfig class Wx_App(wx.App): def __init__(self, *args, **kwargs): @@ -15,7 +15,7 @@ def __init__(self, *args, **kwargs): def _create_config(self): """Set up config""" - self.config= Ide_config() + self.config= IdeConfig() def OnExit(self): """Handles the frame closing function""" diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 2b6021f..a87361a 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -1,16 +1,15 @@ """ Created on 31 Jul 2011 -@author: D.W., david +@author: D.W., david, confab @reviewer: david """ import wx import gui_lib.ide_mainframe as ide_mainframe import gui_lib.ide_mainframe_events as ide_mainframe_events -from config.config import Ide_config +from config.config import IdeConfig -#import config.config.Ide_config as Ide_config from twisted.internet import wxreactor wxreactor.install() @@ -31,7 +30,7 @@ def __init__(self, *args, **kwargs): def _create_config(self): """Set up config""" - self.config = Ide_config() + self.config = IdeConfig() def _create_port(self): """Creates a free port""" From 06119a0657a8d671fb614a76fc4245dc2b18d21d Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 19:41:21 +0100 Subject: [PATCH 038/141] Fixed smart indentation bug (sort of) --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 076302a..e8439d9 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -125,7 +125,7 @@ def SmartIndent(self): """Handles smart indentation for the editor""" # Read settings from the config file indent_amount = int(self.conf["indent"]) - usetab = int(self.conf["usetab"]) + usetab = self.conf["usetab"] last_line_no = self.GetCurrentLine() last_line = split_comments(self.GetLine(last_line_no))[0] From c6dbf16882252f0f6ba0f34e2f9e65490d24c732 Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 10:33:10 -0700 Subject: [PATCH 039/141] revamped config/config.py --- TOREVIEW | 5 + latest_diff | 347 +++++++++++++++++++++++++ pythonforumide/Ide_Config.cfg | 2 +- pythonforumide/config/Ide_Config.cfg | 2 +- pythonforumide/config/config.py | 157 ++++++----- pythonforumide/gui_lib/ide_test_app.py | 6 +- pythonforumide/wx_app.py | 7 +- 7 files changed, 455 insertions(+), 71 deletions(-) create mode 100644 latest_diff diff --git a/TOREVIEW b/TOREVIEW index 769bbcf..f9e1382 100644 --- a/TOREVIEW +++ b/TOREVIEW @@ -18,3 +18,8 @@ To get your file reviewed; Files to be reviewed; - +Ide_Config.cfg +config/Ide_Config.cfg +config/config.py +gui_lib/ide_test_app.py +wx_app.py diff --git a/latest_diff b/latest_diff new file mode 100644 index 0000000..3070603 --- /dev/null +++ b/latest_diff @@ -0,0 +1,347 @@ +t eaf1ce1fe147c53035368f7bafcb759a9ba3c17e +Author: confab +Date: Mon Aug 1 10:33:10 2011 -0700 + + revamped config/config.py + +diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg +index ffc8eaa..2697499 100644 +--- a/pythonforumide/Ide_Config.cfg ++++ b/pythonforumide/Ide_Config.cfg +@@ -1,5 +1,5 @@ + [ide] +-usetab = 0 ++usetab = False + mainframe.height = 600 + mainframe.width = 600 + indent = 4 +diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg +index ffc8eaa..2697499 100644 +--- a/pythonforumide/config/Ide_Config.cfg ++++ b/pythonforumide/config/Ide_Config.cfg +@@ -1,5 +1,5 @@ + [ide] +-usetab = 0 ++usetab = False + mainframe.height = 600 + mainframe.width = 600 + indent = 4 +diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py +index 8c76ea1..e5c508f 100644 +--- a/pythonforumide/config/config.py ++++ b/pythonforumide/config/config.py +@@ -15,17 +15,21 @@ except ImportError: + import ConfigParser #Ugly config file :( + has_yaml = False + +-def config_file(profile): +- """Returns the name of the configuration file. It does not guarantee existence. +- Side effect: Tells the user if can't load the configuration file if it is not supported. ++def file_config(profile): ++ """ ++ Returns the name of the configuration file. ++ It does not guarantee existence. ++ Side effect: ++ Tells the user if can't load the configuration file if it is not supported. + """ + + # We will need some fuzzy logic to accomplish this +- (profile, ext) = os.path.splitext(profile) +- yaml_exists = os.path.exists(profile + ".yaml") +- cfg_exists = os.path.exists(profile + ".cfg") ++ profile, ext = os.path.splitext(profile) ++ yaml_exists = os.path.exists(''.join((profile, ".yaml"))) ++ cfg_exists = os.path.exists(''.join((profile, ".cfg"))) + +- # If no extension could be found, guess what configuration type is likely to be preferred ++ # If no extension could be found, ++ # guess what configuration type is likely to be preferred. + if not ext: + if yaml_exists or (has_yaml and not cfg_exists): + ext = ".yaml" +@@ -34,14 +38,16 @@ def config_file(profile): + + # We can't load yaml if it doesn't exist + if ext == ".yaml" and not has_yaml: +- print("yaml configuration could not be loaded because python-yaml is not installed.") ++ print("yaml configuration could not be loaded", ++ "because python-yaml is not installed.") + ext = ".cfg" + +- return profile + ext ++ return ''.join((profile, ext)) + + def load_config(configfile): + """Loads a config file""" +- (profile, ext) = os.path.splitext(configfile) ++ ++ profile, ext = os.path.splitext(configfile) + if ext == ".yaml": + return _BeautifulConfig(profile) + else: +@@ -49,10 +55,14 @@ def load_config(configfile): + + class _BeautifulConfig(object): + """This is the config object if we can import yaml.""" ++ + def __init__(self, profile): +- """Load the yaml config from file, +- if this fails write an empty new one.""" +- filename = profile + ".yaml" ++ """ ++ Load the yaml config from file, ++ if this fails write an empty new one. ++ """ ++ ++ filename = ''.join((profile, ".yaml")) + + try: + self.config = yaml.load(open(filename)) +@@ -60,116 +70,139 @@ class _BeautifulConfig(object): + #Raise a config warning error here? + self.config = {} + +- self.file = open(filename,'w') +- ++ self.file_config = open(filename, 'w') ++ + def __setitem__(self, option, value): + """Set an option to a value inside the config, does not write.""" ++ + self.config[option] = value +- ++ + def __getitem__(self, option): +- """Gets the option from the config, if the option is not there +- returns None""" ++ """ ++ Gets the option from the config. ++ Return None, if the option is not there ++ """ ++ + return self.config.get(option, None) + + def set_default(self, option, value): ++ + self.config.set_default(option, value) + + def save(self): + """Writes the config as yaml out to the config file.""" +- yaml.dump(self.config, self.file) ++ ++ yaml.dump(self.config, self.file_config) + + class _UglyConfig(object): +- """This is the config object created if we can't use YAML and have to +- rely on ConfigParser.""" ++ """ ++ This is the config object created if we can't use YAML and have to ++ rely on ConfigParser. ++ """ ++ + def __init__(self, profile): +- filename = profile + ".cfg" +- filename= profile + ++ filename = ''.join((profile, ".cfg")) ++ filename = profile + self.config = ConfigParser.ConfigParser() +- try: +- self.config.read(filename) +- except IOError: # <<< Exception will never be trown (RTFM, noob!) +- pass #Raise a config warning error here? +- self.config.add_section('ide') ++ self.config.read(filename) + + if not self.config.has_section('ide'): + self.config.add_section('ide') + +- self.file = open(filename,'w') ++ self.file_config = open(filename,'w') + + def __setitem__(self, option, value): + """Set the value to the option inside the default section.""" +- self.config.set('ide',option, value) +- ++ ++ self.config.set('ide', option, value) ++ + def __getitem__(self, option): + """Return the values to the option given, or return None""" ++ + try: + return self.config.get('ide', option) + except ConfigParser.NoOptionError: + return None + + def set_default(self, option, value): ++ + if not self.config.has_option('ide', option): + self[option] = value +- ++ + def save(self): + """Write config to file.""" +- self.config.write(self.file) ++ ++ self.config.write(self.file_config) + + + #If we have yaml then the ConfigObject will point to the cooler config object + if has_yaml: + Config = _BeautifulConfig +-else: # Otherwise we point to the ugly one ++# Otherwise we point to the ugly one ++else: + Config = _UglyConfig + #This way the importers use the config the same way, same api but under the + #hood they are different. + +-class Ide_config(object): +- def __init__(self, filepath= "", filename= "Ide_Config"): ++ ++class IdeConfig(object): ++ ++ def __init__(self, filepath="", filename="Ide_Config"): ++ + if not filepath: +- filepath= os.path.dirname(__file__) ++ filepath = os.path.dirname(__file__) + self._filepath = filepath + self._filename = filename +- self._fullpath= None +- self._data= {} ++ self._fullpath = None ++ self._data = {} + self._get_file_fullpath() + self._get_defaults() +- ++ + def _get_file_fullpath(self): ++ + if has_yaml: +- ext= ".yaml" ++ ext = ".yaml" + else: +- ext= ".cfg" +- self._fullpath= os.path.join(self._filepath, self._filename)+ext +- if not os.path.exists(self._fullpath): +- a= file(self._fullpath, "w") +- a.close() +- print ("Config useing filepath: %s" % (self._fullpath)) +- ++ ext = ".cfg" ++ ++ _temp_path = os.path.join(self._filepath, self._filename) ++ self._fullpath = ''.join((_temp_path, ext)) ++ ++ with open(self._fullpath, "a") as _: ++ # Opens file as append to ensure safe creation of the file. ++ pass ++ ++ print ("Config using filepath: %s" % (self._fullpath)) ++ + def _get_defaults(self): +- confile= Config(self._fullpath) +- self._data["indent"]= confile["indent"] or 4 +- self._data["usetab"]= confile["usetab"] or 0 ++ ++ confile = Config(self._fullpath) ++ self._data["indent"] = confile["indent"] or 4 ++ self._data["usetab"] = confile["usetab"] or False + self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 + self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 +- confile.file.close() +- ++ confile.file_config.close() ++ + def __setitem__(self, key, value): +- self._data[key]= value +- ++ ++ self._data[key] = value ++ + def __getitem__(self, key): ++ + return self._data[key] +- ++ + def update_configfile(self): +- confile= Config(self._fullpath) ++ ++ confile = Config(self._fullpath) + for key, value in self._data.iteritems(): +- confile[key]= value ++ confile[key] = value ++ + confile.save() +- confile.file.close() ++ confile.file_config.close() + + if __name__ == '__main__': +- ide_config= Ide_config() +- print (ide_config["indent"]) ++ ide_config = IdeConfig(config_style=Config) ++ print(ide_config["indent"]) + ide_config.update_configfile() +- ++ +diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py +index e30f2a7..405b1ff 100644 +--- a/pythonforumide/gui_lib/ide_test_app.py ++++ b/pythonforumide/gui_lib/ide_test_app.py +@@ -1,11 +1,11 @@ + """ + Created on 31 Jul 2011 + +-@author: Main ++@author: Main, confab + """ + + import wx +-from pythonforumide.config.config import Ide_config ++from pythonforumide.config.config import IdeConfig + + class Wx_App(wx.App): + def __init__(self, *args, **kwargs): +@@ -15,7 +15,7 @@ class Wx_App(wx.App): + + def _create_config(self): + """Set up config""" +- self.config= Ide_config() ++ self.config= IdeConfig() + + def OnExit(self): + """Handles the frame closing function""" +diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py +index 2b6021f..a87361a 100644 +--- a/pythonforumide/wx_app.py ++++ b/pythonforumide/wx_app.py +@@ -1,16 +1,15 @@ + """ + Created on 31 Jul 2011 + +-@author: D.W., david ++@author: D.W., david, confab + @reviewer: david + """ + + import wx + import gui_lib.ide_mainframe as ide_mainframe + import gui_lib.ide_mainframe_events as ide_mainframe_events +-from config.config import Ide_config ++from config.config import IdeConfig + +-#import config.config.Ide_config as Ide_config + from twisted.internet import wxreactor + wxreactor.install() + +@@ -31,7 +30,7 @@ class Wx_App(wx.App): + + def _create_config(self): + """Set up config""" +- self.config = Ide_config() ++ self.config = IdeConfig() + + def _create_port(self): + """Creates a free port""" diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg index ffc8eaa..2697499 100644 --- a/pythonforumide/Ide_Config.cfg +++ b/pythonforumide/Ide_Config.cfg @@ -1,5 +1,5 @@ [ide] -usetab = 0 +usetab = False mainframe.height = 600 mainframe.width = 600 indent = 4 diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index ffc8eaa..2697499 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,5 +1,5 @@ [ide] -usetab = 0 +usetab = False mainframe.height = 600 mainframe.width = 600 indent = 4 diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index 8c76ea1..e5c508f 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -15,17 +15,21 @@ import ConfigParser #Ugly config file :( has_yaml = False -def config_file(profile): - """Returns the name of the configuration file. It does not guarantee existence. - Side effect: Tells the user if can't load the configuration file if it is not supported. +def file_config(profile): + """ + Returns the name of the configuration file. + It does not guarantee existence. + Side effect: + Tells the user if can't load the configuration file if it is not supported. """ # We will need some fuzzy logic to accomplish this - (profile, ext) = os.path.splitext(profile) - yaml_exists = os.path.exists(profile + ".yaml") - cfg_exists = os.path.exists(profile + ".cfg") + profile, ext = os.path.splitext(profile) + yaml_exists = os.path.exists(''.join((profile, ".yaml"))) + cfg_exists = os.path.exists(''.join((profile, ".cfg"))) - # If no extension could be found, guess what configuration type is likely to be preferred + # If no extension could be found, + # guess what configuration type is likely to be preferred. if not ext: if yaml_exists or (has_yaml and not cfg_exists): ext = ".yaml" @@ -34,14 +38,16 @@ def config_file(profile): # We can't load yaml if it doesn't exist if ext == ".yaml" and not has_yaml: - print("yaml configuration could not be loaded because python-yaml is not installed.") + print("yaml configuration could not be loaded", + "because python-yaml is not installed.") ext = ".cfg" - return profile + ext + return ''.join((profile, ext)) def load_config(configfile): """Loads a config file""" - (profile, ext) = os.path.splitext(configfile) + + profile, ext = os.path.splitext(configfile) if ext == ".yaml": return _BeautifulConfig(profile) else: @@ -49,10 +55,14 @@ def load_config(configfile): class _BeautifulConfig(object): """This is the config object if we can import yaml.""" + def __init__(self, profile): - """Load the yaml config from file, - if this fails write an empty new one.""" - filename = profile + ".yaml" + """ + Load the yaml config from file, + if this fails write an empty new one. + """ + + filename = ''.join((profile, ".yaml")) try: self.config = yaml.load(open(filename)) @@ -60,116 +70,139 @@ def __init__(self, profile): #Raise a config warning error here? self.config = {} - self.file = open(filename,'w') - + self.file_config = open(filename, 'w') + def __setitem__(self, option, value): """Set an option to a value inside the config, does not write.""" + self.config[option] = value - + def __getitem__(self, option): - """Gets the option from the config, if the option is not there - returns None""" + """ + Gets the option from the config. + Return None, if the option is not there + """ + return self.config.get(option, None) def set_default(self, option, value): + self.config.set_default(option, value) def save(self): """Writes the config as yaml out to the config file.""" - yaml.dump(self.config, self.file) + + yaml.dump(self.config, self.file_config) class _UglyConfig(object): - """This is the config object created if we can't use YAML and have to - rely on ConfigParser.""" + """ + This is the config object created if we can't use YAML and have to + rely on ConfigParser. + """ + def __init__(self, profile): - filename = profile + ".cfg" - filename= profile + filename = ''.join((profile, ".cfg")) + filename = profile self.config = ConfigParser.ConfigParser() - try: - self.config.read(filename) - except IOError: # <<< Exception will never be trown (RTFM, noob!) - pass #Raise a config warning error here? - self.config.add_section('ide') + self.config.read(filename) if not self.config.has_section('ide'): self.config.add_section('ide') - self.file = open(filename,'w') + self.file_config = open(filename,'w') def __setitem__(self, option, value): """Set the value to the option inside the default section.""" - self.config.set('ide',option, value) - + + self.config.set('ide', option, value) + def __getitem__(self, option): """Return the values to the option given, or return None""" + try: return self.config.get('ide', option) except ConfigParser.NoOptionError: return None def set_default(self, option, value): + if not self.config.has_option('ide', option): self[option] = value - + def save(self): """Write config to file.""" - self.config.write(self.file) + + self.config.write(self.file_config) #If we have yaml then the ConfigObject will point to the cooler config object if has_yaml: Config = _BeautifulConfig -else: # Otherwise we point to the ugly one +# Otherwise we point to the ugly one +else: Config = _UglyConfig #This way the importers use the config the same way, same api but under the #hood they are different. -class Ide_config(object): - def __init__(self, filepath= "", filename= "Ide_Config"): + +class IdeConfig(object): + + def __init__(self, filepath="", filename="Ide_Config"): + if not filepath: - filepath= os.path.dirname(__file__) + filepath = os.path.dirname(__file__) self._filepath = filepath self._filename = filename - self._fullpath= None - self._data= {} + self._fullpath = None + self._data = {} self._get_file_fullpath() self._get_defaults() - + def _get_file_fullpath(self): + if has_yaml: - ext= ".yaml" + ext = ".yaml" else: - ext= ".cfg" - self._fullpath= os.path.join(self._filepath, self._filename)+ext - if not os.path.exists(self._fullpath): - a= file(self._fullpath, "w") - a.close() - print ("Config useing filepath: %s" % (self._fullpath)) - + ext = ".cfg" + + _temp_path = os.path.join(self._filepath, self._filename) + self._fullpath = ''.join((_temp_path, ext)) + + with open(self._fullpath, "a") as _: + # Opens file as append to ensure safe creation of the file. + pass + + print ("Config using filepath: %s" % (self._fullpath)) + def _get_defaults(self): - confile= Config(self._fullpath) - self._data["indent"]= confile["indent"] or 4 - self._data["usetab"]= confile["usetab"] or 0 + + confile = Config(self._fullpath) + self._data["indent"] = confile["indent"] or 4 + self._data["usetab"] = confile["usetab"] or False self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 - confile.file.close() - + confile.file_config.close() + def __setitem__(self, key, value): - self._data[key]= value - + + self._data[key] = value + def __getitem__(self, key): + return self._data[key] - + def update_configfile(self): - confile= Config(self._fullpath) + + confile = Config(self._fullpath) for key, value in self._data.iteritems(): - confile[key]= value + confile[key] = value + confile.save() - confile.file.close() + confile.file_config.close() if __name__ == '__main__': - ide_config= Ide_config() - print (ide_config["indent"]) + ide_config = IdeConfig(config_style=Config) + print(ide_config["indent"]) ide_config.update_configfile() - + diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py index e30f2a7..405b1ff 100644 --- a/pythonforumide/gui_lib/ide_test_app.py +++ b/pythonforumide/gui_lib/ide_test_app.py @@ -1,11 +1,11 @@ """ Created on 31 Jul 2011 -@author: Main +@author: Main, confab """ import wx -from pythonforumide.config.config import Ide_config +from pythonforumide.config.config import IdeConfig class Wx_App(wx.App): def __init__(self, *args, **kwargs): @@ -15,7 +15,7 @@ def __init__(self, *args, **kwargs): def _create_config(self): """Set up config""" - self.config= Ide_config() + self.config= IdeConfig() def OnExit(self): """Handles the frame closing function""" diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index b9db488..ebd1c4b 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -1,16 +1,15 @@ """ Created on 31 Jul 2011 -@author: D.W., david +@author: D.W., david, confab @reviewer: david """ import wx import gui_lib.ide_mainframe as ide_mainframe import gui_lib.ide_mainframe_events as ide_mainframe_events -from config.config import Ide_config +from config.config import IdeConfig -#import config.config.Ide_config as Ide_config from twisted.internet import wxreactor wxreactor.install() @@ -30,7 +29,7 @@ def __init__(self, *args, **kwargs): def _create_config(self): """Set up config""" - self.config = Ide_config() + self.config = IdeConfig() def _create_port(self): """Creates a free port""" From 2f0bc3cab04f8e34feab7273324e0a4ef6343a79 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 19:41:21 +0100 Subject: [PATCH 040/141] Fixed smart indentation bug (sort of) --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 076302a..e8439d9 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -125,7 +125,7 @@ def SmartIndent(self): """Handles smart indentation for the editor""" # Read settings from the config file indent_amount = int(self.conf["indent"]) - usetab = int(self.conf["usetab"]) + usetab = self.conf["usetab"] last_line_no = self.GetCurrentLine() last_line = split_comments(self.GetLine(last_line_no))[0] From c1cf84e01de03c15bf747766904f5c3c6851bf06 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 21:22:57 +0100 Subject: [PATCH 041/141] Added is_saved() method --- pythonforumide/gui_lib/ide_editor.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index e8439d9..a65d236 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -205,6 +205,17 @@ def on_replace(self): #I think we should create a new frame for this, to be coded yet pass + def is_saved(self): + """Checks if the current file has been updated since last save""" + f = open(self.filepath, "r") + saved_text = f.read() + f.close() + + if saved_text == self.GetText(): + return True + else: + return False + def on_run(self): """Runs selected code in a new window.""" # Create a test frame and hook into the caller. From 56af47bb400f5a92acc7cdd7806133a59cb37a3d Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 21:58:36 +0100 Subject: [PATCH 042/141] Updated on_run to call GetModify() --- pythonforumide/gui_lib/ide_editor.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index a65d236..1511971 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -228,16 +228,16 @@ def on_run(self): run_editor.Layout() if self.filepath: + if self.GetModify(): + filename = os.path.split(self.filepath)[1] + run_panel.WriteText("Running %s" % filename) + run_panel.Newline() - filename = os.path.split(self.filepath)[1] - run_panel.WriteText("Running %s" % filename) - run_panel.Newline() - - run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1]) + run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1]) - reactor.spawnProcess(PythonProcessProtocol(run_panel), - get_python_exe(), - ["python", str(self.filepath)]) + reactor.spawnProcess(PythonProcessProtocol(run_panel), + get_python_exe(), + ["python", str(self.filepath)]) else: run_panel.WriteText("Running unsaved script.") run_panel.Newline() From 80d37f8628535fc37adbfe81fc570927d51e1363 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 23:00:38 +0100 Subject: [PATCH 043/141] Find/Replace working --- pythonforumide/config/Ide_Config.cfg | 6 ------ pythonforumide/gui_lib/ide_editor.py | 17 +++++------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 2697499..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +0,0 @@ -[ide] -usetab = False -mainframe.height = 600 -mainframe.width = 600 -indent = 4 - diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 1511971..63bbe8a 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -21,6 +21,7 @@ from StringIO import StringIO from ide_test_frame import TestFrame, TestPanel +from ide_replace_frame import ReplaceFrame #TODO: make customisable font and sizes. Perhaps maked this named tuple? faces = { 'times': 'Times', @@ -202,20 +203,12 @@ def save_file_as(self, filepath): def on_replace(self): """Displays a find/replace dialog""" - #I think we should create a new frame for this, to be coded yet + replace_frame_app = wx.PySimpleApp() + replace_frame = ReplaceFrame(parent=self, id=-1) + replace_frame.Show() + replace_frame_app.MainLoop() pass - def is_saved(self): - """Checks if the current file has been updated since last save""" - f = open(self.filepath, "r") - saved_text = f.read() - f.close() - - if saved_text == self.GetText(): - return True - else: - return False - def on_run(self): """Runs selected code in a new window.""" # Create a test frame and hook into the caller. From 23910d984f9e78b345322df3bf4b1a41bc254cef Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 1 Aug 2011 23:20:43 +0100 Subject: [PATCH 044/141] Polished Find/Replace Frame and function --- pythonforumide/gui_lib/ide_replace_frame.py | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 pythonforumide/gui_lib/ide_replace_frame.py diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py new file mode 100644 index 0000000..89679a1 --- /dev/null +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -0,0 +1,35 @@ +import wx + +class ReplaceFrame(wx.Frame): + """Class with the GUI and the GUI functions""" + def __init__(self, parent, id): + """"Displays the frame, creates the GUI""" + wx.Frame.__init__(self, parent, id, "Find and Replace", size=(410, 120)) + self.create_gui() + self.to_replace.SetFocus() + self.parent = parent + + def create_gui(self): + """Creates and displays the GUI""" + self.panel = wx.Panel(self) + self.to_replace_label = wx.StaticText(self.panel, -1, "Search for: ", + (5, 8), (100, -1) ) + self.to_replace = wx.TextCtrl(self.panel, id=-1, pos=(100, 5), + size = (300, -1) ) + self.replace_for_label = wx.StaticText(self.panel, -1, "Replace with: ", + (5, 53), (100, -1) ) + self.to_replace_with = wx.TextCtrl(self.panel, id=-1, pos=(100, 50), + size = (300, -1) ) + self.replace_id = wx.NewId() + self.replace_button = wx.Button(self.panel, self.replace_id, "Replace", + pos=(5, 80), size=(90, -1)) + self.Bind(wx.EVT_BUTTON, self.on_replace, id=self.replace_id) + + def on_replace(self, event): + """Replaces text on the current editor (self.parent)""" + self.to_replace_text = self.to_replace.GetValue() + self.to_replace_with_text = self.to_replace_with.GetValue() + self.parent.SetText(self.parent.GetText().replace( + self.to_replace_text, + self.to_replace_with_text)) + self.Destroy() From 9d048e501d71aab45d6ba003d0c8ae0c3585290f Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 23:38:52 +0100 Subject: [PATCH 045/141] Fixed issue 19. --- pythonforumide/config/Ide_Config.cfg | 6 ++++++ pythonforumide/gui_lib/ide_editor.py | 22 ++++++++++----------- pythonforumide/gui_lib/ide_replace_frame.py | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index e69de29..15530b2 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = False +mainframe.width = 600 +indent = 4 +mainframe.height = 600 + diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 63bbe8a..b7e2df4 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -207,7 +207,8 @@ def on_replace(self): replace_frame = ReplaceFrame(parent=self, id=-1) replace_frame.Show() replace_frame_app.MainLoop() - pass + replace_frame_app.Destroy() + def on_run(self): """Runs selected code in a new window.""" @@ -221,16 +222,15 @@ def on_run(self): run_editor.Layout() if self.filepath: - if self.GetModify(): - filename = os.path.split(self.filepath)[1] - run_panel.WriteText("Running %s" % filename) - run_panel.Newline() - - run_panel.WriteText("Running %s." % os.path.split(self.filepath)[-1]) - - reactor.spawnProcess(PythonProcessProtocol(run_panel), - get_python_exe(), - ["python", str(self.filepath)]) + if self.GetModify(): + self.SaveFile(self.filepath) + filename = os.path.split(self.filepath)[1] + run_panel.WriteText("Running %s" % filename) + run_panel.Newline() + reactor.spawnProcess(PythonProcessProtocol(run_panel), + get_python_exe(), + ["python", str(self.filepath)]) + else: run_panel.WriteText("Running unsaved script.") run_panel.Newline() diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 89679a1..b4a9426 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -4,7 +4,7 @@ class ReplaceFrame(wx.Frame): """Class with the GUI and the GUI functions""" def __init__(self, parent, id): """"Displays the frame, creates the GUI""" - wx.Frame.__init__(self, parent, id, "Find and Replace", size=(410, 120)) + wx.Frame.__init__(self, parent, id, "Find and Replace", size=(410, 150)) self.create_gui() self.to_replace.SetFocus() self.parent = parent From a03fbdaa52f83eac30da047abee5990ad8350d4a Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Mon, 1 Aug 2011 23:58:40 +0100 Subject: [PATCH 046/141] Changed a few things around still fixing davids --- pythonforumide/config/Ide_Config.cfg | 6 ------ pythonforumide/gui_lib/ide_editor.py | 8 ++++---- pythonforumide/gui_lib/ide_test_frame.py | 14 +++++++------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 15530b2..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +0,0 @@ -[ide] -usetab = False -mainframe.width = 600 -indent = 4 -mainframe.height = 600 - diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index b7e2df4..b7d0ca6 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -20,7 +20,7 @@ except ImportError: from StringIO import StringIO -from ide_test_frame import TestFrame, TestPanel +from ide_simple_frame import SimpleFrame from ide_replace_frame import ReplaceFrame #TODO: make customisable font and sizes. Perhaps maked this named tuple? @@ -203,8 +203,8 @@ def save_file_as(self, filepath): def on_replace(self): """Displays a find/replace dialog""" - replace_frame_app = wx.PySimpleApp() - replace_frame = ReplaceFrame(parent=self, id=-1) + replace_frame_app = wx.PySimpleApp(False) + replace_frame = ReplaceFrame(self, id=wx.NewId()) replace_frame.Show() replace_frame_app.MainLoop() replace_frame_app.Destroy() @@ -216,7 +216,7 @@ def on_run(self): # Allows this frame to be destroyed by the main window on close. reactor = wx.GetApp().this_reactor - run_editor = TestFrame(wx.GetApp().TopWindow, title="") + run_editor = SimpleFrame(wx.GetApp().TopWindow, title="") run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) run_editor.sizer.Add(run_panel, 1, wx.EXPAND) run_editor.Layout() diff --git a/pythonforumide/gui_lib/ide_test_frame.py b/pythonforumide/gui_lib/ide_test_frame.py index d34776f..0f514ad 100644 --- a/pythonforumide/gui_lib/ide_test_frame.py +++ b/pythonforumide/gui_lib/ide_test_frame.py @@ -6,30 +6,30 @@ import wx -class TestFrame(wx.Frame): +class SimpleFrame(wx.Frame): """Frame to use for testing""" def __init__(self, *args, **kwargs): """Initiates the frame and the GUI""" - super(TestFrame, self).__init__(*args, **kwargs) - self.SetInitialSize((600,600)) + super(SimpleFrame, self).__init__(*args, **kwargs) + self.SetInitialSize(kwargs.get("size", (600,600))) self.Center(wx.BOTH) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.Show() -class TestPanel(wx.Panel): +class SimplePanel(wx.Panel): """Panel to use for testing""" def __init__(self, *args, **kwargs): """Creates the GUI for the test panel""" - super(TestPanel, self).__init__(*args, **kwargs) + super(SimplePanel, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) if __name__ == '__main__': """Adds the test panel to the test frame""" app = wx.PySimpleApp(False) - frame= TestFrame(None, title= "Testing TestFrame") - panel= TestPanel(frame) + frame= SimpleFrame(None, title= "Testing TestFrame") + panel= SimplePanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) frame.Layout() app.MainLoop() From d569d03cc3591f8bb9b03d85b4317cf50b630bce Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 16:10:58 -0700 Subject: [PATCH 047/141] changed to simple_frame --- pythonforumide/gui_lib/ide_editor.py | 6 +++--- pythonforumide/gui_lib/ide_notebook.py | 8 ++++---- .../gui_lib/{ide_test_frame.py => ide_simple_frame.py} | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) rename pythonforumide/gui_lib/{ide_test_frame.py => ide_simple_frame.py} (91%) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index b7d0ca6..546a819 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -247,10 +247,10 @@ def on_run(self): if __name__=='__main__': """Adds the editor to the frame""" import ide_test_app as wx_app - import ide_test_frame + import ide_simple_frame app = wx_app.Wx_App(False) - frame= ide_test_frame.TestFrame(None, title= "Testing editor") - panel= ide_test_frame.TestPanel(frame) + frame= ide_simple_frame.SimpleFrame(None, title= "Testing editor") + panel= ide_simple_frame.TestPanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) editor= Editor(panel) panel.sizer.Add(editor, 1, wx.EXPAND) diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index e6d47fe..d9ce25a 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -205,11 +205,11 @@ def active_editor_can_run(self): if __name__=='__main__': import ide_test_app as wx_app - import ide_test_frame + import ide_simple_frame app = wx_app.Wx_App(False) - frame = ide_test_frame.TestFrame(None, - title="Testing notebook without events") - panel= ide_test_frame.TestPanel(frame) + frame = ide_simple_frame.SimpleFrame(None, + title="Testing notebook without events") + panel= ide_simple_frame.TestPanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) notebook= Notebook(panel) notebook.new_editor_tab() diff --git a/pythonforumide/gui_lib/ide_test_frame.py b/pythonforumide/gui_lib/ide_simple_frame.py similarity index 91% rename from pythonforumide/gui_lib/ide_test_frame.py rename to pythonforumide/gui_lib/ide_simple_frame.py index 0f514ad..6d0c5ed 100644 --- a/pythonforumide/gui_lib/ide_test_frame.py +++ b/pythonforumide/gui_lib/ide_simple_frame.py @@ -28,8 +28,8 @@ def __init__(self, *args, **kwargs): if __name__ == '__main__': """Adds the test panel to the test frame""" app = wx.PySimpleApp(False) - frame= SimpleFrame(None, title= "Testing TestFrame") - panel= SimplePanel(frame) + frame = SimpleFrame(None, title= "Testing SimpleFrame") + panel = SimplePanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) frame.Layout() app.MainLoop() From 3f8d3fc3365e88afd52142f871050edd0fc5c526 Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 17:15:33 -0700 Subject: [PATCH 048/141] temp fix to get the app working again, still needs work to be stable again. --- pythonforumide/gui_lib/ide_editor.py | 14 +++--- pythonforumide/gui_lib/ide_replace_frame.py | 49 ++++++++++++--------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 546a819..cbedda6 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -203,12 +203,14 @@ def save_file_as(self, filepath): def on_replace(self): """Displays a find/replace dialog""" - replace_frame_app = wx.PySimpleApp(False) - replace_frame = ReplaceFrame(self, id=wx.NewId()) - replace_frame.Show() - replace_frame_app.MainLoop() - replace_frame_app.Destroy() - + + # Create a search frame and hook into the caller. + # Allows this frame to be destroyed by the main window on close. + replace_frame = ReplaceFrame(active_editor=self, + parent=wx.GetApp().TopWindow, + title="Find and Replace", size=(410, 150)) + replace_frame.Layout() + def on_run(self): """Runs selected code in a new window.""" diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index b4a9426..2732826 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -1,35 +1,40 @@ import wx -class ReplaceFrame(wx.Frame): +from ide_simple_frame import SimpleFrame + +class ReplaceFrame(SimpleFrame): """Class with the GUI and the GUI functions""" - def __init__(self, parent, id): + + def __init__(self, active_editor, *args, **kwargs): """"Displays the frame, creates the GUI""" - wx.Frame.__init__(self, parent, id, "Find and Replace", size=(410, 150)) + + super(ReplaceFrame, self).__init__(*args, **kwargs) self.create_gui() - self.to_replace.SetFocus() - self.parent = parent + self.txt_to_replace.SetFocus() + self.active_editor = active_editor def create_gui(self): """Creates and displays the GUI""" - self.panel = wx.Panel(self) - self.to_replace_label = wx.StaticText(self.panel, -1, "Search for: ", - (5, 8), (100, -1) ) - self.to_replace = wx.TextCtrl(self.panel, id=-1, pos=(100, 5), - size = (300, -1) ) - self.replace_for_label = wx.StaticText(self.panel, -1, "Replace with: ", - (5, 53), (100, -1) ) - self.to_replace_with = wx.TextCtrl(self.panel, id=-1, pos=(100, 50), - size = (300, -1) ) + + # TODO: This needs to be cleaned up with a sizer instead of absolute values. + # As it is, the text box is cut off. + # Also needs to have id's from gui_lib/ide_constants.py instead of literal constants. + # I'm intentionally leaving it with long lines until that is done. + + _panel = wx.Panel(self) + self.lbl_to_replace = wx.StaticText(_panel, -1, "Search for: ", (5, 8), (100, -1)) + self.txt_to_replace = wx.TextCtrl(_panel, id=-1, pos=(100, 5), size=(300, -1)) + self.lbl_replace_with = wx.StaticText(_panel, -1, "Replace with: ", (5, 53), (100, -1)) + self.txt_replace_with = wx.TextCtrl(_panel, id=-1, pos=(100, 50), size=(300, -1)) self.replace_id = wx.NewId() - self.replace_button = wx.Button(self.panel, self.replace_id, "Replace", - pos=(5, 80), size=(90, -1)) + self.replace_button = wx.Button(_panel, self.replace_id, "Replace", pos=(5, 80), size=(90, -1)) self.Bind(wx.EVT_BUTTON, self.on_replace, id=self.replace_id) + self.sizer.Add(_panel, 1, wx.EXPAND) def on_replace(self, event): - """Replaces text on the current editor (self.parent)""" - self.to_replace_text = self.to_replace.GetValue() - self.to_replace_with_text = self.to_replace_with.GetValue() - self.parent.SetText(self.parent.GetText().replace( - self.to_replace_text, - self.to_replace_with_text)) + """Replaces text on the current editor (self.active_editor)""" + self.str_to_replace = self.txt_to_replace.GetValue() + self.str_replace_with = self.txt_replace_with.GetValue() + self.active_editor.SetText(self.active_editor.GetText().replace( + self.str_to_replace, self.str_replace_with)) self.Destroy() From 7e908551e126d0ed67f148664be3bcb0282cb664 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Tue, 2 Aug 2011 01:34:56 +0100 Subject: [PATCH 049/141] a test config version and a framed panel im working on --- .../config/config- TestVersion Yoriz.py | 219 +++++++++++++ pythonforumide/gui_lib/ide_framed_panel.py | 306 ++++++++++++++++++ 2 files changed, 525 insertions(+) create mode 100644 pythonforumide/config/config- TestVersion Yoriz.py create mode 100644 pythonforumide/gui_lib/ide_framed_panel.py diff --git a/pythonforumide/config/config- TestVersion Yoriz.py b/pythonforumide/config/config- TestVersion Yoriz.py new file mode 100644 index 0000000..73e532e --- /dev/null +++ b/pythonforumide/config/config- TestVersion Yoriz.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Jul 16 21:01:15 2011 +@author: Jakob, Somelauw +@reviewer: David, SomeLauw +""" + +from __future__ import print_function +import os.path + +try: + import yaml #Pretty config file :D + has_yaml = True +except ImportError: + import ConfigParser #Ugly config file :( + has_yaml = False + +def config_file(profile): + """Returns the name of the configuration file. It does not guarantee existence. + Side effect: Tells the user if can't load the configuration file if it is not supported. + """ + + # We will need some fuzzy logic to accomplish this + (profile, ext) = os.path.splitext(profile) + yaml_exists = os.path.exists(profile + ".yaml") + cfg_exists = os.path.exists(profile + ".cfg") + + # If no extension could be found, guess what configuration type is likely to be preferred + if not ext: + if yaml_exists or (has_yaml and not cfg_exists): + ext = ".yaml" + else: + ext = ".cfg" + + # We can't load yaml if it doesn't exist + if ext == ".yaml" and not has_yaml: + print("yaml configuration could not be loaded because python-yaml is not installed.") + ext = ".cfg" + + return profile + ext + +def load_config(configfile): + """Loads a config file""" + (profile, ext) = os.path.splitext(configfile) + if ext == ".yaml": + return _BeautifulConfig(profile) + else: + return _UglyConfig(profile) + +class _BeautifulConfig(object): + """This is the config object if we can import yaml.""" + def __init__(self, profile): + """Load the yaml config from file, + if this fails write an empty new one.""" + filename = profile + ".yaml" + + try: + self.config = yaml.load(open(filename)) + except IOError: + #Raise a config warning error here? + self.config = {} + + self.file = open(filename,'w') + + def __setitem__(self, option, value): + """Set an option to a value inside the config, does not write.""" + self.config[option] = value + + def __getitem__(self, option): + """Gets the option from the config, if the option is not there + returns None""" + return self.config.get(option, None) + + def set_default(self, option, value): + self.config.set_default(option, value) + + def save(self): + """Writes the config as yaml out to the config file.""" + yaml.dump(self.config, self.file) + +class _UglyConfig(object): + """This is the config object created if we can't use YAML and have to + rely on ConfigParser.""" + def __init__(self, profile): + filename = profile + ".cfg" + filename= profile + + self.config = ConfigParser.ConfigParser() + try: + self.config.read(filename) + except IOError: # <<< Exception will never be trown (RTFM, noob!) + pass #Raise a config warning error here? + self.config.add_section('ide') + + if not self.config.has_section('ide'): + self.config.add_section('ide') + + self.file = open(filename,'w') + + def __setitem__(self, option, value): + """Set the value to the option inside the default section.""" + self.config.set('ide',option, value) + + def __getitem__(self, option): + """Return the values to the option given, or return None""" + try: + return self.config.get('ide', option) + except ConfigParser.NoOptionError: + return None + + def set_default(self, option, value): + if not self.config.has_option('ide', option): + self[option] = value + + def save(self): + """Write config to file.""" + self.config.write(self.file) + + +#If we have yaml then the ConfigObject will point to the cooler config object +if has_yaml: + Config = _BeautifulConfig +else: # Otherwise we point to the ugly one + Config = _UglyConfig +#This way the importers use the config the same way, same api but under the +#hood they are different. + +class Ide_config(object): + def __init__(self, filepath= "", filename= "Ide_Config"): + if not filepath: + filepath= os.path.dirname(__file__) + self._filepath = filepath + self._filename = filename + self._fullpath= None + self._data= {} + self._get_file_fullpath() + self.set_defaults() + self.read_from_config_file() + + def _get_file_fullpath(self): + """ Works out which config file type""" + if has_yaml: + ext= ".yaml" + else: + ext= ".cfg" + self._fullpath= os.path.join(self._filepath, self._filename)+ext + if not os.path.exists(self._fullpath): + a= file(self._fullpath, "w") + a.close() + print ("Config useing filepath: %s" % (self._fullpath)) + + def read_from_config_file(self): + """ reads the config file and over writes defaults if theres as entry""" + """ if theres a type flag it converts to the python type""" + confile= Config(self._fullpath) + for key, value in self._data.iteritems(): + config_value= confile[key] + if config_value: + try: + this_type, this_value= config_value.split("<") + print (this_type, this_value[:-1]) + print (__builtins__[this_type](this_value[:-1])) + self._data[key]= __builtins__[this_type](this_value[:-1]) + except Exception, exception: + self._data[key]= config_value + confile.file.close() + + def __setitem__(self, key, value): + """ Sets the in memory value""" + self._data[key]= value + + def __getitem__(self, key): + """ Gets the in memory value""" + return self._data[key] + + def update_configfile(self): + """ Writes back the current values to the config file""" + """ Adds a type flag to the entry""" + confile= Config(self._fullpath) + for key, value in self._data.iteritems(): + this_type= str(type(value)) + this_type= this_type[:-2].split("'")[1] + if this_type not in ("int", "float", "bool"): + this_type= "str" + value= "%s<%s>" % (this_type, str(value)) + confile[key]= value + confile.save() + confile.file.close() + + def set_defaults(self): + """ Sets default values""" + self._data["indent"]= 4 + self._data["usetab"]= 0 + self._data["MainFrame.Height"] = 600 + self._data["MainFrame.Width"] = 600 + self._data["Test.bool"]= True + self._data["Test.int"]= 25 + self._data["Test.float"]= 0.75 + self._data["Test.list"]= ["1", "2", "3"] + self._data["Test.tuple"]= ("1", "2", "3") + +""" This could be usefull not to have to worry about converting value to string + to save them and then changing back toint, bool ect to use them""" + +if __name__ == '__main__': + ide_config= Ide_config() + print (ide_config["indent"]) + ide_config["MainFrame.Height"]= 600 + print (type(ide_config["Test.bool"])) + ide_config["indent"]= 4 + ide_config.update_configfile() + ide_config= Ide_config() + print (ide_config["Test.int"]*ide_config["Test.float"]) + print (type(ide_config["Test.list"])) + print (type(ide_config["Test.tuple"])) + print (ide_config["Test.list"]) + ide_config.update_configfile() + + diff --git a/pythonforumide/gui_lib/ide_framed_panel.py b/pythonforumide/gui_lib/ide_framed_panel.py new file mode 100644 index 0000000..3053170 --- /dev/null +++ b/pythonforumide/gui_lib/ide_framed_panel.py @@ -0,0 +1,306 @@ +""" +Created on 31 Jul 2011 + +@author: D.W. +""" + +import wx + +class FramedPanel(wx.Panel): + def __init__(self, *args, **kwargs): + """ Creates a panel for displaying text """ + super(FramedPanel, self).__init__(*args, **kwargs) + self._create_variables() + + sizer= wx.BoxSizer(wx.VERTICAL) + + self._create_header(sizer) + self._create_textctrl(sizer) + self.SetSizer(sizer) + self.Layout() + + def _create_variables(self): + self._parent= self.GetParent() + self._parents_child_state= {} + + self._minimum_size= 26 + self._current_size= (-1, 50) + self._maximized= False + self._minimized= False + + self._drag_vertical= True + self._sensitivity= 0.2 + self._header_pos= 0 + + def _create_header(self, sizer): +## header_sizer= wx.BoxSizer(wx.VERTICAL) +## sizer.Add(header_sizer, 0, wx.EXPAND|wx.ALL, 2) + + ctrl= HeaderPanel(self, style= wx.BORDER_THEME|wx.TAB_TRAVERSAL) + sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL|wx.ALIGN_CENTER, 2) + self._header_panel= ctrl + + def _create_textctrl(self, sizer): + ctrl= wx.TextCtrl(self, value= "Some text", style=wx.TE_MULTILINE|wx.TE_READONLY) + sizer.Add(ctrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, 2) + self.textctrl= ctrl + + def set_text(self, text): + self.textctrl.SetValue(text) + + def set_caption(self, text): + self._header_panel._header_caption.SetLabel(" %s" % (text)) + + def show(self, is_shown= True): + self.Show(is_shown) + self._parent.Layout() + + def get_parent_client_size(self): + return self._parent.GetClientSize() + + def send_parent_size_evet(self): + self._parent.SendSizeEvent() + + def get_own_client_size_tuple(self): + return self.GetClientSizeTuple() + + def get_sizer_item(self): + sizer= self.GetContainingSizer() + return sizer.GetItem(self) + + def set_sizer_item_proportion(self, proportion): + self.get_sizer_item().SetProportion(proportion) + + def set_size(self, width, height): + sizer_item= self.get_sizer_item() + sizer_item.SetMinSize((width, height)) + self.send_parent_size_evet() + self._parent.Layout() + self._parent.Update() + self._parent.Refresh() + + def get_parents_children(self): + return self._parent.GetChildren() + + + def set_parents_childrens_state(self): + for child_panel in self.get_parents_children(): + if child_panel != self: + self._parents_child_state[child_panel]= child_panel.IsShown() + + def minimize(self): + if self._maximized: + self.restore() + self.set_size(-1, self._minimum_size) + self._minimized= True + + def maximize(self): + self.set_parents_childrens_state() + self._current_size= self.get_own_client_size_tuple() + for child_panel, state in self._parents_child_state.iteritems(): + child_panel.Show(False) + self.set_sizer_item_proportion(1) + self.set_size(-1, self.get_parent_client_size()[1]) + self._maximized= True + self._minimized= False + self._header_panel._btn_maximize.SetLabel("2") + + def restore(self): + self.set_sizer_item_proportion(0) + for child_panel, state in self._parents_child_state.iteritems(): + child_panel.Show(state) + self.set_size(-1, self._current_size[1]) + self._maximized= False + self._minimized= False + self._header_panel._btn_maximize.SetLabel("1") + + + + + + + + +class FramedPanelEvents(object): + """ Creates the events for framed panel""" + def __init__(self, view, model = ""): + """ Sets the view and model if one s required""" + self.view = view + self.model = model + self._create_binds() + + def _create_binds(self): + """ Creates the binds""" +## self._header_panel.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse) + +## self._dragbar_top.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse) +## self._dragbar_bottom.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse) + header_panel= self.view._header_panel + header_panel._btn_minimize.Bind(wx.EVT_BUTTON, self._on_btn_minimize) + header_panel._btn_maximize.Bind(wx.EVT_BUTTON, self._on_btn_maximize) + header_panel._btn_close.Bind(wx.EVT_BUTTON, self._on_btn_close) + +# self.timer = wx.Timer(self) +# self.Bind(wx.EVT_TIMER, self._update_title) +# self.timer.Start(40) # in miliseconds + + def _on_mouse(self, event): +## print "Mouse event" + if event.LeftDown(): +## print "mouse event left down" + if self._drag_vertical: + self._header_pos= event.GetPosition()[1] + else: + self._header_pos= event.GetPosition()[0] + elif event.Dragging(): +## print "Mouse dragging" + x, y = self.ScreenToClient(self._header_panel.ClientToScreen(event.GetPosition())) + if self._drag_vertical: + change= ((y- self._header_pos)*-1)*self._sensitivity + else: + change= ((x- self._header_pos))*self._sensitivity + newsize= self.GetSize()[1]+change + if newsize> self._minimum_size: + self.SetSizeHints(-1, newsize) + self._parent.SendSizeEvent() + self._current_size= newsize +## elif event.ButtonDClick(): +## new_size= self.GetClientSize()[0] +## print "Mouse doubleclick currentsize: %s" % (self._current_size) +## print "Actual size: %s" % (self.GetSize()) +## if not self._maximized: +## self.SetSizeHints(-1, new_size) +## self._maximized= True +## else: +## self.SetSizeHints(-1, self._current_size) +## self._maximized= False +## self.SendSizeEventToParent() + + event.Skip() + + def _on_btn_close(self, event): + self.view.show(False) + + def _on_btn_minimize(self, event): + self.view.minimize() + + + def _on_btn_maximize(self, event): + if not self.view._maximized: + self.view.maximize() + else: + self.view.restore() + + + + def _update_title(self, event): + pos = wx.GetMousePosition() + csize= self.GetClientSizeTuple() +## cpos= self.GetPositionTuple() +## client_to_screen= self.ClientToScreen(cpos) + client_to_screen= self.GetScreenPosition() + panel_top = client_to_screen[1] + panel_bottom= panel_top+csize[1] + panel_left= client_to_screen[0] + panel_right= panel_left+csize[0] + in_width= pos[0]>= panel_left and pos[0]<= panel_right + if pos[1] in xrange(panel_top-5, panel_top+5): + toporbot= "At the top" + elif pos[1]in xrange(panel_bottom-5, panel_bottom+5): + toporbot= "At the Bottom" + else: + toporbot= "Neither" + text = "Window client size: %s \ client to screen pos: %s \n top: %s bottom: %s left: %s Right: %s inwidth: %s toporbottom: %s" % (csize, client_to_screen, panel_top, panel_bottom, panel_left, panel_right, in_width, toporbot) + self.set_text(text) + self.set_caption("Your mouse is at (%s, %s)" % (pos.x, pos.y)) + cur_cursor= self.GetCursor() + if in_width and toporbot!= "Neither": + cursor = wx.StockCursor(wx.CURSOR_SIZENS) + else: + cursor= wx.StockCursor(wx.CURSOR_DEFAULT) + if cur_cursor!= cursor: + self.SetCursor(cursor) + + + +class HeaderPanel(wx.Panel): + def __init__(self, *args, **kwargs): + """ Creates a panel for displaying text """ + super(HeaderPanel, self).__init__(*args, **kwargs) + self._button_font= wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, + u'Webdings') + sizer= wx.BoxSizer(wx.HORIZONTAL) + self._create_statictxt(sizer) + self._create_minimize(sizer) + self._create_maximize(sizer) + self._create_close(sizer) + + self.SetSizer(sizer) + self.Layout() + + def _create_statictxt(self, sizer): + ctrl= wx.StaticText(self, label= " Console") + font= ctrl.GetFont() + font.SetWeight(wx.FONTWEIGHT_BOLD) + ctrl.SetFont(font) + sizer.Add(ctrl, 0, flag= wx.ALIGN_CENTER_VERTICAL) + self._header_caption= ctrl + + def _create_minimize(self, sizer): + sizer.AddStretchSpacer(1) + ctrl= wx.Button(self, label= "0", size= (19, 19)) + ctrl.SetFont(self._button_font) + sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) + self._btn_minimize= ctrl + + def _create_maximize(self, sizer): + ctrl= wx.Button(self, label= "1", size= (19, 19)) + ctrl.SetFont(self._button_font) + sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) + self._btn_maximize= ctrl + # restore = 2 + + def _create_close(self, sizer): + ctrl= wx.Button(self, label= "r", size= (19, 19)) + ctrl.SetFont(self._button_font) + sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) + self._btn_close= ctrl + + + + + +if __name__=='__main__': + import ide_test_app as wx_app + import ide_test_frame + app = wx_app.Wx_App(False) + frame= ide_test_frame.TestFrame(None, title= "Testing TextPanel without events") + panel= ide_test_frame.TestPanel(frame) + frame.sizer.Add(panel, 1, wx.EXPAND) + panel_black= wx.Panel(panel) + panel_black.SetBackgroundColour((0, 0,0 )) + panel.sizer.Add(panel_black, 1, wx.EXPAND) + frame.panel_black= panel_black + + + text_panel= FramedPanel(panel) + FramedPanelEvents(text_panel) + panel.sizer.Add(text_panel, 0, wx.EXPAND) + text_panel.set_caption("New Title") + + text_panel= FramedPanel(panel) + FramedPanelEvents(text_panel) + panel.sizer.Add(text_panel, 0, wx.EXPAND) + text_panel= FramedPanel(panel) + FramedPanelEvents(text_panel) + panel.sizer.Add(text_panel, 0, wx.EXPAND) + text_panel= FramedPanel(panel) + FramedPanelEvents(text_panel) + panel.sizer.Add(text_panel, 0, wx.EXPAND) + + frame.Layout() + + + app.MainLoop() + + \ No newline at end of file From 8e49f54f0daa510283318d2d8c2165d4f747bd0e Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Tue, 2 Aug 2011 01:43:50 +0100 Subject: [PATCH 050/141] changed imports in framed_panel --- pythonforumide/config/Ide_Config.cfg | 6 ++++++ pythonforumide/gui_lib/ide_framed_panel.py | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index e69de29..2697499 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = False +mainframe.height = 600 +mainframe.width = 600 +indent = 4 + diff --git a/pythonforumide/gui_lib/ide_framed_panel.py b/pythonforumide/gui_lib/ide_framed_panel.py index 3053170..9322a10 100644 --- a/pythonforumide/gui_lib/ide_framed_panel.py +++ b/pythonforumide/gui_lib/ide_framed_panel.py @@ -272,10 +272,10 @@ def _create_close(self, sizer): if __name__=='__main__': import ide_test_app as wx_app - import ide_test_frame + from ide_simple_frame import SimpleFrame, SimplePanel app = wx_app.Wx_App(False) - frame= ide_test_frame.TestFrame(None, title= "Testing TextPanel without events") - panel= ide_test_frame.TestPanel(frame) + frame= SimpleFrame(None, title= "Testing TextPanel without events") + panel= SimplePanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) panel_black= wx.Panel(panel) panel_black.SetBackgroundColour((0, 0,0 )) From 463c63586e69d92279ad50c1696fb7544a2a1841 Mon Sep 17 00:00:00 2001 From: confab Date: Mon, 1 Aug 2011 18:02:47 -0700 Subject: [PATCH 051/141] updated HACKING, please no more master pushes unless asked for --- HACKING | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HACKING b/HACKING index b3a300b..a688f86 100644 --- a/HACKING +++ b/HACKING @@ -8,6 +8,13 @@ http://www.python.org/dev/peps/pep-0008/ When in doubt copy the coding style of the file you are editing. +All id's must come from gui_lib/ide_constants.py. +Do not push to master unless explicitly requested. +If you want to implement a new feature, create an issue ticket +and a new branch, preferably with the issue number e.g. issue_42 +Create a separate branch for any issues or new features. +See this guide for help: http://progit.org/book/ch3-0.html + To clone the latest PythonForumIDE version simpy use git to clone it: git clone git@github.com:Python-Forum/PythonForumIDE.git From ee549feb07bdc1ae87af3a22b53a21cb67fc5731 Mon Sep 17 00:00:00 2001 From: bunburya Date: Tue, 2 Aug 2011 02:11:45 +0100 Subject: [PATCH 052/141] Added a auto-complete-suggest backend class to start. --- pythonforumide/utils/autocomplete.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 pythonforumide/utils/autocomplete.py diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py new file mode 100644 index 0000000..5ea2aca --- /dev/null +++ b/pythonforumide/utils/autocomplete.py @@ -0,0 +1,44 @@ +class CodeCompletion: + """A backend class for code completion. + This does more than it needs to for auto-complete in wx (which does + most of the work), but the additional methods will be useful with + different UI toolkits. + """ + + def __init__(self, *modules): + self._suggestions = set() + self._cache = set() + for mod in modules: + self.add_module(m) + + def add_module(self, module): + """Adds the variable and method names from a module to the pool + of potential suggestions. + """ + self._suggestions.update(dir(module)) + + def add_suggestion(self, *suggest): + """Takes at least one string as an argument; adds each argument + to the pool of potential suggestions. + """ + self._suggestions.update(suggest) + + def suggest(self, key): + """Return a set of possible completions based on the keyword + provided. Stores the result in a cache so that future calls + don't unnecessarily repeat searches. + """ + pool = self._cache or self._suggestions + suggs = {s for s in pool if s.startswith(key)} + self._cache = set(suggs) + return suggs + + def cache(self, c): + self._cache = c + + def clear_cache(self): + self._cache = set() + + @property + def choices(self): + return self._suggestions() From d8ca67be6a75ea54f12150b24387cf357fa42ba2 Mon Sep 17 00:00:00 2001 From: bunburya Date: Tue, 2 Aug 2011 02:17:23 +0100 Subject: [PATCH 053/141] Added some more comments and such. --- pythonforumide/utils/autocomplete.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 5ea2aca..707f3dd 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -1,3 +1,8 @@ +""" +@author: bunburya +""" + + class CodeCompletion: """A backend class for code completion. This does more than it needs to for auto-complete in wx (which does @@ -41,4 +46,5 @@ def clear_cache(self): @property def choices(self): + """Return a set of all possible suggestions.""" return self._suggestions() From c7006124ded564009e3ba0297aa412b12fdc16d0 Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 2 Aug 2011 11:54:17 +0200 Subject: [PATCH 054/141] Made CodeCompletion inherit from object. --- pythonforumide/utils/autocomplete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 707f3dd..e0c704b 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -3,7 +3,7 @@ """ -class CodeCompletion: +class CodeCompletion(object): """A backend class for code completion. This does more than it needs to for auto-complete in wx (which does most of the work), but the additional methods will be useful with From 57d2b256af23264411257aeb7e470962a6663e16 Mon Sep 17 00:00:00 2001 From: bunburya Date: Tue, 2 Aug 2011 14:16:34 +0100 Subject: [PATCH 055/141] started trying to implement autocomplete on the GUI side, but am getting weird behaviour. --- pythonforumide/config/Ide_Config.cfg | 6 ----- pythonforumide/gui_lib/ide_editor.py | 38 ++++++++++++++++++++++++++++ pythonforumide/utils/autocomplete.py | 30 ++++++++++++++++++++-- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 2697499..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +0,0 @@ -[ide] -usetab = False -mainframe.height = 600 -mainframe.width = 600 -indent = 4 - diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index cbedda6..df28381 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -10,6 +10,7 @@ from utils.textutils import split_comments from utils.interpreter import PythonProcessProtocol from utils.version import get_python_exe +from utils.autocomplete import CodeCompletion import wx.richtext import wx.stc as stc import os.path @@ -47,6 +48,12 @@ def __init__(self, parent): self.SetMargins() self.SetStyles() self.SetBindings() + self.SetAutoComplete() + + def SetAutoComplete(self): + self.autocomp = CodeCompletion(__builtins__) + import math; self.autocomp.add_module(math) #testing + print(self.autocomp.suggest()) # testing def SetBindings(self): """Sets the key events bindings""" @@ -146,6 +153,36 @@ def SmartIndent(self): self.AddText(indent) print self.conf + + def AutoComp(self, event): + """This is very buggy. Todo: + - Code completion only works the first time you type a character. + - When code completion does work, it doesn't print the first character + of the selected word. + - It seems that backspacing to the start after using code completion + results in a Segmentation Fault. + - The backend class seems to be working as expected. It's AutoCompShow + that appears to be causing the problems. + """ + + try: + ch = chr(event.GetUniChar()).lower() + except ValueError: + self.autocomp.clear_cache() + self.autocomp.key = [] + return + self.autocomp.update_key(ch) + print 'key:', self.autocomp.key + choices = list(self.autocomp.suggest()) + if choices: + choices.sort() + args = self.autocomp.len_entered, ' '.join(choices) + print args + self.AutoCompShow(*args) # this doesn't seem to do anything + # most of the time. + else: + print 'no choices to show' + def OnKeyDown(self, event): """Defines events for when the user presses a key""" @@ -156,6 +193,7 @@ def OnKeyDown(self, event): self.SmartIndent() else: event.Skip() + self.AutoComp(event) def on_undo(self): """Checks if can Undo and if yes undoes""" diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index e0c704b..82397e6 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -10,11 +10,14 @@ class CodeCompletion(object): different UI toolkits. """ + valid_ch = 'abcdefghijklmnopqrstuvwxyz0123456789_' + def __init__(self, *modules): self._suggestions = set() self._cache = set() + self._key = [] for mod in modules: - self.add_module(m) + self.add_module(mod) def add_module(self, module): """Adds the variable and method names from a module to the pool @@ -28,15 +31,26 @@ def add_suggestion(self, *suggest): """ self._suggestions.update(suggest) - def suggest(self, key): + def suggest(self, key=None): """Return a set of possible completions based on the keyword provided. Stores the result in a cache so that future calls don't unnecessarily repeat searches. """ + if key is None: + key = ''.join(self._key) + if not key: + return set() pool = self._cache or self._suggestions suggs = {s for s in pool if s.startswith(key)} self._cache = set(suggs) return suggs + + def update_key(self, char): + if not char in self.valid_ch: + self._key = [] + self.clear_cache() + else: + self._key.append(char) def cache(self, c): self._cache = c @@ -48,3 +62,15 @@ def clear_cache(self): def choices(self): """Return a set of all possible suggestions.""" return self._suggestions() + + @property + def key(self): + return ''.join(self._key) + + @key.setter + def key(self, new): + self._key = list(new) + + @property + def len_entered(self): + return len(self._key) From 191352fdaaa4d681f9de6424aa96c1b69cc728e0 Mon Sep 17 00:00:00 2001 From: bunburya Date: Tue, 2 Aug 2011 14:31:23 +0100 Subject: [PATCH 056/141] fixed most problems with autocomplete, we now have working autocompletion. --- pythonforumide/config/Ide_Config.cfg | 6 ++++++ pythonforumide/gui_lib/ide_editor.py | 22 +++++----------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index e69de29..2697499 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -0,0 +1,6 @@ +[ide] +usetab = False +mainframe.height = 600 +mainframe.width = 600 +indent = 4 + diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index df28381..09cf89b 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -155,15 +155,10 @@ def SmartIndent(self): print self.conf def AutoComp(self, event): - """This is very buggy. Todo: - - Code completion only works the first time you type a character. - - When code completion does work, it doesn't print the first character - of the selected word. - - It seems that backspacing to the start after using code completion - results in a Segmentation Fault. - - The backend class seems to be working as expected. It's AutoCompShow - that appears to be causing the problems. - """ + """TODO: + - If you indent (via tab or SmartIndent) and then autocomplete, + it seems that the program automatically indents again after + printing the word.""" try: ch = chr(event.GetUniChar()).lower() @@ -172,17 +167,10 @@ def AutoComp(self, event): self.autocomp.key = [] return self.autocomp.update_key(ch) - print 'key:', self.autocomp.key choices = list(self.autocomp.suggest()) if choices: choices.sort() - args = self.autocomp.len_entered, ' '.join(choices) - print args - self.AutoCompShow(*args) # this doesn't seem to do anything - # most of the time. - else: - print 'no choices to show' - + self.AutoCompShow(self.autocomp.len_entered-1, ' '.join(choices)) def OnKeyDown(self, event): """Defines events for when the user presses a key""" From e64cb426c27fa4060cb89ea6615a5fbf68bba47e Mon Sep 17 00:00:00 2001 From: bunburya Date: Tue, 2 Aug 2011 23:43:39 +0100 Subject: [PATCH 057/141] fixing py2.6 compatibility issue, adding proper builtin support. --- pythonforumide/gui_lib/ide_editor.py | 5 ++--- pythonforumide/utils/autocomplete.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 09cf89b..3cf4ec4 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -51,9 +51,8 @@ def __init__(self, parent): self.SetAutoComplete() def SetAutoComplete(self): - self.autocomp = CodeCompletion(__builtins__) - import math; self.autocomp.add_module(math) #testing - print(self.autocomp.suggest()) # testing + self.autocomp = CodeCompletion() + self.autocomp.add_suggestion(*__builtins__.viewkeys()) def SetBindings(self): """Sets the key events bindings""" diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 82397e6..45f2a9f 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -41,7 +41,7 @@ def suggest(self, key=None): if not key: return set() pool = self._cache or self._suggestions - suggs = {s for s in pool if s.startswith(key)} + suggs = set(s for s in pool if s.startswith(key)) self._cache = set(suggs) return suggs From 35657f403e987aeeddfc190d2bf38fbc89384c44 Mon Sep 17 00:00:00 2001 From: bunburya Date: Tue, 2 Aug 2011 23:52:43 +0100 Subject: [PATCH 058/141] more fixing for 2.6 --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 3cf4ec4..79d990a 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -52,7 +52,7 @@ def __init__(self, parent): def SetAutoComplete(self): self.autocomp = CodeCompletion() - self.autocomp.add_suggestion(*__builtins__.viewkeys()) + self.autocomp.add_suggestion(*__builtins__.keys()) def SetBindings(self): """Sets the key events bindings""" From 9754d44992e62af8143a09bd2d35e2807bd84204 Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 02:26:11 +0100 Subject: [PATCH 059/141] added keywords; fixed backspacing; changed the way builtins are added; half-fixed uppercase issue --- pythonforumide/gui_lib/ide_editor.py | 39 +++++++++++++++++++--------- pythonforumide/utils/autocomplete.py | 30 ++++++++++++++++++--- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 79d990a..2470595 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -52,7 +52,8 @@ def __init__(self, parent): def SetAutoComplete(self): self.autocomp = CodeCompletion() - self.autocomp.add_suggestion(*__builtins__.keys()) + self.autocomp.add_builtins() + self.autocomp.add_keywords() def SetBindings(self): """Sets the key events bindings""" @@ -131,6 +132,11 @@ def SetStyles(self, lang='python'): def SmartIndent(self): """Handles smart indentation for the editor""" # Read settings from the config file + + # Suggestion: instead of indent_amount and use_tab, maybe just + # have one config value, specifying what is to be used as indent. + # -bunburya + indent_amount = int(self.conf["indent"]) usetab = self.conf["usetab"] @@ -153,19 +159,28 @@ def SmartIndent(self): self.AddText(indent) print self.conf - def AutoComp(self, event): + def AutoComp(self, event, keycode): """TODO: - If you indent (via tab or SmartIndent) and then autocomplete, it seems that the program automatically indents again after - printing the word.""" - - try: - ch = chr(event.GetUniChar()).lower() - except ValueError: - self.autocomp.clear_cache() - self.autocomp.key = [] - return - self.autocomp.update_key(ch) + printing the word. + - Properly handle uppercase; the current implementation ignores + caps lock. + """ + if keycode == wx.WXK_BACK: + self.autocomp.back() + else: + try: + # this isn't perfect, doesn't handle caps lock + if event.ShiftDown(): + ch = chr(event.GetUniChar()) + else: + ch = chr(event.GetUniChar()).lower() + print ch + self.autocomp.update_key(ch) + except ValueError: + self.autocomp.clear() + return choices = list(self.autocomp.suggest()) if choices: choices.sort() @@ -180,7 +195,7 @@ def OnKeyDown(self, event): self.SmartIndent() else: event.Skip() - self.AutoComp(event) + self.AutoComp(event, key) def on_undo(self): """Checks if can Undo and if yes undoes""" diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 45f2a9f..26b8e9a 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -2,6 +2,12 @@ @author: bunburya """ +# maybe move these to a separate file eventually +keywords = set(['and', 'elif', 'is', 'global', 'pass', 'if', 'from', + 'raise', 'for', 'except', 'finally', 'print', 'import', 'return', + 'exec', 'else', 'break', 'not', 'class', 'assert', 'in', 'yield', + 'try', 'while', 'continue', 'del', 'or', 'def', 'lambda']) +builtins = set(__builtins__.keys()) class CodeCompletion(object): """A backend class for code completion. @@ -10,7 +16,7 @@ class CodeCompletion(object): different UI toolkits. """ - valid_ch = 'abcdefghijklmnopqrstuvwxyz0123456789_' + valid_ch = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' def __init__(self, *modules): self._suggestions = set() @@ -19,6 +25,12 @@ def __init__(self, *modules): for mod in modules: self.add_module(mod) + def add_builtins(self): + self._suggestions.update(builtins) + + def add_keywords(self): + self._suggestions.update(keywords) + def add_module(self, module): """Adds the variable and method names from a module to the pool of potential suggestions. @@ -47,17 +59,29 @@ def suggest(self, key=None): def update_key(self, char): if not char in self.valid_ch: - self._key = [] - self.clear_cache() + self.clear() else: self._key.append(char) + def back(self): + try: + self._key.pop() + except IndexError: + pass + def cache(self, c): self._cache = c def clear_cache(self): self._cache = set() + def clear_key(self): + self._key = [] + + def clear(self): + self._cache = set() + self._key = [] + @property def choices(self): """Return a set of all possible suggestions.""" From 24e6313ec929587cd3d4daec4c609ef9858a7cef Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 03:40:53 +0200 Subject: [PATCH 060/141] Updated version requirements in HACKING --- HACKING | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/HACKING b/HACKING index a688f86..c3de5a9 100644 --- a/HACKING +++ b/HACKING @@ -1,5 +1,5 @@ If you want to hack on PythonForumIDE you need the following software: - - Python 2.7.1 + - Python 2.6 or 2.7 - wxPython 2.8 - Twisted 10.2.0-1 @@ -15,6 +15,10 @@ and a new branch, preferably with the issue number e.g. issue_42 Create a separate branch for any issues or new features. See this guide for help: http://progit.org/book/ch3-0.html +The IDE must be able to run on Python 2.6 and 2.7, so please note +any compatibility issues between the two and avoid them. +Code should be tested on both versions before being committed to master. + To clone the latest PythonForumIDE version simpy use git to clone it: git clone git@github.com:Python-Forum/PythonForumIDE.git From 2224966ed7365ca990a00b7df255ad2badc8d19f Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 03:17:18 +0100 Subject: [PATCH 061/141] just updating the docstring --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/utils/autocomplete.py | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 2697499..0477dcd 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = False -mainframe.height = 600 -mainframe.width = 600 +mainframe.height = 747 +mainframe.width = 1022 indent = 4 diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 45f2a9f..e209682 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -4,11 +4,7 @@ class CodeCompletion(object): - """A backend class for code completion. - This does more than it needs to for auto-complete in wx (which does - most of the work), but the additional methods will be useful with - different UI toolkits. - """ + """A backend class for code completion.""" valid_ch = 'abcdefghijklmnopqrstuvwxyz0123456789_' From 6ae3283894db7aabcfb3c1f6480ae35e0c9b8d3d Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 03:19:52 +0100 Subject: [PATCH 062/141] just changed the docstring. --- pythonforumide/utils/autocomplete.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 26b8e9a..2261bd7 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -11,9 +11,6 @@ class CodeCompletion(object): """A backend class for code completion. - This does more than it needs to for auto-complete in wx (which does - most of the work), but the additional methods will be useful with - different UI toolkits. """ valid_ch = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' From ebdbac9cb955ddc870f1674e3bf0b47dcfea1766 Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 03:39:27 +0100 Subject: [PATCH 063/141] removed test print --- pythonforumide/gui_lib/ide_editor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 2470595..f71f896 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -176,7 +176,6 @@ def AutoComp(self, event, keycode): ch = chr(event.GetUniChar()) else: ch = chr(event.GetUniChar()).lower() - print ch self.autocomp.update_key(ch) except ValueError: self.autocomp.clear() From 9ddeb557d5fb88cfa0bccd9bc0ed601fb65a7e44 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Wed, 3 Aug 2011 10:46:15 +0100 Subject: [PATCH 064/141] Fixed replace menu name --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/gui_lib/ide_constant.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 0477dcd..5905929 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = False -mainframe.height = 747 -mainframe.width = 1022 +mainframe.height = 744 +mainframe.width = 1366 indent = 4 diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py index fac361d..c8347b3 100644 --- a/pythonforumide/gui_lib/ide_constant.py +++ b/pythonforumide/gui_lib/ide_constant.py @@ -75,8 +75,8 @@ # Search ID #=============================================================================== ID_SEARCH= wx.NewId() -id_text_search= _id_text("Search\tCtrl+H", "Search", - "Search/replace the active file", wx.ITEM_NORMAL) +id_text_search= _id_text("Replace\tCtrl+H", "Replace", + "Find and replace in the active file", wx.ITEM_NORMAL) #=============================================================================== # Run ID #=============================================================================== From ed708686179341440f87747cdd3f830056cef819 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Wed, 3 Aug 2011 11:48:06 +0200 Subject: [PATCH 065/141] Config Files added to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c11bd69..70f31cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc +*.cfg .*.swp *~ From 450b0b2ca304acad58b6a18d0dad6353535372a8 Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 12:03:37 +0100 Subject: [PATCH 066/141] added spacing after keywords where appropriate --- pythonforumide/gui_lib/ide_editor.py | 3 ++- pythonforumide/utils/autocomplete.py | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index f71f896..8960aa9 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -51,6 +51,7 @@ def __init__(self, parent): self.SetAutoComplete() def SetAutoComplete(self): + self.AutoCompSetSeparator(124) self.autocomp = CodeCompletion() self.autocomp.add_builtins() self.autocomp.add_keywords() @@ -183,7 +184,7 @@ def AutoComp(self, event, keycode): choices = list(self.autocomp.suggest()) if choices: choices.sort() - self.AutoCompShow(self.autocomp.len_entered-1, ' '.join(choices)) + self.AutoCompShow(self.autocomp.len_entered-1, '|'.join(choices)) def OnKeyDown(self, event): """Defines events for when the user presses a key""" diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 2261bd7..6289e82 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -3,10 +3,11 @@ """ # maybe move these to a separate file eventually -keywords = set(['and', 'elif', 'is', 'global', 'pass', 'if', 'from', - 'raise', 'for', 'except', 'finally', 'print', 'import', 'return', - 'exec', 'else', 'break', 'not', 'class', 'assert', 'in', 'yield', - 'try', 'while', 'continue', 'del', 'or', 'def', 'lambda']) +keywords = set(['and ', 'elif ', 'is ', 'global ', 'pass', 'if ', + 'from ', 'raise ', 'for ', 'except ', 'finally', 'print ', + 'import ', 'return ', 'exec ', 'else', 'break', 'not ', 'class ', + 'assert ', 'in ', 'yield ', 'try', 'while ', 'continue', 'del ', + 'or ', 'def ', 'lambda ']) builtins = set(__builtins__.keys()) class CodeCompletion(object): From bbf90ee3de84a126953ab3a1dce116831a0ec9ba Mon Sep 17 00:00:00 2001 From: bunburya Date: Wed, 3 Aug 2011 12:57:09 +0100 Subject: [PATCH 067/141] just added a coment about reading indentation data from config --- pythonforumide/gui_lib/ide_editor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index f71f896..d0a428a 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -65,6 +65,7 @@ def SetGenerics(self): options like Tabwidth, expandtab and indentation guides + others.""" self.SetLexer(stc.STC_LEX_PYTHON) #is this giving us trouble? self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) #set mono spacing here! + # Presumably we should be reading this stuff from config? self.SetTabWidth(4) self.SetIndentationGuides(1) #Indentation will only use space characters if useTabs is false From 83f32f0f42b0c03f0134aa7e6cda05631bb98e00 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Wed, 3 Aug 2011 13:41:36 +0100 Subject: [PATCH 068/141] Changed quite a bit around. --- HACKING | 11 ++++++++--- README | 4 ++++ pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/gui_lib/ide_editor.py | 21 ++++++++++++--------- pythonforumide/utils/interpreter.py | 9 +++------ pythonforumide/utils/version.py | 9 +++++++-- 6 files changed, 36 insertions(+), 22 deletions(-) diff --git a/HACKING b/HACKING index c3de5a9..5e064b3 100644 --- a/HACKING +++ b/HACKING @@ -1,7 +1,8 @@ If you want to hack on PythonForumIDE you need the following software: - - Python 2.6 or 2.7 - - wxPython 2.8 - - Twisted 10.2.0-1 + - Python 2.6 or 2.7 + - wxPython 2.8 + - Twisted 10.2.0-1 (Although the needed files do come bundled in the repo) + PythonForumIDE uses the PEP-8 Coding Style, which can be found here: http://www.python.org/dev/peps/pep-0008/ @@ -9,14 +10,18 @@ http://www.python.org/dev/peps/pep-0008/ When in doubt copy the coding style of the file you are editing. All id's must come from gui_lib/ide_constants.py. + Do not push to master unless explicitly requested. + If you want to implement a new feature, create an issue ticket and a new branch, preferably with the issue number e.g. issue_42 + Create a separate branch for any issues or new features. See this guide for help: http://progit.org/book/ch3-0.html The IDE must be able to run on Python 2.6 and 2.7, so please note any compatibility issues between the two and avoid them. + Code should be tested on both versions before being committed to master. To clone the latest PythonForumIDE version simpy use git to clone it: diff --git a/README b/README index e69de29..001ca4d 100644 --- a/README +++ b/README @@ -0,0 +1,4 @@ +Still in development but now can be considered at Alpha stage. + +Report all bugs to +https://github.com/Python-Forum/PythonForumIDE/issues diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 5905929..2697499 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,6 @@ [ide] usetab = False -mainframe.height = 744 -mainframe.width = 1366 +mainframe.height = 600 +mainframe.width = 600 indent = 4 diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 8960aa9..8164b76 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -3,16 +3,18 @@ @reviewer: Somelauw, ghoulmaster, David """ -#YES DIRT HACK GET OVER IT. Dont remove it might go before it goes into master +#Do not remove import sys sys.path.append('..') +# from utils.textutils import split_comments from utils.interpreter import PythonProcessProtocol -from utils.version import get_python_exe +from utils.version import get_python_exe, introduction from utils.autocomplete import CodeCompletion import wx.richtext import wx.stc as stc +from wx import _stc import os.path import wx @@ -51,7 +53,6 @@ def __init__(self, parent): self.SetAutoComplete() def SetAutoComplete(self): - self.AutoCompSetSeparator(124) self.autocomp = CodeCompletion() self.autocomp.add_builtins() self.autocomp.add_keywords() @@ -64,7 +65,7 @@ def SetGenerics(self): """Rather than do it in the __init__ and to help debugging the styles and settings are split into seperate SetOptions, this sets the generic options like Tabwidth, expandtab and indentation guides + others.""" - self.SetLexer(stc.STC_LEX_PYTHON) #is this giving us trouble? + self.SetLexer(stc.STC_LEX_PYTHON) self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) #set mono spacing here! self.SetTabWidth(4) self.SetIndentationGuides(1) @@ -89,8 +90,7 @@ def SetMargins(self): def SetStyles(self, lang='python'): """This is different from the other Set methods that - http://paste.pocoo.org/show/446107/ are called in the - __init__ this one is for the highlighting and syntax of the langauge, + are called in the __init__ this one is for the highlighting and syntax of the langauge, this will eventually be callable with different langauge styles. For the moment, leave the lang kwarg in. """ @@ -184,7 +184,8 @@ def AutoComp(self, event, keycode): choices = list(self.autocomp.suggest()) if choices: choices.sort() - self.AutoCompShow(self.autocomp.len_entered-1, '|'.join(choices)) + self.AutoCompShow(self.autocomp.len_entered-1, ' '.join(choices)) + def OnKeyDown(self, event): """Defines events for when the user presses a key""" @@ -199,7 +200,7 @@ def OnKeyDown(self, event): def on_undo(self): """Checks if can Undo and if yes undoes""" - if self.CanUndo() == 1: + if self.CanUndo() == 1: #same as `if not self.CanUndo():` ?? self.Undo() def on_redo(self): @@ -254,7 +255,7 @@ def on_replace(self): def on_run(self): """Runs selected code in a new window.""" - # Create a test frame and hook into the caller. + # Create a frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. reactor = wx.GetApp().this_reactor @@ -262,6 +263,8 @@ def on_run(self): run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) run_editor.sizer.Add(run_panel, 1, wx.EXPAND) run_editor.Layout() + run_panel.WriteText(introduction()) + run_panel.Newline() if self.filepath: if self.GetModify(): diff --git a/pythonforumide/utils/interpreter.py b/pythonforumide/utils/interpreter.py index 1016d0d..672abf8 100644 --- a/pythonforumide/utils/interpreter.py +++ b/pythonforumide/utils/interpreter.py @@ -4,9 +4,6 @@ @author: jakob """ -import sys -sys.path.append('..') - from twisted.internet.protocol import ProcessProtocol class PythonProcessProtocol(ProcessProtocol): @@ -14,7 +11,7 @@ def __init__(self, frame): self.frame = frame def connectionMade(self): - print "subprocess open.!" + print "subprocess open." def connectionLost(self, reason): self.frame.Newline() @@ -25,13 +22,13 @@ def outReceived(self, data): print "Got stdout." def errReceived(self, data): - print "Got stderr!" + print "Got stderr." self.frame.Newline() self.frame.BeginTextColour("Red") self.frame.WriteText(data) def errConnectionLost(self): - print "errConnectionLost! The child closed their stderr." + print "errConnectionLost, The child closed their stderr." def processEnded(self, reason): print "processEnded, status %d" % (reason.value.exitCode,) diff --git a/pythonforumide/utils/version.py b/pythonforumide/utils/version.py index da6418f..0165c04 100644 --- a/pythonforumide/utils/version.py +++ b/pythonforumide/utils/version.py @@ -14,7 +14,12 @@ def is_windows(): return True else: return False - + +def introduction(): + """Returns interpreter infomation.""" + #yes this is kind of cheeky. + return "Python %s" % sys.version + def get_ip(): """Try and return the local private IP that this computer is using.""" return socket.gethostbyname(socket.getfqdn()) @@ -30,4 +35,4 @@ def get_free_port(): sock.bind(('', 0)) port = sock.getsockname()[1] sock.close() - return port \ No newline at end of file + return port From a5f46a539a4d04d3544a6569309bebcc2d4bdfc7 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Wed, 3 Aug 2011 15:13:52 +0100 Subject: [PATCH 069/141] Fix for issue 28, line endings were \r\n now \n --- pythonforumide/gui_lib/ide_editor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 51657db..dfed786 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -283,9 +283,14 @@ def on_run(self): script = StringIO() script.write(self.GetText()) script.seek(0) + + #For some reason we end up with \r\n for line endings. + #This sorts that issue out + scr = script.read().replace("\r",'') + reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), - ["python", "-c", script.read()]) + ["python", "-c", scr]) return run_panel From 23fa0c60c261a65e0957cfd81101ecc5869b665c Mon Sep 17 00:00:00 2001 From: confab Date: Wed, 3 Aug 2011 08:47:11 -0700 Subject: [PATCH 070/141] removed erroneous files, updated .gitignore to ignore *.yaml --- .gitignore | 1 + TOREVIEW | 5 - latest_diff | 347 ------------------ pythonforumide/Ide_Config.cfg | 6 - .../config/config- TestVersion Yoriz.py | 219 ----------- 5 files changed, 1 insertion(+), 577 deletions(-) delete mode 100644 latest_diff delete mode 100644 pythonforumide/Ide_Config.cfg delete mode 100644 pythonforumide/config/config- TestVersion Yoriz.py diff --git a/.gitignore b/.gitignore index 70f31cb..4738d04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc *.cfg +*.yaml .*.swp *~ diff --git a/TOREVIEW b/TOREVIEW index f9e1382..769bbcf 100644 --- a/TOREVIEW +++ b/TOREVIEW @@ -18,8 +18,3 @@ To get your file reviewed; Files to be reviewed; - -Ide_Config.cfg -config/Ide_Config.cfg -config/config.py -gui_lib/ide_test_app.py -wx_app.py diff --git a/latest_diff b/latest_diff deleted file mode 100644 index 3070603..0000000 --- a/latest_diff +++ /dev/null @@ -1,347 +0,0 @@ -t eaf1ce1fe147c53035368f7bafcb759a9ba3c17e -Author: confab -Date: Mon Aug 1 10:33:10 2011 -0700 - - revamped config/config.py - -diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg -index ffc8eaa..2697499 100644 ---- a/pythonforumide/Ide_Config.cfg -+++ b/pythonforumide/Ide_Config.cfg -@@ -1,5 +1,5 @@ - [ide] --usetab = 0 -+usetab = False - mainframe.height = 600 - mainframe.width = 600 - indent = 4 -diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg -index ffc8eaa..2697499 100644 ---- a/pythonforumide/config/Ide_Config.cfg -+++ b/pythonforumide/config/Ide_Config.cfg -@@ -1,5 +1,5 @@ - [ide] --usetab = 0 -+usetab = False - mainframe.height = 600 - mainframe.width = 600 - indent = 4 -diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py -index 8c76ea1..e5c508f 100644 ---- a/pythonforumide/config/config.py -+++ b/pythonforumide/config/config.py -@@ -15,17 +15,21 @@ except ImportError: - import ConfigParser #Ugly config file :( - has_yaml = False - --def config_file(profile): -- """Returns the name of the configuration file. It does not guarantee existence. -- Side effect: Tells the user if can't load the configuration file if it is not supported. -+def file_config(profile): -+ """ -+ Returns the name of the configuration file. -+ It does not guarantee existence. -+ Side effect: -+ Tells the user if can't load the configuration file if it is not supported. - """ - - # We will need some fuzzy logic to accomplish this -- (profile, ext) = os.path.splitext(profile) -- yaml_exists = os.path.exists(profile + ".yaml") -- cfg_exists = os.path.exists(profile + ".cfg") -+ profile, ext = os.path.splitext(profile) -+ yaml_exists = os.path.exists(''.join((profile, ".yaml"))) -+ cfg_exists = os.path.exists(''.join((profile, ".cfg"))) - -- # If no extension could be found, guess what configuration type is likely to be preferred -+ # If no extension could be found, -+ # guess what configuration type is likely to be preferred. - if not ext: - if yaml_exists or (has_yaml and not cfg_exists): - ext = ".yaml" -@@ -34,14 +38,16 @@ def config_file(profile): - - # We can't load yaml if it doesn't exist - if ext == ".yaml" and not has_yaml: -- print("yaml configuration could not be loaded because python-yaml is not installed.") -+ print("yaml configuration could not be loaded", -+ "because python-yaml is not installed.") - ext = ".cfg" - -- return profile + ext -+ return ''.join((profile, ext)) - - def load_config(configfile): - """Loads a config file""" -- (profile, ext) = os.path.splitext(configfile) -+ -+ profile, ext = os.path.splitext(configfile) - if ext == ".yaml": - return _BeautifulConfig(profile) - else: -@@ -49,10 +55,14 @@ def load_config(configfile): - - class _BeautifulConfig(object): - """This is the config object if we can import yaml.""" -+ - def __init__(self, profile): -- """Load the yaml config from file, -- if this fails write an empty new one.""" -- filename = profile + ".yaml" -+ """ -+ Load the yaml config from file, -+ if this fails write an empty new one. -+ """ -+ -+ filename = ''.join((profile, ".yaml")) - - try: - self.config = yaml.load(open(filename)) -@@ -60,116 +70,139 @@ class _BeautifulConfig(object): - #Raise a config warning error here? - self.config = {} - -- self.file = open(filename,'w') -- -+ self.file_config = open(filename, 'w') -+ - def __setitem__(self, option, value): - """Set an option to a value inside the config, does not write.""" -+ - self.config[option] = value -- -+ - def __getitem__(self, option): -- """Gets the option from the config, if the option is not there -- returns None""" -+ """ -+ Gets the option from the config. -+ Return None, if the option is not there -+ """ -+ - return self.config.get(option, None) - - def set_default(self, option, value): -+ - self.config.set_default(option, value) - - def save(self): - """Writes the config as yaml out to the config file.""" -- yaml.dump(self.config, self.file) -+ -+ yaml.dump(self.config, self.file_config) - - class _UglyConfig(object): -- """This is the config object created if we can't use YAML and have to -- rely on ConfigParser.""" -+ """ -+ This is the config object created if we can't use YAML and have to -+ rely on ConfigParser. -+ """ -+ - def __init__(self, profile): -- filename = profile + ".cfg" -- filename= profile - -+ filename = ''.join((profile, ".cfg")) -+ filename = profile - self.config = ConfigParser.ConfigParser() -- try: -- self.config.read(filename) -- except IOError: # <<< Exception will never be trown (RTFM, noob!) -- pass #Raise a config warning error here? -- self.config.add_section('ide') -+ self.config.read(filename) - - if not self.config.has_section('ide'): - self.config.add_section('ide') - -- self.file = open(filename,'w') -+ self.file_config = open(filename,'w') - - def __setitem__(self, option, value): - """Set the value to the option inside the default section.""" -- self.config.set('ide',option, value) -- -+ -+ self.config.set('ide', option, value) -+ - def __getitem__(self, option): - """Return the values to the option given, or return None""" -+ - try: - return self.config.get('ide', option) - except ConfigParser.NoOptionError: - return None - - def set_default(self, option, value): -+ - if not self.config.has_option('ide', option): - self[option] = value -- -+ - def save(self): - """Write config to file.""" -- self.config.write(self.file) -+ -+ self.config.write(self.file_config) - - - #If we have yaml then the ConfigObject will point to the cooler config object - if has_yaml: - Config = _BeautifulConfig --else: # Otherwise we point to the ugly one -+# Otherwise we point to the ugly one -+else: - Config = _UglyConfig - #This way the importers use the config the same way, same api but under the - #hood they are different. - --class Ide_config(object): -- def __init__(self, filepath= "", filename= "Ide_Config"): -+ -+class IdeConfig(object): -+ -+ def __init__(self, filepath="", filename="Ide_Config"): -+ - if not filepath: -- filepath= os.path.dirname(__file__) -+ filepath = os.path.dirname(__file__) - self._filepath = filepath - self._filename = filename -- self._fullpath= None -- self._data= {} -+ self._fullpath = None -+ self._data = {} - self._get_file_fullpath() - self._get_defaults() -- -+ - def _get_file_fullpath(self): -+ - if has_yaml: -- ext= ".yaml" -+ ext = ".yaml" - else: -- ext= ".cfg" -- self._fullpath= os.path.join(self._filepath, self._filename)+ext -- if not os.path.exists(self._fullpath): -- a= file(self._fullpath, "w") -- a.close() -- print ("Config useing filepath: %s" % (self._fullpath)) -- -+ ext = ".cfg" -+ -+ _temp_path = os.path.join(self._filepath, self._filename) -+ self._fullpath = ''.join((_temp_path, ext)) -+ -+ with open(self._fullpath, "a") as _: -+ # Opens file as append to ensure safe creation of the file. -+ pass -+ -+ print ("Config using filepath: %s" % (self._fullpath)) -+ - def _get_defaults(self): -- confile= Config(self._fullpath) -- self._data["indent"]= confile["indent"] or 4 -- self._data["usetab"]= confile["usetab"] or 0 -+ -+ confile = Config(self._fullpath) -+ self._data["indent"] = confile["indent"] or 4 -+ self._data["usetab"] = confile["usetab"] or False - self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 - self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 -- confile.file.close() -- -+ confile.file_config.close() -+ - def __setitem__(self, key, value): -- self._data[key]= value -- -+ -+ self._data[key] = value -+ - def __getitem__(self, key): -+ - return self._data[key] -- -+ - def update_configfile(self): -- confile= Config(self._fullpath) -+ -+ confile = Config(self._fullpath) - for key, value in self._data.iteritems(): -- confile[key]= value -+ confile[key] = value -+ - confile.save() -- confile.file.close() -+ confile.file_config.close() - - if __name__ == '__main__': -- ide_config= Ide_config() -- print (ide_config["indent"]) -+ ide_config = IdeConfig(config_style=Config) -+ print(ide_config["indent"]) - ide_config.update_configfile() -- -+ -diff --git a/pythonforumide/gui_lib/ide_test_app.py b/pythonforumide/gui_lib/ide_test_app.py -index e30f2a7..405b1ff 100644 ---- a/pythonforumide/gui_lib/ide_test_app.py -+++ b/pythonforumide/gui_lib/ide_test_app.py -@@ -1,11 +1,11 @@ - """ - Created on 31 Jul 2011 - --@author: Main -+@author: Main, confab - """ - - import wx --from pythonforumide.config.config import Ide_config -+from pythonforumide.config.config import IdeConfig - - class Wx_App(wx.App): - def __init__(self, *args, **kwargs): -@@ -15,7 +15,7 @@ class Wx_App(wx.App): - - def _create_config(self): - """Set up config""" -- self.config= Ide_config() -+ self.config= IdeConfig() - - def OnExit(self): - """Handles the frame closing function""" -diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py -index 2b6021f..a87361a 100644 ---- a/pythonforumide/wx_app.py -+++ b/pythonforumide/wx_app.py -@@ -1,16 +1,15 @@ - """ - Created on 31 Jul 2011 - --@author: D.W., david -+@author: D.W., david, confab - @reviewer: david - """ - - import wx - import gui_lib.ide_mainframe as ide_mainframe - import gui_lib.ide_mainframe_events as ide_mainframe_events --from config.config import Ide_config -+from config.config import IdeConfig - --#import config.config.Ide_config as Ide_config - from twisted.internet import wxreactor - wxreactor.install() - -@@ -31,7 +30,7 @@ class Wx_App(wx.App): - - def _create_config(self): - """Set up config""" -- self.config = Ide_config() -+ self.config = IdeConfig() - - def _create_port(self): - """Creates a free port""" diff --git a/pythonforumide/Ide_Config.cfg b/pythonforumide/Ide_Config.cfg deleted file mode 100644 index 2697499..0000000 --- a/pythonforumide/Ide_Config.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[ide] -usetab = False -mainframe.height = 600 -mainframe.width = 600 -indent = 4 - diff --git a/pythonforumide/config/config- TestVersion Yoriz.py b/pythonforumide/config/config- TestVersion Yoriz.py deleted file mode 100644 index 73e532e..0000000 --- a/pythonforumide/config/config- TestVersion Yoriz.py +++ /dev/null @@ -1,219 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jul 16 21:01:15 2011 -@author: Jakob, Somelauw -@reviewer: David, SomeLauw -""" - -from __future__ import print_function -import os.path - -try: - import yaml #Pretty config file :D - has_yaml = True -except ImportError: - import ConfigParser #Ugly config file :( - has_yaml = False - -def config_file(profile): - """Returns the name of the configuration file. It does not guarantee existence. - Side effect: Tells the user if can't load the configuration file if it is not supported. - """ - - # We will need some fuzzy logic to accomplish this - (profile, ext) = os.path.splitext(profile) - yaml_exists = os.path.exists(profile + ".yaml") - cfg_exists = os.path.exists(profile + ".cfg") - - # If no extension could be found, guess what configuration type is likely to be preferred - if not ext: - if yaml_exists or (has_yaml and not cfg_exists): - ext = ".yaml" - else: - ext = ".cfg" - - # We can't load yaml if it doesn't exist - if ext == ".yaml" and not has_yaml: - print("yaml configuration could not be loaded because python-yaml is not installed.") - ext = ".cfg" - - return profile + ext - -def load_config(configfile): - """Loads a config file""" - (profile, ext) = os.path.splitext(configfile) - if ext == ".yaml": - return _BeautifulConfig(profile) - else: - return _UglyConfig(profile) - -class _BeautifulConfig(object): - """This is the config object if we can import yaml.""" - def __init__(self, profile): - """Load the yaml config from file, - if this fails write an empty new one.""" - filename = profile + ".yaml" - - try: - self.config = yaml.load(open(filename)) - except IOError: - #Raise a config warning error here? - self.config = {} - - self.file = open(filename,'w') - - def __setitem__(self, option, value): - """Set an option to a value inside the config, does not write.""" - self.config[option] = value - - def __getitem__(self, option): - """Gets the option from the config, if the option is not there - returns None""" - return self.config.get(option, None) - - def set_default(self, option, value): - self.config.set_default(option, value) - - def save(self): - """Writes the config as yaml out to the config file.""" - yaml.dump(self.config, self.file) - -class _UglyConfig(object): - """This is the config object created if we can't use YAML and have to - rely on ConfigParser.""" - def __init__(self, profile): - filename = profile + ".cfg" - filename= profile - - self.config = ConfigParser.ConfigParser() - try: - self.config.read(filename) - except IOError: # <<< Exception will never be trown (RTFM, noob!) - pass #Raise a config warning error here? - self.config.add_section('ide') - - if not self.config.has_section('ide'): - self.config.add_section('ide') - - self.file = open(filename,'w') - - def __setitem__(self, option, value): - """Set the value to the option inside the default section.""" - self.config.set('ide',option, value) - - def __getitem__(self, option): - """Return the values to the option given, or return None""" - try: - return self.config.get('ide', option) - except ConfigParser.NoOptionError: - return None - - def set_default(self, option, value): - if not self.config.has_option('ide', option): - self[option] = value - - def save(self): - """Write config to file.""" - self.config.write(self.file) - - -#If we have yaml then the ConfigObject will point to the cooler config object -if has_yaml: - Config = _BeautifulConfig -else: # Otherwise we point to the ugly one - Config = _UglyConfig -#This way the importers use the config the same way, same api but under the -#hood they are different. - -class Ide_config(object): - def __init__(self, filepath= "", filename= "Ide_Config"): - if not filepath: - filepath= os.path.dirname(__file__) - self._filepath = filepath - self._filename = filename - self._fullpath= None - self._data= {} - self._get_file_fullpath() - self.set_defaults() - self.read_from_config_file() - - def _get_file_fullpath(self): - """ Works out which config file type""" - if has_yaml: - ext= ".yaml" - else: - ext= ".cfg" - self._fullpath= os.path.join(self._filepath, self._filename)+ext - if not os.path.exists(self._fullpath): - a= file(self._fullpath, "w") - a.close() - print ("Config useing filepath: %s" % (self._fullpath)) - - def read_from_config_file(self): - """ reads the config file and over writes defaults if theres as entry""" - """ if theres a type flag it converts to the python type""" - confile= Config(self._fullpath) - for key, value in self._data.iteritems(): - config_value= confile[key] - if config_value: - try: - this_type, this_value= config_value.split("<") - print (this_type, this_value[:-1]) - print (__builtins__[this_type](this_value[:-1])) - self._data[key]= __builtins__[this_type](this_value[:-1]) - except Exception, exception: - self._data[key]= config_value - confile.file.close() - - def __setitem__(self, key, value): - """ Sets the in memory value""" - self._data[key]= value - - def __getitem__(self, key): - """ Gets the in memory value""" - return self._data[key] - - def update_configfile(self): - """ Writes back the current values to the config file""" - """ Adds a type flag to the entry""" - confile= Config(self._fullpath) - for key, value in self._data.iteritems(): - this_type= str(type(value)) - this_type= this_type[:-2].split("'")[1] - if this_type not in ("int", "float", "bool"): - this_type= "str" - value= "%s<%s>" % (this_type, str(value)) - confile[key]= value - confile.save() - confile.file.close() - - def set_defaults(self): - """ Sets default values""" - self._data["indent"]= 4 - self._data["usetab"]= 0 - self._data["MainFrame.Height"] = 600 - self._data["MainFrame.Width"] = 600 - self._data["Test.bool"]= True - self._data["Test.int"]= 25 - self._data["Test.float"]= 0.75 - self._data["Test.list"]= ["1", "2", "3"] - self._data["Test.tuple"]= ("1", "2", "3") - -""" This could be usefull not to have to worry about converting value to string - to save them and then changing back toint, bool ect to use them""" - -if __name__ == '__main__': - ide_config= Ide_config() - print (ide_config["indent"]) - ide_config["MainFrame.Height"]= 600 - print (type(ide_config["Test.bool"])) - ide_config["indent"]= 4 - ide_config.update_configfile() - ide_config= Ide_config() - print (ide_config["Test.int"]*ide_config["Test.float"]) - print (type(ide_config["Test.list"])) - print (type(ide_config["Test.tuple"])) - print (ide_config["Test.list"]) - ide_config.update_configfile() - - From 423e94647d2d10eace3e4e2c7935efdf84b3d29a Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Wed, 3 Aug 2011 21:27:13 +0100 Subject: [PATCH 071/141] i suck at git, removed the lot as it wouldnt rebase then pasted back in my changed config file --- pythonforumide/config/config.py | 123 +++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 41 deletions(-) diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index e5c508f..9a19601 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -67,7 +67,9 @@ def __init__(self, profile): try: self.config = yaml.load(open(filename)) except IOError: - #Raise a config warning error here? + self.config = None + + if self.config is None: self.config = {} self.file_config = open(filename, 'w') @@ -103,14 +105,13 @@ class _UglyConfig(object): def __init__(self, profile): filename = ''.join((profile, ".cfg")) - filename = profile self.config = ConfigParser.ConfigParser() self.config.read(filename) if not self.config.has_section('ide'): self.config.add_section('ide') - self.file_config = open(filename,'w') + self.file_config = open(filename, 'w') def __setitem__(self, option, value): """Set the value to the option inside the default section.""" @@ -147,62 +148,102 @@ def save(self): class IdeConfig(object): - + """ Store's config values in a dict, and to file + converts to and from python type""" def __init__(self, filepath="", filename="Ide_Config"): - + """ Initialize""" if not filepath: filepath = os.path.dirname(__file__) - self._filepath = filepath - self._filename = filename self._fullpath = None self._data = {} - self._get_file_fullpath() - self._get_defaults() - - def _get_file_fullpath(self): - - if has_yaml: - ext = ".yaml" - else: - ext = ".cfg" - - _temp_path = os.path.join(self._filepath, self._filename) - self._fullpath = ''.join((_temp_path, ext)) - + self._get_file_fullpath(filepath, filename) + self.set_defaults() + self.read_from_config_file() + + def _get_file_fullpath(self, filepath, filename): + """ Works out which config file type and creates if not exists""" + _temp_path = os.path.join(filepath, filename) + self._fullpath= file_config(_temp_path) with open(self._fullpath, "a") as _: # Opens file as append to ensure safe creation of the file. pass - print ("Config using filepath: %s" % (self._fullpath)) - def _get_defaults(self): - - confile = Config(self._fullpath) - self._data["indent"] = confile["indent"] or 4 - self._data["usetab"] = confile["usetab"] or False - self._data["MainFrame.Height"] = confile["MainFrame.Height"] or 600 - self._data["MainFrame.Width"] = confile["MainFrame.Width"] or 600 + def read_from_config_file(self): + """ reads the config file and over writes defaults if theres as entry""" + """ if theres a type flag it converts to the python type""" + confile = load_config(self._fullpath) + for key, value in self._data.iteritems(): + config_value = confile[key] + if config_value: + self._data[key] = self.convert_to_pytype(config_value) confile.file_config.close() - + + def convert_to_pytype(self, config_value): + """ Converts the file stored string to a few python types""" + try: + get_type, get_value = config_value.split(">") + if get_type == "int": + return int(get_value) + elif get_type == "float": + return float(get_value) + elif get_type == "bool" and get_value == "True": + return True + elif get_type == "bool" and get_value == "False": + return False + else: + return str(get_value) + except Exception, exception: + print ("Exception when convert_to_pytype", exception.message) + return str(config_value) + + def update_configfile(self): + """ Writes back the current values to the config file""" + confile = load_config(self._fullpath) + for key, value in self._data.iteritems(): + confile[key] = self.convert_pytype_to_str(value) + confile.save() + confile.file_config.close() + + def convert_pytype_to_str(self, value): + """ Adds a pytype flag to the entry""" + get_type = str(type(value)) + get_type = get_type[:-2].split("'")[1] + if get_type == "int": + return "int>%s" % (value) + elif get_type == "float": + return "float>%s" % (value) + elif get_type == "bool" and value == True: + return "bool>%s" % (value) + elif get_type == "bool" and value == False: + return "bool>%s" % (value) + else: + return "str>%s" % (value) + def __setitem__(self, key, value): - + """ Set the current config value""" self._data[key] = value def __getitem__(self, key): - + """ Get the current config value""" return self._data[key] - def update_configfile(self): - - confile = Config(self._fullpath) - for key, value in self._data.iteritems(): - confile[key] = value - - confile.save() - confile.file_config.close() + + def set_defaults(self): + """ Sets default values""" + self._data["indent"] = 4 + self._data["usetab"] = 0 + self._data["MainFrame.Height"] = 600 + self._data["MainFrame.Width"] = 600 + self._data["Test.bool"] = True + self._data["Test.int"] = 25 + self._data["Test.float"] = 0.75 if __name__ == '__main__': - ide_config = IdeConfig(config_style=Config) - print(ide_config["indent"]) + ide_config = IdeConfig() + + value = (ide_config["MainFrame.Width"]) + print (value) ide_config.update_configfile() + From d215cecdf8c2d03a561104c9000d49213fc6fd30 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Wed, 3 Aug 2011 22:14:33 +0100 Subject: [PATCH 072/141] runable prototype of headered panel, able to pop panel off the main frame into its own frame and back --- pythonforumide/config/Ide_Config.cfg | 11 +- pythonforumide/gui_lib/ide_headered_panel.py | 286 +++++++++++++++++++ 2 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 pythonforumide/gui_lib/ide_headered_panel.py diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 2697499..27c3de6 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,6 +1,9 @@ [ide] -usetab = False -mainframe.height = 600 -mainframe.width = 600 -indent = 4 +indent = str>4 +usetab = str>False +test.int = int>25 +test.bool = bool>True +test.float = float>0.75 +mainframe.height = int>743 +mainframe.width = int>1134 diff --git a/pythonforumide/gui_lib/ide_headered_panel.py b/pythonforumide/gui_lib/ide_headered_panel.py new file mode 100644 index 0000000..6194654 --- /dev/null +++ b/pythonforumide/gui_lib/ide_headered_panel.py @@ -0,0 +1,286 @@ +''' +Created on 3 Aug 2011 + +@author: Dave Wilson +''' + +import wx + +class HeaderPanel(wx.Panel): + def __init__(self, *args, **kwargs): + """ Creates headered panel with Minimize/Maximize & Close buttons """ + super(HeaderPanel, self).__init__(*args, **kwargs) + self._create_variables() + sizer= wx.BoxSizer(wx.HORIZONTAL) + self._create_statictxt(sizer) + self._create_minimize(sizer) + self._create_maximize(sizer) + self._create_close(sizer) + self.SetSizer(sizer) + self.Layout() + + + def _create_variables(self): + """ Creates variables for internal use only""" + self._button_font= wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, + u'Webdings') + + def _create_statictxt(self, sizer): + """ Create header label""" + ctrl= wx.StaticText(self, label= " Console") + font= ctrl.GetFont() + font.SetWeight(wx.FONTWEIGHT_BOLD) + ctrl.SetFont(font) + sizer.Add(ctrl, 0, flag= wx.ALIGN_CENTER_VERTICAL) + self.header_caption= ctrl + + def _create_minimize(self, sizer): + """ Creates the Minimize button""" + sizer.AddStretchSpacer(1) + ctrl= wx.Button(self, label= "0", size= (19, 19)) + ctrl.SetFont(self._button_font) + ctrl.SetToolTipString("Minimize") + sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) + self.btn_minimize= ctrl + + def _create_maximize(self, sizer): + """ Creates the Maximize button""" + ctrl= wx.Button(self, label= "1", size= (19, 19)) + ctrl.SetFont(self._button_font) + ctrl.SetToolTipString("Maximize") + sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) + self.btn_maximize= ctrl + # restore = 2 + + def _create_close(self, sizer): + """ Creates the Close button""" + ctrl= wx.Button(self, label= "r", size= (19, 19)) + ctrl.SetFont(self._button_font) + ctrl.SetToolTipString("Close") + sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) + self.btn_close= ctrl + + +class FramedPanel(wx.Panel): + def __init__(self, *args, **kwargs): + """ Creates a panel for displaying text """ + super(FramedPanel, self).__init__(*args, **kwargs) + self._create_variables() + self.sizer= wx.BoxSizer(wx.VERTICAL) + self._create_header_panel() + self.SetSizer(self.sizer) + self.Layout() + + def _create_variables(self): + self.child_panel= None + self._proportion= None + self._minimized= False + + def _create_header_panel(self): + """ Creates the header panel""" + ctrl= HeaderPanel(self, style= wx.BORDER_THEME) + self.sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL, 1) + self.header_panel= ctrl + + def add_panel_ctrl(self, panel_class, caption= "", proportion= 1): + self.Freeze() + ctrl= panel_class(self) + self.sizer.Add(ctrl, 1, wx.EXPAND|wx.ALL, 0) + self._update_layout() + self.child_panel= ctrl + self._proportion= proportion + self._set_own_proportion(proportion) + self.set_caption(caption) + self.Thaw() + return ctrl + + def _update_layout(self): + parent= self.GetParent() + parent.Layout() + parent.Update() + parent.Refresh() + self.Layout() + + def _set_restore_state(self): + self.header_panel.btn_maximize.SetLabel("2") + + def _set_after_restore(self): + self.header_panel.btn_maximize.SetLabel("1") + + def _get_items_sizer(self, item): + sizer= item.GetContainingSizer() + return sizer.GetItem(self) + + def _set_own_proportion(self, proportion): + self._get_items_sizer(self).SetProportion(proportion) + + + + def show(self, show= True): + self.Show(show) + self._update_layout() + + def show_child(self, show= True): + self.child_panel.Show(show) + + def _set_no_child_state(self): + self.header_panel.btn_maximize.SetLabel("2") + self._set_own_proportion(0) + self._update_layout() + self._minimized= True + + def minimized_state(self): + if not self.child_panel: + return + self.show_child(False) + self.header_panel.btn_maximize.SetLabel("2") + self.header_panel.btn_maximize.SetToolTipString("Restore") + + self._set_own_proportion(0) + self._update_layout() + self._minimized= True + + def restore_state(self): + if not self.child_panel: + return + if self._minimized: + self.show_child(True) + self.header_panel.btn_maximize.SetLabel("1") + self.header_panel.btn_maximize.SetToolTipString("Maximize") + self._set_own_proportion(self._proportion) + self._update_layout() + self._minimized= False + else: + self._move_child_to_a_frame() + + def close(self): + if not self.child_panel: + return + self.show(False) + + def _move_child_to_a_frame(self): + self.sizer.Detach(self.child_panel) + + self.show(False) + simple_frame= ParentFrame(None, title= self._get_caption()) + simple_frame.add_child(self.child_panel, self) + + + def set_caption(self, text): + self.header_panel.header_caption.SetLabel(" %s" % (text)) + + def _get_caption(self): + return self.header_panel.header_caption.GetLabel() + + + +class FramedPanelEvents(object): + def __init__(self, view, model= ""): + self.view = view + self.model = model + self._create_variables() + self._create_binds() + + def _create_variables(self): + header_panel= self.view.header_panel + self.btn_minimize= header_panel.btn_minimize + self.btn_maximize= header_panel.btn_maximize + self.btn_close= header_panel.btn_close + self.header_panel= self.view.header_panel + + def _create_binds(self): + self.btn_minimize.Bind(wx.EVT_BUTTON, self._on_btn_minimize) + self.btn_maximize.Bind(wx.EVT_BUTTON, self._on_btn_maximize) + self.btn_close.Bind(wx.EVT_BUTTON, self._on_btn_close) + + def _on_btn_minimize(self, event): + self.view.minimized_state() + + def _on_btn_maximize(self, event): + self.view.restore_state() + + def _on_btn_close(self, event): + self.view.close() + + +class ParentFrame(wx.Frame): + """Frame to use for testing""" + def __init__(self, *args, **kwargs): + """Initiates the frame and the GUI""" + super(ParentFrame, self).__init__(*args, **kwargs) + self.SetInitialSize(kwargs.get("size", (600,600))) + self.Center(wx.BOTH) + self.sizer= wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.sizer) + self.Bind(wx.EVT_CLOSE, self._on_close) + + def add_child(self, child_panel, parent_window): + self._child_panel = child_panel + self._parent_window = parent_window + child_panel.Reparent(self) + self.sizer.Add(child_panel, 1, wx.EXPAND) + self.Layout() + self.Show() +# self.Maximize() + + def _move_child_back(self): + self.sizer.Detach(self._child_panel) + self._child_panel.Reparent(self._parent_window) + self._parent_window.sizer.Add(self._child_panel,1, wx.EXPAND|wx.ALL, 0) + self._parent_window.show(True) + + def _on_close(self, event): + self._move_child_back() + self.Destroy() + + +class TestTextPanel(wx.Panel): + def __init__(self, *args, **kwargs): + """ Creates a panel for with a TextCtrl """ + super(TestTextPanel, self).__init__(*args, **kwargs) + self.sizer= wx.BoxSizer(wx.VERTICAL) + self._create_textctrl() + + + self.SetSizer(self.sizer) + self.Layout() + + def _create_textctrl(self): + ctrl= wx.TextCtrl(self, + size= (-1, 300) + ) + self.sizer.Add(ctrl, 1, wx.EXPAND|wx.ALL, 1) + + +if __name__ == '__main__': + from ide_simple_frame import SimpleFrame, SimplePanel + app= wx.App(None) + frame= SimpleFrame(None, title= "Test frame") + + s_panel= SimplePanel(frame) + frame.sizer.Add(s_panel, 1, wx.EXPAND) + + parent= s_panel + + panel= FramedPanel(parent, style= wx.BORDER_THEME) + FramedPanelEvents(panel) + parent.sizer.Add(panel, 0, wx.EXPAND) + panel.add_panel_ctrl(TestTextPanel, "Panel1", 1) + panel.minimized_state() + panel.set_caption("Panel1 has proportion 1 and set minimized") + + panel= FramedPanel(parent, style= wx.BORDER_THEME) + FramedPanelEvents(panel) + parent.sizer.Add(panel, 1, wx.EXPAND) + panel.add_panel_ctrl(TestTextPanel, "Panel2", 2) + panel.set_caption("Panel1 has proportion 2") + + panel= FramedPanel(parent, style= wx.BORDER_THEME) + FramedPanelEvents(panel) + parent.sizer.Add(panel, 1, wx.EXPAND) + panel.add_panel_ctrl(TestTextPanel, "Panel3", 3) + panel.set_caption("Panel1 has proportion 3") + + + frame.Layout() + app.MainLoop() From d424bcd8425d666731a68c80cad3372be94b476d Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Thu, 4 Aug 2011 14:20:10 +0100 Subject: [PATCH 073/141] Added the zope dependancy. Its only 256kb. --- pythonforumide/zope/__init__.py | 7 + pythonforumide/zope/interface/__init__.py | 79 + pythonforumide/zope/interface/_flatten.py | 35 + .../_zope_interface_coptimizations.c | 1682 +++++++++++++++++ pythonforumide/zope/interface/adapter.py | 692 +++++++ pythonforumide/zope/interface/advice.py | 201 ++ .../zope/interface/common/__init__.py | 2 + .../zope/interface/common/idatetime.py | 575 ++++++ .../zope/interface/common/interfaces.py | 102 + .../zope/interface/common/mapping.py | 125 ++ .../zope/interface/common/sequence.py | 160 ++ pythonforumide/zope/interface/declarations.py | 1395 ++++++++++++++ pythonforumide/zope/interface/document.py | 105 + pythonforumide/zope/interface/exceptions.py | 67 + pythonforumide/zope/interface/interface.py | 833 ++++++++ pythonforumide/zope/interface/interfaces.py | 746 ++++++++ pythonforumide/zope/interface/ro.py | 69 + pythonforumide/zope/interface/verify.py | 115 ++ 18 files changed, 6990 insertions(+) create mode 100644 pythonforumide/zope/__init__.py create mode 100644 pythonforumide/zope/interface/__init__.py create mode 100644 pythonforumide/zope/interface/_flatten.py create mode 100644 pythonforumide/zope/interface/_zope_interface_coptimizations.c create mode 100644 pythonforumide/zope/interface/adapter.py create mode 100644 pythonforumide/zope/interface/advice.py create mode 100644 pythonforumide/zope/interface/common/__init__.py create mode 100644 pythonforumide/zope/interface/common/idatetime.py create mode 100644 pythonforumide/zope/interface/common/interfaces.py create mode 100644 pythonforumide/zope/interface/common/mapping.py create mode 100644 pythonforumide/zope/interface/common/sequence.py create mode 100644 pythonforumide/zope/interface/declarations.py create mode 100644 pythonforumide/zope/interface/document.py create mode 100644 pythonforumide/zope/interface/exceptions.py create mode 100644 pythonforumide/zope/interface/interface.py create mode 100644 pythonforumide/zope/interface/interfaces.py create mode 100644 pythonforumide/zope/interface/ro.py create mode 100644 pythonforumide/zope/interface/verify.py diff --git a/pythonforumide/zope/__init__.py b/pythonforumide/zope/__init__.py new file mode 100644 index 0000000..2e2033b --- /dev/null +++ b/pythonforumide/zope/__init__.py @@ -0,0 +1,7 @@ +# this is a namespace package +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/pythonforumide/zope/interface/__init__.py b/pythonforumide/zope/interface/__init__.py new file mode 100644 index 0000000..8b05f6b --- /dev/null +++ b/pythonforumide/zope/interface/__init__.py @@ -0,0 +1,79 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Interfaces + +This package implements the Python "scarecrow" proposal. + +The package exports two objects, `Interface` and `Attribute` directly. It also +exports several helper methods. Interface is used to create an interface with +a class statement, as in: + + class IMyInterface(Interface): + '''Interface documentation + ''' + + def meth(arg1, arg2): + '''Documentation for meth + ''' + + # Note that there is no self argument + +To find out what you can do with interfaces, see the interface +interface, `IInterface` in the `interfaces` module. + +The package has several public modules: + + o `declarations` provides utilities to declare interfaces on objects. It + also provides a wide range of helpful utilities that aid in managing + declared interfaces. Most of its public names are however imported here. + + o `document` has a utility for documenting an interface as structured text. + + o `exceptions` has the interface-defined exceptions + + o `interfaces` contains a list of all public interfaces for this package. + + o `verify` has utilities for verifying implementations of interfaces. + +See the module doc strings for more information. +""" +__docformat__ = 'restructuredtext' + +from zope.interface.interface import Interface, _wire + +# Need to actually get the interface elements to implement the right interfaces +_wire() +del _wire + +from zope.interface.interface import Attribute, invariant, taggedValue + +from zope.interface.declarations import providedBy, implementedBy +from zope.interface.declarations import classImplements, classImplementsOnly +from zope.interface.declarations import directlyProvidedBy, directlyProvides +from zope.interface.declarations import alsoProvides, provider +from zope.interface.declarations import implementer, implementer_only +from zope.interface.declarations import implements, implementsOnly +from zope.interface.declarations import classProvides, moduleProvides +from zope.interface.declarations import noLongerProvides, Declaration +from zope.interface.exceptions import Invalid + +# The following are to make spec pickles cleaner +from zope.interface.declarations import Provides + + +from zope.interface.interfaces import IInterfaceDeclaration + +moduleProvides(IInterfaceDeclaration) + +__all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration) diff --git a/pythonforumide/zope/interface/_flatten.py b/pythonforumide/zope/interface/_flatten.py new file mode 100644 index 0000000..a80c2de --- /dev/null +++ b/pythonforumide/zope/interface/_flatten.py @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Adapter-style interface registry + +See Adapter class. +""" +from zope.interface import Declaration + +def _flatten(implements, include_None=0): + + try: + r = implements.flattened() + except AttributeError: + if implements is None: + r=() + else: + r = Declaration(implements).flattened() + + if not include_None: + return r + + r = list(r) + r.append(None) + return r diff --git a/pythonforumide/zope/interface/_zope_interface_coptimizations.c b/pythonforumide/zope/interface/_zope_interface_coptimizations.c new file mode 100644 index 0000000..7b0f816 --- /dev/null +++ b/pythonforumide/zope/interface/_zope_interface_coptimizations.c @@ -0,0 +1,1682 @@ +/*########################################################################### + # + # Copyright (c) 2003 Zope Foundation and Contributors. + # All Rights Reserved. + # + # This software is subject to the provisions of the Zope Public License, + # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. + # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED + # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS + # FOR A PARTICULAR PURPOSE. + # + ############################################################################*/ + +#include "Python.h" +#include "structmember.h" + +#define TYPE(O) ((PyTypeObject*)(O)) +#define OBJECT(O) ((PyObject*)(O)) +#define CLASSIC(O) ((PyClassObject*)(O)) +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(a, b) PyObject_HEAD_INIT(a) b, +#endif +#ifndef Py_TYPE +#define Py_TYPE(o) ((o)->ob_type) +#endif + +static PyObject *str__dict__, *str__implemented__, *strextends; +static PyObject *BuiltinImplementationSpecifications, *str__provides__; +static PyObject *str__class__, *str__providedBy__; +static PyObject *empty, *fallback, *str_implied, *str_cls, *str_implements; +static PyObject *str__conform__, *str_call_conform, *adapter_hooks; +static PyObject *str_uncached_lookup, *str_uncached_lookupAll; +static PyObject *str_uncached_subscriptions; +static PyObject *str_registry, *strro, *str_generation, *strchanged; + +static PyTypeObject *Implements; + +static int imported_declarations = 0; + +static int +import_declarations(void) +{ + PyObject *declarations, *i; + + declarations = PyImport_ImportModule("zope.interface.declarations"); + if (declarations == NULL) + return -1; + + BuiltinImplementationSpecifications = PyObject_GetAttrString( + declarations, "BuiltinImplementationSpecifications"); + if (BuiltinImplementationSpecifications == NULL) + return -1; + + empty = PyObject_GetAttrString(declarations, "_empty"); + if (empty == NULL) + return -1; + + fallback = PyObject_GetAttrString(declarations, "implementedByFallback"); + if (fallback == NULL) + return -1; + + + + i = PyObject_GetAttrString(declarations, "Implements"); + if (i == NULL) + return -1; + + if (! PyType_Check(i)) + { + PyErr_SetString(PyExc_TypeError, + "zope.interface.declarations.Implements is not a type"); + return -1; + } + + Implements = (PyTypeObject *)i; + + Py_DECREF(declarations); + + imported_declarations = 1; + return 0; +} + +static PyTypeObject SpecType; /* Forward */ + +static PyObject * +implementedByFallback(PyObject *cls) +{ + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + return PyObject_CallFunctionObjArgs(fallback, cls, NULL); +} + +static PyObject * +implementedBy(PyObject *ignored, PyObject *cls) +{ + /* Fast retrieval of implements spec, if possible, to optimize + common case. Use fallback code if we get stuck. + */ + + PyObject *dict = NULL, *spec; + + if (PyType_Check(cls)) + { + dict = TYPE(cls)->tp_dict; + Py_XINCREF(dict); + } + + if (dict == NULL) + dict = PyObject_GetAttr(cls, str__dict__); + + if (dict == NULL) + { + /* Probably a security proxied class, use more expensive fallback code */ + PyErr_Clear(); + return implementedByFallback(cls); + } + + spec = PyObject_GetItem(dict, str__implemented__); + Py_DECREF(dict); + if (spec) + { + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + if (PyObject_TypeCheck(spec, Implements)) + return spec; + + /* Old-style declaration, use more expensive fallback code */ + Py_DECREF(spec); + return implementedByFallback(cls); + } + + PyErr_Clear(); + + /* Maybe we have a builtin */ + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + spec = PyDict_GetItem(BuiltinImplementationSpecifications, cls); + if (spec != NULL) + { + Py_INCREF(spec); + return spec; + } + + /* We're stuck, use fallback */ + return implementedByFallback(cls); +} + +static PyObject * +getObjectSpecification(PyObject *ignored, PyObject *ob) +{ + PyObject *cls, *result; + + result = PyObject_GetAttr(ob, str__provides__); + if (result != NULL && PyObject_TypeCheck(result, &SpecType)) + return result; + + PyErr_Clear(); + + /* We do a getattr here so as not to be defeated by proxies */ + cls = PyObject_GetAttr(ob, str__class__); + if (cls == NULL) + { + PyErr_Clear(); + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + Py_INCREF(empty); + return empty; + } + + result = implementedBy(NULL, cls); + Py_DECREF(cls); + + return result; +} + +static PyObject * +providedBy(PyObject *ignored, PyObject *ob) +{ + PyObject *result, *cls, *cp; + + result = PyObject_GetAttr(ob, str__providedBy__); + if (result == NULL) + { + PyErr_Clear(); + return getObjectSpecification(NULL, ob); + } + + + /* We want to make sure we have a spec. We can't do a type check + because we may have a proxy, so we'll just try to get the + only attribute. + */ + if (PyObject_TypeCheck(result, &SpecType) + || + PyObject_HasAttr(result, strextends) + ) + return result; + + /* + The object's class doesn't understand descriptors. + Sigh. We need to get an object descriptor, but we have to be + careful. We want to use the instance's __provides__,l if + there is one, but only if it didn't come from the class. + */ + Py_DECREF(result); + + cls = PyObject_GetAttr(ob, str__class__); + if (cls == NULL) + return NULL; + + result = PyObject_GetAttr(ob, str__provides__); + if (result == NULL) + { + /* No __provides__, so just fall back to implementedBy */ + PyErr_Clear(); + result = implementedBy(NULL, cls); + Py_DECREF(cls); + return result; + } + + cp = PyObject_GetAttr(cls, str__provides__); + if (cp == NULL) + { + /* The the class has no provides, assume we're done: */ + PyErr_Clear(); + Py_DECREF(cls); + return result; + } + + if (cp == result) + { + /* + Oops, we got the provides from the class. This means + the object doesn't have it's own. We should use implementedBy + */ + Py_DECREF(result); + result = implementedBy(NULL, cls); + } + + Py_DECREF(cls); + Py_DECREF(cp); + + return result; +} + +/* + Get an attribute from an inst dict. Return a borrowed reference. + + This has a number of advantages: + + - It avoids layers of Python api + + - It doesn't waste time looking for descriptors + + - It fails wo raising an exception, although that shouldn't really + matter. + +*/ +static PyObject * +inst_attr(PyObject *self, PyObject *name) +{ + PyObject **dictp, *v; + + dictp = _PyObject_GetDictPtr(self); + if (dictp && *dictp && (v = PyDict_GetItem(*dictp, name))) + return v; + PyErr_SetObject(PyExc_AttributeError, name); + return NULL; +} + + +static PyObject * +Spec_extends(PyObject *self, PyObject *other) +{ + PyObject *implied; + + implied = inst_attr(self, str_implied); + if (implied == NULL) + return NULL; + +#ifdef Py_True + if (PyDict_GetItem(implied, other) != NULL) + { + Py_INCREF(Py_True); + return Py_True; + } + Py_INCREF(Py_False); + return Py_False; +#else + return PyInt_FromLong(PyDict_GetItem(implied, other) != NULL); +#endif +} + +static char Spec_extends__doc__[] = +"Test whether a specification is or extends another" +; + +static char Spec_providedBy__doc__[] = +"Test whether an interface is implemented by the specification" +; + +static PyObject * +Spec_call(PyObject *self, PyObject *args, PyObject *kw) +{ + PyObject *spec; + + if (! PyArg_ParseTuple(args, "O", &spec)) + return NULL; + return Spec_extends(self, spec); +} + +static PyObject * +Spec_providedBy(PyObject *self, PyObject *ob) +{ + PyObject *decl, *item; + + decl = providedBy(NULL, ob); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecType)) + item = Spec_extends(decl, self); + else + /* decl is probably a security proxy. We have to go the long way + around. + */ + item = PyObject_CallFunctionObjArgs(decl, self, NULL); + + Py_DECREF(decl); + return item; +} + + +static char Spec_implementedBy__doc__[] = +"Test whether the specification is implemented by a class or factory.\n" +"Raise TypeError if argument is neither a class nor a callable." +; + +static PyObject * +Spec_implementedBy(PyObject *self, PyObject *cls) +{ + PyObject *decl, *item; + + decl = implementedBy(NULL, cls); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecType)) + item = Spec_extends(decl, self); + else + item = PyObject_CallFunctionObjArgs(decl, self, NULL); + + Py_DECREF(decl); + return item; +} + +static struct PyMethodDef Spec_methods[] = { + {"providedBy", + (PyCFunction)Spec_providedBy, METH_O, + Spec_providedBy__doc__}, + {"implementedBy", + (PyCFunction)Spec_implementedBy, METH_O, + Spec_implementedBy__doc__}, + {"isOrExtends", (PyCFunction)Spec_extends, METH_O, + Spec_extends__doc__}, + + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject SpecType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "SpecificationBase", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)Spec_call, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + "Base type for Specification objects", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Spec_methods, +}; + +static PyObject * +OSD_descr_get(PyObject *self, PyObject *inst, PyObject *cls) +{ + PyObject *provides; + + if (inst == NULL) + return getObjectSpecification(NULL, cls); + + provides = PyObject_GetAttr(inst, str__provides__); + if (provides != NULL) + return provides; + PyErr_Clear(); + return implementedBy(NULL, cls); +} + +static PyTypeObject OSDType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ObjectSpecificationDescriptor", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE , + "Object Specification Descriptor", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)OSD_descr_get, +}; + +static PyObject * +CPB_descr_get(PyObject *self, PyObject *inst, PyObject *cls) +{ + PyObject *mycls, *implements; + + mycls = inst_attr(self, str_cls); + if (mycls == NULL) + return NULL; + + if (cls == mycls) + { + if (inst == NULL) + { + Py_INCREF(self); + return OBJECT(self); + } + + implements = inst_attr(self, str_implements); + Py_XINCREF(implements); + return implements; + } + + PyErr_SetObject(PyExc_AttributeError, str__provides__); + return NULL; +} + +static PyTypeObject CPBType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ClassProvidesBase", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + "C Base class for ClassProvides", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &SpecType, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)CPB_descr_get, +}; + +/* ==================================================================== */ +/* ========== Begin: __call__ and __adapt__ =========================== */ + +/* + def __adapt__(self, obj): + """Adapt an object to the reciever + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + +*/ +static PyObject * +__adapt__(PyObject *self, PyObject *obj) +{ + PyObject *decl, *args, *adapter; + int implements, i, l; + + decl = providedBy(NULL, obj); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecType)) + { + PyObject *implied; + + implied = inst_attr(decl, str_implied); + if (implied == NULL) + { + Py_DECREF(decl); + return NULL; + } + + implements = PyDict_GetItem(implied, self) != NULL; + Py_DECREF(decl); + } + else + { + /* decl is probably a security proxy. We have to go the long way + around. + */ + PyObject *r; + r = PyObject_CallFunctionObjArgs(decl, self, NULL); + Py_DECREF(decl); + if (r == NULL) + return NULL; + implements = PyObject_IsTrue(r); + Py_DECREF(r); + } + + if (implements) + { + Py_INCREF(obj); + return obj; + } + + l = PyList_GET_SIZE(adapter_hooks); + args = PyTuple_New(2); + if (args == NULL) + return NULL; + Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); + Py_INCREF(obj); + PyTuple_SET_ITEM(args, 1, obj); + for (i = 0; i < l; i++) + { + adapter = PyObject_CallObject(PyList_GET_ITEM(adapter_hooks, i), args); + if (adapter == NULL || adapter != Py_None) + { + Py_DECREF(args); + return adapter; + } + Py_DECREF(adapter); + } + + Py_DECREF(args); + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef ib_methods[] = { + {"__adapt__", (PyCFunction)__adapt__, METH_O, + "Adapt an object to the reciever"}, + {NULL, NULL} /* sentinel */ +}; + +/* + def __call__(self, obj, alternate=_marker): + conform = getattr(obj, '__conform__', None) + if conform is not None: + adapter = self._call_conform(conform) + if adapter is not None: + return adapter + + adapter = self.__adapt__(obj) + + if adapter is not None: + return adapter + elif alternate is not _marker: + return alternate + else: + raise TypeError("Could not adapt", obj, self) +*/ +static PyObject * +ib_call(PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *conform, *obj, *alternate=NULL, *adapter; + + static char *kwlist[] = {"obj", "alternate", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, + &obj, &alternate)) + return NULL; + + conform = PyObject_GetAttr(obj, str__conform__); + if (conform != NULL) + { + adapter = PyObject_CallMethodObjArgs(self, str_call_conform, + conform, NULL); + Py_DECREF(conform); + if (adapter == NULL || adapter != Py_None) + return adapter; + Py_DECREF(adapter); + } + else + PyErr_Clear(); + + adapter = __adapt__(self, obj); + if (adapter == NULL || adapter != Py_None) + return adapter; + Py_DECREF(adapter); + + if (alternate != NULL) + { + Py_INCREF(alternate); + return alternate; + } + + adapter = Py_BuildValue("sOO", "Could not adapt", obj, self); + if (adapter != NULL) + { + PyErr_SetObject(PyExc_TypeError, adapter); + Py_DECREF(adapter); + } + return NULL; +} + +static PyTypeObject InterfaceBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "InterfaceBase", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)ib_call, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE , + /* tp_doc */ "Interface base type providing __call__ and __adapt__", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ ib_methods, +}; + +/* =================== End: __call__ and __adapt__ ==================== */ +/* ==================================================================== */ + +/* ==================================================================== */ +/* ========================== Begin: Lookup Bases ===================== */ + +typedef struct { + PyObject_HEAD + PyObject *_cache; + PyObject *_mcache; + PyObject *_scache; +} lookup; + +typedef struct { + PyObject_HEAD + PyObject *_cache; + PyObject *_mcache; + PyObject *_scache; + PyObject *_verify_ro; + PyObject *_verify_generations; +} verify; + +static int +lookup_traverse(lookup *self, visitproc visit, void *arg) +{ + int vret; + + if (self->_cache) { + vret = visit(self->_cache, arg); + if (vret != 0) + return vret; + } + + if (self->_mcache) { + vret = visit(self->_mcache, arg); + if (vret != 0) + return vret; + } + + if (self->_scache) { + vret = visit(self->_scache, arg); + if (vret != 0) + return vret; + } + + return 0; +} + +static int +lookup_clear(lookup *self) +{ + Py_CLEAR(self->_cache); + Py_CLEAR(self->_mcache); + Py_CLEAR(self->_scache); + return 0; +} + +static void +lookup_dealloc(lookup *self) +{ + lookup_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + def changed(self, ignored=None): + self._cache.clear() + self._mcache.clear() + self._scache.clear() +*/ +static PyObject * +lookup_changed(lookup *self, PyObject *ignored) +{ + lookup_clear(self); + Py_INCREF(Py_None); + return Py_None; +} + +#define ASSURE_DICT(N) if (N == NULL) { N = PyDict_New(); \ + if (N == NULL) return NULL; \ + } + +/* + def _getcache(self, provided, name): + cache = self._cache.get(provided) + if cache is None: + cache = {} + self._cache[provided] = cache + if name: + c = cache.get(name) + if c is None: + c = {} + cache[name] = c + cache = c + return cache +*/ +static PyObject * +_subcache(PyObject *cache, PyObject *key) +{ + PyObject *subcache; + + subcache = PyDict_GetItem(cache, key); + if (subcache == NULL) + { + int status; + + subcache = PyDict_New(); + if (subcache == NULL) + return NULL; + status = PyDict_SetItem(cache, key, subcache); + Py_DECREF(subcache); + if (status < 0) + return NULL; + } + + return subcache; +} +static PyObject * +_getcache(lookup *self, PyObject *provided, PyObject *name) +{ + PyObject *cache; + + ASSURE_DICT(self->_cache); + cache = _subcache(self->_cache, provided); + if (cache == NULL) + return NULL; + + if (name != NULL && PyObject_IsTrue(name)) + cache = _subcache(cache, name); + + return cache; +} + + +/* + def lookup(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + if len(required) == 1: + result = cache.get(required[0], _not_in_mapping) + else: + result = cache.get(tuple(required), _not_in_mapping) + + if result is _not_in_mapping: + result = self._uncached_lookup(required, provided, name) + if len(required) == 1: + cache[required[0]] = result + else: + cache[tuple(required)] = result + + if result is None: + return default + + return result +*/ +static PyObject * +tuplefy(PyObject *v) +{ + if (! PyTuple_Check(v)) + { + v = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), v, NULL); + if (v == NULL) + return NULL; + } + else + Py_INCREF(v); + + return v; +} +static PyObject * +_lookup(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *key, *cache; + + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == NULL) + return NULL; + + if (PyTuple_GET_SIZE(required) == 1) + key = PyTuple_GET_ITEM(required, 0); + else + key = required; + + result = PyDict_GetItem(cache, key); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookup, + required, provided, name, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, key, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + if (result == Py_None && default_ != NULL) + { + Py_DECREF(Py_None); + Py_INCREF(default_); + return default_; + } + + return result; +} +static PyObject * +lookup_lookup(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + return _lookup(self, required, provided, name, default_); +} + + +/* + def lookup1(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + return self.lookup((required, ), provided, name, default) + + if result is None: + return default + + return result +*/ +static PyObject * +_lookup1(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *cache; + + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + PyObject *tup; + + tup = PyTuple_New(1); + if (tup == NULL) + return NULL; + Py_INCREF(required); + PyTuple_SET_ITEM(tup, 0, required); + result = _lookup(self, tup, provided, name, default_); + Py_DECREF(tup); + } + else + Py_INCREF(result); + + return result; +} +static PyObject * +lookup_lookup1(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + return _lookup1(self, required, provided, name, default_); +} + +/* + def adapter_hook(self, provided, object, name=u'', default=None): + required = providedBy(object) + cache = self._getcache(provided, name) + factory = cache.get(required, _not_in_mapping) + if factory is _not_in_mapping: + factory = self.lookup((required, ), provided, name) + + if factory is not None: + result = factory(object) + if result is not None: + return result + + return default +*/ +static PyObject * +_adapter_hook(lookup *self, + PyObject *provided, PyObject *object, PyObject *name, + PyObject *default_) +{ + PyObject *required, *factory, *result; + + required = providedBy(NULL, object); + if (required == NULL) + return NULL; + + factory = _lookup1(self, required, provided, name, Py_None); + Py_DECREF(required); + if (factory == NULL) + return NULL; + + if (factory != Py_None) + { + result = PyObject_CallFunctionObjArgs(factory, object, NULL); + Py_DECREF(factory); + if (result == NULL || result != Py_None) + return result; + } + else + result = factory; /* None */ + + if (default_ == NULL || default_ == result) /* No default specified, */ + return result; /* Return None. result is owned None */ + + Py_DECREF(result); + Py_INCREF(default_); + + return default_; +} +static PyObject * +lookup_adapter_hook(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"provided", "object", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &provided, &object, &name, &default_)) + return NULL; + + return _adapter_hook(self, provided, object, name, default_); +} + +static PyObject * +lookup_queryAdapter(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "provided", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &object, &provided, &name, &default_)) + return NULL; + + return _adapter_hook(self, provided, object, name, default_); +} + +/* + def lookupAll(self, required, provided): + cache = self._mcache.get(provided) + if cache is None: + cache = {} + self._mcache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_lookupAll(required, provided) + cache[required] = result + + return result +*/ +static PyObject * +_lookupAll(lookup *self, PyObject *required, PyObject *provided) +{ + PyObject *cache, *result; + + ASSURE_DICT(self->_mcache); + cache = _subcache(self->_mcache, provided); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookupAll, + required, provided, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, required, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + return result; +} +static PyObject * +lookup_lookupAll(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + return _lookupAll(self, required, provided); +} + +/* + def subscriptions(self, required, provided): + cache = self._scache.get(provided) + if cache is None: + cache = {} + self._scache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_subscriptions(required, provided) + cache[required] = result + + return result +*/ +static PyObject * +_subscriptions(lookup *self, PyObject *required, PyObject *provided) +{ + PyObject *cache, *result; + + ASSURE_DICT(self->_scache); + cache = _subcache(self->_scache, provided); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs( + OBJECT(self), str_uncached_subscriptions, + required, provided, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, required, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + return result; +} +static PyObject * +lookup_subscriptions(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + return _subscriptions(self, required, provided); +} + +static struct PyMethodDef lookup_methods[] = { + {"changed", (PyCFunction)lookup_changed, METH_O, ""}, + {"lookup", (PyCFunction)lookup_lookup, METH_KEYWORDS, ""}, + {"lookup1", (PyCFunction)lookup_lookup1, METH_KEYWORDS, ""}, + {"queryAdapter", (PyCFunction)lookup_queryAdapter, METH_KEYWORDS, ""}, + {"adapter_hook", (PyCFunction)lookup_adapter_hook, METH_KEYWORDS, ""}, + {"lookupAll", (PyCFunction)lookup_lookupAll, METH_KEYWORDS, ""}, + {"subscriptions", (PyCFunction)lookup_subscriptions, METH_KEYWORDS, ""}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject LookupBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "LookupBase", + /* tp_basicsize */ sizeof(lookup), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&lookup_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "", + /* tp_traverse */ (traverseproc)lookup_traverse, + /* tp_clear */ (inquiry)lookup_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ lookup_methods, +}; + +static int +verifying_traverse(verify *self, visitproc visit, void *arg) +{ + int vret; + + vret = lookup_traverse((lookup *)self, visit, arg); + if (vret != 0) + return vret; + + if (self->_verify_ro) { + vret = visit(self->_verify_ro, arg); + if (vret != 0) + return vret; + } + if (self->_verify_generations) { + vret = visit(self->_verify_generations, arg); + if (vret != 0) + return vret; + } + + return 0; +} + +static int +verifying_clear(verify *self) +{ + lookup_clear((lookup *)self); + Py_CLEAR(self->_verify_generations); + Py_CLEAR(self->_verify_ro); + return 0; +} + + +static void +verifying_dealloc(verify *self) +{ + verifying_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + def changed(self, originally_changed): + super(VerifyingBasePy, self).changed(originally_changed) + self._verify_ro = self._registry.ro[1:] + self._verify_generations = [r._generation for r in self._verify_ro] +*/ +static PyObject * +_generations_tuple(PyObject *ro) +{ + int i, l; + PyObject *generations; + + l = PyTuple_GET_SIZE(ro); + generations = PyTuple_New(l); + for (i=0; i < l; i++) + { + PyObject *generation; + + generation = PyObject_GetAttr(PyTuple_GET_ITEM(ro, i), str_generation); + if (generation == NULL) + { + Py_DECREF(generations); + return NULL; + } + PyTuple_SET_ITEM(generations, i, generation); + } + + return generations; +} +static PyObject * +verifying_changed(verify *self, PyObject *ignored) +{ + PyObject *t, *ro; + + verifying_clear(self); + + t = PyObject_GetAttr(OBJECT(self), str_registry); + if (t == NULL) + return NULL; + ro = PyObject_GetAttr(t, strro); + Py_DECREF(t); + if (ro == NULL) + return NULL; + + t = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), ro, NULL); + Py_DECREF(ro); + if (t == NULL) + return NULL; + + ro = PyTuple_GetSlice(t, 1, PyTuple_GET_SIZE(t)); + Py_DECREF(t); + if (ro == NULL) + return NULL; + + self->_verify_generations = _generations_tuple(ro); + if (self->_verify_generations == NULL) + { + Py_DECREF(ro); + return NULL; + } + + self->_verify_ro = ro; + + Py_INCREF(Py_None); + return Py_None; +} + +/* + def _verify(self): + if ([r._generation for r in self._verify_ro] + != self._verify_generations): + self.changed(None) +*/ +static int +_verify(verify *self) +{ + PyObject *changed_result; + + if (self->_verify_ro != NULL && self->_verify_generations != NULL) + { + PyObject *generations; + int changed; + + generations = _generations_tuple(self->_verify_ro); + if (generations == NULL) + return -1; + + changed = PyObject_RichCompareBool(self->_verify_generations, + generations, Py_NE); + Py_DECREF(generations); + if (changed == -1) + return -1; + + if (changed == 0) + return 0; + } + + changed_result = PyObject_CallMethodObjArgs(OBJECT(self), strchanged, + Py_None, NULL); + if (changed_result == NULL) + return -1; + + Py_DECREF(changed_result); + return 0; +} + +static PyObject * +verifying_lookup(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookup((lookup *)self, required, provided, name, default_); +} + +static PyObject * +verifying_lookup1(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookup1((lookup *)self, required, provided, name, default_); +} + +static PyObject * +verifying_adapter_hook(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"provided", "object", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &provided, &object, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _adapter_hook((lookup *)self, provided, object, name, default_); +} + +static PyObject * +verifying_queryAdapter(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "provided", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &object, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _adapter_hook((lookup *)self, provided, object, name, default_); +} + +static PyObject * +verifying_lookupAll(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookupAll((lookup *)self, required, provided); +} + +static PyObject * +verifying_subscriptions(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _subscriptions((lookup *)self, required, provided); +} + +static struct PyMethodDef verifying_methods[] = { + {"changed", (PyCFunction)verifying_changed, METH_O, ""}, + {"lookup", (PyCFunction)verifying_lookup, METH_KEYWORDS, ""}, + {"lookup1", (PyCFunction)verifying_lookup1, METH_KEYWORDS, ""}, + {"queryAdapter", (PyCFunction)verifying_queryAdapter, METH_KEYWORDS, ""}, + {"adapter_hook", (PyCFunction)verifying_adapter_hook, METH_KEYWORDS, ""}, + {"lookupAll", (PyCFunction)verifying_lookupAll, METH_KEYWORDS, ""}, + {"subscriptions", (PyCFunction)verifying_subscriptions, METH_KEYWORDS, ""}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject VerifyingBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "VerifyingBase", + /* tp_basicsize */ sizeof(verify), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&verifying_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "", + /* tp_traverse */ (traverseproc)verifying_traverse, + /* tp_clear */ (inquiry)verifying_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ verifying_methods, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &LookupBase, +}; + +/* ========================== End: Lookup Bases ======================= */ +/* ==================================================================== */ + + + +static struct PyMethodDef m_methods[] = { + {"implementedBy", (PyCFunction)implementedBy, METH_O, + "Interfaces implemented by a class or factory.\n" + "Raises TypeError if argument is neither a class nor a callable."}, + {"getObjectSpecification", (PyCFunction)getObjectSpecification, METH_O, + "Get an object's interfaces (internal api)"}, + {"providedBy", (PyCFunction)providedBy, METH_O, + "Get an object's interfaces"}, + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 +static char module_doc[] = "C optimizations for zope.interface\n\n"; + +static struct PyModuleDef _zic_module = { + PyModuleDef_HEAD_INIT, + "_zope_interface_coptimizations", + module_doc, + -1, + m_methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +static PyObject * +init(void) +{ + PyObject *m; + +#if PY_MAJOR_VERSION < 3 +#define DEFINE_STRING(S) \ + if(! (str ## S = PyString_FromString(# S))) return NULL +#else +#define DEFINE_STRING(S) \ + if(! (str ## S = PyUnicode_FromString(# S))) return NULL +#endif + + DEFINE_STRING(__dict__); + DEFINE_STRING(__implemented__); + DEFINE_STRING(__provides__); + DEFINE_STRING(__class__); + DEFINE_STRING(__providedBy__); + DEFINE_STRING(extends); + DEFINE_STRING(_implied); + DEFINE_STRING(_implements); + DEFINE_STRING(_cls); + DEFINE_STRING(__conform__); + DEFINE_STRING(_call_conform); + DEFINE_STRING(_uncached_lookup); + DEFINE_STRING(_uncached_lookupAll); + DEFINE_STRING(_uncached_subscriptions); + DEFINE_STRING(_registry); + DEFINE_STRING(_generation); + DEFINE_STRING(ro); + DEFINE_STRING(changed); +#undef DEFINE_STRING + adapter_hooks = PyList_New(0); + if (adapter_hooks == NULL) + return NULL; + + /* Initialize types: */ + SpecType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&SpecType) < 0) + return NULL; + OSDType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&OSDType) < 0) + return NULL; + CPBType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&CPBType) < 0) + return NULL; + + InterfaceBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&InterfaceBase) < 0) + return NULL; + + LookupBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&LookupBase) < 0) + return NULL; + + VerifyingBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&VerifyingBase) < 0) + return NULL; + + #if PY_MAJOR_VERSION < 3 + /* Create the module and add the functions */ + m = Py_InitModule3("_zope_interface_coptimizations", m_methods, + "C optimizations for zope.interface\n\n"); + #else + m = PyModule_Create(&_zic_module); + #endif + if (m == NULL) + return NULL; + + /* Add types: */ + if (PyModule_AddObject(m, "SpecificationBase", OBJECT(&SpecType)) < 0) + return NULL; + if (PyModule_AddObject(m, "ObjectSpecificationDescriptor", + (PyObject *)&OSDType) < 0) + return NULL; + if (PyModule_AddObject(m, "ClassProvidesBase", OBJECT(&CPBType)) < 0) + return NULL; + if (PyModule_AddObject(m, "InterfaceBase", OBJECT(&InterfaceBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "LookupBase", OBJECT(&LookupBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "VerifyingBase", OBJECT(&VerifyingBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "adapter_hooks", adapter_hooks) < 0) + return NULL; + return m; +} + +PyMODINIT_FUNC +#if PY_MAJOR_VERSION < 3 +init_zope_interface_coptimizations(void) +{ + init(); +} +#else +PyInit__zope_interface_coptimizations(void) +{ + return init(); +} +#endif diff --git a/pythonforumide/zope/interface/adapter.py b/pythonforumide/zope/interface/adapter.py new file mode 100644 index 0000000..2ad145b --- /dev/null +++ b/pythonforumide/zope/interface/adapter.py @@ -0,0 +1,692 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Adapter management +""" + +import weakref +from zope.interface import providedBy, Interface, ro + +_marker = object +class BaseAdapterRegistry(object): + + # List of methods copied from lookup sub-objects: + _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter', + 'adapter_hook', 'lookupAll', 'names', + 'subscriptions', 'subscribers') + + # All registries maintain a generation that can be used by verifying + # registries + _generation = 0 + + def __init__(self, bases=()): + + # The comments here could be improved. Possibly this bit needs + # explaining in a separate document, as the comments here can + # be quite confusing. /regebro + + # {order -> {required -> {provided -> {name -> value}}}} + # Here "order" is actually an index in a list, "required" and + # "provided" are interfaces, and "required" is really a nested + # key. So, for example: + # for order == 0 (that is, self._adapters[0]), we have: + # {provided -> {name -> value}} + # but for order == 2 (that is, self._adapters[2]), we have: + # {r1 -> {r2 -> {provided -> {name -> value}}}} + # + self._adapters = [] + + # {order -> {required -> {provided -> {name -> [value]}}}} + # where the remarks about adapters above apply + self._subscribers = [] + + # Set, with a reference count, keeping track of the interfaces + # for which we have provided components: + self._provided = {} + + # Create ``_v_lookup`` object to perform lookup. We make this a + # separate object to to make it easier to implement just the + # lookup functionality in C. This object keeps track of cache + # invalidation data in two kinds of registries. + + # Invalidating registries have caches that are invalidated + # when they or their base registies change. An invalidating + # registry can only have invalidating registries as bases. + # See LookupBasePy below for the pertinent logic. + + # Verifying registies can't rely on getting invalidation messages, + # so have to check the generations of base registries to determine + # if their cache data are current. See VerifyingBasePy below + # for the pertinent object. + self._createLookup() + + # Setting the bases causes the registries described above + # to be initialized (self._setBases -> self.changed -> + # self._v_lookup.changed). + + self.__bases__ = bases + + def _setBases(self, bases): + self.__dict__['__bases__'] = bases + self.ro = ro.ro(self) + self.changed(self) + + __bases__ = property(lambda self: self.__dict__['__bases__'], + lambda self, bases: self._setBases(bases), + ) + + def _createLookup(self): + self._v_lookup = self.LookupClass(self) + for name in self._delegated: + self.__dict__[name] = getattr(self._v_lookup, name) + + def changed(self, originally_changed): + self._generation += 1 + self._v_lookup.changed(originally_changed) + + def register(self, required, provided, name, value): + if value is None: + self.unregister(required, provided, name, value) + return + + required = tuple(map(_convert_None_to_Interface, required)) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + while len(byorder) <= order: + byorder.append({}) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = {} + components[k] = d + components = d + + if components.get(name) is value: + return + + components[name] = value + + n = self._provided.get(provided, 0) + 1 + self._provided[provided] = n + if n == 1: + self._v_lookup.add_extendor(provided) + + self.changed(self) + + def registered(self, required, provided, name=u''): + required = tuple(map(_convert_None_to_Interface, required)) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + if len(byorder) <= order: + return None + + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + return None + components = d + + return components.get(name) + + def unregister(self, required, provided, name, value=None): + required = tuple(map(_convert_None_to_Interface, required)) + order = len(required) + byorder = self._adapters + if order >= len(byorder): + return False + components = byorder[order] + key = required + (provided,) + + # Keep track of how we got to `components`: + lookups = [] + # Keep track of how we got to `components`: + lookups = [] + for k in key: + d = components.get(k) + if d is None: + return + lookups.append((components, k)) + components = d + + old = components.get(name) + if old is None: + return + if (value is not None) and (old is not value): + return + + del components[name] + if not components: + # Clean out empty containers, since we don't want our keys + # to reference global objects (interfaces) unnecessarily. + # This is often a problem when an interface is slated for + # removal; a hold-over entry in the registry can make it + # difficult to remove such interfaces. + for comp, k in reversed(lookups): + d = comp[k] + if d: + break + else: + del comp[k] + while byorder and not byorder[-1]: + del byorder[-1] + n = self._provided[provided] - 1 + if n == 0: + del self._provided[provided] + self._v_lookup.remove_extendor(provided) + else: + self._provided[provided] = n + + self.changed(self) + + def subscribe(self, required, provided, value): + required = tuple(map(_convert_None_to_Interface, required)) + name = u'' + order = len(required) + byorder = self._subscribers + while len(byorder) <= order: + byorder.append({}) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = {} + components[k] = d + components = d + + components[name] = components.get(name, ()) + (value, ) + + if provided is not None: + n = self._provided.get(provided, 0) + 1 + self._provided[provided] = n + if n == 1: + self._v_lookup.add_extendor(provided) + + self.changed(self) + + def unsubscribe(self, required, provided, value=None): + required = tuple(map(_convert_None_to_Interface, required)) + order = len(required) + byorder = self._subscribers + if order >= len(byorder): + return + components = byorder[order] + key = required + (provided,) + + # Keep track of how we got to `components`: + lookups = [] + # Keep track of how we got to `components`: + lookups = [] + for k in key: + d = components.get(k) + if d is None: + return + lookups.append((components, k)) + components = d + + old = components.get(u'') + if not old: + return + + if value is None: + new = () + else: + new = tuple([v for v in old if v is not value]) + + if new == old: + return + + if new: + components[u''] = new + else: + # Instead of setting components[u''] = new, we clean out + # empty containers, since we don't want our keys to + # reference global objects (interfaces) unnecessarily. This + # is often a problem when an interface is slated for + # removal; a hold-over entry in the registry can make it + # difficult to remove such interfaces. + if u'' in components: + del components[u''] + for comp, k in reversed(lookups): + d = comp[k] + if d: + break + else: + del comp[k] + while byorder and not byorder[-1]: + del byorder[-1] + + if provided is not None: + n = self._provided[provided] + len(new) - len(old) + if n == 0: + del self._provided[provided] + self._v_lookup.remove_extendor(provided) + + self.changed(self) + + # XXX hack to fake out twisted's use of a private api. We need to get them + # to use the new registed method. + def get(self, _): + class XXXTwistedFakeOut: + selfImplied = {} + return XXXTwistedFakeOut + + +_not_in_mapping = object() +class LookupBasePy(object): + + def __init__(self): + self._cache = {} + self._mcache = {} + self._scache = {} + + def changed(self, ignored=None): + self._cache.clear() + self._mcache.clear() + self._scache.clear() + + def _getcache(self, provided, name): + cache = self._cache.get(provided) + if cache is None: + cache = {} + self._cache[provided] = cache + if name: + c = cache.get(name) + if c is None: + c = {} + cache[name] = c + cache = c + return cache + + def lookup(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + if len(required) == 1: + result = cache.get(required[0], _not_in_mapping) + else: + result = cache.get(tuple(required), _not_in_mapping) + + if result is _not_in_mapping: + result = self._uncached_lookup(required, provided, name) + if len(required) == 1: + cache[required[0]] = result + else: + cache[tuple(required)] = result + + if result is None: + return default + + return result + + def lookup1(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + return self.lookup((required, ), provided, name, default) + + if result is None: + return default + + return result + + def queryAdapter(self, object, provided, name=u'', default=None): + return self.adapter_hook(provided, object, name, default) + + def adapter_hook(self, provided, object, name=u'', default=None): + required = providedBy(object) + cache = self._getcache(provided, name) + factory = cache.get(required, _not_in_mapping) + if factory is _not_in_mapping: + factory = self.lookup((required, ), provided, name) + + if factory is not None: + result = factory(object) + if result is not None: + return result + + return default + + def lookupAll(self, required, provided): + cache = self._mcache.get(provided) + if cache is None: + cache = {} + self._mcache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_lookupAll(required, provided) + cache[required] = result + + return result + + + def subscriptions(self, required, provided): + cache = self._scache.get(provided) + if cache is None: + cache = {} + self._scache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_subscriptions(required, provided) + cache[required] = result + + return result + +LookupBase = LookupBasePy + +class VerifyingBasePy(LookupBasePy): + + def changed(self, originally_changed): + LookupBasePy.changed(self, originally_changed) + self._verify_ro = self._registry.ro[1:] + self._verify_generations = [r._generation for r in self._verify_ro] + + def _verify(self): + if ([r._generation for r in self._verify_ro] + != self._verify_generations): + self.changed(None) + + def _getcache(self, provided, name): + self._verify() + return LookupBasePy._getcache(self, provided, name) + + def lookupAll(self, required, provided): + self._verify() + return LookupBasePy.lookupAll(self, required, provided) + + def subscriptions(self, required, provided): + self._verify() + return LookupBasePy.subscriptions(self, required, provided) + +VerifyingBase = VerifyingBasePy + + +try: + import _zope_interface_coptimizations +except ImportError: + pass +else: + from _zope_interface_coptimizations import LookupBase, VerifyingBase + +class AdapterLookupBase(object): + + def __init__(self, registry): + self._registry = registry + self._required = {} + self.init_extendors() + super(AdapterLookupBase, self).__init__() + + def changed(self, ignored=None): + super(AdapterLookupBase, self).changed(None) + for r in self._required.keys(): + r = r() + if r is not None: + r.unsubscribe(self) + self._required.clear() + + + # Extendors + # --------- + + # When given an target interface for an adapter lookup, we need to consider + # adapters for interfaces that extend the target interface. This is + # what the extendors dictionary is about. It tells us all of the + # interfaces that extend an interface for which there are adapters + # registered. + + # We could separate this by order and name, thus reducing the + # number of provided interfaces to search at run time. The tradeoff, + # however, is that we have to store more information. For example, + # is the same interface is provided for multiple names and if the + # interface extends many interfaces, we'll have to keep track of + # a fair bit of information for each name. It's better to + # be space efficient here and be time efficient in the cache + # implementation. + + # TODO: add invalidation when a provided interface changes, in case + # the interface's __iro__ has changed. This is unlikely enough that + # we'll take our chances for now. + + def init_extendors(self): + self._extendors = {} + for p in self._registry._provided: + self.add_extendor(p) + + def add_extendor(self, provided): + _extendors = self._extendors + for i in provided.__iro__: + extendors = _extendors.get(i, ()) + _extendors[i] = ( + [e for e in extendors if provided.isOrExtends(e)] + + + [provided] + + + [e for e in extendors if not provided.isOrExtends(e)] + ) + + def remove_extendor(self, provided): + _extendors = self._extendors + for i in provided.__iro__: + _extendors[i] = [e for e in _extendors.get(i, ()) + if e != provided] + + + def _subscribe(self, *required): + _refs = self._required + for r in required: + ref = r.weakref() + if ref not in _refs: + r.subscribe(self) + _refs[ref] = 1 + + def _uncached_lookup(self, required, provided, name=u''): + result = None + order = len(required) + for registry in self._registry.ro: + byorder = registry._adapters + if order >= len(byorder): + continue + + extendors = registry._v_lookup._extendors.get(provided) + if not extendors: + continue + + components = byorder[order] + result = _lookup(components, required, extendors, name, 0, + order) + if result is not None: + break + + self._subscribe(*required) + + return result + + def queryMultiAdapter(self, objects, provided, name=u'', default=None): + factory = self.lookup(map(providedBy, objects), provided, name) + if factory is None: + return default + + result = factory(*objects) + if result is None: + return default + + return result + + def _uncached_lookupAll(self, required, provided): + order = len(required) + result = {} + for registry in reversed(self._registry.ro): + byorder = registry._adapters + if order >= len(byorder): + continue + extendors = registry._v_lookup._extendors.get(provided) + if not extendors: + continue + components = byorder[order] + _lookupAll(components, required, extendors, result, 0, order) + + self._subscribe(*required) + + return tuple(result.iteritems()) + + def names(self, required, provided): + return [c[0] for c in self.lookupAll(required, provided)] + + def _uncached_subscriptions(self, required, provided): + order = len(required) + result = [] + for registry in reversed(self._registry.ro): + byorder = registry._subscribers + if order >= len(byorder): + continue + + if provided is None: + extendors = (provided, ) + else: + extendors = registry._v_lookup._extendors.get(provided) + if extendors is None: + continue + + _subscriptions(byorder[order], required, extendors, u'', + result, 0, order) + + self._subscribe(*required) + + return result + + def subscribers(self, objects, provided): + subscriptions = self.subscriptions(map(providedBy, objects), provided) + if provided is None: + result = () + for subscription in subscriptions: + subscription(*objects) + else: + result = [] + for subscription in subscriptions: + subscriber = subscription(*objects) + if subscriber is not None: + result.append(subscriber) + return result + +class AdapterLookup(AdapterLookupBase, LookupBase): + pass + +class AdapterRegistry(BaseAdapterRegistry): + + LookupClass = AdapterLookup + + def __init__(self, bases=()): + # AdapterRegisties are invalidating registries, so + # we need to keep track of out invalidating subregistries. + self._v_subregistries = weakref.WeakKeyDictionary() + + super(AdapterRegistry, self).__init__(bases) + + def _addSubregistry(self, r): + self._v_subregistries[r] = 1 + + def _removeSubregistry(self, r): + if r in self._v_subregistries: + del self._v_subregistries[r] + + def _setBases(self, bases): + old = self.__dict__.get('__bases__', ()) + for r in old: + if r not in bases: + r._removeSubregistry(self) + for r in bases: + if r not in old: + r._addSubregistry(self) + + super(AdapterRegistry, self)._setBases(bases) + + def changed(self, originally_changed): + super(AdapterRegistry, self).changed(originally_changed) + + for sub in self._v_subregistries.keys(): + sub.changed(originally_changed) + + +class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): + pass + +class VerifyingAdapterRegistry(BaseAdapterRegistry): + + LookupClass = VerifyingAdapterLookup + +def _convert_None_to_Interface(x): + if x is None: + return Interface + else: + return x + +def _normalize_name(name): + if isinstance(name, basestring): + return unicode(name) + + raise TypeError("name must be a regular or unicode string") + +def _lookup(components, specs, provided, name, i, l): + if i < l: + for spec in specs[i].__sro__: + comps = components.get(spec) + if comps: + r = _lookup(comps, specs, provided, name, i+1, l) + if r is not None: + return r + else: + for iface in provided: + comps = components.get(iface) + if comps: + r = comps.get(name) + if r is not None: + return r + + return None + +def _lookupAll(components, specs, provided, result, i, l): + if i < l: + for spec in reversed(specs[i].__sro__): + comps = components.get(spec) + if comps: + _lookupAll(comps, specs, provided, result, i+1, l) + else: + for iface in reversed(provided): + comps = components.get(iface) + if comps: + result.update(comps) + +def _subscriptions(components, specs, provided, name, result, i, l): + if i < l: + for spec in reversed(specs[i].__sro__): + comps = components.get(spec) + if comps: + _subscriptions(comps, specs, provided, name, result, i+1, l) + else: + for iface in reversed(provided): + comps = components.get(iface) + if comps: + comps = comps.get(name) + if comps: + result.extend(comps) diff --git a/pythonforumide/zope/interface/advice.py b/pythonforumide/zope/interface/advice.py new file mode 100644 index 0000000..d23818e --- /dev/null +++ b/pythonforumide/zope/interface/advice.py @@ -0,0 +1,201 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Class advice. + +This module was adapted from 'protocols.advice', part of the Python +Enterprise Application Kit (PEAK). Please notify the PEAK authors +(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or +Zope-specific changes are required, so that the PEAK version of this module +can be kept in sync. + +PEAK is a Python application framework that interoperates with (but does +not require) Zope 3 and Twisted. It provides tools for manipulating UML +models, object-relational persistence, aspect-oriented programming, and more. +Visit the PEAK home page at http://peak.telecommunity.com for more information. +""" + +from types import FunctionType +try: + from types import ClassType + __python3 = False +except ImportError: + __python3 = True + +import sys + +def getFrameInfo(frame): + """Return (kind,module,locals,globals) for a frame + + 'kind' is one of "exec", "module", "class", "function call", or "unknown". + """ + + f_locals = frame.f_locals + f_globals = frame.f_globals + + sameNamespace = f_locals is f_globals + hasModule = '__module__' in f_locals + hasName = '__name__' in f_globals + + sameName = hasModule and hasName + sameName = sameName and f_globals['__name__']==f_locals['__module__'] + + module = hasName and sys.modules.get(f_globals['__name__']) or None + + namespaceIsModule = module and module.__dict__ is f_globals + + if not namespaceIsModule: + # some kind of funky exec + kind = "exec" + elif sameNamespace and not hasModule: + kind = "module" + elif sameName and not sameNamespace: + kind = "class" + elif not sameNamespace: + kind = "function call" + else: + # How can you have f_locals is f_globals, and have '__module__' set? + # This is probably module-level code, but with a '__module__' variable. + kind = "unknown" + return kind, module, f_locals, f_globals + + +def addClassAdvisor(callback, depth=2): + """Set up 'callback' to be passed the containing class upon creation + + This function is designed to be called by an "advising" function executed + in a class suite. The "advising" function supplies a callback that it + wishes to have executed when the containing class is created. The + callback will be given one argument: the newly created containing class. + The return value of the callback will be used in place of the class, so + the callback should return the input if it does not wish to replace the + class. + + The optional 'depth' argument to this function determines the number of + frames between this function and the targeted class suite. 'depth' + defaults to 2, since this skips this function's frame and one calling + function frame. If you use this function from a function called directly + in the class suite, the default will be correct, otherwise you will need + to determine the correct depth yourself. + + This function works by installing a special class factory function in + place of the '__metaclass__' of the containing class. Therefore, only + callbacks *after* the last '__metaclass__' assignment in the containing + class will be executed. Be sure that classes using "advising" functions + declare any '__metaclass__' *first*, to ensure all callbacks are run.""" + + frame = sys._getframe(depth) + kind, module, caller_locals, caller_globals = getFrameInfo(frame) + + # This causes a problem when zope interfaces are used from doctest. + # In these cases, kind == "exec". + # + #if kind != "class": + # raise SyntaxError( + # "Advice must be in the body of a class statement" + # ) + + previousMetaclass = caller_locals.get('__metaclass__') + if __python3: + defaultMetaclass = caller_globals.get('__metaclass__', type) + else: + defaultMetaclass = caller_globals.get('__metaclass__', ClassType) + + + def advise(name, bases, cdict): + + if '__metaclass__' in cdict: + del cdict['__metaclass__'] + + if previousMetaclass is None: + if bases: + # find best metaclass or use global __metaclass__ if no bases + meta = determineMetaclass(bases) + else: + meta = defaultMetaclass + + elif isClassAdvisor(previousMetaclass): + # special case: we can't compute the "true" metaclass here, + # so we need to invoke the previous metaclass and let it + # figure it out for us (and apply its own advice in the process) + meta = previousMetaclass + + else: + meta = determineMetaclass(bases, previousMetaclass) + + newClass = meta(name,bases,cdict) + + # this lets the callback replace the class completely, if it wants to + return callback(newClass) + + # introspection data only, not used by inner function + advise.previousMetaclass = previousMetaclass + advise.callback = callback + + # install the advisor + caller_locals['__metaclass__'] = advise + + +def isClassAdvisor(ob): + """True if 'ob' is a class advisor function""" + return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass') + + +def determineMetaclass(bases, explicit_mc=None): + """Determine metaclass from 1+ bases and optional explicit __metaclass__""" + + meta = [getattr(b,'__class__',type(b)) for b in bases] + + if explicit_mc is not None: + # The explicit metaclass needs to be verified for compatibility + # as well, and allowed to resolve the incompatible bases, if any + meta.append(explicit_mc) + + if len(meta)==1: + # easy case + return meta[0] + + candidates = minimalBases(meta) # minimal set of metaclasses + + if not candidates: + # they're all "classic" classes + assert(not __python3) # This should not happen under Python 3 + return ClassType + + elif len(candidates)>1: + # We could auto-combine, but for now we won't... + raise TypeError("Incompatible metatypes",bases) + + # Just one, return it + return candidates[0] + + +def minimalBases(classes): + """Reduce a list of base classes to its ordered minimum equivalent""" + + if not __python3: + classes = [c for c in classes if c is not ClassType] + candidates = [] + + for m in classes: + for n in classes: + if issubclass(n,m) and m is not n: + break + else: + # m has no subclasses in 'classes' + if m in candidates: + candidates.remove(m) # ensure that we're later in the list + candidates.append(m) + + return candidates + diff --git a/pythonforumide/zope/interface/common/__init__.py b/pythonforumide/zope/interface/common/__init__.py new file mode 100644 index 0000000..b711d36 --- /dev/null +++ b/pythonforumide/zope/interface/common/__init__.py @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff --git a/pythonforumide/zope/interface/common/idatetime.py b/pythonforumide/zope/interface/common/idatetime.py new file mode 100644 index 0000000..e8700af --- /dev/null +++ b/pythonforumide/zope/interface/common/idatetime.py @@ -0,0 +1,575 @@ +############################################################################## +# Copyright (c) 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +############################################################################## +"""Datetime interfaces. + +This module is called idatetime because if it were called datetime the import +of the real datetime would fail. +""" + +from zope.interface import Interface, Attribute +from zope.interface import classImplements + +from datetime import timedelta, date, datetime, time, tzinfo + + +class ITimeDeltaClass(Interface): + """This is the timedelta class interface.""" + + min = Attribute("The most negative timedelta object") + + max = Attribute("The most positive timedelta object") + + resolution = Attribute( + "The smallest difference between non-equal timedelta objects") + + +class ITimeDelta(ITimeDeltaClass): + """Represent the difference between two datetime objects. + + Supported operators: + + - add, subtract timedelta + - unary plus, minus, abs + - compare to timedelta + - multiply, divide by int/long + + In addition, datetime supports subtraction of two datetime objects + returning a timedelta, and addition or subtraction of a datetime + and a timedelta giving a datetime. + + Representation: (days, seconds, microseconds). + """ + + days = Attribute("Days between -999999999 and 999999999 inclusive") + + seconds = Attribute("Seconds between 0 and 86399 inclusive") + + microseconds = Attribute("Microseconds between 0 and 999999 inclusive") + + +class IDateClass(Interface): + """This is the date class interface.""" + + min = Attribute("The earliest representable date") + + max = Attribute("The latest representable date") + + resolution = Attribute( + "The smallest difference between non-equal date objects") + + def today(): + """Return the current local time. + + This is equivalent to date.fromtimestamp(time.time())""" + + def fromtimestamp(timestamp): + """Return the local date from a POSIX timestamp (like time.time()) + + This may raise ValueError, if the timestamp is out of the range of + values supported by the platform C localtime() function. It's common + for this to be restricted to years from 1970 through 2038. Note that + on non-POSIX systems that include leap seconds in their notion of a + timestamp, leap seconds are ignored by fromtimestamp(). + """ + + def fromordinal(ordinal): + """Return the date corresponding to the proleptic Gregorian ordinal. + + January 1 of year 1 has ordinal 1. ValueError is raised unless + 1 <= ordinal <= date.max.toordinal(). + For any date d, date.fromordinal(d.toordinal()) == d. + """ + + +class IDate(IDateClass): + """Represents a date (year, month and day) in an idealized calendar. + + Operators: + + __repr__, __str__ + __cmp__, __hash__ + __add__, __radd__, __sub__ (add/radd only with timedelta arg) + """ + + year = Attribute("Between MINYEAR and MAXYEAR inclusive.") + + month = Attribute("Between 1 and 12 inclusive") + + day = Attribute( + "Between 1 and the number of days in the given month of the given year.") + + def replace(year, month, day): + """Return a date with the same value. + + Except for those members given new values by whichever keyword + arguments are specified. For example, if d == date(2002, 12, 31), then + d.replace(day=26) == date(2000, 12, 26). + """ + + def timetuple(): + """Return a 9-element tuple of the form returned by time.localtime(). + + The hours, minutes and seconds are 0, and the DST flag is -1. + d.timetuple() is equivalent to + (d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() - + date(d.year, 1, 1).toordinal() + 1, -1) + """ + + def toordinal(): + """Return the proleptic Gregorian ordinal of the date + + January 1 of year 1 has ordinal 1. For any date object d, + date.fromordinal(d.toordinal()) == d. + """ + + def weekday(): + """Return the day of the week as an integer. + + Monday is 0 and Sunday is 6. For example, + date(2002, 12, 4).weekday() == 2, a Wednesday. + + See also isoweekday(). + """ + + def isoweekday(): + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. For example, + date(2002, 12, 4).isoweekday() == 3, a Wednesday. + + See also weekday(), isocalendar(). + """ + + def isocalendar(): + """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + + The ISO calendar is a widely used variant of the Gregorian calendar. + See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good + explanation. + + The ISO year consists of 52 or 53 full weeks, and where a week starts + on a Monday and ends on a Sunday. The first week of an ISO year is the + first (Gregorian) calendar week of a year containing a Thursday. This + is called week number 1, and the ISO year of that Thursday is the same + as its Gregorian year. + + For example, 2004 begins on a Thursday, so the first week of ISO year + 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so + that date(2003, 12, 29).isocalendar() == (2004, 1, 1) and + date(2004, 1, 4).isocalendar() == (2004, 1, 7). + """ + + def isoformat(): + """Return a string representing the date in ISO 8601 format. + + This is 'YYYY-MM-DD'. + For example, date(2002, 12, 4).isoformat() == '2002-12-04'. + """ + + def __str__(): + """For a date d, str(d) is equivalent to d.isoformat().""" + + def ctime(): + """Return a string representing the date. + + For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'. + d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) + on platforms where the native C ctime() function + (which time.ctime() invokes, but which date.ctime() does not invoke) + conforms to the C standard. + """ + + def strftime(format): + """Return a string representing the date. + + Controlled by an explicit format string. Format codes referring to + hours, minutes or seconds will see 0 values. + """ + + +class IDateTimeClass(Interface): + """This is the datetime class interface.""" + + min = Attribute("The earliest representable datetime") + + max = Attribute("The latest representable datetime") + + resolution = Attribute( + "The smallest possible difference between non-equal datetime objects") + + def today(): + """Return the current local datetime, with tzinfo None. + + This is equivalent to datetime.fromtimestamp(time.time()). + See also now(), fromtimestamp(). + """ + + def now(tz=None): + """Return the current local date and time. + + If optional argument tz is None or not specified, this is like today(), + but, if possible, supplies more precision than can be gotten from going + through a time.time() timestamp (for example, this may be possible on + platforms supplying the C gettimeofday() function). + + Else tz must be an instance of a class tzinfo subclass, and the current + date and time are converted to tz's time zone. In this case the result + is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). + + See also today(), utcnow(). + """ + + def utcnow(): + """Return the current UTC date and time, with tzinfo None. + + This is like now(), but returns the current UTC date and time, as a + naive datetime object. + + See also now(). + """ + + def fromtimestamp(timestamp, tz=None): + """Return the local date and time corresponding to the POSIX timestamp. + + Same as is returned by time.time(). If optional argument tz is None or + not specified, the timestamp is converted to the platform's local date + and time, and the returned datetime object is naive. + + Else tz must be an instance of a class tzinfo subclass, and the + timestamp is converted to tz's time zone. In this case the result is + equivalent to + tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz)). + + fromtimestamp() may raise ValueError, if the timestamp is out of the + range of values supported by the platform C localtime() or gmtime() + functions. It's common for this to be restricted to years in 1970 + through 2038. Note that on non-POSIX systems that include leap seconds + in their notion of a timestamp, leap seconds are ignored by + fromtimestamp(), and then it's possible to have two timestamps + differing by a second that yield identical datetime objects. + + See also utcfromtimestamp(). + """ + + def utcfromtimestamp(timestamp): + """Return the UTC datetime from the POSIX timestamp with tzinfo None. + + This may raise ValueError, if the timestamp is out of the range of + values supported by the platform C gmtime() function. It's common for + this to be restricted to years in 1970 through 2038. + + See also fromtimestamp(). + """ + + def fromordinal(ordinal): + """Return the datetime from the proleptic Gregorian ordinal. + + January 1 of year 1 has ordinal 1. ValueError is raised unless + 1 <= ordinal <= datetime.max.toordinal(). + The hour, minute, second and microsecond of the result are all 0, and + tzinfo is None. + """ + + def combine(date, time): + """Return a new datetime object. + + Its date members are equal to the given date object's, and whose time + and tzinfo members are equal to the given time object's. For any + datetime object d, d == datetime.combine(d.date(), d.timetz()). + If date is a datetime object, its time and tzinfo members are ignored. + """ + + +class IDateTime(IDate, IDateTimeClass): + """Object contains all the information from a date object and a time object. + """ + + year = Attribute("Year between MINYEAR and MAXYEAR inclusive") + + month = Attribute("Month between 1 and 12 inclusive") + + day = Attribute( + "Day between 1 and the number of days in the given month of the year") + + hour = Attribute("Hour in range(24)") + + minute = Attribute("Minute in range(60)") + + second = Attribute("Second in range(60)") + + microsecond = Attribute("Microsecond in range(1000000)") + + tzinfo = Attribute( + """The object passed as the tzinfo argument to the datetime constructor + or None if none was passed""") + + def date(): + """Return date object with same year, month and day.""" + + def time(): + """Return time object with same hour, minute, second, microsecond. + + tzinfo is None. See also method timetz(). + """ + + def timetz(): + """Return time object with same hour, minute, second, microsecond, + and tzinfo. + + See also method time(). + """ + + def replace(year, month, day, hour, minute, second, microsecond, tzinfo): + """Return a datetime with the same members, except for those members + given new values by whichever keyword arguments are specified. + + Note that tzinfo=None can be specified to create a naive datetime from + an aware datetime with no conversion of date and time members. + """ + + def astimezone(tz): + """Return a datetime object with new tzinfo member tz, adjusting the + date and time members so the result is the same UTC time as self, but + in tz's local time. + + tz must be an instance of a tzinfo subclass, and its utcoffset() and + dst() methods must not return None. self must be aware (self.tzinfo + must not be None, and self.utcoffset() must not return None). + + If self.tzinfo is tz, self.astimezone(tz) is equal to self: no + adjustment of date or time members is performed. Else the result is + local time in time zone tz, representing the same UTC time as self: + after astz = dt.astimezone(tz), astz - astz.utcoffset() + will usually have the same date and time members as dt - dt.utcoffset(). + The discussion of class tzinfo explains the cases at Daylight Saving + Time transition boundaries where this cannot be achieved (an issue only + if tz models both standard and daylight time). + + If you merely want to attach a time zone object tz to a datetime dt + without adjustment of date and time members, use dt.replace(tzinfo=tz). + If you merely want to remove the time zone object from an aware + datetime dt without conversion of date and time members, use + dt.replace(tzinfo=None). + + Note that the default tzinfo.fromutc() method can be overridden in a + tzinfo subclass to effect the result returned by astimezone(). + """ + + def utcoffset(): + """Return the timezone offset in minutes east of UTC (negative west of + UTC).""" + + def dst(): + """Return 0 if DST is not in effect, or the DST offset (in minutes + eastward) if DST is in effect. + """ + + def tzname(): + """Return the timezone name.""" + + def timetuple(): + """Return a 9-element tuple of the form returned by time.localtime().""" + + def utctimetuple(): + """Return UTC time tuple compatilble with time.gmtimr().""" + + def toordinal(): + """Return the proleptic Gregorian ordinal of the date. + + The same as self.date().toordinal(). + """ + + def weekday(): + """Return the day of the week as an integer. + + Monday is 0 and Sunday is 6. The same as self.date().weekday(). + See also isoweekday(). + """ + + def isoweekday(): + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. The same as self.date().isoweekday. + See also weekday(), isocalendar(). + """ + + def isocalendar(): + """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + + The same as self.date().isocalendar(). + """ + + def isoformat(sep='T'): + """Return a string representing the date and time in ISO 8601 format. + + YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0 + + If utcoffset() does not return None, a 6-character string is appended, + giving the UTC offset in (signed) hours and minutes: + + YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM + if microsecond is 0. + + The optional argument sep (default 'T') is a one-character separator, + placed between the date and time portions of the result. + """ + + def __str__(): + """For a datetime instance d, str(d) is equivalent to d.isoformat(' '). + """ + + def ctime(): + """Return a string representing the date and time. + + datetime(2002, 12, 4, 20, 30, 40).ctime() == 'Wed Dec 4 20:30:40 2002'. + d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) on + platforms where the native C ctime() function (which time.ctime() + invokes, but which datetime.ctime() does not invoke) conforms to the + C standard. + """ + + def strftime(format): + """Return a string representing the date and time. + + This is controlled by an explicit format string. + """ + + +class ITimeClass(Interface): + """This is the time class interface.""" + + min = Attribute("The earliest representable time") + + max = Attribute("The latest representable time") + + resolution = Attribute( + "The smallest possible difference between non-equal time objects") + + +class ITime(ITimeClass): + """Represent time with time zone. + + Operators: + + __repr__, __str__ + __cmp__, __hash__ + """ + + hour = Attribute("Hour in range(24)") + + minute = Attribute("Minute in range(60)") + + second = Attribute("Second in range(60)") + + microsecond = Attribute("Microsecond in range(1000000)") + + tzinfo = Attribute( + """The object passed as the tzinfo argument to the time constructor + or None if none was passed.""") + + def replace(hour, minute, second, microsecond, tzinfo): + """Return a time with the same value. + + Except for those members given new values by whichever keyword + arguments are specified. Note that tzinfo=None can be specified + to create a naive time from an aware time, without conversion of the + time members. + """ + + def isoformat(): + """Return a string representing the time in ISO 8601 format. + + That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS + If utcoffset() does not return None, a 6-character string is appended, + giving the UTC offset in (signed) hours and minutes: + HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM + """ + + def __str__(): + """For a time t, str(t) is equivalent to t.isoformat().""" + + def strftime(format): + """Return a string representing the time. + + This is controlled by an explicit format string. + """ + + def utcoffset(): + """Return the timezone offset in minutes east of UTC (negative west of + UTC). + + If tzinfo is None, returns None, else returns + self.tzinfo.utcoffset(None), and raises an exception if the latter + doesn't return None or a timedelta object representing a whole number + of minutes with magnitude less than one day. + """ + + def dst(): + """Return 0 if DST is not in effect, or the DST offset (in minutes + eastward) if DST is in effect. + + If tzinfo is None, returns None, else returns self.tzinfo.dst(None), + and raises an exception if the latter doesn't return None, or a + timedelta object representing a whole number of minutes with + magnitude less than one day. + """ + + def tzname(): + """Return the timezone name. + + If tzinfo is None, returns None, else returns self.tzinfo.tzname(None), + or raises an exception if the latter doesn't return None or a string + object. + """ + + +class ITZInfo(Interface): + """Time zone info class. + """ + + def utcoffset(dt): + """Return offset of local time from UTC, in minutes east of UTC. + + If local time is west of UTC, this should be negative. + Note that this is intended to be the total offset from UTC; + for example, if a tzinfo object represents both time zone and DST + adjustments, utcoffset() should return their sum. If the UTC offset + isn't known, return None. Else the value returned must be a timedelta + object specifying a whole number of minutes in the range -1439 to 1439 + inclusive (1440 = 24*60; the magnitude of the offset must be less + than one day). + """ + + def dst(dt): + """Return the daylight saving time (DST) adjustment, in minutes east + of UTC, or None if DST information isn't known. + """ + + def tzname(dt): + """Return the time zone name corresponding to the datetime object as + a string. + """ + + def fromutc(dt): + """Return an equivalent datetime in self's local time.""" + + +classImplements(timedelta, ITimeDelta) +classImplements(date, IDate) +classImplements(datetime, IDateTime) +classImplements(time, ITime) +classImplements(tzinfo, ITZInfo) + +## directlyProvides(timedelta, ITimeDeltaClass) +## directlyProvides(date, IDateClass) +## directlyProvides(datetime, IDateTimeClass) +## directlyProvides(time, ITimeClass) diff --git a/pythonforumide/zope/interface/common/interfaces.py b/pythonforumide/zope/interface/common/interfaces.py new file mode 100644 index 0000000..47e9de7 --- /dev/null +++ b/pythonforumide/zope/interface/common/interfaces.py @@ -0,0 +1,102 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Interfaces for standard python exceptions +""" +from zope.interface import Interface +from zope.interface import classImplements + +class IException(Interface): pass +class IStandardError(IException): pass +class IWarning(IException): pass +class ISyntaxError(IStandardError): pass +class ILookupError(IStandardError): pass +class IValueError(IStandardError): pass +class IRuntimeError(IStandardError): pass +class IArithmeticError(IStandardError): pass +class IAssertionError(IStandardError): pass +class IAttributeError(IStandardError): pass +class IDeprecationWarning(IWarning): pass +class IEOFError(IStandardError): pass +class IEnvironmentError(IStandardError): pass +class IFloatingPointError(IArithmeticError): pass +class IIOError(IEnvironmentError): pass +class IImportError(IStandardError): pass +class IIndentationError(ISyntaxError): pass +class IIndexError(ILookupError): pass +class IKeyError(ILookupError): pass +class IKeyboardInterrupt(IStandardError): pass +class IMemoryError(IStandardError): pass +class INameError(IStandardError): pass +class INotImplementedError(IRuntimeError): pass +class IOSError(IEnvironmentError): pass +class IOverflowError(IArithmeticError): pass +class IOverflowWarning(IWarning): pass +class IReferenceError(IStandardError): pass +class IRuntimeWarning(IWarning): pass +class IStopIteration(IException): pass +class ISyntaxWarning(IWarning): pass +class ISystemError(IStandardError): pass +class ISystemExit(IException): pass +class ITabError(IIndentationError): pass +class ITypeError(IStandardError): pass +class IUnboundLocalError(INameError): pass +class IUnicodeError(IValueError): pass +class IUserWarning(IWarning): pass +class IZeroDivisionError(IArithmeticError): pass + +classImplements(ArithmeticError, IArithmeticError) +classImplements(AssertionError, IAssertionError) +classImplements(AttributeError, IAttributeError) +classImplements(DeprecationWarning, IDeprecationWarning) +classImplements(EnvironmentError, IEnvironmentError) +classImplements(EOFError, IEOFError) +classImplements(Exception, IException) +classImplements(FloatingPointError, IFloatingPointError) +classImplements(ImportError, IImportError) +classImplements(IndentationError, IIndentationError) +classImplements(IndexError, IIndexError) +classImplements(IOError, IIOError) +classImplements(KeyboardInterrupt, IKeyboardInterrupt) +classImplements(KeyError, IKeyError) +classImplements(LookupError, ILookupError) +classImplements(MemoryError, IMemoryError) +classImplements(NameError, INameError) +classImplements(NotImplementedError, INotImplementedError) +classImplements(OSError, IOSError) +classImplements(OverflowError, IOverflowError) +try: + classImplements(OverflowWarning, IOverflowWarning) +except NameError: + pass # OverflowWarning was removed in Python 2.5 +classImplements(ReferenceError, IReferenceError) +classImplements(RuntimeError, IRuntimeError) +classImplements(RuntimeWarning, IRuntimeWarning) +try: + classImplements(StandardError, IStandardError) +except NameError: + pass # StandardError does not exist in Python 3 +classImplements(StopIteration, IStopIteration) +classImplements(SyntaxError, ISyntaxError) +classImplements(SyntaxWarning, ISyntaxWarning) +classImplements(SystemError, ISystemError) +classImplements(SystemExit, ISystemExit) +classImplements(TabError, ITabError) +classImplements(TypeError, ITypeError) +classImplements(UnboundLocalError, IUnboundLocalError) +classImplements(UnicodeError, IUnicodeError) +classImplements(UserWarning, IUserWarning) +classImplements(ValueError, IValueError) +classImplements(Warning, IWarning) +classImplements(ZeroDivisionError, IZeroDivisionError) + diff --git a/pythonforumide/zope/interface/common/mapping.py b/pythonforumide/zope/interface/common/mapping.py new file mode 100644 index 0000000..139715f --- /dev/null +++ b/pythonforumide/zope/interface/common/mapping.py @@ -0,0 +1,125 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Mapping Interfaces +""" +from zope.interface import Interface + +class IItemMapping(Interface): + """Simplest readable mapping object + """ + + def __getitem__(key): + """Get a value for a key + + A KeyError is raised if there is no value for the key. + """ + + +class IReadMapping(IItemMapping): + """Basic mapping interface + """ + + def get(key, default=None): + """Get a value for a key + + The default is returned if there is no value for the key. + """ + + def __contains__(key): + """Tell if a key exists in the mapping.""" + + +class IWriteMapping(Interface): + """Mapping methods for changing data""" + + def __delitem__(key): + """Delete a value from the mapping using the key.""" + + def __setitem__(key, value): + """Set a new item in the mapping.""" + + +class IEnumerableMapping(IReadMapping): + """Mapping objects whose items can be enumerated. + """ + + def keys(): + """Return the keys of the mapping object. + """ + + def __iter__(): + """Return an iterator for the keys of the mapping object. + """ + + def values(): + """Return the values of the mapping object. + """ + + def items(): + """Return the items of the mapping object. + """ + + def __len__(): + """Return the number of items. + """ + +class IMapping(IWriteMapping, IEnumerableMapping): + ''' Simple mapping interface ''' + +class IIterableMapping(IEnumerableMapping): + + def iterkeys(): + "iterate over keys; equivalent to __iter__" + + def itervalues(): + "iterate over values" + + def iteritems(): + "iterate over items" + +class IClonableMapping(Interface): + + def copy(): + "return copy of dict" + +class IExtendedReadMapping(IIterableMapping): + + def has_key(key): + """Tell if a key exists in the mapping; equivalent to __contains__""" + +class IExtendedWriteMapping(IWriteMapping): + + def clear(): + "delete all items" + + def update(d): + " Update D from E: for k in E.keys(): D[k] = E[k]" + + def setdefault(key, default=None): + "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" + + def pop(k, *args): + """remove specified key and return the corresponding value + *args may contain a single default value, or may not be supplied. + If key is not found, default is returned if given, otherwise + KeyError is raised""" + + def popitem(): + """remove and return some (key, value) pair as a + 2-tuple; but raise KeyError if mapping is empty""" + +class IFullMapping( + IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping): + ''' Full mapping interface ''' # IMapping included so tests for IMapping + # succeed with IFullMapping diff --git a/pythonforumide/zope/interface/common/sequence.py b/pythonforumide/zope/interface/common/sequence.py new file mode 100644 index 0000000..223a94e --- /dev/null +++ b/pythonforumide/zope/interface/common/sequence.py @@ -0,0 +1,160 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Sequence Interfaces +""" +__docformat__ = 'restructuredtext' +from zope import interface + +class IMinimalSequence(interface.Interface): + """Most basic sequence interface. + + All sequences are iterable. This requires at least one of the + following: + + - a `__getitem__()` method that takes a single argument; interger + values starting at 0 must be supported, and `IndexError` should + be raised for the first index for which there is no value, or + + - an `__iter__()` method that returns an iterator as defined in + the Python documentation (http://docs.python.org/lib/typeiter.html). + + """ + + def __getitem__(index): + """`x.__getitem__(index)` <==> `x[index]` + + Declaring this interface does not specify whether `__getitem__` + supports slice objects.""" + +class IFiniteSequence(IMinimalSequence): + + def __len__(): + """`x.__len__()` <==> `len(x)`""" + +class IReadSequence(IFiniteSequence): + """read interface shared by tuple and list""" + + def __contains__(item): + """`x.__contains__(item)` <==> `item in x`""" + + def __lt__(other): + """`x.__lt__(other)` <==> `x < other`""" + + def __le__(other): + """`x.__le__(other)` <==> `x <= other`""" + + def __eq__(other): + """`x.__eq__(other)` <==> `x == other`""" + + def __ne__(other): + """`x.__ne__(other)` <==> `x != other`""" + + def __gt__(other): + """`x.__gt__(other)` <==> `x > other`""" + + def __ge__(other): + """`x.__ge__(other)` <==> `x >= other`""" + + def __add__(other): + """`x.__add__(other)` <==> `x + other`""" + + def __mul__(n): + """`x.__mul__(n)` <==> `x * n`""" + + def __rmul__(n): + """`x.__rmul__(n)` <==> `n * x`""" + + def __getslice__(i, j): + """`x.__getslice__(i, j)` <==> `x[i:j]` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + +class IExtendedReadSequence(IReadSequence): + """Full read interface for lists""" + + def count(item): + """Return number of occurrences of value""" + + def index(item, *args): + """Return first index of value + + `L.index(value, [start, [stop]])` -> integer""" + +class IUniqueMemberWriteSequence(interface.Interface): + """The write contract for a sequence that may enforce unique members""" + + def __setitem__(index, item): + """`x.__setitem__(index, item)` <==> `x[index] = item` + + Declaring this interface does not specify whether `__setitem__` + supports slice objects. + """ + + def __delitem__(index): + """`x.__delitem__(index)` <==> `del x[index]` + + Declaring this interface does not specify whether `__delitem__` + supports slice objects. + """ + + def __setslice__(i, j, other): + """`x.__setslice__(i, j, other)` <==> `x[i:j]=other` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + + def __delslice__(i, j): + """`x.__delslice__(i, j)` <==> `del x[i:j]` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + def __iadd__(y): + """`x.__iadd__(y)` <==> `x += y`""" + + def append(item): + """Append item to end""" + + def insert(index, item): + """Insert item before index""" + + def pop(index=-1): + """Remove and return item at index (default last)""" + + def remove(item): + """Remove first occurrence of value""" + + def reverse(): + """Reverse *IN PLACE*""" + + def sort(cmpfunc=None): + """Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1""" + + def extend(iterable): + """Extend list by appending elements from the iterable""" + +class IWriteSequence(IUniqueMemberWriteSequence): + """Full write contract for sequences""" + + def __imul__(n): + """`x.__imul__(n)` <==> `x *= n`""" + +class ISequence(IReadSequence, IWriteSequence): + """Full sequence contract""" diff --git a/pythonforumide/zope/interface/declarations.py b/pythonforumide/zope/interface/declarations.py new file mode 100644 index 0000000..3ee27c4 --- /dev/null +++ b/pythonforumide/zope/interface/declarations.py @@ -0,0 +1,1395 @@ +############################################################################## +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +############################################################################## +"""Implementation of interface declarations + +There are three flavors of declarations: + + - Declarations are used to simply name declared interfaces. + + - ImplementsDeclarations are used to express the interfaces that a + class implements (that instances of the class provides). + + Implements specifications support inheriting interfaces. + + - ProvidesDeclarations are used to express interfaces directly + provided by objects. + +""" +__docformat__ = 'restructuredtext' + +import sys +import weakref +from zope.interface.interface import InterfaceClass, Specification +from zope.interface.interface import SpecificationBase +from types import ModuleType, MethodType, FunctionType +from zope.interface.advice import addClassAdvisor + +# Registry of class-implementation specifications +BuiltinImplementationSpecifications = {} + +class Declaration(Specification): + """Interface declarations""" + + def __init__(self, *interfaces): + Specification.__init__(self, _normalizeargs(interfaces)) + + def changed(self, originally_changed): + Specification.changed(self, originally_changed) + try: + del self._v_attrs + except AttributeError: + pass + + def __contains__(self, interface): + """Test whether an interface is in the specification + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration(I2, I3) + >>> spec = Declaration(I4, spec) + >>> int(I1 in spec) + 0 + >>> int(I2 in spec) + 1 + >>> int(I3 in spec) + 1 + >>> int(I4 in spec) + 1 + """ + return self.extends(interface) and interface in self.interfaces() + + def __iter__(self): + """Return an iterator for the interfaces in the specification + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration(I2, I3) + >>> spec = Declaration(I4, spec) + >>> i = iter(spec) + >>> [x.getName() for x in i] + ['I4', 'I2', 'I3'] + >>> list(i) + [] + """ + return self.interfaces() + + def flattened(self): + """Return an iterator of all included and extended interfaces + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration(I2, I3) + >>> spec = Declaration(I4, spec) + >>> i = spec.flattened() + >>> [x.getName() for x in i] + ['I4', 'I2', 'I1', 'I3', 'Interface'] + >>> list(i) + [] + + """ + return iter(self.__iro__) + + def __sub__(self, other): + """Remove interfaces from a specification + + Examples: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> [iface.getName() for iface in spec] + [] + >>> spec -= I1 + >>> [iface.getName() for iface in spec] + [] + >>> spec -= Declaration(I1, I2) + >>> [iface.getName() for iface in spec] + [] + >>> spec = Declaration(I2, I4) + >>> [iface.getName() for iface in spec] + ['I2', 'I4'] + >>> [iface.getName() for iface in spec - I4] + ['I2'] + >>> [iface.getName() for iface in spec - I1] + ['I4'] + >>> [iface.getName() for iface + ... in spec - Declaration(I3, I4)] + ['I2'] + + """ + + return Declaration( + *[i for i in self.interfaces() + if not [j for j in other.interfaces() + if i.extends(j, 0)] + ] + ) + + def __add__(self, other): + """Add two specifications or a specification and an interface + + Examples: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> [iface.getName() for iface in spec] + [] + >>> [iface.getName() for iface in spec+I1] + ['I1'] + >>> [iface.getName() for iface in I1+spec] + ['I1'] + >>> spec2 = spec + >>> spec += I1 + >>> [iface.getName() for iface in spec] + ['I1'] + >>> [iface.getName() for iface in spec2] + [] + >>> spec2 += Declaration(I3, I4) + >>> [iface.getName() for iface in spec2] + ['I3', 'I4'] + >>> [iface.getName() for iface in spec+spec2] + ['I1', 'I3', 'I4'] + >>> [iface.getName() for iface in spec2+spec] + ['I3', 'I4', 'I1'] + + """ + + seen = {} + result = [] + for i in self.interfaces(): + if i not in seen: + seen[i] = 1 + result.append(i) + for i in other.interfaces(): + if i not in seen: + seen[i] = 1 + result.append(i) + + return Declaration(*result) + + __radd__ = __add__ + + +############################################################################## +# +# Implementation specifications +# +# These specify interfaces implemented by instances of classes + +class Implements(Declaration): + + # class whose specification should be used as additional base + inherit = None + + # interfaces actually declared for a class + declared = () + + __name__ = '?' + + def __repr__(self): + return '' % (self.__name__) + + def __reduce__(self): + return implementedBy, (self.inherit, ) + +def implementedByFallback(cls): + """Return the interfaces implemented for a class' instances + + The value returned is an IDeclaration. + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> class C1(object): + ... implements(I2) + >>> class C2(C1): + ... implements(I3) + >>> [i.getName() for i in implementedBy(C2)] + ['I3', 'I2'] + + Really, any object should be able to receive a successful answer, even + an instance: + + >>> class Callable(object): + ... def __call__(self): + ... return self + + >>> implementedBy(Callable()) + + + Note that the name of the spec ends with a '?', because the `Callable` + instance does not have a `__name__` attribute. + """ + # This also manages storage of implementation specifications + + try: + spec = cls.__dict__.get('__implemented__') + except AttributeError: + + # we can't get the class dict. This is probably due to a + # security proxy. If this is the case, then probably no + # descriptor was installed for the class. + + # We don't want to depend directly on zope.security in + # zope.interface, but we'll try to make reasonable + # accommodations in an indirect way. + + # We'll check to see if there's an implements: + + spec = getattr(cls, '__implemented__', None) + if spec is None: + # There's no spec stred in the class. Maybe its a builtin: + spec = BuiltinImplementationSpecifications.get(cls) + if spec is not None: + return spec + return _empty + + if spec.__class__ == Implements: + # we defaulted to _empty or there was a spec. Good enough. + # Return it. + return spec + + # TODO: need old style __implements__ compatibility? + # Hm, there's an __implemented__, but it's not a spec. Must be + # an old-style declaration. Just compute a spec for it + return Declaration(*_normalizeargs((spec, ))) + + if isinstance(spec, Implements): + return spec + + if spec is None: + spec = BuiltinImplementationSpecifications.get(cls) + if spec is not None: + return spec + + # TODO: need old style __implements__ compatibility? + if spec is not None: + # old-style __implemented__ = foo declaration + spec = (spec, ) # tuplefy, as it might be just an int + spec = Implements(*_normalizeargs(spec)) + spec.inherit = None # old-style implies no inherit + del cls.__implemented__ # get rid of the old-style declaration + else: + try: + bases = cls.__bases__ + except AttributeError: + if not callable(cls): + raise TypeError("ImplementedBy called for non-factory", cls) + bases = () + + spec = Implements(*[implementedBy(c) for c in bases]) + spec.inherit = cls + + spec.__name__ = (getattr(cls, '__module__', '?') or '?') + \ + '.' + (getattr(cls, '__name__', '?') or '?') + + try: + cls.__implemented__ = spec + if not hasattr(cls, '__providedBy__'): + cls.__providedBy__ = objectSpecificationDescriptor + + if (isinstance(cls, DescriptorAwareMetaClasses) + and + '__provides__' not in cls.__dict__): + # Make sure we get a __provides__ descriptor + cls.__provides__ = ClassProvides( + cls, + getattr(cls, '__class__', type(cls)), + ) + + except TypeError: + if not isinstance(cls, type): + raise TypeError("ImplementedBy called for non-type", cls) + BuiltinImplementationSpecifications[cls] = spec + + return spec + +implementedBy = implementedByFallback + +def classImplementsOnly(cls, *interfaces): + """Declare the only interfaces implemented by instances of a class + + The arguments after the class are one or more interfaces or interface + specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + replace any previous declarations. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(Interface): pass + ... + >>> class A(object): + ... implements(I3) + >>> class B(object): + ... implements(I4) + >>> class C(A, B): + ... pass + >>> classImplementsOnly(C, I1, I2) + >>> [i.getName() for i in implementedBy(C)] + ['I1', 'I2'] + + Instances of ``C`` provide only ``I1``, ``I2``, and regardless of + whatever interfaces instances of ``A`` and ``B`` implement. + """ + spec = implementedBy(cls) + spec.declared = () + spec.inherit = None + classImplements(cls, *interfaces) + +def classImplements(cls, *interfaces): + """Declare additional interfaces implemented for instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + are added to any interfaces previously declared. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(Interface): pass + ... + >>> class I5(Interface): pass + ... + >>> class A(object): + ... implements(I3) + >>> class B(object): + ... implements(I4) + >>> class C(A, B): + ... pass + >>> classImplements(C, I1, I2) + >>> [i.getName() for i in implementedBy(C)] + ['I1', 'I2', 'I3', 'I4'] + >>> classImplements(C, I5) + >>> [i.getName() for i in implementedBy(C)] + ['I1', 'I2', 'I5', 'I3', 'I4'] + + Instances of ``C`` provide ``I1``, ``I2``, ``I5``, and whatever + interfaces instances of ``A`` and ``B`` provide. + """ + + spec = implementedBy(cls) + spec.declared += tuple(_normalizeargs(interfaces)) + + # compute the bases + bases = [] + seen = {} + for b in spec.declared: + if b not in seen: + seen[b] = 1 + bases.append(b) + + if spec.inherit is not None: + + for c in spec.inherit.__bases__: + b = implementedBy(c) + if b not in seen: + seen[b] = 1 + bases.append(b) + + spec.__bases__ = tuple(bases) + +def _implements_advice(cls): + interfaces, classImplements = cls.__dict__['__implements_advice_data__'] + del cls.__implements_advice_data__ + classImplements(cls, *interfaces) + return cls + + +class implementer: + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, DescriptorAwareMetaClasses): + classImplements(ob, *self.interfaces) + return ob + + spec = Implements(*self.interfaces) + try: + ob.__implemented__ = spec + except AttributeError: + raise TypeError("Can't declare implements", ob) + return ob + +class implementer_only: + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, (FunctionType, MethodType)): + # XXX Does this decorator make sense for anything but classes? + # I don't think so. There can be no inheritance of interfaces + # on a method pr function.... + raise ValueError('The implementor_only decorator is not ' + 'supported for methods or functions.') + else: + # Assume it's a class: + classImplementsOnly(ob, *self.interfaces) + return ob + +def _implements(name, interfaces, classImplements): + frame = sys._getframe(2) + locals = frame.f_locals + + # Try to make sure we were called from a class def. In 2.2.0 we can't + # check for __module__ since it doesn't seem to be added to the locals + # until later on. + if (locals is frame.f_globals) or ( + ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)): + raise TypeError(name+" can be used only from a class definition.") + + if '__implements_advice_data__' in locals: + raise TypeError(name+" can be used only once in a class definition.") + + locals['__implements_advice_data__'] = interfaces, classImplements + addClassAdvisor(_implements_advice, depth=3) + +def implements(*interfaces): + """Declare interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Previous declarations include declarations for base classes + unless implementsOnly was used. + + This function is provided for convenience. It provides a more + convenient way to call classImplements. For example:: + + implements(I1) + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + + Consider the following example:: + + + >>> from zope.interface import Interface + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implements(IC) + + >>> ob = C() + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` implement. + + """ + _implements("implements", interfaces, classImplements) + +def implementsOnly(*interfaces): + """Declare the only interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + Previous declarations including declarations for base classes + are overridden. + + This function is provided for convenience. It provides a more + convenient way to call classImplementsOnly. For example:: + + implementsOnly(I1) + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + + Consider the following example:: + + >>> from zope.interface import Interface + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implementsOnly(IC) + + >>> ob = C() + >>> int(IA1 in providedBy(ob)) + 0 + >>> int(IA2 in providedBy(ob)) + 0 + >>> int(IB in providedBy(ob)) + 0 + >>> int(IC in providedBy(ob)) + 1 + + + Instances of ``C`` implement ``IC``, regardless of what + instances of ``A`` and ``B`` implement. + + """ + _implements("implementsOnly", interfaces, classImplementsOnly) + +############################################################################## +# +# Instance declarations + +class Provides(Declaration): # Really named ProvidesClass + """Implement __provides__, the instance-specific specification + + When an object is pickled, we pickle the interfaces that it implements. + """ + + def __init__(self, cls, *interfaces): + self.__args = (cls, ) + interfaces + self._cls = cls + Declaration.__init__(self, *(interfaces + (implementedBy(cls), ))) + + def __reduce__(self): + return Provides, self.__args + + __module__ = 'zope.interface' + + def __get__(self, inst, cls): + """Make sure that a class __provides__ doesn't leak to an instance + + For example: + + >>> from zope.interface import Interface + >>> class IFooFactory(Interface): pass + ... + + >>> class C(object): + ... pass + + >>> C.__provides__ = ProvidesClass(C, IFooFactory) + >>> [i.getName() for i in C.__provides__] + ['IFooFactory'] + >>> getattr(C(), '__provides__', 0) + 0 + + """ + if inst is None and cls is self._cls: + # We were accessed through a class, so we are the class' + # provides spec. Just return this object, but only if we are + # being called on the same class that we were defined for: + return self + + raise AttributeError('__provides__') + +ProvidesClass = Provides + +# Registry of instance declarations +# This is a memory optimization to allow objects to share specifications. +InstanceDeclarations = weakref.WeakValueDictionary() + +def Provides(*interfaces): + """Cache instance declarations + + Instance declarations are shared among instances that have the same + declaration. The declarations are cached in a weak value dictionary. + + (Note that, in the examples below, we are going to make assertions about + the size of the weakvalue dictionary. For the assertions to be + meaningful, we need to force garbage collection to make sure garbage + objects are, indeed, removed from the system. Depending on how Python + is run, we may need to make multiple calls to be sure. We provide a + collect function to help with this: + + >>> import gc + >>> def collect(): + ... for i in range(4): + ... gc.collect() + + ) + + >>> collect() + >>> before = len(InstanceDeclarations) + + >>> class C(object): + ... pass + + >>> from zope.interface import Interface + >>> class I(Interface): + ... pass + + >>> c1 = C() + >>> c2 = C() + + >>> len(InstanceDeclarations) == before + 1 + + >>> directlyProvides(c1, I) + >>> len(InstanceDeclarations) == before + 1 + 1 + + >>> directlyProvides(c2, I) + >>> len(InstanceDeclarations) == before + 1 + 1 + + >>> del c1 + >>> collect() + >>> len(InstanceDeclarations) == before + 1 + 1 + + >>> del c2 + >>> collect() + >>> len(InstanceDeclarations) == before + 1 + """ + + spec = InstanceDeclarations.get(interfaces) + if spec is None: + spec = ProvidesClass(*interfaces) + InstanceDeclarations[interfaces] = spec + + return spec +Provides.__safe_for_unpickling__ = True + +try: + from types import ClassType + DescriptorAwareMetaClasses = ClassType, type +except ImportError: # Python 3 + DescriptorAwareMetaClasses = (type,) + +def directlyProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + replace interfaces previously declared for the object. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implements(IC) + + >>> ob = C() + >>> directlyProvides(ob, I1, I2) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 1 + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. + + To remove directly provided interfaces, use ``directlyProvidedBy`` and + subtract the unwanted interfaces. For example: + + >>> directlyProvides(ob, directlyProvidedBy(ob)-I2) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 0 + + removes I2 from the interfaces directly provided by ``ob``. The object, + ``ob`` no longer directly provides ``I2``, although it might still + provide ``I2`` if it's class implements ``I2``. + + To add directly provided interfaces, use ``directlyProvidedBy`` and + include additional interfaces. For example: + + >>> int(I2 in providedBy(ob)) + 0 + >>> directlyProvides(ob, directlyProvidedBy(ob), I2) + + adds ``I2`` to the interfaces directly provided by ob:: + + >>> int(I2 in providedBy(ob)) + 1 + + """ + + # We need to avoid setting this attribute on meta classes that + # don't support descriptors. + # We can do away with this check when we get rid of the old EC + cls = getattr(object, '__class__', None) + if cls is not None and getattr(cls, '__class__', None) is cls: + # It's a meta class (well, at least it it could be an extension class) + if not isinstance(object, DescriptorAwareMetaClasses): + raise TypeError("Attempt to make an interface declaration on a " + "non-descriptor-aware class") + + interfaces = _normalizeargs(interfaces) + if cls is None: + cls = type(object) + + issub = False + for damc in DescriptorAwareMetaClasses: + if issubclass(cls, damc): + issub = True + break + if issub: + # we have a class or type. We'll use a special descriptor + # that provides some extra caching + object.__provides__ = ClassProvides(object, cls, *interfaces) + else: + object.__provides__ = Provides(cls, *interfaces) + + +def alsoProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) are + added to the interfaces previously declared for the object. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implements(IC) + + >>> ob = C() + >>> directlyProvides(ob, I1) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 0 + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + >>> alsoProvides(ob, I2) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 1 + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. Notice that the + alsoProvides just extends the provided interfaces. + """ + directlyProvides(object, directlyProvidedBy(object), *interfaces) + +def noLongerProvides(object, interface): + """ + This removes a directly provided interface from an object. + Consider the following two interfaces: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + + ``I1`` is provided through the class, ``I2`` is directly provided + by the object: + + >>> class C(object): + ... implements(I1) + >>> c = C() + >>> alsoProvides(c, I2) + >>> I2.providedBy(c) + True + + Remove I2 from c again: + + >>> noLongerProvides(c, I2) + >>> I2.providedBy(c) + False + + Removing an interface that is provided through the class is not possible: + + >>> noLongerProvides(c, I1) + Traceback (most recent call last): + ... + ValueError: Can only remove directly provided interfaces. + + """ + directlyProvides(object, directlyProvidedBy(object)-interface) + if interface.providedBy(object): + raise ValueError("Can only remove directly provided interfaces.") + +class ClassProvidesBasePy(object): + + def __get__(self, inst, cls): + if cls is self._cls: + # We only work if called on the class we were defined for + + if inst is None: + # We were accessed through a class, so we are the class' + # provides spec. Just return this object as is: + return self + + return self._implements + + raise AttributeError('__provides__') + +ClassProvidesBase = ClassProvidesBasePy + +# Try to get C base: +try: + import _zope_interface_coptimizations +except ImportError: + pass +else: + from _zope_interface_coptimizations import ClassProvidesBase + + +class ClassProvides(Declaration, ClassProvidesBase): + """Special descriptor for class __provides__ + + The descriptor caches the implementedBy info, so that + we can get declarations for objects without instance-specific + interfaces a bit quicker. + + For example: + + >>> from zope.interface import Interface + >>> class IFooFactory(Interface): + ... pass + >>> class IFoo(Interface): + ... pass + >>> class C(object): + ... implements(IFoo) + ... classProvides(IFooFactory) + >>> [i.getName() for i in C.__provides__] + ['IFooFactory'] + + >>> [i.getName() for i in C().__provides__] + ['IFoo'] + """ + + def __init__(self, cls, metacls, *interfaces): + self._cls = cls + self._implements = implementedBy(cls) + self.__args = (cls, metacls, ) + interfaces + Declaration.__init__(self, *(interfaces + (implementedBy(metacls), ))) + + def __reduce__(self): + return self.__class__, self.__args + + # Copy base-class method for speed + __get__ = ClassProvidesBase.__get__ + +def directlyProvidedBy(object): + """Return the interfaces directly provided by the given object + + The value returned is an ``IDeclaration``. + """ + provides = getattr(object, "__provides__", None) + if (provides is None # no spec + or + # We might have gotten the implements spec, as an + # optimization. If so, it's like having only one base, that we + # lop off to exclude class-supplied declarations: + isinstance(provides, Implements) + ): + return _empty + + # Strip off the class part of the spec: + return Declaration(provides.__bases__[:-1]) + +def classProvides(*interfaces): + """Declare interfaces provided directly by a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface specifications + (``IDeclaration`` objects). + + The given interfaces (including the interfaces in the specifications) + are used to create the class's direct-object interface specification. + An error will be raised if the module class has an direct interface + specification. In other words, it is an error to call this function more + than once in a class definition. + + Note that the given interfaces have nothing to do with the interfaces + implemented by instances of the class. + + This function is provided for convenience. It provides a more convenient + way to call directlyProvides for a class. For example:: + + classProvides(I1) + + is equivalent to calling:: + + directlyProvides(theclass, I1) + + after the class has been created. + + For example: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + ... + >>> class IFooFactory(Interface): pass + ... + >>> class C(object): + ... implements(IFoo) + ... classProvides(IFooFactory) + >>> [i.getName() for i in C.__providedBy__] + ['IFooFactory'] + >>> [i.getName() for i in C().__providedBy__] + ['IFoo'] + + if equivalent to: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + ... + >>> class IFooFactory(Interface): pass + ... + >>> class C(object): + ... implements(IFoo) + >>> directlyProvides(C, IFooFactory) + >>> [i.getName() for i in C.__providedBy__] + ['IFooFactory'] + >>> [i.getName() for i in C().__providedBy__] + ['IFoo'] + + """ + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def + if (locals is frame.f_globals) or ('__module__' not in locals): + raise TypeError("classProvides can be used only from a class definition.") + + if '__provides__' in locals: + raise TypeError( + "classProvides can only be used once in a class definition.") + + locals["__provides__"] = _normalizeargs(interfaces) + + addClassAdvisor(_classProvides_advice, depth=2) + +def _classProvides_advice(cls): + interfaces = cls.__dict__['__provides__'] + del cls.__provides__ + directlyProvides(cls, *interfaces) + return cls + +class provider: + """Class decorator version of classProvides""" + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + directlyProvides(ob, *self.interfaces) + return ob + +def moduleProvides(*interfaces): + """Declare interfaces provided by a module + + This function is used in a module definition. + + The arguments are one or more interfaces or interface specifications + (``IDeclaration`` objects). + + The given interfaces (including the interfaces in the specifications) are + used to create the module's direct-object interface specification. An + error will be raised if the module already has an interface specification. + In other words, it is an error to call this function more than once in a + module definition. + + This function is provided for convenience. It provides a more convenient + way to call directlyProvides. For example:: + + moduleImplements(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + """ + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def + if (locals is not frame.f_globals) or ('__name__' not in locals): + raise TypeError( + "moduleProvides can only be used from a module definition.") + + if '__provides__' in locals: + raise TypeError( + "moduleProvides can only be used once in a module definition.") + + locals["__provides__"] = Provides(ModuleType, + *_normalizeargs(interfaces)) + +############################################################################## +# +# Declaration querying support + +def ObjectSpecification(direct, cls): + """Provide object specifications + + These combine information for the object and for it's classes. + + For example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class I3(Interface): pass + ... + >>> class I31(I3): pass + ... + >>> class I4(Interface): pass + ... + >>> class I5(Interface): pass + ... + >>> class A(object): + ... implements(I1) + >>> class B(object): __implemented__ = I2 + ... + >>> class C(A, B): + ... implements(I31) + >>> c = C() + >>> directlyProvides(c, I4) + >>> [i.getName() for i in providedBy(c)] + ['I4', 'I31', 'I1', 'I2'] + >>> [i.getName() for i in providedBy(c).flattened()] + ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface'] + >>> int(I1 in providedBy(c)) + 1 + >>> int(I3 in providedBy(c)) + 0 + >>> int(providedBy(c).extends(I3)) + 1 + >>> int(providedBy(c).extends(I31)) + 1 + >>> int(providedBy(c).extends(I5)) + 0 + >>> class COnly(A, B): + ... implementsOnly(I31) + >>> class D(COnly): + ... implements(I5) + >>> c = D() + >>> directlyProvides(c, I4) + >>> [i.getName() for i in providedBy(c)] + ['I4', 'I5', 'I31'] + >>> [i.getName() for i in providedBy(c).flattened()] + ['I4', 'I5', 'I31', 'I3', 'Interface'] + >>> int(I1 in providedBy(c)) + 0 + >>> int(I3 in providedBy(c)) + 0 + >>> int(providedBy(c).extends(I3)) + 1 + >>> int(providedBy(c).extends(I1)) + 0 + >>> int(providedBy(c).extends(I31)) + 1 + >>> int(providedBy(c).extends(I5)) + 1 + """ + + return Provides(cls, direct) + +def getObjectSpecification(ob): + + provides = getattr(ob, '__provides__', None) + if provides is not None: + if isinstance(provides, SpecificationBase): + return provides + + try: + cls = ob.__class__ + except AttributeError: + # We can't get the class, so just consider provides + return _empty + + return implementedBy(cls) + +def providedBy(ob): + + # Here we have either a special object, an old-style declaration + # or a descriptor + + # Try to get __providedBy__ + try: + r = ob.__providedBy__ + except AttributeError: + # Not set yet. Fall back to lower-level thing that computes it + return getObjectSpecification(ob) + + try: + # We might have gotten a descriptor from an instance of a + # class (like an ExtensionClass) that doesn't support + # descriptors. We'll make sure we got one by trying to get + # the only attribute, which all specs have. + r.extends + + except AttributeError: + + # The object's class doesn't understand descriptors. + # Sigh. We need to get an object descriptor, but we have to be + # careful. We want to use the instance's __provides__, if + # there is one, but only if it didn't come from the class. + + try: + r = ob.__provides__ + except AttributeError: + # No __provides__, so just fall back to implementedBy + return implementedBy(ob.__class__) + + # We need to make sure we got the __provides__ from the + # instance. We'll do this by making sure we don't get the same + # thing from the class: + + try: + cp = ob.__class__.__provides__ + except AttributeError: + # The ob doesn't have a class or the class has no + # provides, assume we're done: + return r + + if r is cp: + # Oops, we got the provides from the class. This means + # the object doesn't have it's own. We should use implementedBy + return implementedBy(ob.__class__) + + return r + +class ObjectSpecificationDescriptorPy(object): + """Implement the `__providedBy__` attribute + + The `__providedBy__` attribute computes the interfaces peovided by + an object. + """ + + def __get__(self, inst, cls): + """Get an object specification for an object + + For example: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + ... + >>> class IFooFactory(Interface): pass + ... + >>> class C(object): + ... implements(IFoo) + ... classProvides(IFooFactory) + >>> [i.getName() for i in C.__providedBy__] + ['IFooFactory'] + >>> [i.getName() for i in C().__providedBy__] + ['IFoo'] + + """ + + # Get an ObjectSpecification bound to either an instance or a class, + # depending on how we were accessed. + + if inst is None: + return getObjectSpecification(cls) + + provides = getattr(inst, '__provides__', None) + if provides is not None: + return provides + + return implementedBy(cls) + +ObjectSpecificationDescriptor = ObjectSpecificationDescriptorPy + +############################################################################## + +def _normalizeargs(sequence, output = None): + """Normalize declaration arguments + + Normalization arguments might contain Declarions, tuples, or single + interfaces. + + Anything but individial interfaces or implements specs will be expanded. + """ + if output is None: + output = [] + + cls = sequence.__class__ + if InterfaceClass in cls.__mro__ or Implements in cls.__mro__: + output.append(sequence) + else: + for v in sequence: + _normalizeargs(v, output) + + return output + +_empty = Declaration() + +try: + import _zope_interface_coptimizations +except ImportError: + pass +else: + from _zope_interface_coptimizations import implementedBy, providedBy + from _zope_interface_coptimizations import getObjectSpecification + from _zope_interface_coptimizations import ObjectSpecificationDescriptor + +objectSpecificationDescriptor = ObjectSpecificationDescriptor() diff --git a/pythonforumide/zope/interface/document.py b/pythonforumide/zope/interface/document.py new file mode 100644 index 0000000..ba93bed --- /dev/null +++ b/pythonforumide/zope/interface/document.py @@ -0,0 +1,105 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +""" Pretty-Print an Interface object as structured text (Yum) + +This module provides a function, asStructuredText, for rendering an +interface as structured text. +""" +import zope.interface + +def asStructuredText(I, munge=0): + """ Output structured text format. Note, this will whack any existing + 'structured' format of the text. """ + + r = [I.getName()] + outp = r.append + level = 1 + + if I.getDoc(): + outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level)) + + bases = [base + for base in I.__bases__ + if base is not zope.interface.Interface + ] + if bases: + outp(_justify_and_indent("This interface extends:", level, munge)) + level += 1 + for b in bases: + item = "o %s" % b.getName() + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + level -= 1 + + namesAndDescriptions = I.namesAndDescriptions() + namesAndDescriptions.sort() + + outp(_justify_and_indent("Attributes:", level, munge)) + level += 1 + for name, desc in namesAndDescriptions: + if not hasattr(desc, 'getSignatureString'): # ugh... + item = "%s -- %s" % (desc.getName(), + desc.getDoc() or 'no documentation') + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + level -= 1 + + outp(_justify_and_indent("Methods:", level, munge)) + level += 1 + for name, desc in namesAndDescriptions: + if hasattr(desc, 'getSignatureString'): # ugh... + item = "%s%s -- %s" % (desc.getName(), + desc.getSignatureString(), + desc.getDoc() or 'no documentation') + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + + return "\n\n".join(r) + "\n\n" + + +def _trim_doc_string(text): + """ Trims a doc string to make it format + correctly with structured text. """ + + lines = text.replace('\r\n', '\n').split('\n') + nlines = [lines.pop(0)] + if lines: + min_indent = min([len(line) - len(line.lstrip()) + for line in lines]) + for line in lines: + nlines.append(line[min_indent:]) + + return '\n'.join(nlines) + + +def _justify_and_indent(text, level, munge=0, width=72): + """ indent and justify text, rejustify (munge) if specified """ + + indent = " " * level + + if munge: + lines = [] + line = indent + text = text.split() + + for word in text: + line = ' '.join([line, word]) + if len(line) > width: + lines.append(line) + line = indent + else: + lines.append(line) + + return '\n'.join(lines) + + else: + return indent + \ + text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent) diff --git a/pythonforumide/zope/interface/exceptions.py b/pythonforumide/zope/interface/exceptions.py new file mode 100644 index 0000000..e9a4788 --- /dev/null +++ b/pythonforumide/zope/interface/exceptions.py @@ -0,0 +1,67 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Interface-specific exceptions +""" + +class Invalid(Exception): + """A specification is violated + """ + +class DoesNotImplement(Invalid): + """ This object does not implement """ + def __init__(self, interface): + self.interface = interface + + def __str__(self): + return """An object does not implement interface %(interface)s + + """ % self.__dict__ + +class BrokenImplementation(Invalid): + """An attribute is not completely implemented. + """ + + def __init__(self, interface, name): + self.interface=interface + self.name=name + + def __str__(self): + return """An object has failed to implement interface %(interface)s + + The %(name)s attribute was not provided. + """ % self.__dict__ + +class BrokenMethodImplementation(Invalid): + """An method is not completely implemented. + """ + + def __init__(self, method, mess): + self.method=method + self.mess=mess + + def __str__(self): + return """The implementation of %(method)s violates its contract + because %(mess)s. + """ % self.__dict__ + +class InvalidInterface(Exception): + """The interface has invalid contents + """ + +class BadImplements(TypeError): + """An implementation assertion is invalid + + because it doesn't contain an interface or a sequence of valid + implementation assertions. + """ diff --git a/pythonforumide/zope/interface/interface.py b/pythonforumide/zope/interface/interface.py new file mode 100644 index 0000000..6b2d513 --- /dev/null +++ b/pythonforumide/zope/interface/interface.py @@ -0,0 +1,833 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Interface object implementation +""" +from __future__ import generators + +import sys +from types import FunctionType +import weakref + +from zope.interface.exceptions import Invalid +from zope.interface.ro import ro + + +CO_VARARGS = 4 +CO_VARKEYWORDS = 8 +TAGGED_DATA = '__interface_tagged_values__' + +_decorator_non_return = object() + +def invariant(call): + f_locals = sys._getframe(1).f_locals + tags = f_locals.setdefault(TAGGED_DATA, {}) + invariants = tags.setdefault('invariants', []) + invariants.append(call) + return _decorator_non_return + + +def taggedValue(key, value): + """Attaches a tagged value to an interface at definition time.""" + f_locals = sys._getframe(1).f_locals + tagged_values = f_locals.setdefault(TAGGED_DATA, {}) + tagged_values[key] = value + return _decorator_non_return + + +class Element(object): + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IElement) + + def __init__(self, __name__, __doc__=''): + """Create an 'attribute' description + """ + if not __doc__ and __name__.find(' ') >= 0: + __doc__ = __name__ + __name__ = None + + self.__name__=__name__ + self.__doc__=__doc__ + self.__tagged_values = {} + + def getName(self): + """ Returns the name of the object. """ + return self.__name__ + + def getDoc(self): + """ Returns the documentation for the object. """ + return self.__doc__ + + def getTaggedValue(self, tag): + """ Returns the value associated with 'tag'. """ + return self.__tagged_values[tag] + + def queryTaggedValue(self, tag, default=None): + """ Returns the value associated with 'tag'. """ + return self.__tagged_values.get(tag, default) + + def getTaggedValueTags(self): + """ Returns a list of all tags. """ + return self.__tagged_values.keys() + + def setTaggedValue(self, tag, value): + """ Associates 'value' with 'key'. """ + self.__tagged_values[tag] = value + +class SpecificationBasePy(object): + + def providedBy(self, ob): + """Is the interface implemented by an object + + >>> from zope.interface import * + >>> class I1(Interface): + ... pass + >>> class C(object): + ... implements(I1) + >>> c = C() + >>> class X(object): + ... pass + >>> x = X() + >>> I1.providedBy(x) + False + >>> I1.providedBy(C) + False + >>> I1.providedBy(c) + True + >>> directlyProvides(x, I1) + >>> I1.providedBy(x) + True + >>> directlyProvides(C, I1) + >>> I1.providedBy(C) + True + + """ + spec = providedBy(ob) + return self in spec._implied + + def implementedBy(self, cls): + """Test whether the specification is implemented by a class or factory. + Raise TypeError if argument is neither a class nor a callable.""" + spec = implementedBy(cls) + return self in spec._implied + + def isOrExtends(self, interface): + """Is the interface the same as or extend the given interface + + Examples:: + + >>> from zope.interface import Interface + >>> from zope.interface.declarations import Declaration + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> int(spec.extends(Interface)) + 1 + >>> spec = Declaration(I2) + >>> int(spec.extends(Interface)) + 1 + >>> int(spec.extends(I1)) + 1 + >>> int(spec.extends(I2)) + 1 + >>> int(spec.extends(I3)) + 0 + >>> int(spec.extends(I4)) + 0 + + """ + return interface in self._implied + + __call__ = isOrExtends + +SpecificationBase = SpecificationBasePy +try: + from _zope_interface_coptimizations import SpecificationBase +except ImportError: + pass + +_marker = object() +class InterfaceBasePy(object): + """Base class that wants to be replaced with a C base :) + """ + + def __call__(self, obj, alternate=_marker): + """Adapt an object to the interface + """ + conform = getattr(obj, '__conform__', None) + if conform is not None: + adapter = self._call_conform(conform) + if adapter is not None: + return adapter + + adapter = self.__adapt__(obj) + + if adapter is not None: + return adapter + elif alternate is not _marker: + return alternate + else: + raise TypeError("Could not adapt", obj, self) + + def __adapt__(self, obj): + """Adapt an object to the reciever + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + +InterfaceBase = InterfaceBasePy +try: + from _zope_interface_coptimizations import InterfaceBase +except ImportError: + pass + + +adapter_hooks = [] +try: + from _zope_interface_coptimizations import adapter_hooks +except ImportError: + pass + + +class Specification(SpecificationBase): + """Specifications + + An interface specification is used to track interface declarations + and component registrations. + + This class is a base class for both interfaces themselves and for + interface specifications (declarations). + + Specifications are mutable. If you reassign their cases, their + relations with other specifications are adjusted accordingly. + + For example: + + >>> from zope.interface import Interface + >>> class I1(Interface): + ... pass + >>> class I2(I1): + ... pass + >>> class I3(I2): + ... pass + + >>> [i.__name__ for i in I1.__bases__] + ['Interface'] + + >>> [i.__name__ for i in I2.__bases__] + ['I1'] + + >>> I3.extends(I1) + 1 + + >>> I2.__bases__ = (Interface, ) + + >>> [i.__name__ for i in I2.__bases__] + ['Interface'] + + >>> I3.extends(I1) + 0 + + """ + + # Copy some base class methods for speed + isOrExtends = SpecificationBase.isOrExtends + providedBy = SpecificationBase.providedBy + + def __init__(self, bases=()): + self._implied = {} + self.dependents = weakref.WeakKeyDictionary() + self.__bases__ = tuple(bases) + + def subscribe(self, dependent): + self.dependents[dependent] = self.dependents.get(dependent, 0) + 1 + + def unsubscribe(self, dependent): + n = self.dependents.get(dependent, 0) - 1 + if not n: + del self.dependents[dependent] + elif n > 0: + self.dependents[dependent] = n + else: + raise KeyError(dependent) + + def __setBases(self, bases): + # Register ourselves as a dependent of our old bases + for b in self.__bases__: + b.unsubscribe(self) + + # Register ourselves as a dependent of our bases + self.__dict__['__bases__'] = bases + for b in bases: + b.subscribe(self) + + self.changed(self) + + __bases__ = property( + + lambda self: self.__dict__.get('__bases__', ()), + __setBases, + ) + + def changed(self, originally_changed): + """We, or something we depend on, have changed + """ + try: + del self._v_attrs + except AttributeError: + pass + + implied = self._implied + implied.clear() + + ancestors = ro(self) + + try: + if Interface not in ancestors: + ancestors.append(Interface) + except NameError: + pass # defining Interface itself + + self.__sro__ = tuple(ancestors) + self.__iro__ = tuple([ancestor for ancestor in ancestors + if isinstance(ancestor, InterfaceClass) + ]) + + for ancestor in ancestors: + # We directly imply our ancestors: + implied[ancestor] = () + + # Now, advise our dependents of change: + for dependent in self.dependents.keys(): + dependent.changed(originally_changed) + + + def interfaces(self): + """Return an iterator for the interfaces in the specification + + for example:: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Specification((I2, I3)) + >>> spec = Specification((I4, spec)) + >>> i = spec.interfaces() + >>> [x.getName() for x in i] + ['I4', 'I2', 'I3'] + >>> list(i) + [] + """ + seen = {} + for base in self.__bases__: + for interface in base.interfaces(): + if interface not in seen: + seen[interface] = 1 + yield interface + + + def extends(self, interface, strict=True): + """Does the specification extend the given interface? + + Test whether an interface in the specification extends the + given interface + + Examples:: + + >>> from zope.interface import Interface + >>> from zope.interface.declarations import Declaration + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> int(spec.extends(Interface)) + 1 + >>> spec = Declaration(I2) + >>> int(spec.extends(Interface)) + 1 + >>> int(spec.extends(I1)) + 1 + >>> int(spec.extends(I2)) + 1 + >>> int(spec.extends(I3)) + 0 + >>> int(spec.extends(I4)) + 0 + >>> I2.extends(I2) + 0 + >>> I2.extends(I2, False) + 1 + >>> I2.extends(I2, strict=False) + 1 + + """ + return ((interface in self._implied) + and + ((not strict) or (self != interface)) + ) + + def weakref(self, callback=None): + return weakref.ref(self, callback) + + def get(self, name, default=None): + """Query for an attribute description + """ + try: + attrs = self._v_attrs + except AttributeError: + attrs = self._v_attrs = {} + attr = attrs.get(name) + if attr is None: + for iface in self.__iro__: + attr = iface.direct(name) + if attr is not None: + attrs[name] = attr + break + + if attr is None: + return default + else: + return attr + +class InterfaceClass(Element, InterfaceBase, Specification): + """Prototype (scarecrow) Interfaces Implementation.""" + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IInterface) + + def __init__(self, name, bases=(), attrs=None, __doc__=None, + __module__=None): + + if attrs is None: + attrs = {} + + if __module__ is None: + __module__ = attrs.get('__module__') + if isinstance(__module__, str): + del attrs['__module__'] + else: + try: + # Figure out what module defined the interface. + # This is how cPython figures out the module of + # a class, but of course it does it in C. :-/ + __module__ = sys._getframe(1).f_globals['__name__'] + except (AttributeError, KeyError): + pass + + self.__module__ = __module__ + + d = attrs.get('__doc__') + if d is not None: + if not isinstance(d, Attribute): + if __doc__ is None: + __doc__ = d + del attrs['__doc__'] + + if __doc__ is None: + __doc__ = '' + + Element.__init__(self, name, __doc__) + + tagged_data = attrs.pop(TAGGED_DATA, None) + if tagged_data is not None: + for key, val in tagged_data.items(): + self.setTaggedValue(key, val) + + for base in bases: + if not isinstance(base, InterfaceClass): + raise TypeError('Expected base interfaces') + + Specification.__init__(self, bases) + + # Make sure that all recorded attributes (and methods) are of type + # `Attribute` and `Method` + for name, attr in attrs.items(): + if name == '__locals__': + # This happens under Python 3 sometimes, not sure why. /regebro + continue + if isinstance(attr, Attribute): + attr.interface = self + if not attr.__name__: + attr.__name__ = name + elif isinstance(attr, FunctionType): + attrs[name] = fromFunction(attr, self, name=name) + elif attr is _decorator_non_return: + del attrs[name] + else: + raise InvalidInterface("Concrete attribute, " + name) + + self.__attrs = attrs + + self.__identifier__ = "%s.%s" % (self.__module__, self.__name__) + + def interfaces(self): + """Return an iterator for the interfaces in the specification + + for example:: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> + >>> i = I1.interfaces() + >>> [x.getName() for x in i] + ['I1'] + >>> list(i) + [] + """ + yield self + + def getBases(self): + return self.__bases__ + + def isEqualOrExtendedBy(self, other): + """Same interface or extends?""" + return self == other or other.extends(self) + + def names(self, all=False): + """Return the attribute names defined by the interface.""" + if not all: + return self.__attrs.keys() + + r = self.__attrs.copy() + + for base in self.__bases__: + r.update(dict.fromkeys(base.names(all))) + + return r.keys() + + def __iter__(self): + return iter(self.names(all=True)) + + def namesAndDescriptions(self, all=False): + """Return attribute names and descriptions defined by interface.""" + if not all: + return self.__attrs.items() + + r = {} + for base in self.__bases__[::-1]: + r.update(dict(base.namesAndDescriptions(all))) + + r.update(self.__attrs) + + return r.items() + + def getDescriptionFor(self, name): + """Return the attribute description for the given name.""" + r = self.get(name) + if r is not None: + return r + + raise KeyError(name) + + __getitem__ = getDescriptionFor + + def __contains__(self, name): + return self.get(name) is not None + + def direct(self, name): + return self.__attrs.get(name) + + def queryDescriptionFor(self, name, default=None): + return self.get(name, default) + + def deferred(self): + """Return a defered class corresponding to the interface.""" + if hasattr(self, "_deferred"): return self._deferred + + klass={} + exec "class %s: pass" % self.__name__ in klass + klass=klass[self.__name__] + + self.__d(klass) + + self._deferred=klass + + return klass + + def validateInvariants(self, obj, errors=None): + """validate object to defined invariants.""" + for call in self.queryTaggedValue('invariants', []): + try: + call(obj) + except Invalid, e: + if errors is None: + raise + else: + errors.append(e) + for base in self.__bases__: + try: + base.validateInvariants(obj, errors) + except Invalid: + if errors is None: + raise + if errors: + raise Invalid(errors) + + def _getInterface(self, ob, name): + """Retrieve a named interface.""" + return None + + def __d(self, klass): + for k, v in self.__attrs.items(): + if isinstance(v, Method) and not (k in klass.__dict__): + setattr(klass, k, v) + + for b in self.__bases__: + b.__d(klass) + + def __repr__(self): + try: + return self._v_repr + except AttributeError: + name = self.__name__ + m = self.__module__ + if m: + name = '%s.%s' % (m, name) + r = "<%s %s>" % (self.__class__.__name__, name) + self._v_repr = r + return r + + def _call_conform(self, conform): + try: + return conform(self) + except TypeError: + # We got a TypeError. It might be an error raised by + # the __conform__ implementation, or *we* may have + # made the TypeError by calling an unbound method + # (object is a class). In the later case, we behave + # as though there is no __conform__ method. We can + # detect this case by checking whether there is more + # than one traceback object in the traceback chain: + if sys.exc_info()[2].tb_next is not None: + # There is more than one entry in the chain, so + # reraise the error: + raise + # This clever trick is from Phillip Eby + + return None + + def __reduce__(self): + return self.__name__ + + def __cmp(self, o1, o2): + # Yes, I did mean to name this __cmp, rather than __cmp__. + # It is a private method used by __lt__ and __gt__. + # I don't want to override __eq__ because I want the default + # __eq__, which is really fast. + """Make interfaces sortable + + TODO: It would ne nice if: + + More specific interfaces should sort before less specific ones. + Otherwise, sort on name and module. + + But this is too complicated, and we're going to punt on it + for now. + + For now, sort on interface and module name. + + None is treated as a pseudo interface that implies the loosest + contact possible, no contract. For that reason, all interfaces + sort before None. + + """ + if o1 is None: + return 1 + if o2 is None: + return -1 + + n1 = (getattr(o1, '__name__', ''), getattr(o1, '__module__', '')) + n2 = (getattr(o2, '__name__', ''), getattr(o2, '__module__', '')) + + # This spelling works under Python3, which doesn't have cmp(). + return (n1 > n2) - (n1 < n2) + + def __hash__(self): + return hash((self.__name__, self.__module__)) + + def __eq__(self, other): + c = self.__cmp(self, other) + return c == 0 + + def __ne__(self, other): + c = self.__cmp(self, other) + return c != 0 + + def __lt__(self, other): + c = self.__cmp(self, other) + return c < 0 + + def __le__(self, other): + c = self.__cmp(self, other) + return c <= 0 + + def __gt__(self, other): + c = self.__cmp(self, other) + return c > 0 + + def __ge__(self, other): + c = self.__cmp(self, other) + return c >= 0 + + +Interface = InterfaceClass("Interface", __module__ = 'zope.interface') + +class Attribute(Element): + """Attribute descriptions + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IAttribute) + + interface = None + + +class Method(Attribute): + """Method interfaces + + The idea here is that you have objects that describe methods. + This provides an opportunity for rich meta-data. + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IMethod) + + def __call__(self, *args, **kw): + raise BrokenImplementation(self.interface, self.__name__) + + def getSignatureInfo(self): + return {'positional': self.positional, + 'required': self.required, + 'optional': self.optional, + 'varargs': self.varargs, + 'kwargs': self.kwargs, + } + + def getSignatureString(self): + sig = [] + for v in self.positional: + sig.append(v) + if v in self.optional.keys(): + sig[-1] += "=" + `self.optional[v]` + if self.varargs: + sig.append("*" + self.varargs) + if self.kwargs: + sig.append("**" + self.kwargs) + + return "(%s)" % ", ".join(sig) + + +def fromFunction(func, interface=None, imlevel=0, name=None): + name = name or func.__name__ + method = Method(name, func.__doc__) + defaults = func.func_defaults or () + code = func.func_code + # Number of positional arguments + na = code.co_argcount-imlevel + names = code.co_varnames[imlevel:] + opt = {} + # Number of required arguments + nr = na-len(defaults) + if nr < 0: + defaults=defaults[-nr:] + nr = 0 + + # Determine the optional arguments. + opt.update(dict(zip(names[nr:], defaults))) + + method.positional = names[:na] + method.required = names[:nr] + method.optional = opt + + argno = na + + # Determine the function's variable argument's name (i.e. *args) + if code.co_flags & CO_VARARGS: + method.varargs = names[argno] + argno = argno + 1 + else: + method.varargs = None + + # Determine the function's keyword argument's name (i.e. **kw) + if code.co_flags & CO_VARKEYWORDS: + method.kwargs = names[argno] + else: + method.kwargs = None + + method.interface = interface + + for key, value in func.__dict__.items(): + method.setTaggedValue(key, value) + + return method + + +def fromMethod(meth, interface=None, name=None): + func = meth.im_func + return fromFunction(func, interface, imlevel=1, name=name) + + +# Now we can create the interesting interfaces and wire them up: +def _wire(): + from zope.interface.declarations import classImplements + + from zope.interface.interfaces import IAttribute + classImplements(Attribute, IAttribute) + + from zope.interface.interfaces import IMethod + classImplements(Method, IMethod) + + from zope.interface.interfaces import IInterface + classImplements(InterfaceClass, IInterface) + + from zope.interface.interfaces import ISpecification + classImplements(Specification, ISpecification) + +# We import this here to deal with module dependencies. +from zope.interface.declarations import implementedBy +from zope.interface.declarations import providedBy +from zope.interface.exceptions import InvalidInterface +from zope.interface.exceptions import BrokenImplementation diff --git a/pythonforumide/zope/interface/interfaces.py b/pythonforumide/zope/interface/interfaces.py new file mode 100644 index 0000000..9a37c06 --- /dev/null +++ b/pythonforumide/zope/interface/interfaces.py @@ -0,0 +1,746 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Interface Package Interfaces +""" +__docformat__ = 'restructuredtext' + + +from zope.interface import Interface +from zope.interface.interface import Attribute + +class IElement(Interface): + """Objects that have basic documentation and tagged values. + """ + + __name__ = Attribute('__name__', 'The object name') + __doc__ = Attribute('__doc__', 'The object doc string') + + def getTaggedValue(tag): + """Returns the value associated with `tag`. + + Raise a `KeyError` of the tag isn't set. + """ + + def queryTaggedValue(tag, default=None): + """Returns the value associated with `tag`. + + Return the default value of the tag isn't set. + """ + + def getTaggedValueTags(): + """Returns a list of all tags.""" + + def setTaggedValue(tag, value): + """Associates `value` with `key`.""" + + +class IAttribute(IElement): + """Attribute descriptors""" + + interface = Attribute('interface', + 'Stores the interface instance in which the ' + 'attribute is located.') + + +class IMethod(IAttribute): + """Method attributes""" + + def getSignatureInfo(): + """Returns the signature information. + + This method returns a dictionary with the following keys: + + o `positional` - All positional arguments. + + o `required` - A list of all required arguments. + + o `optional` - A list of all optional arguments. + + o `varargs` - The name of the varargs argument. + + o `kwargs` - The name of the kwargs argument. + """ + + def getSignatureString(): + """Return a signature string suitable for inclusion in documentation. + + This method returns the function signature string. For example, if you + have `func(a, b, c=1, d='f')`, then the signature string is `(a, b, + c=1, d='f')`. + """ + +class ISpecification(Interface): + """Object Behavioral specifications""" + + def extends(other, strict=True): + """Test whether a specification extends another + + The specification extends other if it has other as a base + interface or if one of it's bases extends other. + + If strict is false, then the specification extends itself. + """ + + def isOrExtends(other): + """Test whether the specification is or extends another + """ + + def weakref(callback=None): + """Return a weakref to the specification + + This method is, regrettably, needed to allow weakrefs to be + computed to security-proxied specifications. While the + zope.interface package does not require zope.security or + zope.proxy, it has to be able to coexist with it. + + """ + + __bases__ = Attribute("""Base specifications + + A tuple if specifications from which this specification is + directly derived. + + """) + + __sro__ = Attribute("""Specification-resolution order + + A tuple of the specification and all of it's ancestor + specifications from most specific to least specific. + + (This is similar to the method-resolution order for new-style classes.) + """) + + __iro__ = Attribute("""Interface-resolution order + + A tuple of the of the specification's ancestor interfaces from + most specific to least specific. The specification itself is + included if it is an interface. + + (This is similar to the method-resolution order for new-style classes.) + """) + + def get(name, default=None): + """Look up the description for a name + + If the named attribute is not defined, the default is + returned. + """ + + +class IInterface(ISpecification, IElement): + """Interface objects + + Interface objects describe the behavior of an object by containing + useful information about the object. This information includes: + + o Prose documentation about the object. In Python terms, this + is called the "doc string" of the interface. In this element, + you describe how the object works in prose language and any + other useful information about the object. + + o Descriptions of attributes. Attribute descriptions include + the name of the attribute and prose documentation describing + the attributes usage. + + o Descriptions of methods. Method descriptions can include: + + - Prose "doc string" documentation about the method and its + usage. + + - A description of the methods arguments; how many arguments + are expected, optional arguments and their default values, + the position or arguments in the signature, whether the + method accepts arbitrary arguments and whether the method + accepts arbitrary keyword arguments. + + o Optional tagged data. Interface objects (and their attributes and + methods) can have optional, application specific tagged data + associated with them. Examples uses for this are examples, + security assertions, pre/post conditions, and other possible + information you may want to associate with an Interface or its + attributes. + + Not all of this information is mandatory. For example, you may + only want the methods of your interface to have prose + documentation and not describe the arguments of the method in + exact detail. Interface objects are flexible and let you give or + take any of these components. + + Interfaces are created with the Python class statement using + either Interface.Interface or another interface, as in:: + + from zope.interface import Interface + + class IMyInterface(Interface): + '''Interface documentation''' + + def meth(arg1, arg2): + '''Documentation for meth''' + + # Note that there is no self argument + + class IMySubInterface(IMyInterface): + '''Interface documentation''' + + def meth2(): + '''Documentation for meth2''' + + You use interfaces in two ways: + + o You assert that your object implement the interfaces. + + There are several ways that you can assert that an object + implements an interface: + + 1. Call zope.interface.implements in your class definition. + + 2. Call zope.interfaces.directlyProvides on your object. + + 3. Call 'zope.interface.classImplements' to assert that instances + of a class implement an interface. + + For example:: + + from zope.interface import classImplements + + classImplements(some_class, some_interface) + + This approach is useful when it is not an option to modify + the class source. Note that this doesn't affect what the + class itself implements, but only what its instances + implement. + + o You query interface meta-data. See the IInterface methods and + attributes for details. + + """ + + def providedBy(object): + """Test whether the interface is implemented by the object + + Return true of the object asserts that it implements the + interface, including asserting that it implements an extended + interface. + """ + + def implementedBy(class_): + """Test whether the interface is implemented by instances of the class + + Return true of the class asserts that its instances implement the + interface, including asserting that they implement an extended + interface. + """ + + def names(all=False): + """Get the interface attribute names + + Return a sequence of the names of the attributes, including + methods, included in the interface definition. + + Normally, only directly defined attributes are included. If + a true positional or keyword argument is given, then + attributes defined by base classes will be included. + """ + + def namesAndDescriptions(all=False): + """Get the interface attribute names and descriptions + + Return a sequence of the names and descriptions of the + attributes, including methods, as name-value pairs, included + in the interface definition. + + Normally, only directly defined attributes are included. If + a true positional or keyword argument is given, then + attributes defined by base classes will be included. + """ + + def __getitem__(name): + """Get the description for a name + + If the named attribute is not defined, a KeyError is raised. + """ + + def direct(name): + """Get the description for the name if it was defined by the interface + + If the interface doesn't define the name, returns None. + """ + + def validateInvariants(obj, errors=None): + """Validate invariants + + Validate object to defined invariants. If errors is None, + raises first Invalid error; if errors is a list, appends all errors + to list, then raises Invalid with the errors as the first element + of the "args" tuple.""" + + def __contains__(name): + """Test whether the name is defined by the interface""" + + def __iter__(): + """Return an iterator over the names defined by the interface + + The names iterated include all of the names defined by the + interface directly and indirectly by base interfaces. + """ + + __module__ = Attribute("""The name of the module defining the interface""") + +class IDeclaration(ISpecification): + """Interface declaration + + Declarations are used to express the interfaces implemented by + classes or provided by objects. + """ + + def __contains__(interface): + """Test whether an interface is in the specification + + Return true if the given interface is one of the interfaces in + the specification and false otherwise. + """ + + def __iter__(): + """Return an iterator for the interfaces in the specification + """ + + def flattened(): + """Return an iterator of all included and extended interfaces + + An iterator is returned for all interfaces either included in + or extended by interfaces included in the specifications + without duplicates. The interfaces are in "interface + resolution order". The interface resolution order is such that + base interfaces are listed after interfaces that extend them + and, otherwise, interfaces are included in the order that they + were defined in the specification. + """ + + def __sub__(interfaces): + """Create an interface specification with some interfaces excluded + + The argument can be an interface or an interface + specifications. The interface or interfaces given in a + specification are subtracted from the interface specification. + + Removing an interface that is not in the specification does + not raise an error. Doing so has no effect. + + Removing an interface also removes sub-interfaces of the interface. + + """ + + def __add__(interfaces): + """Create an interface specification with some interfaces added + + The argument can be an interface or an interface + specifications. The interface or interfaces given in a + specification are added to the interface specification. + + Adding an interface that is already in the specification does + not raise an error. Doing so has no effect. + """ + + def __nonzero__(): + """Return a true value of the interface specification is non-empty + """ + +class IInterfaceDeclaration(Interface): + """Declare and check the interfaces of objects + + The functions defined in this interface are used to declare the + interfaces that objects provide and to query the interfaces that have + been declared. + + Interfaces can be declared for objects in two ways: + + - Interfaces are declared for instances of the object's class + + - Interfaces are declared for the object directly. + + The interfaces declared for an object are, therefore, the union of + interfaces declared for the object directly and the interfaces + declared for instances of the object's class. + + Note that we say that a class implements the interfaces provided + by it's instances. An instance can also provide interfaces + directly. The interfaces provided by an object are the union of + the interfaces provided directly and the interfaces implemented by + the class. + """ + + def providedBy(ob): + """Return the interfaces provided by an object + + This is the union of the interfaces directly provided by an + object and interfaces implemented by it's class. + + The value returned is an IDeclaration. + """ + + def implementedBy(class_): + """Return the interfaces implemented for a class' instances + + The value returned is an IDeclaration. + """ + + def classImplements(class_, *interfaces): + """Declare additional interfaces implemented for instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Consider the following example:: + + class C(A, B): + ... + + classImplements(C, I1, I2) + + + Instances of ``C`` provide ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` provide. + """ + + def implementer(*interfaces): + """Create a decorator for declaring interfaces implemented by a facory + + A callable is returned that makes an implements declaration on + objects passed to it. + """ + + def classImplementsOnly(class_, *interfaces): + """Declare the only interfaces implemented by instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) replace any previous declarations. + + Consider the following example:: + + class C(A, B): + ... + + classImplements(C, IA, IB. IC) + classImplementsOnly(C. I1, I2) + + Instances of ``C`` provide only ``I1``, ``I2``, and regardless of + whatever interfaces instances of ``A`` and ``B`` implement. + """ + + def implementer_only(*interfaces): + """Create a decorator for declaring the only interfaces implemented + + A callable is returned that makes an implements declaration on + objects passed to it. + """ + + def directlyProvidedBy(object): + """Return the interfaces directly provided by the given object + + The value returned is an IDeclaration. + """ + + def directlyProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) replace interfaces previously + declared for the object. + + Consider the following example:: + + class C(A, B): + ... + + ob = C() + directlyProvides(ob, I1, I2) + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. + + To remove directly provided interfaces, use ``directlyProvidedBy`` and + subtract the unwanted interfaces. For example:: + + directlyProvides(ob, directlyProvidedBy(ob)-I2) + + removes I2 from the interfaces directly provided by + ``ob``. The object, ``ob`` no longer directly provides ``I2``, + although it might still provide ``I2`` if it's class + implements ``I2``. + + To add directly provided interfaces, use ``directlyProvidedBy`` and + include additional interfaces. For example:: + + directlyProvides(ob, directlyProvidedBy(ob), I2) + + adds I2 to the interfaces directly provided by ob. + """ + + def alsoProvides(object, *interfaces): + """Declare additional interfaces directly for an object:: + + alsoProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob), I1) + """ + + def noLongerProvides(object, interface): + """Remove an interface from the list of an object's directly + provided interfaces:: + + noLongerProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob)-I1) + + with the exception that if ``I1`` is an interface that is + provided by ``ob`` through the class's implementation, + ValueError is raised. + """ + + def implements(*interfaces): + """Declare interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Previous declarations include declarations for base classes + unless implementsOnly was used. + + This function is provided for convenience. It provides a more + convenient way to call classImplements. For example:: + + implements(I1) + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + + Consider the following example:: + + class C(A, B): + implements(I1, I2) + + + Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` implement. + """ + + def implementsOnly(*interfaces): + """Declare the only interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + Previous declarations including declarations for base classes + are overridden. + + This function is provided for convenience. It provides a more + convenient way to call classImplementsOnly. For example:: + + implementsOnly(I1) + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + + Consider the following example:: + + class C(A, B): + implementsOnly(I1, I2) + + + Instances of ``C`` implement ``I1``, ``I2``, regardless of what + instances of ``A`` and ``B`` implement. + """ + + def classProvides(*interfaces): + """Declare interfaces provided directly by a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The given interfaces (including the interfaces in the + specifications) are used to create the class's direct-object + interface specification. An error will be raised if the module + class has an direct interface specification. In other words, it is + an error to call this function more than once in a class + definition. + + Note that the given interfaces have nothing to do with the + interfaces implemented by instances of the class. + + This function is provided for convenience. It provides a more + convenient way to call directlyProvides for a class. For example:: + + classProvides(I1) + + is equivalent to calling:: + + directlyProvides(theclass, I1) + + after the class has been created. + """ + def provider(*interfaces): + """A class decorator version of classProvides""" + + def moduleProvides(*interfaces): + """Declare interfaces provided by a module + + This function is used in a module definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The given interfaces (including the interfaces in the + specifications) are used to create the module's direct-object + interface specification. An error will be raised if the module + already has an interface specification. In other words, it is + an error to call this function more than once in a module + definition. + + This function is provided for convenience. It provides a more + convenient way to call directlyProvides for a module. For example:: + + moduleImplements(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + """ + + def Declaration(*interfaces): + """Create an interface specification + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + A new interface specification (IDeclaration) with + the given interfaces is returned. + """ + +class IAdapterRegistry(Interface): + """Provide an interface-based registry for adapters + + This registry registers objects that are in some sense "from" a + sequence of specification to an interface and a name. + + No specific semantics are assumed for the registered objects, + however, the most common application will be to register factories + that adapt objects providing required specifications to a provided + interface. + """ + + def register(required, provided, name, value): + """Register a value + + A value is registered for a *sequence* of required specifications, a + provided interface, and a name. + """ + + def registered(required, provided, name=u''): + """Return the component registered for the given interfaces and name + + Unlike the lookup method, this methods won't retrieve + components registered for more specific required interfaces or + less specific provided interfaces. + + If no component was registered exactly for the given + interfaces and name, then None is returned. + + """ + + def lookup(required, provided, name='', default=None): + """Lookup a value + + A value is looked up based on a *sequence* of required + specifications, a provided interface, and a name. + """ + + def queryMultiAdapter(objects, provided, name=u'', default=None): + """Adapt a sequence of objects to a named, provided, interface + """ + + def lookup1(required, provided, name=u'', default=None): + """Lookup a value using a single required interface + + A value is looked up based on a single required + specifications, a provided interface, and a name. + """ + + def queryAdapter(object, provided, name=u'', default=None): + """Adapt an object using a registered adapter factory. + """ + + def adapter_hook(provided, object, name=u'', default=None): + """Adapt an object using a registered adapter factory. + """ + + def lookupAll(required, provided): + """Find all adapters from the required to the provided interfaces + + An iterable object is returned that provides name-value two-tuples. + """ + + def names(required, provided): + """Return the names for which there are registered objects + """ + + def subscribe(required, provided, subscriber, name=u''): + """Register a subscriber + + A subscriber is registered for a *sequence* of required + specifications, a provided interface, and a name. + + Multiple subscribers may be registered for the same (or + equivalent) interfaces. + """ + + def subscriptions(required, provided, name=u''): + """Get a sequence of subscribers + + Subscribers for a *sequence* of required interfaces, and a provided + interface are returned. + """ + + def subscribers(objects, provided, name=u''): + """Get a sequence of subscription adapters + """ diff --git a/pythonforumide/zope/interface/ro.py b/pythonforumide/zope/interface/ro.py new file mode 100644 index 0000000..47a7ea2 --- /dev/null +++ b/pythonforumide/zope/interface/ro.py @@ -0,0 +1,69 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Compute a resolution order for an object and its bases +""" +__docformat__ = 'restructuredtext' + + +def ro(object): + """Compute a "resolution order" for an object + """ + return mergeOrderings([_flatten(object)]) + +def mergeOrderings(orderings, seen=None): + """Merge multiple orderings so that within-ordering order is preserved + + Orderings are constrained in such a way that if an object appears + in two or more orderings, then the suffix that begins with the + object must be in both orderings. + + For example: + + >>> _mergeOrderings([ + ... ['x', 'y', 'z'], + ... ['q', 'z'], + ... [1, 3, 5], + ... ['z'] + ... ]) + ['x', 'y', 'q', 1, 3, 5, 'z'] + + """ + + if seen is None: + seen = {} + result = [] + orderings.reverse() + for ordering in orderings: + ordering = list(ordering) + ordering.reverse() + for o in ordering: + if o not in seen: + seen[o] = 1 + result.append(o) + + result.reverse() + return result + +def _flatten(ob): + result = [ob] + i = 0 + for ob in iter(result): + i += 1 + # The recursive calls can be avoided by inserting the base classes + # into the dynamically growing list directly after the currently + # considered object; the iterator makes sure this will keep working + # in the future, since it cannot rely on the length of the list + # by definition. + result[i:i] = ob.__bases__ + return result diff --git a/pythonforumide/zope/interface/verify.py b/pythonforumide/zope/interface/verify.py new file mode 100644 index 0000000..da60b6e --- /dev/null +++ b/pythonforumide/zope/interface/verify.py @@ -0,0 +1,115 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Verify interface implementations +""" +from zope.interface.exceptions import BrokenImplementation, DoesNotImplement +from zope.interface.exceptions import BrokenMethodImplementation +from types import FunctionType, MethodType +from zope.interface.interface import fromMethod, fromFunction, Method +import sys + +# This will be monkey-patched when running under Zope 2, so leave this +# here: +MethodTypes = (MethodType, ) + + +def _verify(iface, candidate, tentative=0, vtype=None): + """Verify that 'candidate' might correctly implements 'iface'. + + This involves: + + o Making sure the candidate defines all the necessary methods + + o Making sure the methods have the correct signature + + o Making sure the candidate asserts that it implements the interface + + Note that this isn't the same as verifying that the class does + implement the interface. + + If optional tentative is true, suppress the "is implemented by" test. + """ + + if vtype == 'c': + tester = iface.implementedBy + else: + tester = iface.providedBy + + if not tentative and not tester(candidate): + raise DoesNotImplement(iface) + + # Here the `desc` is either an `Attribute` or `Method` instance + for name, desc in iface.namesAndDescriptions(1): + try: + attr = getattr(candidate, name) + except AttributeError: + if (not isinstance(desc, Method)) and vtype == 'c': + # We can't verify non-methods on classes, since the + # class may provide attrs in it's __init__. + continue + + raise BrokenImplementation(iface, name) + + if not isinstance(desc, Method): + # If it's not a method, there's nothing else we can test + continue + + if isinstance(attr, FunctionType): + if sys.version[0] == '3' and isinstance(candidate, type): + # This is an "unbound method" in Python 3. + meth = fromFunction(attr, iface, name=name, imlevel=1) + else: + # Nope, just a normal function + meth = fromFunction(attr, iface, name=name) + elif (isinstance(attr, MethodTypes) + and type(attr.im_func) is FunctionType): + meth = fromMethod(attr, iface, name) + else: + if not callable(attr): + raise BrokenMethodImplementation(name, "Not a method") + # sigh, it's callable, but we don't know how to intrspect it, so + # we have to give it a pass. + continue + + # Make sure that the required and implemented method signatures are + # the same. + desc = desc.getSignatureInfo() + meth = meth.getSignatureInfo() + + mess = _incompat(desc, meth) + if mess: + raise BrokenMethodImplementation(name, mess) + + return True + +def verifyClass(iface, candidate, tentative=0): + return _verify(iface, candidate, tentative, vtype='c') + +def verifyObject(iface, candidate, tentative=0): + return _verify(iface, candidate, tentative, vtype='o') + +def _incompat(required, implemented): + #if (required['positional'] != + # implemented['positional'][:len(required['positional'])] + # and implemented['kwargs'] is None): + # return 'imlementation has different argument names' + if len(implemented['required']) > len(required['required']): + return 'implementation requires too many arguments' + if ((len(implemented['positional']) < len(required['positional'])) + and not implemented['varargs']): + return "implementation doesn't allow enough arguments" + if required['kwargs'] and not implemented['kwargs']: + return "implementation doesn't support keyword arguments" + if required['varargs'] and not implemented['varargs']: + return "implementation doesn't support variable arguments" From 73aadb7d59f1ff4cead3f0010503695bb08d416d Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 4 Aug 2011 14:36:10 +0100 Subject: [PATCH 074/141] Added Case Sensitive Checkbox to replace frame, no action yet --- pythonforumide/gui_lib/ide_replace_frame.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 2732826..6a4ea48 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -27,7 +27,9 @@ def create_gui(self): self.lbl_replace_with = wx.StaticText(_panel, -1, "Replace with: ", (5, 53), (100, -1)) self.txt_replace_with = wx.TextCtrl(_panel, id=-1, pos=(100, 50), size=(300, -1)) self.replace_id = wx.NewId() - self.replace_button = wx.Button(_panel, self.replace_id, "Replace", pos=(5, 80), size=(90, -1)) + self.replace_button = wx.Button(_panel, self.replace_id, "Replace", pos=(5, 110), size=(90, -1)) + self.case_check = wx.CheckBox(_panel, -1, "Case Sensitive", pos=(5, 80), size=(-1, -1)) + self.Bind(wx.EVT_BUTTON, self.on_replace, id=self.replace_id) self.sizer.Add(_panel, 1, wx.EXPAND) From 11de7aa5d87783d310b5e9dd4995b2aea9d1fce9 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 4 Aug 2011 17:02:10 +0100 Subject: [PATCH 075/141] Added case sensitive choice to Replace Frame --- pythonforumide/gui_lib/ide_replace_frame.py | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 6a4ea48..30d585b 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -1,4 +1,6 @@ import wx +import string +from itertools import izip_longest from ide_simple_frame import SimpleFrame @@ -33,10 +35,28 @@ def create_gui(self): self.Bind(wx.EVT_BUTTON, self.on_replace, id=self.replace_id) self.sizer.Add(_panel, 1, wx.EXPAND) + def incase_replace(self, st, x, y): + """Replaces x with y in an non case sensitive way""" + mod = st.lower().replace(x.lower(), y) + out = '' + for x, y in izip_longest(st, mod, fillvalue=' '): + if x == y: + out += y + elif (x in string.ascii_uppercase) and (x == y.upper()): + out += x + else: + out += y + return out + def on_replace(self, event): """Replaces text on the current editor (self.active_editor)""" self.str_to_replace = self.txt_to_replace.GetValue() self.str_replace_with = self.txt_replace_with.GetValue() - self.active_editor.SetText(self.active_editor.GetText().replace( + if self.case_check.GetValue(): #If case sensitive on + self.active_editor.SetText(self.active_editor.GetText().replace( self.str_to_replace, self.str_replace_with)) + else: #If case sensitive disabled + self.active_editor.SetText(self.incase_replace( + self.active_editor.GetText(), self.str_to_replace, + self.str_replace_with)) self.Destroy() From a0b9684df4decfc2097eb7e6b0cea16475cbe308 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 4 Aug 2011 17:28:41 +0100 Subject: [PATCH 076/141] Fixed Replace Frame Size --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index dfed786..6db02de 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -250,7 +250,7 @@ def on_replace(self): # Allows this frame to be destroyed by the main window on close. replace_frame = ReplaceFrame(active_editor=self, parent=wx.GetApp().TopWindow, - title="Find and Replace", size=(410, 150)) + title="Find and Replace", size=(500, 200)) replace_frame.Layout() From 4b4b2c7247ef7c4de52ce8754fa30633ade532b8 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Thu, 4 Aug 2011 23:53:45 +0100 Subject: [PATCH 077/141] config variables updated --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/config/config.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 27c3de6..ae26cbe 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -4,6 +4,6 @@ usetab = str>False test.int = int>25 test.bool = bool>True test.float = float>0.75 -mainframe.height = int>743 -mainframe.width = int>1134 +mainframe.height = int>826 +mainframe.width = int>842 diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index 9a19601..e1cf8aa 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -235,9 +235,10 @@ def set_defaults(self): self._data["usetab"] = 0 self._data["MainFrame.Height"] = 600 self._data["MainFrame.Width"] = 600 - self._data["Test.bool"] = True - self._data["Test.int"] = 25 - self._data["Test.float"] = 0.75 + self._data["MainFrame.XPos"]= 0 + self._data["MainFrame.YPos"]= 0 + self._data["MainMenu.View.Toolbar.Show"]= True + if __name__ == '__main__': ide_config = IdeConfig() From 01b364711a19d3cc03f228a20977742c4f310759 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:00:04 +0100 Subject: [PATCH 078/141] removed framed panel --- pythonforumide/gui_lib/ide_framed_panel.py | 306 --------------------- 1 file changed, 306 deletions(-) delete mode 100644 pythonforumide/gui_lib/ide_framed_panel.py diff --git a/pythonforumide/gui_lib/ide_framed_panel.py b/pythonforumide/gui_lib/ide_framed_panel.py deleted file mode 100644 index 9322a10..0000000 --- a/pythonforumide/gui_lib/ide_framed_panel.py +++ /dev/null @@ -1,306 +0,0 @@ -""" -Created on 31 Jul 2011 - -@author: D.W. -""" - -import wx - -class FramedPanel(wx.Panel): - def __init__(self, *args, **kwargs): - """ Creates a panel for displaying text """ - super(FramedPanel, self).__init__(*args, **kwargs) - self._create_variables() - - sizer= wx.BoxSizer(wx.VERTICAL) - - self._create_header(sizer) - self._create_textctrl(sizer) - self.SetSizer(sizer) - self.Layout() - - def _create_variables(self): - self._parent= self.GetParent() - self._parents_child_state= {} - - self._minimum_size= 26 - self._current_size= (-1, 50) - self._maximized= False - self._minimized= False - - self._drag_vertical= True - self._sensitivity= 0.2 - self._header_pos= 0 - - def _create_header(self, sizer): -## header_sizer= wx.BoxSizer(wx.VERTICAL) -## sizer.Add(header_sizer, 0, wx.EXPAND|wx.ALL, 2) - - ctrl= HeaderPanel(self, style= wx.BORDER_THEME|wx.TAB_TRAVERSAL) - sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL|wx.ALIGN_CENTER, 2) - self._header_panel= ctrl - - def _create_textctrl(self, sizer): - ctrl= wx.TextCtrl(self, value= "Some text", style=wx.TE_MULTILINE|wx.TE_READONLY) - sizer.Add(ctrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, 2) - self.textctrl= ctrl - - def set_text(self, text): - self.textctrl.SetValue(text) - - def set_caption(self, text): - self._header_panel._header_caption.SetLabel(" %s" % (text)) - - def show(self, is_shown= True): - self.Show(is_shown) - self._parent.Layout() - - def get_parent_client_size(self): - return self._parent.GetClientSize() - - def send_parent_size_evet(self): - self._parent.SendSizeEvent() - - def get_own_client_size_tuple(self): - return self.GetClientSizeTuple() - - def get_sizer_item(self): - sizer= self.GetContainingSizer() - return sizer.GetItem(self) - - def set_sizer_item_proportion(self, proportion): - self.get_sizer_item().SetProportion(proportion) - - def set_size(self, width, height): - sizer_item= self.get_sizer_item() - sizer_item.SetMinSize((width, height)) - self.send_parent_size_evet() - self._parent.Layout() - self._parent.Update() - self._parent.Refresh() - - def get_parents_children(self): - return self._parent.GetChildren() - - - def set_parents_childrens_state(self): - for child_panel in self.get_parents_children(): - if child_panel != self: - self._parents_child_state[child_panel]= child_panel.IsShown() - - def minimize(self): - if self._maximized: - self.restore() - self.set_size(-1, self._minimum_size) - self._minimized= True - - def maximize(self): - self.set_parents_childrens_state() - self._current_size= self.get_own_client_size_tuple() - for child_panel, state in self._parents_child_state.iteritems(): - child_panel.Show(False) - self.set_sizer_item_proportion(1) - self.set_size(-1, self.get_parent_client_size()[1]) - self._maximized= True - self._minimized= False - self._header_panel._btn_maximize.SetLabel("2") - - def restore(self): - self.set_sizer_item_proportion(0) - for child_panel, state in self._parents_child_state.iteritems(): - child_panel.Show(state) - self.set_size(-1, self._current_size[1]) - self._maximized= False - self._minimized= False - self._header_panel._btn_maximize.SetLabel("1") - - - - - - - - -class FramedPanelEvents(object): - """ Creates the events for framed panel""" - def __init__(self, view, model = ""): - """ Sets the view and model if one s required""" - self.view = view - self.model = model - self._create_binds() - - def _create_binds(self): - """ Creates the binds""" -## self._header_panel.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse) - -## self._dragbar_top.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse) -## self._dragbar_bottom.Bind(wx.EVT_MOUSE_EVENTS, self._on_mouse) - header_panel= self.view._header_panel - header_panel._btn_minimize.Bind(wx.EVT_BUTTON, self._on_btn_minimize) - header_panel._btn_maximize.Bind(wx.EVT_BUTTON, self._on_btn_maximize) - header_panel._btn_close.Bind(wx.EVT_BUTTON, self._on_btn_close) - -# self.timer = wx.Timer(self) -# self.Bind(wx.EVT_TIMER, self._update_title) -# self.timer.Start(40) # in miliseconds - - def _on_mouse(self, event): -## print "Mouse event" - if event.LeftDown(): -## print "mouse event left down" - if self._drag_vertical: - self._header_pos= event.GetPosition()[1] - else: - self._header_pos= event.GetPosition()[0] - elif event.Dragging(): -## print "Mouse dragging" - x, y = self.ScreenToClient(self._header_panel.ClientToScreen(event.GetPosition())) - if self._drag_vertical: - change= ((y- self._header_pos)*-1)*self._sensitivity - else: - change= ((x- self._header_pos))*self._sensitivity - newsize= self.GetSize()[1]+change - if newsize> self._minimum_size: - self.SetSizeHints(-1, newsize) - self._parent.SendSizeEvent() - self._current_size= newsize -## elif event.ButtonDClick(): -## new_size= self.GetClientSize()[0] -## print "Mouse doubleclick currentsize: %s" % (self._current_size) -## print "Actual size: %s" % (self.GetSize()) -## if not self._maximized: -## self.SetSizeHints(-1, new_size) -## self._maximized= True -## else: -## self.SetSizeHints(-1, self._current_size) -## self._maximized= False -## self.SendSizeEventToParent() - - event.Skip() - - def _on_btn_close(self, event): - self.view.show(False) - - def _on_btn_minimize(self, event): - self.view.minimize() - - - def _on_btn_maximize(self, event): - if not self.view._maximized: - self.view.maximize() - else: - self.view.restore() - - - - def _update_title(self, event): - pos = wx.GetMousePosition() - csize= self.GetClientSizeTuple() -## cpos= self.GetPositionTuple() -## client_to_screen= self.ClientToScreen(cpos) - client_to_screen= self.GetScreenPosition() - panel_top = client_to_screen[1] - panel_bottom= panel_top+csize[1] - panel_left= client_to_screen[0] - panel_right= panel_left+csize[0] - in_width= pos[0]>= panel_left and pos[0]<= panel_right - if pos[1] in xrange(panel_top-5, panel_top+5): - toporbot= "At the top" - elif pos[1]in xrange(panel_bottom-5, panel_bottom+5): - toporbot= "At the Bottom" - else: - toporbot= "Neither" - text = "Window client size: %s \ client to screen pos: %s \n top: %s bottom: %s left: %s Right: %s inwidth: %s toporbottom: %s" % (csize, client_to_screen, panel_top, panel_bottom, panel_left, panel_right, in_width, toporbot) - self.set_text(text) - self.set_caption("Your mouse is at (%s, %s)" % (pos.x, pos.y)) - cur_cursor= self.GetCursor() - if in_width and toporbot!= "Neither": - cursor = wx.StockCursor(wx.CURSOR_SIZENS) - else: - cursor= wx.StockCursor(wx.CURSOR_DEFAULT) - if cur_cursor!= cursor: - self.SetCursor(cursor) - - - -class HeaderPanel(wx.Panel): - def __init__(self, *args, **kwargs): - """ Creates a panel for displaying text """ - super(HeaderPanel, self).__init__(*args, **kwargs) - self._button_font= wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, - u'Webdings') - sizer= wx.BoxSizer(wx.HORIZONTAL) - self._create_statictxt(sizer) - self._create_minimize(sizer) - self._create_maximize(sizer) - self._create_close(sizer) - - self.SetSizer(sizer) - self.Layout() - - def _create_statictxt(self, sizer): - ctrl= wx.StaticText(self, label= " Console") - font= ctrl.GetFont() - font.SetWeight(wx.FONTWEIGHT_BOLD) - ctrl.SetFont(font) - sizer.Add(ctrl, 0, flag= wx.ALIGN_CENTER_VERTICAL) - self._header_caption= ctrl - - def _create_minimize(self, sizer): - sizer.AddStretchSpacer(1) - ctrl= wx.Button(self, label= "0", size= (19, 19)) - ctrl.SetFont(self._button_font) - sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) - self._btn_minimize= ctrl - - def _create_maximize(self, sizer): - ctrl= wx.Button(self, label= "1", size= (19, 19)) - ctrl.SetFont(self._button_font) - sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) - self._btn_maximize= ctrl - # restore = 2 - - def _create_close(self, sizer): - ctrl= wx.Button(self, label= "r", size= (19, 19)) - ctrl.SetFont(self._button_font) - sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) - self._btn_close= ctrl - - - - - -if __name__=='__main__': - import ide_test_app as wx_app - from ide_simple_frame import SimpleFrame, SimplePanel - app = wx_app.Wx_App(False) - frame= SimpleFrame(None, title= "Testing TextPanel without events") - panel= SimplePanel(frame) - frame.sizer.Add(panel, 1, wx.EXPAND) - panel_black= wx.Panel(panel) - panel_black.SetBackgroundColour((0, 0,0 )) - panel.sizer.Add(panel_black, 1, wx.EXPAND) - frame.panel_black= panel_black - - - text_panel= FramedPanel(panel) - FramedPanelEvents(text_panel) - panel.sizer.Add(text_panel, 0, wx.EXPAND) - text_panel.set_caption("New Title") - - text_panel= FramedPanel(panel) - FramedPanelEvents(text_panel) - panel.sizer.Add(text_panel, 0, wx.EXPAND) - text_panel= FramedPanel(panel) - FramedPanelEvents(text_panel) - panel.sizer.Add(text_panel, 0, wx.EXPAND) - text_panel= FramedPanel(panel) - FramedPanelEvents(text_panel) - panel.sizer.Add(text_panel, 0, wx.EXPAND) - - frame.Layout() - - - app.MainLoop() - - \ No newline at end of file From 2c3e0e5ed9f88744053540fe655775aa35a6595b Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:13:08 +0100 Subject: [PATCH 079/141] code restructuring, replace and run moved into main frame, new CanCopy, CanUndo ect. --- pythonforumide/gui_lib/ide_console.py | 34 +++++++++ pythonforumide/gui_lib/ide_editor.py | 99 +++++---------------------- 2 files changed, 53 insertions(+), 80 deletions(-) create mode 100644 pythonforumide/gui_lib/ide_console.py diff --git a/pythonforumide/gui_lib/ide_console.py b/pythonforumide/gui_lib/ide_console.py new file mode 100644 index 0000000..f42118f --- /dev/null +++ b/pythonforumide/gui_lib/ide_console.py @@ -0,0 +1,34 @@ +''' +Created on 4 Aug 2011 + +@author: D.W. +''' + +import wx +from wx.richtext import RichTextCtrl + +class ConsolePanel(wx.Panel): + def __init__(self, *args, **kwargs): + super(ConsolePanel, self).__init__(*args, **kwargs) + self.sizer = wx.BoxSizer(wx.VERTICAL) + self._create_rich_text_ctrl() + + self.SetSizer(self.sizer) + self.Layout() + + def _create_rich_text_ctrl(self): + self._rt_ctrl = RichTextCtrl(self, style=wx.TE_READONLY) + self.sizer.Add(self._rt_ctrl, 1, wx.EXPAND | wx.ALL, 1) + + + +if __name__ == '__main__': + import ide_test_app as wx_app + import ide_simple_frame + app = wx_app.Wx_App(False) + frame = ide_simple_frame.SimpleFrame(None, + title="Testing console without events") + panel = ConsolePanel(frame) + frame.sizer.Add(panel, 1, wx.EXPAND) + frame.Layout() + app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 6db02de..a2d95ea 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -199,35 +199,26 @@ def OnKeyDown(self, event): event.Skip() self.AutoComp(event, key) - def on_undo(self): - """Checks if can Undo and if yes undoes""" - if self.CanUndo() == 1: #same as `if not self.CanUndo():` ?? - self.Undo() - - def on_redo(self): - """Checks if can Redo and if yes redoes""" - if self.CanRedo() == 1: - self.Redo() - - def on_cut(self): - """Cuts selected text""" - self.Cut() - - def on_copy(self): - """Copies selected text""" - self.Copy() - - def on_paste(self): - """Pastes selected text""" - self.Paste() - - def on_clear(self): - """Deletes selected text""" - self.Clear() + def CanCut(self): + """ Returns true if there's a selection that can be cut""" + if self.GetSelectedText(): + return True + return False + + def CanCopy(self): + """ Returns true if there's a selection that can be copied""" + """ Uses CanCut could be altered for its own checking if required""" + return self.CanCut() + + def CanPaste(self): + """ Returns ture if the clipboard contains text""" + """ Note: I dont know at present how to check clipboard for contents""" + return True - def on_select_all(self): - """Selects all the text""" - self.SelectAll() + def CanDelete(self): + """ Returns true if there's a selection that can be deleted""" + """ Uses CanCut could be altered for its own checking if required""" + return self.CanCut() def load_file(self, path): """Loads a new file""" @@ -242,58 +233,6 @@ def save_file_as(self, filepath): """Save the current file as a new filepath""" self.filepath= filepath return self.save_file() - - def on_replace(self): - """Displays a find/replace dialog""" - - # Create a search frame and hook into the caller. - # Allows this frame to be destroyed by the main window on close. - replace_frame = ReplaceFrame(active_editor=self, - parent=wx.GetApp().TopWindow, - title="Find and Replace", size=(500, 200)) - replace_frame.Layout() - - - def on_run(self): - """Runs selected code in a new window.""" - # Create a frame and hook into the caller. - # Allows this frame to be destroyed by the main window on close. - reactor = wx.GetApp().this_reactor - - run_editor = SimpleFrame(wx.GetApp().TopWindow, title="") - run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) - run_editor.sizer.Add(run_panel, 1, wx.EXPAND) - run_editor.Layout() - run_panel.WriteText(introduction()) - run_panel.Newline() - - if self.filepath: - if self.GetModify(): - self.SaveFile(self.filepath) - filename = os.path.split(self.filepath)[1] - run_panel.WriteText("Running %s" % filename) - run_panel.Newline() - reactor.spawnProcess(PythonProcessProtocol(run_panel), - get_python_exe(), - ["python", str(self.filepath)]) - - else: - run_panel.WriteText("Running unsaved script.") - run_panel.Newline() - script = StringIO() - script.write(self.GetText()) - script.seek(0) - - #For some reason we end up with \r\n for line endings. - #This sorts that issue out - scr = script.read().replace("\r",'') - - reactor.spawnProcess(PythonProcessProtocol(run_panel), - get_python_exe(), - ["python", "-c", scr]) - - - return run_panel if __name__=='__main__': """Adds the editor to the frame""" From 0fdffb82dc7e560bdfed57a334f581219cb8a1a2 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:18:17 +0100 Subject: [PATCH 080/141] Majour restructuring of mainframe, panel moved out into its own ide_mainframe_panel --- pythonforumide/gui_lib/ide_headered_panel.py | 127 +++++----- pythonforumide/gui_lib/ide_mainframe.py | 253 +++++++++++++++---- 2 files changed, 267 insertions(+), 113 deletions(-) diff --git a/pythonforumide/gui_lib/ide_headered_panel.py b/pythonforumide/gui_lib/ide_headered_panel.py index 6194654..da5c34f 100644 --- a/pythonforumide/gui_lib/ide_headered_panel.py +++ b/pythonforumide/gui_lib/ide_headered_panel.py @@ -11,7 +11,7 @@ def __init__(self, *args, **kwargs): """ Creates headered panel with Minimize/Maximize & Close buttons """ super(HeaderPanel, self).__init__(*args, **kwargs) self._create_variables() - sizer= wx.BoxSizer(wx.HORIZONTAL) + sizer = wx.BoxSizer(wx.HORIZONTAL) self._create_statictxt(sizer) self._create_minimize(sizer) self._create_maximize(sizer) @@ -22,80 +22,81 @@ def __init__(self, *args, **kwargs): def _create_variables(self): """ Creates variables for internal use only""" - self._button_font= wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, + self._button_font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Webdings') def _create_statictxt(self, sizer): """ Create header label""" - ctrl= wx.StaticText(self, label= " Console") - font= ctrl.GetFont() + ctrl = wx.StaticText(self, label=" Console") + font = ctrl.GetFont() font.SetWeight(wx.FONTWEIGHT_BOLD) ctrl.SetFont(font) - sizer.Add(ctrl, 0, flag= wx.ALIGN_CENTER_VERTICAL) - self.header_caption= ctrl + sizer.Add(ctrl, 0, flag=wx.ALIGN_CENTER_VERTICAL) + self.header_caption = ctrl def _create_minimize(self, sizer): """ Creates the Minimize button""" sizer.AddStretchSpacer(1) - ctrl= wx.Button(self, label= "0", size= (19, 19)) + ctrl = wx.Button(self, label="0", size=(19, 19)) ctrl.SetFont(self._button_font) ctrl.SetToolTipString("Minimize") sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) - self.btn_minimize= ctrl + self.btn_minimize = ctrl def _create_maximize(self, sizer): """ Creates the Maximize button""" - ctrl= wx.Button(self, label= "1", size= (19, 19)) + ctrl = wx.Button(self, label="1", size=(19, 19)) ctrl.SetFont(self._button_font) ctrl.SetToolTipString("Maximize") sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) - self.btn_maximize= ctrl + self.btn_maximize = ctrl # restore = 2 def _create_close(self, sizer): """ Creates the Close button""" - ctrl= wx.Button(self, label= "r", size= (19, 19)) + ctrl = wx.Button(self, label="r", size=(19, 19)) ctrl.SetFont(self._button_font) ctrl.SetToolTipString("Close") sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) - self.btn_close= ctrl + self.btn_close = ctrl -class FramedPanel(wx.Panel): +class HeaderedPanel(wx.Panel): def __init__(self, *args, **kwargs): """ Creates a panel for displaying text """ - super(FramedPanel, self).__init__(*args, **kwargs) + super(HeaderedPanel, self).__init__(*args, **kwargs) self._create_variables() - self.sizer= wx.BoxSizer(wx.VERTICAL) + self.sizer = wx.BoxSizer(wx.VERTICAL) self._create_header_panel() self.SetSizer(self.sizer) self.Layout() def _create_variables(self): - self.child_panel= None - self._proportion= None - self._minimized= False + self.child_panel = None + self._proportion = None + self._minimized = False + self._parent_frame = None def _create_header_panel(self): """ Creates the header panel""" - ctrl= HeaderPanel(self, style= wx.BORDER_THEME) - self.sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL, 1) - self.header_panel= ctrl + ctrl = HeaderPanel(self, style=wx.BORDER_THEME) + self.sizer.Add(ctrl, 0, wx.EXPAND | wx.ALL, 1) + self.header_panel = ctrl - def add_panel_ctrl(self, panel_class, caption= "", proportion= 1): + def add_panel_ctrl(self, panel_class, caption="", proportion=1): self.Freeze() - ctrl= panel_class(self) - self.sizer.Add(ctrl, 1, wx.EXPAND|wx.ALL, 0) + ctrl = panel_class(self) + self.sizer.Add(ctrl, 1, wx.EXPAND | wx.ALL, 0) self._update_layout() - self.child_panel= ctrl - self._proportion= proportion + self.child_panel = ctrl + self._proportion = proportion self._set_own_proportion(proportion) self.set_caption(caption) self.Thaw() return ctrl def _update_layout(self): - parent= self.GetParent() + parent = self.GetParent() parent.Layout() parent.Update() parent.Refresh() @@ -108,7 +109,7 @@ def _set_after_restore(self): self.header_panel.btn_maximize.SetLabel("1") def _get_items_sizer(self, item): - sizer= item.GetContainingSizer() + sizer = item.GetContainingSizer() return sizer.GetItem(self) def _set_own_proportion(self, proportion): @@ -116,18 +117,18 @@ def _set_own_proportion(self, proportion): - def show(self, show= True): + def show(self, show=True): self.Show(show) self._update_layout() - def show_child(self, show= True): + def show_child(self, show=True): self.child_panel.Show(show) def _set_no_child_state(self): self.header_panel.btn_maximize.SetLabel("2") self._set_own_proportion(0) self._update_layout() - self._minimized= True + self._minimized = True def minimized_state(self): if not self.child_panel: @@ -138,7 +139,7 @@ def minimized_state(self): self._set_own_proportion(0) self._update_layout() - self._minimized= True + self._minimized = True def restore_state(self): if not self.child_panel: @@ -149,7 +150,7 @@ def restore_state(self): self.header_panel.btn_maximize.SetToolTipString("Maximize") self._set_own_proportion(self._proportion) self._update_layout() - self._minimized= False + self._minimized = False else: self._move_child_to_a_frame() @@ -160,11 +161,10 @@ def close(self): def _move_child_to_a_frame(self): self.sizer.Detach(self.child_panel) - self.show(False) - simple_frame= ParentFrame(None, title= self._get_caption()) - simple_frame.add_child(self.child_panel, self) - + self._parent_frame = ParentFrame(self, title=self._get_caption()) + self._parent_frame.add_child(self.child_panel, self) + def set_caption(self, text): self.header_panel.header_caption.SetLabel(" %s" % (text)) @@ -174,19 +174,19 @@ def _get_caption(self): -class FramedPanelEvents(object): - def __init__(self, view, model= ""): +class HeaderedPanelEvents(object): + def __init__(self, view, model=""): self.view = view self.model = model self._create_variables() self._create_binds() def _create_variables(self): - header_panel= self.view.header_panel - self.btn_minimize= header_panel.btn_minimize - self.btn_maximize= header_panel.btn_maximize - self.btn_close= header_panel.btn_close - self.header_panel= self.view.header_panel + header_panel = self.view.header_panel + self.btn_minimize = header_panel.btn_minimize + self.btn_maximize = header_panel.btn_maximize + self.btn_close = header_panel.btn_close +# self.header_panel= self.view.header_panel def _create_binds(self): self.btn_minimize.Bind(wx.EVT_BUTTON, self._on_btn_minimize) @@ -208,9 +208,9 @@ class ParentFrame(wx.Frame): def __init__(self, *args, **kwargs): """Initiates the frame and the GUI""" super(ParentFrame, self).__init__(*args, **kwargs) - self.SetInitialSize(kwargs.get("size", (600,600))) + self.SetInitialSize(kwargs.get("size", (600, 600))) self.Center(wx.BOTH) - self.sizer= wx.BoxSizer(wx.VERTICAL) + self.sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.Bind(wx.EVT_CLOSE, self._on_close) @@ -226,19 +226,22 @@ def add_child(self, child_panel, parent_window): def _move_child_back(self): self.sizer.Detach(self._child_panel) self._child_panel.Reparent(self._parent_window) - self._parent_window.sizer.Add(self._child_panel,1, wx.EXPAND|wx.ALL, 0) + self._parent_window.sizer.Add(self._child_panel, 1, wx.EXPAND | wx.ALL, 0) self._parent_window.show(True) + self._parent_window._parent_frame = None + def _on_close(self, event): self._move_child_back() - self.Destroy() + event.Skip() + class TestTextPanel(wx.Panel): def __init__(self, *args, **kwargs): """ Creates a panel for with a TextCtrl """ super(TestTextPanel, self).__init__(*args, **kwargs) - self.sizer= wx.BoxSizer(wx.VERTICAL) + self.sizer = wx.BoxSizer(wx.VERTICAL) self._create_textctrl() @@ -246,37 +249,37 @@ def __init__(self, *args, **kwargs): self.Layout() def _create_textctrl(self): - ctrl= wx.TextCtrl(self, - size= (-1, 300) + ctrl = wx.TextCtrl(self, + size=(-1, 300) ) - self.sizer.Add(ctrl, 1, wx.EXPAND|wx.ALL, 1) + self.sizer.Add(ctrl, 1, wx.EXPAND | wx.ALL, 1) if __name__ == '__main__': from ide_simple_frame import SimpleFrame, SimplePanel - app= wx.App(None) - frame= SimpleFrame(None, title= "Test frame") + app = wx.App(None) + frame = SimpleFrame(None, title="Test frame") - s_panel= SimplePanel(frame) + s_panel = SimplePanel(frame) frame.sizer.Add(s_panel, 1, wx.EXPAND) - parent= s_panel + parent = s_panel - panel= FramedPanel(parent, style= wx.BORDER_THEME) - FramedPanelEvents(panel) + panel = HeaderedPanel(parent, style=wx.BORDER_THEME) + HeaderedPanelEvents(panel) parent.sizer.Add(panel, 0, wx.EXPAND) panel.add_panel_ctrl(TestTextPanel, "Panel1", 1) panel.minimized_state() panel.set_caption("Panel1 has proportion 1 and set minimized") - panel= FramedPanel(parent, style= wx.BORDER_THEME) - FramedPanelEvents(panel) + panel = HeaderedPanel(parent, style=wx.BORDER_THEME) + HeaderedPanelEvents(panel) parent.sizer.Add(panel, 1, wx.EXPAND) panel.add_panel_ctrl(TestTextPanel, "Panel2", 2) panel.set_caption("Panel1 has proportion 2") - panel= FramedPanel(parent, style= wx.BORDER_THEME) - FramedPanelEvents(panel) + panel = HeaderedPanel(parent, style=wx.BORDER_THEME) + HeaderedPanelEvents(panel) parent.sizer.Add(panel, 1, wx.EXPAND) panel.add_panel_ctrl(TestTextPanel, "Panel3", 3) panel.set_caption("Panel1 has proportion 3") diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 30b2c7a..7726232 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -5,81 +5,223 @@ @author: jakob, David, confab """ import wx -import gui_lib.ide_menu as ide_menu -import gui_lib.ide_toolbar as ide_toolbar -from ide_notebook import Notebook -from ide_notebook_events import NotebookEvents +import os +from ide_menu import MenuBar +from ide_constant import ID_SHOW_TOOLBAR +from ide_toolbar import ToolBarPanel +from ide_mainframe_panel import MainFramePanel +from ide_replace_frame import ReplaceFrame +from utils.interpreter import PythonProcessProtocol +from utils.version import get_python_exe + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + class MainFrame(wx.Frame): """Class with the GUI and GUI functions""" def __init__(self, *args, **kwargs): """Creates the frame, calls some construction methods.""" - wx.Frame.__init__(self, *args, **kwargs) - self.config= wx.GetApp().config + super(MainFrame, self).__init__(*args, **kwargs) + self._config= wx.GetApp().config + self._create_menu() self.SetInitialSize((600,600)) - self.SetSize((int(self.config["MainFrame.Width"]), - int(self.config["MainFrame.Height"]))) - self.Center(wx.BOTH) self.CreateStatusBar() - self.frame_sizer= wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.frame_sizer) - self.frame_panel= wx.Panel(self, style= wx.BORDER_THEME) - self.frame_sizer.Add(self.frame_panel, 1, wx.EXPAND|wx.ALL, 1) - - self.panel_sizer= wx.BoxSizer(wx.VERTICAL) - self.frame_panel.SetSizer(self.panel_sizer) - - self._create_notebook_panel() - - self._create_menu() - self._create_toolbar() - #self._create_notebook_panel() - self._start_up() + self._frame_sizer= wx.BoxSizer(wx.VERTICAL) + self._frame_panel= wx.Panel(self) + self._frame_sizer.Add(self._frame_panel, 1, wx.EXPAND) + self._sizer= wx.BoxSizer(wx.VERTICAL) + self._frame_panel.SetSizer(self._sizer) + self._create_main_panel() + self.SetSizer(self._frame_sizer) + self._apply_config_settings() self.Layout() self.Show() def _create_menu(self): """Creates a menu""" - self.SetMenuBar(ide_menu.MenuBar(self)) - - def _create_toolbar(self): - """Creates a toolbar""" - tb= ide_toolbar.ToolBar(self, style= wx.TB_HORIZONTAL|wx.NO_BORDER| - wx.TB_FLAT) - self.SetToolBar(tb) - - def _create_notebook_panel(self): - '''Create the notebook panel''' - self.notebook = Notebook(self.frame_panel) - NotebookEvents(self.notebook) - self.panel_sizer.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 0) + self.SetMenuBar(MenuBar(self)) + + def _create_main_panel(self): + self._sizer.AddSpacer((-1, 2)) + ctrl= MainFramePanel(self._frame_panel) + self._sizer.Add(ctrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 2) + self.mainframe_panel= ctrl + self._sizer.AddSpacer((-1, 2)) + self.notebook = ctrl.notebook + self.console= ctrl.console_rich_text +#=============================================================================== +# Apply config settings +#=============================================================================== + def _apply_config_settings(self): + """ Applys the stored config values""" + self.SetSize((self._config["MainFrame.Width"], + self._config["MainFrame.Height"])) + self.MoveXY(self._config["MainFrame.XPos"], + self._config["MainFrame.YPos"]) + self.menu_view_toolbar_show( + self._config["MainMenu.View.Toolbar.Show"]) + + +#=============================================================================== +# Store config settings +#=============================================================================== + def _store_config_settings(self): + """ Stores the current config values""" + width, height = self.GetSizeTuple() + self._config["MainFrame.Width"]= width + self._config["MainFrame.Height"]= height + xpos, ypos = self.GetPositionTuple() + self._config["MainFrame.XPos"]= xpos + self._config["MainFrame.YPos"]= ypos + self._config["MainMenu.View.Toolbar.Show"]= \ + self.MenuBar.IsChecked(ID_SHOW_TOOLBAR) +#=============================================================================== +# Editor actions +#=============================================================================== + def editor_tab_get_editor(self): + """ Get the currently active editor instance from notebook""" + return self.notebook.editor_tab_get_editor() + + def editor_open_file(self): + """ Opens a dialof to chose the file to open""" + dirname, filename = self.file_dialog('Open a file', wx.OPEN) + self.notebook.editor_tab_open_file(dirname, filename) - def _start_up(self): - ''' - Adds a new blank editor tab - perhaps open last edited in the future, for now just open new. - ''' - self.notebook.new_editor_tab() + def editor_save(self): + """Saves the currently active editor file""" + active_editor= self.editor_tab_get_editor() + if active_editor.filepath: + active_editor.save_file() + else: + self.save_as_active_editor() + + def editor_save_as(self): + """Save as for the currently active editor file""" + dirname, filename = self.file_dialog('Save file as', + wx.SAVE) + if dirname and filename: + path = os.path.join(dirname, filename) + if path: + active_editor= self.editor_tab_get_editor() + if active_editor.save_file_as(path): + self.notebook.editor_tab_set_active_tab_text(filename) + active_editor.filepath = path + + def editor_undo(self): + """Undo changes in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.Undo() + + def editor_redo(self): + """Redo changes in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.Redo() + + def editor_cut(self): + """Cut changes in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.Cut() + + def editor_copy(self): + """Copy changes in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.Copy() + + def editor_paste(self): + """Paste changes in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.Paste() + + def editor_delete_selection(self): + """Deletes the selection in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.Clear() + + def editor_selectall(self): + """Selectall in active editor""" + active_editor= self.editor_tab_get_editor() + if active_editor: + active_editor.SelectAll() + + def editor_search_and_replace(self): + """ Search and replace in active editor""" + # Create a search frame and hook into the caller. + # Allows this frame to be destroyed by the main window on close. + active_editor= self.editor_tab_get_editor() + replace_frame = ReplaceFrame(active_editor, self, + title="Find and Replace", + size=(410, 150)) + replace_frame.Layout() + def on_run(self): + """Runs selected code in a new window.""" + # Create a test frame and hook into the caller. + # Allows this frame to be destroyed by the main window on close. + reactor = wx.GetApp().this_reactor + +# run_editor = SimpleFrame(wx.GetApp().TopWindow, title="") +# run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) +# run_editor.sizer.Add(run_panel, 1, wx.EXPAND) +# run_editor.Layout() + + run_panel= self.console + active_editor= self.editor_tab_get_editor() + + if active_editor.filepath: + if active_editor.GetModify(): + active_editor.SaveFile(active_editor.filepath) + filename = os.path.split(active_editor.filepath)[1] + run_panel.WriteText("Running %s" % filename) + run_panel.Newline() + reactor.spawnProcess(PythonProcessProtocol(run_panel), + get_python_exe(), + ["python", str(active_editor.filepath)]) + + else: + run_panel.WriteText("Running unsaved script.") + run_panel.Newline() + script = StringIO() + script.write(active_editor.GetText()) + script.seek(0) + reactor.spawnProcess(PythonProcessProtocol(run_panel), + get_python_exe(), + ["python", "-c", script.read()]) + + + return run_panel +#=============================================================================== +# Mainframe actions +#=============================================================================== def on_exit(self): '''Handles the event triggered by the user to exit''' dial = wx.MessageDialog(self,'Do you really want to exit?', 'Exit Python IDE', wx.YES_NO | wx.ICON_QUESTION) - # TODO: we also need to work in a way of detecting if a file - # has changed since last save/load, and if so prompt the user - # to save before exit. - - self.config["MainFrame.Width"]= self.GetSize()[0] - self.config["MainFrame.Height"]= self.GetSize()[1] if dial.ShowModal() == wx.ID_YES: - self.Destroy() + self._store_config_settings() + self.Destroy() - def get_file(self, prompt, style): + def file_dialog(self, prompt, style): """Abstracted method to prompt the user for a file path. Returns a 2-tuple consisting of directory path and file name.""" + + # TODO: we also need to work in a way of detecting if a file + # has changed since last save/load, and if so prompt the user + # to save before exit. (Yoriz - building on the above id like it + # only to ask if there are unsaved changes otherwise just close + # it can get anoying having to click ok every time you want to close) + dlg = wx.FileDialog(self, prompt, '.', '', '*.*', style) if dlg.ShowModal() == wx.ID_OK: dirname = dlg.GetDirectory() @@ -88,9 +230,18 @@ def get_file(self, prompt, style): return dirname, filename else: return "", "" #Cancels the action +#=============================================================================== +# state setters +#=============================================================================== + def menu_view_toolbar_show(self, enable= True): + self.MenuBar.Check(ID_SHOW_TOOLBAR, enable) + self.mainframe_panel.toolbar_show(enable) + if __name__=='__main__': import ide_test_app as wx_app app = wx_app.Wx_App(False) - MainFrame(None, title= "Testing main frame with no events") + main_frame= MainFrame(None, title= "Testing main frame with no events") + main_frame.Layout() + main_frame.mainframe_panel.toolbar_show(False) app.MainLoop() From af3061156e5f12512ad93031d96b62d8ef40d54a Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:20:32 +0100 Subject: [PATCH 081/141] More restructuring --- .../gui_lib/ide_mainframe_events.py | 189 ++++++++++++------ 1 file changed, 125 insertions(+), 64 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 89fc570..00a4db9 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -15,114 +15,175 @@ def __init__(self, view, model= None): """Sets the binds and does other stuff""" self.view = view self.model = model - self._create_binds() - - def _create_binds(self): - """Create binds""" - self.view.Bind(wx.EVT_UPDATE_UI, self._evt_update_ui) - self.view.Bind(wx.EVT_MENU, self._on_new, id=ide.ID_NEW) - self.view.Bind(wx.EVT_MENU, self._on_open, id=ide.ID_OPEN) - self.view.Bind(wx.EVT_MENU, self._on_exit, id=ide.ID_EXITAPP) - self.view.Bind(wx.EVT_MENU, self._on_save, id=ide.ID_SAVE) - self.view.Bind(wx.EVT_MENU, self._on_save_as, id=ide.ID_SAVEAS) - self.view.Bind(wx.EVT_CLOSE, self._on_exit) - self.view.Bind(wx.EVT_MENU, self._on_editor_close, id=ide.ID_CLOSETAB) - self.view.Bind(wx.EVT_MENU, self._on_editor_undo, id=ide.ID_UNDO) - self.view.Bind(wx.EVT_MENU, self._on_editor_redo, id=ide.ID_REDO) - self.view.Bind(wx.EVT_MENU, self._on_editor_cut, id=ide.ID_CUT) - self.view.Bind(wx.EVT_MENU, self._on_editor_copy, id=ide.ID_COPY) - self.view.Bind(wx.EVT_MENU, self._on_editor_paste, id=ide.ID_PASTE) - self.view.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) - self.view.Bind(wx.EVT_MENU, self._on_editor_selectall, - id=ide.ID_SELECTALL) - self.view.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, - id=ide.ID_SEARCH) - self.view.Bind(wx.EVT_MENU, self._on_editor_run, id=ide.ID_RUNFILE) - + self.mainframe= self.view + self.mainframe_panel= self.mainframe.mainframe_panel + self.notebook= self.view.mainframe_panel.notebook + self._create_menu_file_binds() + self._create_menu_edit_binds() + self._create_menu_view_binds() + self._create_menu_search_binds() + self._create_menu_run_binds() + self._create_remaining_binds() +#-------------------------------------------------------------------- Menu File + def _create_menu_file_binds(self): + """ Creates the binds for the file menu""" + self.mainframe.Bind(wx.EVT_MENU, self._on_new, id=ide.ID_NEW) + self.mainframe.Bind(wx.EVT_MENU, self._on_open, id=ide.ID_OPEN) + self.mainframe.Bind(wx.EVT_MENU, self._on_save, id=ide.ID_SAVE) + self.mainframe.Bind(wx.EVT_MENU, self._on_save_as, id=ide.ID_SAVEAS) + self.mainframe.Bind(wx.EVT_MENU, self._on_close_tab, id=ide.ID_CLOSETAB) + self.mainframe.Bind(wx.EVT_MENU, self._on_exit_app, id=ide.ID_EXITAPP) + def _on_new(self, event): """Opens a new tab with a new editor instance""" - self.view.notebook.new_editor_tab() + self.notebook.editor_tab_new() def _on_open(self, event): """Opens a new tab and ask for a file to load""" - self.view.notebook.open_editor_tab() + self.mainframe.editor_open_file() - def _on_editor_close(self, event): - """Closes the current editor tab""" - self.view.notebook.close_active_editor() - def _on_save(self, event): """Saves the currently active file""" - self.view.notebook.save_active_editor_tab() + self.mainframe.editor_save() def _on_save_as(self, event): """Save as required filename""" - self.view.notebook.save_as_active_editor_tab() - + self.mainframe.editor_save_as() + + def _on_close_tab(self, event): + """Closes the current editor tab""" + self.notebook.editor_tab_close_active() + + def _on_exit_app(self, event): + """ application wants to exit""" + self.mainframe.on_exit() +#-------------------------------------------------------------------- Menu edit + def _create_menu_edit_binds(self): + """ Creates the binds for the edit menu""" + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_undo, id=ide.ID_UNDO) + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_redo, id=ide.ID_REDO) + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_cut, id=ide.ID_CUT) + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_copy, id=ide.ID_COPY) + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_paste, id=ide.ID_PASTE) + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_selectall, + id=ide.ID_SELECTALL) + def _on_editor_undo(self, event): """Undo for the current editor tab""" - self.view.notebook.undo_active_editor() + active_editor= self.notebook.editor_tab_get_editor() + if active_editor.CanUndo(): + self.mainframe.editor_undo() def _on_editor_redo(self, event): """Redo for the current editor tab""" - self.view.notebook.redo_active_editor() + active_editor= self.notebook.editor_tab_get_editor() + if active_editor.CanRedo(): + self.mainframe.editor_redo() def _on_editor_cut(self, event): """Cut for the current editor tab""" - self.view.notebook.cut_active_editor() + active_editor= self.notebook.editor_tab_get_editor() + if active_editor.CanCut(): + self.mainframe.editor_cut() def _on_editor_copy(self, event): """Copy for the current editor tab""" - self.view.notebook.copy_active_editor() + active_editor= self.notebook.editor_tab_get_editor() + if active_editor.CanCopy(): + self.mainframe.editor_copy() def _on_editor_paste(self, event): """paste for the current editor tab""" - self.view.notebook.paste_active_editor() + active_editor= self.notebook.editor_tab_get_editor() + if active_editor.CanPaste(): + self.mainframe.editor_paste() def _on_editor_delete(self, event): """Clear for the current editor tab""" - self.view.notebook.clear_active_editor() + active_editor= self.notebook.editor_tab_get_editor() + if active_editor.CanDelete(): + self.mainframe.editor_delete_selection() def _on_editor_selectall(self, event): """Selectall for the current editor tab""" - self.view.notebook.selectall_active_editor() + if active_editor: + self.mainframe.editor_selectall() +#-------------------------------------------------------------------- Menu View + def _create_menu_view_binds(self): + """ Creates the binds for the view menu""" + self.mainframe.Bind(wx.EVT_MENU, self._on_view_toolbar_state, + id= ide.ID_SHOW_TOOLBAR) + def _on_view_toolbar_state(self, event): + self.mainframe_panel.toolbar_show(event.Checked()) +#------------------------------------------------------------------ Menu search + def _create_menu_search_binds(self): + """ Creates the binds for the search menu""" + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, + id=ide.ID_SEARCH) + def _on_editor_search_and_replace(self, event): """Replace for the current editor tab""" - self.view.notebook.replace_active_editor() - - def _on_exit(self, event): - """ application wants to exit""" - self.view.on_exit() - + self.mainframe.editor_search_and_replace() +#--------------------------------------------------------------------- Menu run + def _create_menu_run_binds(self): + """ Creates the binds for the run menu""" + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_run, + id=ide.ID_RUNFILE) + def _on_editor_run(self, event): """Runs selected code in a new window.""" - self.view.notebook.run_active_editor() - + self.mainframe.on_run() +#--------------------------------------------------------------- Non menu binds + def _create_remaining_binds(self): + """ Creates the non menu binds""" + self.view.Bind(wx.EVT_UPDATE_UI, self._evt_update_ui) + self.mainframe.Bind(wx.EVT_CLOSE, self._on_exit_app) + def _evt_update_ui(self, event): """Events to update the view""" + event_id = event.GetId() + active_editor= self.notebook.editor_tab_get_editor() if event_id== ide.ID_CUT: - event.Enable(self.view.notebook.active_editor_can_cut()) + if active_editor: + event.Enable(active_editor.CanCut()) + else: + event.Enable(False) elif event_id== ide.ID_COPY: - event.Enable(self.view.notebook.active_editor_can_copy()) + if active_editor: + event.Enable(active_editor.CanCopy()) + else: + event.Enable(False) elif event_id== ide.ID_PASTE: - event.Enable(self.view.notebook.active_editor_can_paste()) + if active_editor: + event.Enable(active_editor.CanPaste()) + else: + event.Enable(False) elif event_id== ide.ID_DELETE: - event.Enable(self.view.notebook.active_editor_can_delete()) + if active_editor: + event.Enable(active_editor.CanDelete()) + else: + event.Enable(False) elif event_id== ide.ID_UNDO: - event.Enable(self.view.notebook.active_editor_can_undo()) + if active_editor: + event.Enable(active_editor.CanUndo()) + else: + event.Enable(False) elif event_id== ide.ID_REDO: - event.Enable(self.view.notebook.active_editor_can_redo()) - elif event_id== ide.ID_SAVE: - event.Enable(self.view.notebook.active_editor_can_save()) - elif event_id== ide.ID_SAVEAS: - event.Enable(self.view.notebook.active_editor_can_saveas()) - elif event_id== ide.ID_CLOSETAB: - event.Enable(self.view.notebook.active_editor_can_close_tab()) - elif event_id== ide.ID_SEARCH: - event.Enable(self.view.notebook.active_editor_can_search()) - elif event_id== ide.ID_RUNFILE: - event.Enable(self.view.notebook.active_editor_can_run()) + if active_editor: + event.Enable(active_editor.CanRedo()) + else: + event.Enable(False) + elif event_id in (ide.ID_SAVE, ide.ID_SAVEAS, ide.ID_CLOSETAB, + ide.ID_SEARCH, ide.ID_RUNFILE): + if active_editor: + event.Enable(True) + else: + event.Enable(False) else: event.Skip() + + + From 6b56b878a73e83ed66e3dcf3f39e01a420dc99f7 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:22:44 +0100 Subject: [PATCH 082/141] New module, mainframe items split into there own panel, tool bar able to be shown/hiden, and added to config --- pythonforumide/gui_lib/ide_mainframe_panel.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 pythonforumide/gui_lib/ide_mainframe_panel.py diff --git a/pythonforumide/gui_lib/ide_mainframe_panel.py b/pythonforumide/gui_lib/ide_mainframe_panel.py new file mode 100644 index 0000000..12998d5 --- /dev/null +++ b/pythonforumide/gui_lib/ide_mainframe_panel.py @@ -0,0 +1,75 @@ +''' +Created on 4 Aug 2011 + +@author: Main +''' + +import wx + +from ide_notebook import NoteBookPanel +from ide_notebook_events import NotebookEvents +from ide_headered_panel import HeaderedPanel, HeaderedPanelEvents +from ide_console import ConsolePanel + +from ide_constant import ID_SHOW_TOOLBAR +from ide_toolbar import ToolBarPanel + +class MainFramePanel(wx.Panel): + """ Creates the mainframe panel""" + def __init__(self, *args, **kwargs): + super(MainFramePanel, self).__init__(*args, **kwargs) + self._sizer = wx.BoxSizer(wx.VERTICAL) + self._create_toolbar() + self._create_notebook_panel() + self._create_console() + self._create_first_tab() + self.SetSizer(self._sizer) + self.Layout() + + def _create_toolbar(self): + """Creates a toolbar""" + ctrl= ToolBarPanel(self) + self._sizer.Add(ctrl, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 3) + self.toolbar= ctrl + + def _create_notebook_panel(self): + """ Create the notebook panel """ + self._sizer.AddSpacer((-1, 2)) + headered_panel = HeaderedPanel(self, style=wx.BORDER_THEME) + HeaderedPanelEvents(headered_panel) + self._sizer.Add(headered_panel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) + ctrl = headered_panel.add_panel_ctrl(NoteBookPanel, "Editor", 100) + NotebookEvents(ctrl) + self.notebook = ctrl.notebook + + def _create_console(self): + """ Create a console window """ + self._sizer.AddSpacer((-1, 4)) + headered_panel = HeaderedPanel(self, style=wx.BORDER_THEME) + HeaderedPanelEvents(headered_panel) + self._sizer.Add(headered_panel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) + ctrl = headered_panel.add_panel_ctrl(ConsolePanel, "Console", 30) + self.console_rich_text = ctrl._rt_ctrl + self._sizer.AddSpacer((-1, 2)) + + def _create_first_tab(self): + """Adds a new blank editor tab + perhaps open last edited in the future, for now just open new.""" + self.notebook.editor_tab_new() + + def toolbar_show(self, enable= True): + """ Show/hide the toolbar""" + self.toolbar.Show(enable) + self.Layout() + + +if __name__ == '__main__': + import ide_test_app as wx_app + import ide_simple_frame + app = wx_app.Wx_App(False) + frame = ide_simple_frame.SimpleFrame(None, + title="Testing mainframe_panel without events") + panel = MainFramePanel(frame) + frame.sizer.Add(panel, 1, wx.EXPAND) + frame.Layout() + app.MainLoop() From ab80831a890be788ab4fe01d1d65c970bbfec036 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:26:28 +0100 Subject: [PATCH 083/141] Lots of code shifting, using differant more flexable notebook, that can have option tab style in the view menu (to be added) --- pythonforumide/gui_lib/ide_notebook.py | 208 +++++++------------------ 1 file changed, 59 insertions(+), 149 deletions(-) diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index d9ce25a..60ea843 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -8,119 +8,80 @@ import os import wx -import wx.aui as aui -from ide_editor import Editor +import wx.lib.agw.flatnotebook as fnb +from ide_editor import EditorPanel +from ide_framed_panel import HeaderPanel -class Notebook(aui.AuiNotebook): +class NoteBookPanel(wx.Panel): def __init__(self, *args, **kwargs): - super(Notebook, self).__init__(*args, **kwargs) - self._active_editor_page= None - self._active_tab_index= None + """ Create a panel with a containing NoteBook control""" + super(NoteBookPanel, self).__init__(*args, **kwargs) + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + self._create_notebook(sizer) + + def _create_notebook(self, sizer): + """ Creates the NoteBook control""" + ctrl = NoteBook(self, agwStyle=fnb.FNB_X_ON_TAB | + fnb.FNB_NO_X_BUTTON | fnb.FNB_NO_TAB_FOCUS | fnb.FNB_VC8, + pos=(-100, -100)) + sizer.Add(ctrl, 1 , wx.EXPAND | wx.ALL, 0) + self.notebook = ctrl + +class NoteBook(fnb.FlatNotebook): + def __init__(self, *args, **kwargs): + super(NoteBook, self).__init__(*args, **kwargs) + self._active_editor = None + self._active_tab_index = None - def new_editor_tab(self, page_name= ""): + def editor_tab_new(self, page_name=""): """Opens a new editor tab""" - self.Freeze() - editor= Editor(self) - self.Thaw() - self.AddPage(editor, page_name) - self._active_editor_page= editor - self.name_untitled_pages() - wx.CallAfter(self.SetSelection, self.GetPageCount()-1) - return editor + editor_panel = EditorPanel(self) + self.AddPage(editor_panel, page_name) + self._active_editor = editor_panel.editor + self.editor_tab_name_untitled_tabs() + wx.CallAfter(self.SetSelection, self.GetPageCount() - 1) + return editor_panel.editor - def open_editor_tab(self): + def editor_tab_open_file(self, dirname, filename): """Loads a slected file into a new editor tab""" - dirname, filename= self.GetGrandParent().get_file('Open a file', - wx.OPEN) + if dirname and filename: - editor= self.new_editor_tab(filename) + editor = self.editor_tab_new(filename) path = os.path.join(dirname, filename) editor.load_file(path) - - def save_active_editor_tab(self): - """Saves the currently active editor file""" - if self._active_editor_page.filepath: - self._active_editor_page.save_file() - else: - self.save_as_active_editor_tab() - def save_as_active_editor_tab(self): - """Save as for the currently active editor file""" - dirname, filename = self.GetGrandParent().get_file('Save file as', - wx.SAVE) - if dirname and filename: - path = os.path.join(dirname, filename) - if path: - if self._active_editor_page.save_file_as(path): - self.set_active_tab_text(filename) - self._active_editor_page.filepath = path + def editor_tab_close_active(self): + """Closes the currently active editor tab""" + self.DeletePage(self._active_tab_index) + wx.CallAfter(self.editor_tab_name_untitled_tabs) - def set_active_tab_text(self, text): + def editor_tab_get_editor(self): + """ Returns the currently active editor instance or None""" + return self._active_editor + + def editor_tab_set_active_tab_text(self, text): """Rename the currently active tab text""" - if self._active_tab_index> -1: + if self._active_tab_index > -1: self.SetPageText(self._active_tab_index, text) - def name_untitled_pages(self): + def editor_tab_name_untitled_tabs(self): """Renumbers the untitled pages""" self.Freeze() - empty_page_no= 1 + empty_page_no = 1 for page_no in xrange(self.GetPageCount()): - page_text= self.GetPageText(page_no) + page_text = self.GetPageText(page_no) if "Untitled" in page_text or not page_text: - page= self.GetPage(page_no) + page = self.GetPage(page_no) self.SetPageText(page_no, "Untitled%s.py" % (empty_page_no)) - empty_page_no+= 1 + empty_page_no += 1 self.Thaw() - - def get_active_editor(self): - return self._active_editor_page - - def close_active_editor(self): - """Closes the currently active editor tab""" - self.DeletePage(self._active_tab_index) - wx.CallAfter(self.name_untitled_pages) - - def undo_active_editor(self): - """Undo changes in active editor""" - self._active_editor_page.on_undo() - - def redo_active_editor(self): - """Redo changes in active editor""" - self._active_editor_page.on_redo() - - def cut_active_editor(self): - """Cut changes in active editor""" - self._active_editor_page.on_cut() - - def copy_active_editor(self): - """Copy changes in active editor""" - self._active_editor_page.on_copy() - - def paste_active_editor(self): - """Paste changes in active editor""" - self._active_editor_page.on_paste() - - def clear_active_editor(self): - """Paste changes in active editor""" - self._active_editor_page.on_clear() - - def selectall_active_editor(self): - """Sslectall changes in active editor""" - self._active_editor_page.on_select_all() - - def replace_active_editor(self): - """Replace changes in active editor""" - self._active_editor_page.on_replace() - - def run_active_editor(self): - """Runs selected code in a new window.""" - self._active_editor_page.on_run() def active_editor_can_cut(self): """Returns True if the active editor can cut""" try: - if self._active_editor_page: - return self._active_editor_page.CanCut() + if self._active_editor: + return self._active_editor.CanCut() else: return False except AttributeError: @@ -129,8 +90,8 @@ def active_editor_can_cut(self): def active_editor_can_copy(self): """Returns True if the active editor can copy""" try: - if self._active_editor_page: - return self._active_editor_page.CanCopy() + if self._active_editor: + return self._active_editor.CanCopy() else: return False except AttributeError: @@ -138,81 +99,30 @@ def active_editor_can_copy(self): def active_editor_can_paste(self): """Returns True if the active editor can paste""" - if self._active_editor_page: - return self._active_editor_page.CanPaste() + if self._active_editor: + return self._active_editor.CanPaste() else: return False def active_editor_can_delete(self): """Returns True if the active editor can delete""" try: - if self._active_editor_page: - return self._active_editor_page.HasSelection() + if self._active_editor: + return self._active_editor.HasSelection() else: return False except AttributeError: return True - - def active_editor_can_undo(self): - """Returns True if the active editor can undo""" - if self._active_editor_page: - return self._active_editor_page.CanUndo() - else: - return False - - def active_editor_can_redo(self): - """Returns True if the active editor can redo""" - if self._active_editor_page: - return self._active_editor_page.CanRedo() - else: - return False - - def active_editor_can_save(self): - """Returns True if the active editor can save""" - if self._active_editor_page: - return True - else: - return False - - def active_editor_can_saveas(self): - """Returns True if the active editor can saveas""" - if self._active_editor_page: - return True - else: - return False - - def active_editor_can_close_tab(self): - """Returns True if the active editor can close""" - if self._active_editor_page: - return True - else: - return False - - def active_editor_can_search(self): - """Returns True if the active editor can search""" - if self._active_editor_page: - return True - else: - return False - - def active_editor_can_run(self): - """Returns True if the active editor can search""" - if self._active_editor_page: - return True - else: - return False -if __name__=='__main__': +if __name__ == '__main__': import ide_test_app as wx_app import ide_simple_frame app = wx_app.Wx_App(False) frame = ide_simple_frame.SimpleFrame(None, title="Testing notebook without events") - panel= ide_simple_frame.TestPanel(frame) + panel = NoteBookPanel(frame) frame.sizer.Add(panel, 1, wx.EXPAND) - notebook= Notebook(panel) - notebook.new_editor_tab() - panel.sizer.Add(notebook, 1, wx.EXPAND) + panel.notebook.editor_tab_new() frame.Layout() app.MainLoop() From dfe2a97edbce8276cb27e524691c1d6cce0abf83 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:27:50 +0100 Subject: [PATCH 084/141] Made the notebook tab changes work correctly --- pythonforumide/gui_lib/ide_notebook_events.py | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/pythonforumide/gui_lib/ide_notebook_events.py b/pythonforumide/gui_lib/ide_notebook_events.py index b9d6fab..4f7b56f 100644 --- a/pythonforumide/gui_lib/ide_notebook_events.py +++ b/pythonforumide/gui_lib/ide_notebook_events.py @@ -5,28 +5,46 @@ """ import wx -import wx.aui as aui +import wx.lib.agw.flatnotebook as fnb +from wx.lib.agw.flatnotebook import EVT_FLATNOTEBOOK_PAGE_CLOSED, \ + EVT_FLATNOTEBOOK_PAGE_CHANGED, \ + EVT_FLATNOTEBOOK_PAGE_CLOSING class NotebookEvents(object): - def __init__(self, view, model= None): + """ Creates the events for the notebook""" + def __init__(self, view, model=None): self.view = view self.model = model + self.notebook= self.view.notebook self._create_binds() def _create_binds(self): """Create binds""" - self.view.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, + self.notebook.Bind(EVT_FLATNOTEBOOK_PAGE_CLOSED, self._on_page_closed) - self.view.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, + + self.notebook.Bind(EVT_FLATNOTEBOOK_PAGE_CHANGED, self._on_page_changed) + self.notebook.Bind(EVT_FLATNOTEBOOK_PAGE_CLOSING, + self._on_page_closing) + def _on_page_changed(self, event): """sets the currently active page index and editor page""" - self.view._active_tab_index = event.Selection - self.view._active_editor_page = self.view.GetPage(self.view._active_tab_index) + nb_obj = event.GetEventObject() + self.notebook._active_tab_index = nb_obj.GetSelection() + active_editor_panel = nb_obj.GetCurrentPage() + self.notebook._active_editor = active_editor_panel.editor + event.Skip() def _on_page_closed(self, event): """Sets currently active page to none and renames the untitled pages""" event.Skip() - self.view._active_editor_page = None - wx.CallAfter(self.view.name_untitled_pages) + wx.CallAfter(self.notebook.editor_tab_name_untitled_tabs) + + def _on_page_closing(self, event): + new_index= event.GetSelection() + if not new_index: + self.notebook._active_editor = None + event.Skip() + From 102b25c928e3be8ac115618c433eec28f33fb9ee Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:33:47 +0100 Subject: [PATCH 085/141] Added sizers and some more gui ctrls --- pythonforumide/gui_lib/ide_replace_frame.py | 96 +++++++++++++++++++-- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 30d585b..df4e903 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -1,19 +1,25 @@ import wx import string from itertools import izip_longest - from ide_simple_frame import SimpleFrame -class ReplaceFrame(SimpleFrame): +class ReplaceFrame(wx.Dialog): """Class with the GUI and the GUI functions""" def __init__(self, active_editor, *args, **kwargs): """"Displays the frame, creates the GUI""" - super(ReplaceFrame, self).__init__(*args, **kwargs) - self.create_gui() - self.txt_to_replace.SetFocus() - self.active_editor = active_editor + self.sizer= wx.BoxSizer(wx.VERTICAL) + + self.panel= ReplaceFramePanel(self) + self.sizer.Add(self.panel, 1, wx.EXPAND) + self.SetSizerAndFit(self.sizer) + +# self.create_gui() +# self.txt_to_replace.SetFocus() +# self.active_editor = active_editor + + self.Show() def create_gui(self): """Creates and displays the GUI""" @@ -31,7 +37,6 @@ def create_gui(self): self.replace_id = wx.NewId() self.replace_button = wx.Button(_panel, self.replace_id, "Replace", pos=(5, 110), size=(90, -1)) self.case_check = wx.CheckBox(_panel, -1, "Case Sensitive", pos=(5, 80), size=(-1, -1)) - self.Bind(wx.EVT_BUTTON, self.on_replace, id=self.replace_id) self.sizer.Add(_panel, 1, wx.EXPAND) @@ -60,3 +65,80 @@ def on_replace(self, event): self.active_editor.GetText(), self.str_to_replace, self.str_replace_with)) self.Destroy() + +class ReplaceFramePanel(wx.Panel): + def __init__(self, *args, **kwargs): + super(ReplaceFramePanel, self).__init__(*args, **kwargs) + self.sizer= wx.BoxSizer(wx.HORIZONTAL) + self._create_vsizer(self.sizer) + self._create_buttons(self.sizer) + self.SetSizer(self.sizer) + self.Layout() + + def _create_vsizer(self, sizer): + vsizer= wx.BoxSizer(wx.VERTICAL) + sizer.Add(vsizer) + vsizer.AddSpacer((-1, 10)) + self._create_inputs(vsizer) + vsizer.AddSpacer((-1, 10)) + self._create_options(vsizer) + vsizer.AddSpacer((-1, 10)) + + def _create_inputs(self, sizer): + grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap=10) + sizer.Add(grid_sizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10) + ctrl= wx.StaticText(self, label= "Search for:") + grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + ctrl= wx.TextCtrl(self, size= (150, -1)) + grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + self.ctrl_txt_exisiting= ctrl + ctrl= wx.StaticText(self, label= "Replace with:") + grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + ctrl= wx.TextCtrl(self, size= (150, -1)) + grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + self.ctrl_txt_new= ctrl + + def _create_buttons(self, sizer): + box_sizer= wx.BoxSizer(wx.VERTICAL) + sizer.Add(box_sizer) + box_sizer.AddSpacer((-1, 10)) + ctrl= wx.Button(self, label= "Find next") + box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.btn_next= ctrl + box_sizer.AddSpacer((-1, 5)) + ctrl= wx.Button(self, label= "Replace") + box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.btn_replace= ctrl + box_sizer.AddSpacer((-1, 5)) + ctrl= wx.Button(self, label= "Replace all") + box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.btn_replace_all= ctrl + box_sizer.AddSpacer((-1, 5)) + ctrl= wx.Button(self, label= "Cancel") + box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.btn_cancel= ctrl + box_sizer.AddSpacer((-1, 10)) + + def _create_options(self, sizer): + box_sizer= wx.BoxSizer(wx.VERTICAL) + sizer.Add(box_sizer) + ctrl= wx.CheckBox(self, label= " Match whole word only") + box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.check_whole_word= ctrl + box_sizer.AddSpacer((-1, 10)) + ctrl= wx.CheckBox(self, label= " Match case") + box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.check_match_case= ctrl + + + + + + + + +if __name__ == '__main__': + wx_app= wx.App(None) + ReplaceFrame(active_editor= None, parent= None, + title="Find and Replace", size=(410, 150)) + wx_app.MainLoop() \ No newline at end of file From ab9414c6bc9990934b7a59f16c5e12095f5e089f Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:36:02 +0100 Subject: [PATCH 086/141] Toolbar now sits on its own panel which can be shown/hiden --- pythonforumide/gui_lib/ide_toolbar.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/pythonforumide/gui_lib/ide_toolbar.py b/pythonforumide/gui_lib/ide_toolbar.py index 3cb0d7b..9f23ca3 100644 --- a/pythonforumide/gui_lib/ide_toolbar.py +++ b/pythonforumide/gui_lib/ide_toolbar.py @@ -8,12 +8,24 @@ import ide_constant as ide from ide_images import menu_icons +class ToolBarPanel(wx.Panel): + """ Creates a panel to hold the toolbar""" + def __init__(self, *args, **kwargs): + super(ToolBarPanel, self).__init__(*args, **kwargs) + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + ctrl = ToolBar(self, style=wx.TB_HORIZONTAL | wx.TB_FLAT) + sizer.Add(ctrl, 0, wx.EXPAND | wx.LEFT) + self.toolbar = ctrl + self.Layout() + + class ToolBar(wx.ToolBar): def __init__(self, *args, **kwargs): """Create the toolbar""" - super(ToolBar, self).__init__( *args, **kwargs) - self.SetToolBitmapSize((24,24)) + super(ToolBar, self).__init__(*args, **kwargs) + self.SetToolBitmapSize((24, 24)) self._add_toolbar_btn(ide.ID_NEW, ide.id_text_new, menu_icons.get_icon_new()) @@ -47,6 +59,7 @@ def __init__(self, *args, **kwargs): self.Realize() - def _add_toolbar_btn(self, id, id_text, icon_bmp= None): - self.AddLabelTool(id= id, label= id_text.toolbar, bitmap= icon_bmp, - shortHelp= id_text.toolbar, longHelp= id_text.status) + def _add_toolbar_btn(self, id, id_text, icon_bmp=None): + """ Creates tool bar buttons""" + self.AddLabelTool(id=id, label=id_text.toolbar, bitmap=icon_bmp, + shortHelp=id_text.toolbar, longHelp=id_text.status) From ef8c176e21d5940400f38a46b6f66c1c9deb2b3c Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:41:45 +0100 Subject: [PATCH 087/141] Panel added to contain the editor --- pythonforumide/gui_lib/ide_editor.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index a2d95ea..540bee1 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -35,6 +35,22 @@ 'size2': 10, } +class EditorPanel(wx.Panel): + def __init__(self, *args, **kwargs): + """ Create a panel with a containing Editor control""" + super(EditorPanel, self).__init__(*args, **kwargs) + self.SetBackgroundColour((255, 255, 255)) + sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(sizer) + self._create_editor(sizer) + + def _create_editor(self, sizer): + """ Creates the Editor control""" + ctrl = Editor(self, style=wx.BORDER_THEME, pos=(-100, -100)) + sizer.Add(ctrl, 1 , wx.EXPAND | wx.ALL, 1) + self.editor = ctrl + + class Editor(stc.StyledTextCtrl): """Inherits wxStyledTextCtrl and handles all editor functions""" def __init__(self, parent): From be34b99dd22fb19d510d6b937da32b3d791ed509 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:44:17 +0100 Subject: [PATCH 088/141] Removed unwanted import --- pythonforumide/gui_lib/ide_notebook.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 60ea843..84fb230 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -10,7 +10,6 @@ import wx import wx.lib.agw.flatnotebook as fnb from ide_editor import EditorPanel -from ide_framed_panel import HeaderPanel class NoteBookPanel(wx.Panel): def __init__(self, *args, **kwargs): From ee44655c3f291e812f52bb5ce57f2f07e82effc5 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 00:52:11 +0100 Subject: [PATCH 089/141] minor change, but that should be a runnable, new look, new features, still commenting to do and stuff, run now kinda working in its own console panel that can be maximized off the main frame, but it needs some one to do there twisted magic on it --- pythonforumide/config/Ide_Config.cfg | 14 +++++++------- pythonforumide/gui_lib/ide_editor.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index ae26cbe..dd3cc81 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] -indent = str>4 -usetab = str>False -test.int = int>25 -test.bool = bool>True -test.float = float>0.75 -mainframe.height = int>826 -mainframe.width = int>842 +mainmenu.view.toolbar.show = bool>True +indent = int>4 +mainframe.ypos = int>107 +usetab = int>0 +mainframe.xpos = int>183 +mainframe.height = int>856 +mainframe.width = int>1301 diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 540bee1..42de5c7 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -53,9 +53,9 @@ def _create_editor(self, sizer): class Editor(stc.StyledTextCtrl): """Inherits wxStyledTextCtrl and handles all editor functions""" - def __init__(self, parent): + def __init__(self, *args, **kwargs): """Starts the editor and calls some editor-related functions""" - super(Editor, self).__init__(parent) + super(Editor, self).__init__(*args, **kwargs) self.conf = wx.GetApp().config From ba32f1633758d04220d2714ffc782b0374115115 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Fri, 5 Aug 2011 01:11:30 +0100 Subject: [PATCH 090/141] Bugfix on selectall event --- pythonforumide/config/Ide_Config.cfg | 8 ++++---- pythonforumide/gui_lib/ide_mainframe_events.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index dd3cc81..7d56a39 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>107 +mainframe.ypos = int>53 usetab = int>0 -mainframe.xpos = int>183 -mainframe.height = int>856 -mainframe.width = int>1301 +mainframe.xpos = int>70 +mainframe.height = int>848 +mainframe.width = int>1259 diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 00a4db9..4ccce9e 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -107,6 +107,7 @@ def _on_editor_delete(self, event): def _on_editor_selectall(self, event): """Selectall for the current editor tab""" + active_editor= self.notebook.editor_tab_get_editor() if active_editor: self.mainframe.editor_selectall() #-------------------------------------------------------------------- Menu View From 9b46a8f01c53830b7eb070ad5799b39de439899f Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Fri, 5 Aug 2011 09:48:21 +0100 Subject: [PATCH 091/141] p --- pythonforumide/gui_lib/ide_editor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index dfed786..4a040db 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -14,7 +14,6 @@ from utils.autocomplete import CodeCompletion import wx.richtext import wx.stc as stc -from wx import _stc import os.path import wx From e673385af8c9dc4d2d24617f030ca241db0c3b8a Mon Sep 17 00:00:00 2001 From: David Gomes Date: Fri, 5 Aug 2011 17:59:30 +0200 Subject: [PATCH 092/141] Edited the README file a bit. --- README | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README b/README index 001ca4d..2c90216 100644 --- a/README +++ b/README @@ -1,4 +1,6 @@ -Still in development but now can be considered at Alpha stage. +PythonForumIDE is a cross-platform Python IDE, and it is a Python-Forum Project (www.python-forum.org/pythonforum/). + +Try it at your own rish, since it is still in development, even though it can be considered to be at Alpha stage. Report all bugs to https://github.com/Python-Forum/PythonForumIDE/issues From 792acdc9707a30f69996e026d7eea55165056b3e Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Thu, 11 Aug 2011 20:51:46 +0100 Subject: [PATCH 093/141] changed icons to 16, 16 from 24, 24 looks better and takes up less room --- pythonforumide/config/Ide_Config.cfg | 8 +++---- pythonforumide/gui_lib/ide_toolbar.py | 2 +- pythonforumide/ide_images/menu_icons.py | 30 ++++++++++++------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 7d56a39..e4cddd4 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>53 +mainframe.ypos = int>117 usetab = int>0 -mainframe.xpos = int>70 -mainframe.height = int>848 -mainframe.width = int>1259 +mainframe.xpos = int>276 +mainframe.height = int>810 +mainframe.width = int>1024 diff --git a/pythonforumide/gui_lib/ide_toolbar.py b/pythonforumide/gui_lib/ide_toolbar.py index 9f23ca3..ad8639d 100644 --- a/pythonforumide/gui_lib/ide_toolbar.py +++ b/pythonforumide/gui_lib/ide_toolbar.py @@ -25,7 +25,7 @@ class ToolBar(wx.ToolBar): def __init__(self, *args, **kwargs): """Create the toolbar""" super(ToolBar, self).__init__(*args, **kwargs) - self.SetToolBitmapSize((24, 24)) + self.SetToolBitmapSize((16, 16)) self._add_toolbar_btn(ide.ID_NEW, ide.id_text_new, menu_icons.get_icon_new()) diff --git a/pythonforumide/ide_images/menu_icons.py b/pythonforumide/ide_images/menu_icons.py index 44afcbf..83da564 100644 --- a/pythonforumide/ide_images/menu_icons.py +++ b/pythonforumide/ide_images/menu_icons.py @@ -21,68 +21,68 @@ def __call__(self): @MissingArt def get_icon_new(): - """Returns a (24,24) new icon bmp""" + """Returns a new icon bmp""" return _get_art_bmp(wx.ART_NEW) @MissingArt def get_icon_open(): - """Returns a (24,24) open icon bmp""" + """Returns a open icon bmp""" return _get_art_bmp(wx.ART_FILE_OPEN) @MissingArt def get_icon_save(): - """"Returns a (24,24) save icon bmp""" + """"Returns a save icon bmp""" return _get_art_bmp(wx.ART_FILE_SAVE) @MissingArt def get_icon_saveas(): - """Returns a (24,24) saveas icon bmp""" + """Returns a saveas icon bmp""" return _get_art_bmp(wx.ART_FILE_SAVE_AS) @MissingArt def get_icon_cut(): - """Returns a (24,24) cut icon bmp""" + """Returns a cut icon bmp""" return _get_art_bmp(wx.ART_CUT) @MissingArt def get_icon_copy(): - """Returns a (24,24) copy icon bmp""" + """Returns a copy icon bmp""" return _get_art_bmp(wx.ART_COPY) @MissingArt def get_icon_paste(): - """Returns a (24,24) paste icon bmp""" + """Returns a paste icon bmp""" return _get_art_bmp(wx.ART_PASTE) @MissingArt def get_icon_undo(): - """Returns a (24,24) undo icon bmp""" + """Returns a undo icon bmp""" return _get_art_bmp(wx.ART_UNDO) def get_icon_redo(): - """Returns a (24,24) redo icon bmp""" + """Returns a redo icon bmp""" return _get_art_bmp(wx.ART_REDO) @MissingArt def get_icon_close(): - """Returns a (24,24) close icon bmp""" + """Returns a close icon bmp""" return _get_art_bmp(wx.ART_CLOSE) @MissingArt def get_icon_quit(): - """Returns a (24,24) quit icon bmp""" + """Returns a quit icon bmp""" return _get_art_bmp(wx.ART_QUIT) @MissingArt def get_icon_delete(): - """Returns a (24,24) delete icon bmp""" + """Returns a delete icon bmp""" return _get_art_bmp(wx.ART_DELETE) @MissingArt def get_find_and_replace(): - """Returns a (24,24) find and replace icon bmp""" + """Returns a find and replace icon bmp""" return _get_art_bmp(wx.ART_FIND_AND_REPLACE) def _get_art_bmp(art_id): - """Returns the passed in art_id as a (24,24) icon bmp""" - return wx.ArtProvider.GetBitmap(art_id, wx.ART_TOOLBAR, (24,24)) + """Returns the passed in art_id as a icon bmp""" + return wx.ArtProvider.GetBitmap(art_id, wx.ART_TOOLBAR, (16, 16)) From 74cb7fc9c7a92c256ca9646831e8f90d63e43437 Mon Sep 17 00:00:00 2001 From: Jakob Bowyer Date: Thu, 11 Aug 2011 23:07:53 +0100 Subject: [PATCH 094/141] Made a change to on_run removing a bug. --- pythonforumide/config/Ide_Config.cfg | 10 +++++----- pythonforumide/gui_lib/ide_mainframe.py | 15 +++++---------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index e4cddd4..f9cc9a2 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] -mainmenu.view.toolbar.show = bool>True +mainframe.width = int>1274 indent = int>4 -mainframe.ypos = int>117 +mainframe.ypos = int>-8 usetab = int>0 -mainframe.xpos = int>276 -mainframe.height = int>810 -mainframe.width = int>1024 +mainframe.xpos = int>-8 +mainframe.height = int>784 +mainmenu.view.toolbar.show = bool>True diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 7726232..837c2b1 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -53,9 +53,7 @@ def _create_main_panel(self): self._sizer.AddSpacer((-1, 2)) self.notebook = ctrl.notebook self.console= ctrl.console_rich_text -#=============================================================================== -# Apply config settings -#=============================================================================== + def _apply_config_settings(self): """ Applys the stored config values""" self.SetSize((self._config["MainFrame.Width"], @@ -66,9 +64,6 @@ def _apply_config_settings(self): self._config["MainMenu.View.Toolbar.Show"]) -#=============================================================================== -# Store config settings -#=============================================================================== def _store_config_settings(self): """ Stores the current config values""" width, height = self.GetSizeTuple() @@ -79,9 +74,7 @@ def _store_config_settings(self): self._config["MainFrame.YPos"]= ypos self._config["MainMenu.View.Toolbar.Show"]= \ self.MenuBar.IsChecked(ID_SHOW_TOOLBAR) -#=============================================================================== -# Editor actions -#=============================================================================== + def editor_tab_get_editor(self): """ Get the currently active editor instance from notebook""" return self.notebook.editor_tab_get_editor() @@ -193,9 +186,11 @@ def on_run(self): script = StringIO() script.write(active_editor.GetText()) script.seek(0) + scr = script.read().replace("\r",'') + reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), - ["python", "-c", script.read()]) + ["python", "-c", scr]) return run_panel From 3b6bceee1bb298778e2cee693f8fd7fb41218948 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 8 Sep 2011 15:51:50 +0100 Subject: [PATCH 095/141] The Open file dialog can now display and open files without extension --- pythonforumide/config/Ide_Config.cfg | 10 ++-- pythonforumide/gui_lib/ide_mainframe.py | 77 +++++++++++++------------ 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index f9cc9a2..7733341 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] -mainframe.width = int>1274 +mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>-8 +mainframe.ypos = int>24 usetab = int>0 -mainframe.xpos = int>-8 -mainframe.height = int>784 -mainmenu.view.toolbar.show = bool>True +mainframe.xpos = int>0 +mainframe.height = int>744 +mainframe.width = int>1366 diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 837c2b1..12c5613 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -6,6 +6,7 @@ """ import wx import os + from ide_menu import MenuBar from ide_constant import ID_SHOW_TOOLBAR from ide_toolbar import ToolBarPanel @@ -34,17 +35,17 @@ def __init__(self, *args, **kwargs): self._frame_panel= wx.Panel(self) self._frame_sizer.Add(self._frame_panel, 1, wx.EXPAND) self._sizer= wx.BoxSizer(wx.VERTICAL) - self._frame_panel.SetSizer(self._sizer) + self._frame_panel.SetSizer(self._sizer) self._create_main_panel() self.SetSizer(self._frame_sizer) self._apply_config_settings() self.Layout() self.Show() - + def _create_menu(self): """Creates a menu""" self.SetMenuBar(MenuBar(self)) - + def _create_main_panel(self): self._sizer.AddSpacer((-1, 2)) ctrl= MainFramePanel(self._frame_panel) @@ -62,7 +63,7 @@ def _apply_config_settings(self): self._config["MainFrame.YPos"]) self.menu_view_toolbar_show( self._config["MainMenu.View.Toolbar.Show"]) - + def _store_config_settings(self): """ Stores the current config values""" @@ -71,19 +72,19 @@ def _store_config_settings(self): self._config["MainFrame.Height"]= height xpos, ypos = self.GetPositionTuple() self._config["MainFrame.XPos"]= xpos - self._config["MainFrame.YPos"]= ypos + self._config["MainFrame.YPos"]= ypos self._config["MainMenu.View.Toolbar.Show"]= \ self.MenuBar.IsChecked(ID_SHOW_TOOLBAR) def editor_tab_get_editor(self): """ Get the currently active editor instance from notebook""" return self.notebook.editor_tab_get_editor() - + def editor_open_file(self): """ Opens a dialof to chose the file to open""" dirname, filename = self.file_dialog('Open a file', wx.OPEN) self.notebook.editor_tab_open_file(dirname, filename) - + def editor_save(self): """Saves the currently active editor file""" active_editor= self.editor_tab_get_editor() @@ -91,7 +92,7 @@ def editor_save(self): active_editor.save_file() else: self.save_as_active_editor() - + def editor_save_as(self): """Save as for the currently active editor file""" dirname, filename = self.file_dialog('Save file as', @@ -103,49 +104,49 @@ def editor_save_as(self): if active_editor.save_file_as(path): self.notebook.editor_tab_set_active_tab_text(filename) active_editor.filepath = path - + def editor_undo(self): """Undo changes in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.Undo() - + def editor_redo(self): """Redo changes in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.Redo() - + def editor_cut(self): """Cut changes in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.Cut() - + def editor_copy(self): """Copy changes in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.Copy() - + def editor_paste(self): """Paste changes in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.Paste() - + def editor_delete_selection(self): """Deletes the selection in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.Clear() - + def editor_selectall(self): """Selectall in active editor""" active_editor= self.editor_tab_get_editor() if active_editor: active_editor.SelectAll() - + def editor_search_and_replace(self): """ Search and replace in active editor""" # Create a search frame and hook into the caller. @@ -155,23 +156,23 @@ def editor_search_and_replace(self): title="Find and Replace", size=(410, 150)) replace_frame.Layout() - + def on_run(self): """Runs selected code in a new window.""" # Create a test frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. - reactor = wx.GetApp().this_reactor - + reactor = wx.GetApp().this_reactor + # run_editor = SimpleFrame(wx.GetApp().TopWindow, title="") # run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) # run_editor.sizer.Add(run_panel, 1, wx.EXPAND) # run_editor.Layout() - + run_panel= self.console active_editor= self.editor_tab_get_editor() - + if active_editor.filepath: - if active_editor.GetModify(): + if active_editor.GetModify(): active_editor.SaveFile(active_editor.filepath) filename = os.path.split(active_editor.filepath)[1] run_panel.WriteText("Running %s" % filename) @@ -179,30 +180,30 @@ def on_run(self): reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), ["python", str(active_editor.filepath)]) - + else: run_panel.WriteText("Running unsaved script.") run_panel.Newline() script = StringIO() script.write(active_editor.GetText()) script.seek(0) - scr = script.read().replace("\r",'') - + scr = script.read().replace("\r",'') + reactor.spawnProcess(PythonProcessProtocol(run_panel), get_python_exe(), ["python", "-c", scr]) - - + + return run_panel #=============================================================================== -# Mainframe actions -#=============================================================================== +# Mainframe actions +#=============================================================================== def on_exit(self): '''Handles the event triggered by the user to exit''' dial = wx.MessageDialog(self,'Do you really want to exit?', 'Exit Python IDE', wx.YES_NO | wx.ICON_QUESTION) - + if dial.ShowModal() == wx.ID_YES: self._store_config_settings() self.Destroy() @@ -210,14 +211,15 @@ def on_exit(self): def file_dialog(self, prompt, style): """Abstracted method to prompt the user for a file path. Returns a 2-tuple consisting of directory path and file name.""" - + # TODO: we also need to work in a way of detecting if a file # has changed since last save/load, and if so prompt the user # to save before exit. (Yoriz - building on the above id like it - # only to ask if there are unsaved changes otherwise just close + # only to ask if there are unsaved changes otherwise just close # it can get anoying having to click ok every time you want to close) - - dlg = wx.FileDialog(self, prompt, '.', '', '*.*', style) + + #The dialog is able to open any kind of files, even without extension + dlg = wx.FileDialog(self, prompt, '.', '', '*', style) if dlg.ShowModal() == wx.ID_OK: dirname = dlg.GetDirectory() filename = dlg.GetFilename() @@ -226,14 +228,13 @@ def file_dialog(self, prompt, style): else: return "", "" #Cancels the action #=============================================================================== -# state setters -#=============================================================================== +# state setters +#=============================================================================== def menu_view_toolbar_show(self, enable= True): self.MenuBar.Check(ID_SHOW_TOOLBAR, enable) self.mainframe_panel.toolbar_show(enable) - -if __name__=='__main__': +if __name__ == "__main__": import ide_test_app as wx_app app = wx_app.Wx_App(False) main_frame= MainFrame(None, title= "Testing main frame with no events") From f5fa039a53cd6c452638367136423447362ac60c Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 8 Sep 2011 21:11:21 +0100 Subject: [PATCH 096/141] When the user tries to quit, the IDE now checks if the file has been saved or not. If not, it does not ask the user if he wants to quit --- pythonforumide/gui_lib/ide_mainframe.py | 54 +++++++++++++++---------- pythonforumide/wx_app.py | 4 +- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 12c5613..bfbecc9 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -20,21 +20,20 @@ except ImportError: from StringIO import StringIO - class MainFrame(wx.Frame): """Class with the GUI and GUI functions""" def __init__(self, *args, **kwargs): """Creates the frame, calls some construction methods.""" super(MainFrame, self).__init__(*args, **kwargs) - self._config= wx.GetApp().config + self._config = wx.GetApp().config self._create_menu() self.SetInitialSize((600,600)) self.CreateStatusBar() - self._frame_sizer= wx.BoxSizer(wx.VERTICAL) - self._frame_panel= wx.Panel(self) + self._frame_sizer = wx.BoxSizer(wx.VERTICAL) + self._frame_panel = wx.Panel(self) self._frame_sizer.Add(self._frame_panel, 1, wx.EXPAND) - self._sizer= wx.BoxSizer(wx.VERTICAL) + self._sizer = wx.BoxSizer(wx.VERTICAL) self._frame_panel.SetSizer(self._sizer) self._create_main_panel() self.SetSizer(self._frame_sizer) @@ -50,10 +49,10 @@ def _create_main_panel(self): self._sizer.AddSpacer((-1, 2)) ctrl= MainFramePanel(self._frame_panel) self._sizer.Add(ctrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 2) - self.mainframe_panel= ctrl + self.mainframe_panel = ctrl self._sizer.AddSpacer((-1, 2)) self.notebook = ctrl.notebook - self.console= ctrl.console_rich_text + self.console = ctrl.console_rich_text def _apply_config_settings(self): """ Applys the stored config values""" @@ -64,7 +63,6 @@ def _apply_config_settings(self): self.menu_view_toolbar_show( self._config["MainMenu.View.Toolbar.Show"]) - def _store_config_settings(self): """ Stores the current config values""" width, height = self.GetSizeTuple() @@ -77,7 +75,7 @@ def _store_config_settings(self): self.MenuBar.IsChecked(ID_SHOW_TOOLBAR) def editor_tab_get_editor(self): - """ Get the currently active editor instance from notebook""" + """Returns the currently active editor instance from notebook""" return self.notebook.editor_tab_get_editor() def editor_open_file(self): @@ -131,19 +129,19 @@ def editor_copy(self): def editor_paste(self): """Paste changes in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.Paste() def editor_delete_selection(self): """Deletes the selection in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.Clear() def editor_selectall(self): """Selectall in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.SelectAll() @@ -153,8 +151,8 @@ def editor_search_and_replace(self): # Allows this frame to be destroyed by the main window on close. active_editor= self.editor_tab_get_editor() replace_frame = ReplaceFrame(active_editor, self, - title="Find and Replace", - size=(410, 150)) + title = "Find and Replace", + size = (410, 150)) replace_frame.Layout() def on_run(self): @@ -193,21 +191,34 @@ def on_run(self): get_python_exe(), ["python", "-c", scr]) - return run_panel #=============================================================================== # Mainframe actions #=============================================================================== - def on_exit(self): - '''Handles the event triggered by the user to exit''' - dial = wx.MessageDialog(self,'Do you really want to exit?', - 'Exit Python IDE', - wx.YES_NO | wx.ICON_QUESTION) + def ask_exit(self): + """Asks the user if he really wants to quit""" + dial = wx.MessageDialog(None, "Do you really want to exit?", + "Exit Hex Baker", wx.YES_NO | wx.ICON_QUESTION) if dial.ShowModal() == wx.ID_YES: self._store_config_settings() self.Destroy() + def on_exit(self): + """Handles the event triggered by the user to exit""" + current_text = self.editor_tab_get_editor().GetText() + + #Right now, this check if the current open file (the viewable tab) + #has been saved or not. If not, prompt the user about quitting + #If it has, just quit + + #TODO Check all tabs, not just the current one + + if self.editor_tab_get_editor().GetModify() == 1: + return self.ask_exit() + + return self.Destroy() + def file_dialog(self, prompt, style): """Abstracted method to prompt the user for a file path. Returns a 2-tuple consisting of directory path and file name.""" @@ -230,7 +241,8 @@ def file_dialog(self, prompt, style): #=============================================================================== # state setters #=============================================================================== - def menu_view_toolbar_show(self, enable= True): + def menu_view_toolbar_show(self, enable = True): + """Hides or shows the toolbar""" self.MenuBar.Check(ID_SHOW_TOOLBAR, enable) self.mainframe_panel.toolbar_show(enable) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index ebd1c4b..82f1dce 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -1,8 +1,8 @@ """ Created on 31 Jul 2011 -@author: D.W., david, confab -@reviewer: david +@author: D.W., David, confab +@reviewer: David """ import wx From 7ac8ee7adad07342371206e848eefeea72d26cb7 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 8 Sep 2011 21:15:06 +0100 Subject: [PATCH 097/141] Made console font Monospace --- pythonforumide/config/Ide_Config.cfg | 6 +++--- pythonforumide/gui_lib/ide_console.py | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 7733341..78f3319 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>24 +mainframe.ypos = int>0 usetab = int>0 mainframe.xpos = int>0 -mainframe.height = int>744 -mainframe.width = int>1366 +mainframe.height = int>600 +mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_console.py b/pythonforumide/gui_lib/ide_console.py index f42118f..91d7698 100644 --- a/pythonforumide/gui_lib/ide_console.py +++ b/pythonforumide/gui_lib/ide_console.py @@ -12,19 +12,20 @@ def __init__(self, *args, **kwargs): super(ConsolePanel, self).__init__(*args, **kwargs) self.sizer = wx.BoxSizer(wx.VERTICAL) self._create_rich_text_ctrl() - + self.SetSizer(self.sizer) self.Layout() - + def _create_rich_text_ctrl(self): - self._rt_ctrl = RichTextCtrl(self, style=wx.TE_READONLY) + self._rt_ctrl = RichTextCtrl(self, style = wx.TE_READONLY) + monospace_font = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL, + False, u"Monospace") + self._rt_ctrl.SetFont(monospace_font) self.sizer.Add(self._rt_ctrl, 1, wx.EXPAND | wx.ALL, 1) - - if __name__ == '__main__': import ide_test_app as wx_app - import ide_simple_frame + import ide_simple_frame app = wx_app.Wx_App(False) frame = ide_simple_frame.SimpleFrame(None, title="Testing console without events") From e6dd0e1c9193a4d6b11b7e9617188ba2594744a0 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Sun, 11 Sep 2011 14:51:31 +0100 Subject: [PATCH 098/141] Organized Find and Replace frame, gave functionality to Cancel button --- pythonforumide/gui_lib/ide_constant.py | 6 +- pythonforumide/gui_lib/ide_replace_frame.py | 66 +++++++++------------ 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py index c8347b3..9cf9962 100644 --- a/pythonforumide/gui_lib/ide_constant.py +++ b/pythonforumide/gui_lib/ide_constant.py @@ -61,7 +61,7 @@ ID_DELETE= wx.NewId() id_text_delete= _id_text("Delete", "Delete", "Delete the selected text", wx.ITEM_NORMAL) - + ID_SELECTALL= wx.NewId() id_text_selectall= _id_text("Select All\tCtrl+A", "Select all", "Select all", wx.ITEM_NORMAL) @@ -83,3 +83,7 @@ ID_RUNFILE=wx.NewId() id_text_runfile= _id_text("Run file\tF5", "Run", "Run the active file", wx.ITEM_NORMAL) +#=============================================================================== +# Find and Replace IDs +#=============================================================================== +ID_FIND_CANCEL = wx.NewId() \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index df4e903..c7a1db1 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -1,7 +1,15 @@ +# -*- coding: utf-8 -*- +""" +Created Sun Sep 11 14:16:42 2011 + +@author: David +""" + import wx import string from itertools import izip_longest from ide_simple_frame import SimpleFrame +from ide_constant import ID_FIND_CANCEL class ReplaceFrame(wx.Dialog): """Class with the GUI and the GUI functions""" @@ -10,36 +18,13 @@ def __init__(self, active_editor, *args, **kwargs): """"Displays the frame, creates the GUI""" super(ReplaceFrame, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.VERTICAL) - + self.panel= ReplaceFramePanel(self) self.sizer.Add(self.panel, 1, wx.EXPAND) self.SetSizerAndFit(self.sizer) - -# self.create_gui() -# self.txt_to_replace.SetFocus() -# self.active_editor = active_editor self.Show() - def create_gui(self): - """Creates and displays the GUI""" - - # TODO: This needs to be cleaned up with a sizer instead of absolute values. - # As it is, the text box is cut off. - # Also needs to have id's from gui_lib/ide_constants.py instead of literal constants. - # I'm intentionally leaving it with long lines until that is done. - - _panel = wx.Panel(self) - self.lbl_to_replace = wx.StaticText(_panel, -1, "Search for: ", (5, 8), (100, -1)) - self.txt_to_replace = wx.TextCtrl(_panel, id=-1, pos=(100, 5), size=(300, -1)) - self.lbl_replace_with = wx.StaticText(_panel, -1, "Replace with: ", (5, 53), (100, -1)) - self.txt_replace_with = wx.TextCtrl(_panel, id=-1, pos=(100, 50), size=(300, -1)) - self.replace_id = wx.NewId() - self.replace_button = wx.Button(_panel, self.replace_id, "Replace", pos=(5, 110), size=(90, -1)) - self.case_check = wx.CheckBox(_panel, -1, "Case Sensitive", pos=(5, 80), size=(-1, -1)) - self.Bind(wx.EVT_BUTTON, self.on_replace, id=self.replace_id) - self.sizer.Add(_panel, 1, wx.EXPAND) - def incase_replace(self, st, x, y): """Replaces x with y in an non case sensitive way""" mod = st.lower().replace(x.lower(), y) @@ -53,6 +38,10 @@ def incase_replace(self, st, x, y): out += y return out + def on_cancel(self, event): + """Destroys the frame when user hits Cancel button""" + self.Destroy() + def on_replace(self, event): """Replaces text on the current editor (self.active_editor)""" self.str_to_replace = self.txt_to_replace.GetValue() @@ -65,7 +54,7 @@ def on_replace(self, event): self.active_editor.GetText(), self.str_to_replace, self.str_replace_with)) self.Destroy() - + class ReplaceFramePanel(wx.Panel): def __init__(self, *args, **kwargs): super(ReplaceFramePanel, self).__init__(*args, **kwargs) @@ -74,7 +63,8 @@ def __init__(self, *args, **kwargs): self._create_buttons(self.sizer) self.SetSizer(self.sizer) self.Layout() - + self.set_binds() + def _create_vsizer(self, sizer): vsizer= wx.BoxSizer(wx.VERTICAL) sizer.Add(vsizer) @@ -83,7 +73,7 @@ def _create_vsizer(self, sizer): vsizer.AddSpacer((-1, 10)) self._create_options(vsizer) vsizer.AddSpacer((-1, 10)) - + def _create_inputs(self, sizer): grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap=10) sizer.Add(grid_sizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10) @@ -97,7 +87,7 @@ def _create_inputs(self, sizer): ctrl= wx.TextCtrl(self, size= (150, -1)) grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) self.ctrl_txt_new= ctrl - + def _create_buttons(self, sizer): box_sizer= wx.BoxSizer(wx.VERTICAL) sizer.Add(box_sizer) @@ -114,11 +104,16 @@ def _create_buttons(self, sizer): box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_replace_all= ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, label= "Cancel") + ctrl= wx.Button(self, id = ID_FIND_CANCEL, label= "Cancel") box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_cancel= ctrl box_sizer.AddSpacer((-1, 10)) - + + def set_binds(self): + """Binds the events for the panel and the frame""" + self.Bind(wx.EVT_BUTTON, self.GetParent().on_cancel, + id = ID_FIND_CANCEL) + def _create_options(self, sizer): box_sizer= wx.BoxSizer(wx.VERTICAL) sizer.Add(box_sizer) @@ -130,15 +125,8 @@ def _create_options(self, sizer): box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.check_match_case= ctrl - - - - - - - if __name__ == '__main__': wx_app= wx.App(None) - ReplaceFrame(active_editor= None, parent= None, - title="Find and Replace", size=(410, 150)) + ReplaceFrame(active_editor = None, parent = None, + title = "Find and Replace", size = (410, 150)) wx_app.MainLoop() \ No newline at end of file From 3b0c460212739d0b0b90c7bceaf5fc2d71262eb9 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 17:12:45 +0100 Subject: [PATCH 099/141] Replace All now works --- pythonforumide/config/Ide_Config.cfg | 4 +- pythonforumide/gui_lib/ide_constant.py | 3 +- pythonforumide/gui_lib/ide_replace_frame.py | 77 ++++++++++----------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 78f3319..f63a6d7 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>0 +mainframe.ypos = int>99 usetab = int>0 -mainframe.xpos = int>0 +mainframe.xpos = int>294 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py index 9cf9962..a1e0fdd 100644 --- a/pythonforumide/gui_lib/ide_constant.py +++ b/pythonforumide/gui_lib/ide_constant.py @@ -86,4 +86,5 @@ #=============================================================================== # Find and Replace IDs #=============================================================================== -ID_FIND_CANCEL = wx.NewId() \ No newline at end of file +ID_FIND_CANCEL = wx.NewId() +ID_FIND_REPLACE_ALL = wx.NewId() diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index c7a1db1..23bfc64 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -9,7 +9,7 @@ import string from itertools import izip_longest from ide_simple_frame import SimpleFrame -from ide_constant import ID_FIND_CANCEL +from ide_constant import ID_FIND_CANCEL, ID_FIND_REPLACE_ALL class ReplaceFrame(wx.Dialog): """Class with the GUI and the GUI functions""" @@ -17,6 +17,7 @@ class ReplaceFrame(wx.Dialog): def __init__(self, active_editor, *args, **kwargs): """"Displays the frame, creates the GUI""" super(ReplaceFrame, self).__init__(*args, **kwargs) + self.active_editor = active_editor self.sizer= wx.BoxSizer(wx.VERTICAL) self.panel= ReplaceFramePanel(self) @@ -25,37 +26,12 @@ def __init__(self, active_editor, *args, **kwargs): self.Show() - def incase_replace(self, st, x, y): - """Replaces x with y in an non case sensitive way""" - mod = st.lower().replace(x.lower(), y) - out = '' - for x, y in izip_longest(st, mod, fillvalue=' '): - if x == y: - out += y - elif (x in string.ascii_uppercase) and (x == y.upper()): - out += x - else: - out += y - return out - def on_cancel(self, event): """Destroys the frame when user hits Cancel button""" self.Destroy() - def on_replace(self, event): - """Replaces text on the current editor (self.active_editor)""" - self.str_to_replace = self.txt_to_replace.GetValue() - self.str_replace_with = self.txt_replace_with.GetValue() - if self.case_check.GetValue(): #If case sensitive on - self.active_editor.SetText(self.active_editor.GetText().replace( - self.str_to_replace, self.str_replace_with)) - else: #If case sensitive disabled - self.active_editor.SetText(self.incase_replace( - self.active_editor.GetText(), self.str_to_replace, - self.str_replace_with)) - self.Destroy() - class ReplaceFramePanel(wx.Panel): + def __init__(self, *args, **kwargs): super(ReplaceFramePanel, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.HORIZONTAL) @@ -64,6 +40,7 @@ def __init__(self, *args, **kwargs): self.SetSizer(self.sizer) self.Layout() self.set_binds() + self.active_editor = self.GetParent().active_editor def _create_vsizer(self, sizer): vsizer= wx.BoxSizer(wx.VERTICAL) @@ -79,13 +56,13 @@ def _create_inputs(self, sizer): sizer.Add(grid_sizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10) ctrl= wx.StaticText(self, label= "Search for:") grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) - ctrl= wx.TextCtrl(self, size= (150, -1)) - grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + self.txt_to_replace = wx.TextCtrl(self, size= (150, -1)) + grid_sizer.Add(self.txt_to_replace, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) self.ctrl_txt_exisiting= ctrl ctrl= wx.StaticText(self, label= "Replace with:") grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) - ctrl= wx.TextCtrl(self, size= (150, -1)) - grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + self.txt_replace_with = wx.TextCtrl(self, size= (150, -1)) + grid_sizer.Add(self.txt_replace_with, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) self.ctrl_txt_new= ctrl def _create_buttons(self, sizer): @@ -100,7 +77,7 @@ def _create_buttons(self, sizer): box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_replace= ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, label= "Replace all") + ctrl= wx.Button(self, id=ID_FIND_REPLACE_ALL, label= "Replace all") box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_replace_all= ctrl box_sizer.AddSpacer((-1, 5)) @@ -113,6 +90,8 @@ def set_binds(self): """Binds the events for the panel and the frame""" self.Bind(wx.EVT_BUTTON, self.GetParent().on_cancel, id = ID_FIND_CANCEL) + self.Bind(wx.EVT_BUTTON, self.on_replace, + id = ID_FIND_REPLACE_ALL) def _create_options(self, sizer): box_sizer= wx.BoxSizer(wx.VERTICAL) @@ -121,12 +100,32 @@ def _create_options(self, sizer): box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.check_whole_word= ctrl box_sizer.AddSpacer((-1, 10)) - ctrl= wx.CheckBox(self, label= " Match case") - box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) + self.case_check= wx.CheckBox(self, label= " Match case") + box_sizer.Add(self.case_check, 0, wx.LEFT|wx.RIGHT, 10) self.check_match_case= ctrl -if __name__ == '__main__': - wx_app= wx.App(None) - ReplaceFrame(active_editor = None, parent = None, - title = "Find and Replace", size = (410, 150)) - wx_app.MainLoop() \ No newline at end of file + def on_replace(self, event): + """Replaces text on the current editor (self.active_editor)""" + self.str_to_replace = self.txt_to_replace.GetValue() + self.str_replace_with = self.txt_replace_with.GetValue() + if self.case_check.GetValue(): #If case sensitive on + self.active_editor.SetText(self.active_editor.GetText().replace( + self.str_to_replace, self.str_replace_with)) + else: #If case sensitive disabled + self.active_editor.SetText(self.incase_replace( + self.active_editor.GetText(), self.str_to_replace, + self.str_replace_with)) + self.GetParent().Destroy() + + def incase_replace(self, st, x, y): + """Replaces x with y in an non case sensitive way""" + mod = st.lower().replace(x.lower(), y) + out = '' + for x, y in izip_longest(st, mod, fillvalue=' '): + if x == y: + out += y + elif (x in string.ascii_uppercase) and (x == y.upper()): + out += x + else: + out += y + return out From c5aa4de37b6c0db9cac25aa6e8245ff77687fc47 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 17:17:49 +0100 Subject: [PATCH 100/141] Code cleanup --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/gui_lib/ide_replace_frame.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index f63a6d7..a3ac620 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>99 +mainframe.ypos = int>95 usetab = int>0 -mainframe.xpos = int>294 +mainframe.xpos = int>317 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 23bfc64..c7daa02 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -31,8 +31,10 @@ def on_cancel(self, event): self.Destroy() class ReplaceFramePanel(wx.Panel): + """Replace Frame Panel, creates GUI, handles events""" def __init__(self, *args, **kwargs): + """Displays the frame, creates the GUI, inherits variables""" super(ReplaceFramePanel, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.HORIZONTAL) self._create_vsizer(self.sizer) @@ -43,6 +45,7 @@ def __init__(self, *args, **kwargs): self.active_editor = self.GetParent().active_editor def _create_vsizer(self, sizer): + """Creates the vertical sizer for the GUI""" vsizer= wx.BoxSizer(wx.VERTICAL) sizer.Add(vsizer) vsizer.AddSpacer((-1, 10)) @@ -52,6 +55,7 @@ def _create_vsizer(self, sizer): vsizer.AddSpacer((-1, 10)) def _create_inputs(self, sizer): + """Draws the input textboxes""" grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap=10) sizer.Add(grid_sizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10) ctrl= wx.StaticText(self, label= "Search for:") @@ -66,6 +70,7 @@ def _create_inputs(self, sizer): self.ctrl_txt_new= ctrl def _create_buttons(self, sizer): + """Draws the event buttons""" box_sizer= wx.BoxSizer(wx.VERTICAL) sizer.Add(box_sizer) box_sizer.AddSpacer((-1, 10)) @@ -94,6 +99,7 @@ def set_binds(self): id = ID_FIND_REPLACE_ALL) def _create_options(self, sizer): + """Draws the checkboxes for options""" box_sizer= wx.BoxSizer(wx.VERTICAL) sizer.Add(box_sizer) ctrl= wx.CheckBox(self, label= " Match whole word only") From 13621047f8e8450ed571edc2fc32fc9e59999c58 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 17:43:09 +0100 Subject: [PATCH 101/141] Replace from current position working --- pythonforumide/config/Ide_Config.cfg | 4 +- pythonforumide/gui_lib/ide_constant.py | 1 + pythonforumide/gui_lib/ide_replace_frame.py | 52 +++++++++++++++------ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index a3ac620..f458f35 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>95 +mainframe.ypos = int>103 usetab = int>0 -mainframe.xpos = int>317 +mainframe.xpos = int>292 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_constant.py b/pythonforumide/gui_lib/ide_constant.py index a1e0fdd..dd47fa8 100644 --- a/pythonforumide/gui_lib/ide_constant.py +++ b/pythonforumide/gui_lib/ide_constant.py @@ -88,3 +88,4 @@ #=============================================================================== ID_FIND_CANCEL = wx.NewId() ID_FIND_REPLACE_ALL = wx.NewId() +ID_FIND_REPLACE = wx.NewId() diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index c7daa02..07e871f 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -9,7 +9,7 @@ import string from itertools import izip_longest from ide_simple_frame import SimpleFrame -from ide_constant import ID_FIND_CANCEL, ID_FIND_REPLACE_ALL +from ide_constant import ID_FIND_CANCEL, ID_FIND_REPLACE_ALL, ID_FIND_REPLACE class ReplaceFrame(wx.Dialog): """Class with the GUI and the GUI functions""" @@ -78,25 +78,24 @@ def _create_buttons(self, sizer): box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_next= ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, label= "Replace") + ctrl= wx.Button(self, id = ID_FIND_REPLACE, label = "Replace") box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_replace= ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, id=ID_FIND_REPLACE_ALL, label= "Replace all") + ctrl= wx.Button(self, id = ID_FIND_REPLACE_ALL, label = "Replace all") box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_replace_all= ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, id = ID_FIND_CANCEL, label= "Cancel") + ctrl= wx.Button(self, id = ID_FIND_CANCEL, label = "Cancel") box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) self.btn_cancel= ctrl box_sizer.AddSpacer((-1, 10)) def set_binds(self): """Binds the events for the panel and the frame""" - self.Bind(wx.EVT_BUTTON, self.GetParent().on_cancel, - id = ID_FIND_CANCEL) - self.Bind(wx.EVT_BUTTON, self.on_replace, - id = ID_FIND_REPLACE_ALL) + self.Bind(wx.EVT_BUTTON, self.GetParent().on_cancel, id=ID_FIND_CANCEL) + self.Bind(wx.EVT_BUTTON, self.on_replace, id = ID_FIND_REPLACE) + self.Bind(wx.EVT_BUTTON, self.on_replace_all, id = ID_FIND_REPLACE_ALL) def _create_options(self, sizer): """Draws the checkboxes for options""" @@ -112,15 +111,38 @@ def _create_options(self, sizer): def on_replace(self, event): """Replaces text on the current editor (self.active_editor)""" - self.str_to_replace = self.txt_to_replace.GetValue() - self.str_replace_with = self.txt_replace_with.GetValue() + str_to_replace = self.txt_to_replace.GetValue() + str_replace_with = self.txt_replace_with.GetValue() + + current_position = self.active_editor.GetCurrentPos() + last_position = len( self.active_editor.GetText() ) + + active_text = self.active_editor.GetTextRange(current_position, + last_position) + first_part_of_text = self.active_editor.GetTextRange(0, + current_position) + + new_text = self.replace(active_text, str_to_replace, str_replace_with) + self.active_editor.SetText(first_part_of_text + new_text) + + self.GetParent().Destroy() + + def replace(self, text, to_replace, replace_width): + """Replaces 'to_replace' by 'replace_width' on 'text'""" if self.case_check.GetValue(): #If case sensitive on - self.active_editor.SetText(self.active_editor.GetText().replace( - self.str_to_replace, self.str_replace_with)) + return text.replace(to_replace, replace_width) else: #If case sensitive disabled - self.active_editor.SetText(self.incase_replace( - self.active_editor.GetText(), self.str_to_replace, - self.str_replace_with)) + return self.incase_replace(text, to_replace, replace_width) + + def on_replace_all(self, event): + """Replaces on the whole document""" + str_to_replace = self.txt_to_replace.GetValue() + str_replace_with = self.txt_replace_with.GetValue() + active_text = self.active_editor.GetText() + + new_text = self.replace(active_text, str_to_replace, str_replace_with) + self.active_editor.SetText(new_text) + self.GetParent().Destroy() def incase_replace(self, st, x, y): From 11f545dd738e8c3d99ebc67978f45237035a5bdf Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 17:50:09 +0100 Subject: [PATCH 102/141] More code cleanup --- pythonforumide/gui_lib/ide_replace_frame.py | 67 +++++++++++---------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 07e871f..bb9ddae 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -8,6 +8,7 @@ import wx import string from itertools import izip_longest + from ide_simple_frame import SimpleFrame from ide_constant import ID_FIND_CANCEL, ID_FIND_REPLACE_ALL, ID_FIND_REPLACE @@ -18,9 +19,9 @@ def __init__(self, active_editor, *args, **kwargs): """"Displays the frame, creates the GUI""" super(ReplaceFrame, self).__init__(*args, **kwargs) self.active_editor = active_editor - self.sizer= wx.BoxSizer(wx.VERTICAL) + self.sizer = wx.BoxSizer(wx.VERTICAL) - self.panel= ReplaceFramePanel(self) + self.panel = ReplaceFramePanel(self) self.sizer.Add(self.panel, 1, wx.EXPAND) self.SetSizerAndFit(self.sizer) @@ -56,39 +57,41 @@ def _create_vsizer(self, sizer): def _create_inputs(self, sizer): """Draws the input textboxes""" - grid_sizer= wx.FlexGridSizer(cols= 2, vgap= 10, hgap=10) - sizer.Add(grid_sizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10) - ctrl= wx.StaticText(self, label= "Search for:") - grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) - self.txt_to_replace = wx.TextCtrl(self, size= (150, -1)) - grid_sizer.Add(self.txt_to_replace, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) - self.ctrl_txt_exisiting= ctrl - ctrl= wx.StaticText(self, label= "Replace with:") + grid_sizer = wx.FlexGridSizer(cols = 2, vgap = 10, hgap =10) + sizer.Add(grid_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10) + ctrl = wx.StaticText(self, label = "Search for:") grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) - self.txt_replace_with = wx.TextCtrl(self, size= (150, -1)) - grid_sizer.Add(self.txt_replace_with, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) - self.ctrl_txt_new= ctrl + self.txt_to_replace = wx.TextCtrl(self, size = (150, -1)) + grid_sizer.Add(self.txt_to_replace, 0, + wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) + self.ctrl_txt_exisiting = ctrl + ctrl = wx.StaticText(self, label = "Replace with:") + grid_sizer.Add(ctrl, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) + self.txt_replace_with = wx.TextCtrl(self, size = (150, -1)) + grid_sizer.Add(self.txt_replace_with, 0, + wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) + self.ctrl_txt_new = ctrl def _create_buttons(self, sizer): """Draws the event buttons""" - box_sizer= wx.BoxSizer(wx.VERTICAL) + box_sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(box_sizer) box_sizer.AddSpacer((-1, 10)) - ctrl= wx.Button(self, label= "Find next") - box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) - self.btn_next= ctrl + ctrl = wx.Button(self, label = "Find next") + box_sizer.Add(ctrl, 0, wx.LEFT | wx.RIGHT, 10) + self.btn_next = ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, id = ID_FIND_REPLACE, label = "Replace") - box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) - self.btn_replace= ctrl + ctrl = wx.Button(self, id = ID_FIND_REPLACE, label = "Replace") + box_sizer.Add(ctrl, 0, wx.LEFT | wx.RIGHT, 10) + self.btn_replace = ctrl box_sizer.AddSpacer((-1, 5)) - ctrl= wx.Button(self, id = ID_FIND_REPLACE_ALL, label = "Replace all") - box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) - self.btn_replace_all= ctrl + ctrl = wx.Button(self, id = ID_FIND_REPLACE_ALL, label = "Replace all") + box_sizer.Add(ctrl, 0, wx.LEFT | wx.RIGHT, 10) + self.btn_replace_all = ctrl box_sizer.AddSpacer((-1, 5)) ctrl= wx.Button(self, id = ID_FIND_CANCEL, label = "Cancel") - box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) - self.btn_cancel= ctrl + box_sizer.Add(ctrl, 0, wx.LEFT | wx.RIGHT, 10) + self.btn_cancel = ctrl box_sizer.AddSpacer((-1, 10)) def set_binds(self): @@ -99,15 +102,15 @@ def set_binds(self): def _create_options(self, sizer): """Draws the checkboxes for options""" - box_sizer= wx.BoxSizer(wx.VERTICAL) + box_sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(box_sizer) - ctrl= wx.CheckBox(self, label= " Match whole word only") + ctrl = wx.CheckBox(self, label = " Match whole word only") box_sizer.Add(ctrl, 0, wx.LEFT|wx.RIGHT, 10) - self.check_whole_word= ctrl + self.check_whole_word = ctrl box_sizer.AddSpacer((-1, 10)) - self.case_check= wx.CheckBox(self, label= " Match case") - box_sizer.Add(self.case_check, 0, wx.LEFT|wx.RIGHT, 10) - self.check_match_case= ctrl + self.case_check= wx.CheckBox(self, label = " Match case") + box_sizer.Add(self.case_check, 0, wx.LEFT | wx.RIGHT, 10) + self.check_match_case = ctrl def on_replace(self, event): """Replaces text on the current editor (self.active_editor)""" @@ -115,7 +118,7 @@ def on_replace(self, event): str_replace_with = self.txt_replace_with.GetValue() current_position = self.active_editor.GetCurrentPos() - last_position = len( self.active_editor.GetText() ) + last_position = len(self.active_editor.GetText()) active_text = self.active_editor.GetTextRange(current_position, last_position) From 1fe4f980076a69d7330917c6fd421e1d7ee5ecf2 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 20:45:21 +0100 Subject: [PATCH 103/141] Fixed case-sensitive replace --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/gui_lib/ide_replace_frame.py | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index f458f35..4965346 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>103 +mainframe.ypos = int>22 usetab = int>0 -mainframe.xpos = int>292 +mainframe.xpos = int>620 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index bb9ddae..d2a0fd1 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -148,15 +148,11 @@ def on_replace_all(self, event): self.GetParent().Destroy() - def incase_replace(self, st, x, y): + def incase_replace(st, x, y): """Replaces x with y in an non case sensitive way""" - mod = st.lower().replace(x.lower(), y) - out = '' - for x, y in izip_longest(st, mod, fillvalue=' '): - if x == y: - out += y - elif (x in string.ascii_uppercase) and (x == y.upper()): - out += x - else: - out += y - return out + x = x.lower() + idx = st.lower().find(x) + while idx != -1: + st = st[:idx] + y + st[idx+len(x):] + idx = st.lower().find(x) + return st From f20bf234de86b5d6ed24aa3d09efb3a322b65abe Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 20:49:34 +0100 Subject: [PATCH 104/141] Added self to incase_replac --- pythonforumide/gui_lib/ide_replace_frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index d2a0fd1..df67548 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -148,7 +148,7 @@ def on_replace_all(self, event): self.GetParent().Destroy() - def incase_replace(st, x, y): + def incase_replace(self, st, x, y): """Replaces x with y in an non case sensitive way""" x = x.lower() idx = st.lower().find(x) From 19da0ccf36185ebb68d92cb233cd190c5f224ba4 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 21:36:45 +0100 Subject: [PATCH 105/141] Fixed bug when searching empty strings crashed the program --- pythonforumide/config/Ide_Config.cfg | 4 ++-- pythonforumide/gui_lib/ide_replace_frame.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 4965346..78f3319 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>22 +mainframe.ypos = int>0 usetab = int>0 -mainframe.xpos = int>620 +mainframe.xpos = int>0 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index df67548..bf85219 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -37,6 +37,7 @@ class ReplaceFramePanel(wx.Panel): def __init__(self, *args, **kwargs): """Displays the frame, creates the GUI, inherits variables""" super(ReplaceFramePanel, self).__init__(*args, **kwargs) + self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) self.sizer= wx.BoxSizer(wx.HORIZONTAL) self._create_vsizer(self.sizer) self._create_buttons(self.sizer) @@ -94,6 +95,10 @@ def _create_buttons(self, sizer): self.btn_cancel = ctrl box_sizer.AddSpacer((-1, 10)) + def on_key_down(self, event): + print "hey" + event.Skip() + def set_binds(self): """Binds the events for the panel and the frame""" self.Bind(wx.EVT_BUTTON, self.GetParent().on_cancel, id=ID_FIND_CANCEL) @@ -117,6 +122,9 @@ def on_replace(self, event): str_to_replace = self.txt_to_replace.GetValue() str_replace_with = self.txt_replace_with.GetValue() + if str_to_replace or str_replace_with == "": + return + current_position = self.active_editor.GetCurrentPos() last_position = len(self.active_editor.GetText()) @@ -141,6 +149,10 @@ def on_replace_all(self, event): """Replaces on the whole document""" str_to_replace = self.txt_to_replace.GetValue() str_replace_with = self.txt_replace_with.GetValue() + + if str_to_replace or str_replace_with == "": + return + active_text = self.active_editor.GetText() new_text = self.replace(active_text, str_to_replace, str_replace_with) From 8aaee0d17a395e2d219100a82cdfe99b607838b7 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 21:44:33 +0100 Subject: [PATCH 106/141] Pressing ESC now closes the Find/Replace frame --- pythonforumide/gui_lib/ide_replace_frame.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index bf85219..785ffc9 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -34,10 +34,10 @@ def on_cancel(self, event): class ReplaceFramePanel(wx.Panel): """Replace Frame Panel, creates GUI, handles events""" - def __init__(self, *args, **kwargs): + def __init__(self, parent): """Displays the frame, creates the GUI, inherits variables""" - super(ReplaceFramePanel, self).__init__(*args, **kwargs) - self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) + super(ReplaceFramePanel, self).__init__(parent) + self.Bind(wx.EVT_KEY_UP, self.on_key_down) self.sizer= wx.BoxSizer(wx.HORIZONTAL) self._create_vsizer(self.sizer) self._create_buttons(self.sizer) @@ -96,7 +96,8 @@ def _create_buttons(self, sizer): box_sizer.AddSpacer((-1, 10)) def on_key_down(self, event): - print "hey" + if event.GetKeyCode() == wx.WXK_ESCAPE: + self.GetParent().Destroy() event.Skip() def set_binds(self): From 3f4e6c063c8ef3bc1ca94ee6d72594350e80bf32 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 21:47:04 +0100 Subject: [PATCH 107/141] Minor changes to variable names --- pythonforumide/config/Ide_Config.cfg | 9 --------- pythonforumide/gui_lib/ide_replace_frame.py | 8 ++++---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 78f3319..e69de29 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +0,0 @@ -[ide] -mainmenu.view.toolbar.show = bool>True -indent = int>4 -mainframe.ypos = int>0 -usetab = int>0 -mainframe.xpos = int>0 -mainframe.height = int>600 -mainframe.width = int>600 - diff --git a/pythonforumide/gui_lib/ide_replace_frame.py b/pythonforumide/gui_lib/ide_replace_frame.py index 785ffc9..bb81d9b 100644 --- a/pythonforumide/gui_lib/ide_replace_frame.py +++ b/pythonforumide/gui_lib/ide_replace_frame.py @@ -161,11 +161,11 @@ def on_replace_all(self, event): self.GetParent().Destroy() - def incase_replace(self, st, x, y): + def incase_replace(self, text, x, y): """Replaces x with y in an non case sensitive way""" x = x.lower() - idx = st.lower().find(x) + idx = text.lower().find(x) while idx != -1: - st = st[:idx] + y + st[idx+len(x):] + text = text[:idx] + y + text[idx+len(x):] idx = st.lower().find(x) - return st + return text From 508f357528c52dc4ed5b847f2860b769b946e0f5 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 21:50:16 +0100 Subject: [PATCH 108/141] Fixed error when closing frame with no tabs open that made IDE crash --- pythonforumide/gui_lib/ide_mainframe.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index bfbecc9..1afd6d8 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -206,18 +206,21 @@ def ask_exit(self): def on_exit(self): """Handles the event triggered by the user to exit""" - current_text = self.editor_tab_get_editor().GetText() + try: + current_text = self.editor_tab_get_editor().GetText() - #Right now, this check if the current open file (the viewable tab) - #has been saved or not. If not, prompt the user about quitting - #If it has, just quit + #Right now, this check if the current open file (the viewable tab) + #has been saved or not. If not, prompt the user about quitting + #If it has, just quit - #TODO Check all tabs, not just the current one + #TODO Check all tabs, not just the current one - if self.editor_tab_get_editor().GetModify() == 1: - return self.ask_exit() + if self.editor_tab_get_editor().GetModify() == 1: + return self.ask_exit() - return self.Destroy() + return self.Destroy() + except AttributeError: + return self.Destroy() def file_dialog(self, prompt, style): """Abstracted method to prompt the user for a file path. From b4359be7c8b07e0c04d94d51541b6d95c57bb1f7 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 12 Sep 2011 23:51:08 +0200 Subject: [PATCH 109/141] Made some changes to TOREVIEW --- TOREVIEW | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/TOREVIEW b/TOREVIEW index 769bbcf..1d36f07 100644 --- a/TOREVIEW +++ b/TOREVIEW @@ -1,20 +1,20 @@ All files have to be reviewed by someone else before committing, -here are the guidelines for reviewing a file +here are the guidelines for reviewing a file: -Ensure; -1) its free from simple errors -2) it is nicely written -3) that every function and module is documented -4) comments are where they are needed -5) it has a this at the top +Ensure: +1) it is free from simple errors; +2) it is nicely written; +3) that every function and module is documented; +4) comments are where they are needed; +5) it has the following at the top: """ -@author: there name +@author: author name @reviewer: your name """ -6) if its all looking good, then push it. +6) if it is all looking good, then push it. -To get your file reviewed; -1) pastebin the diff or code, and put its location at the top for the push +To get your file reviewed: +1) pastebin the diff or code, and put it's location at the top for the push. -Files to be reviewed; -- +Files to be reviewed: +- \ No newline at end of file From 2372a74fd95ee0def060d65a288c07a54780f261 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 09:55:23 +0100 Subject: [PATCH 110/141] Configuration settings now saved on exit --- pythonforumide/config/Ide_Config.cfg | 6 +++--- pythonforumide/gui_lib/ide_mainframe.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 78f3319..1b9596d 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] -mainmenu.view.toolbar.show = bool>True +mainmenu.view.toolbar.show = bool>False indent = int>4 -mainframe.ypos = int>0 +mainframe.ypos = int>86 usetab = int>0 -mainframe.xpos = int>0 +mainframe.xpos = int>779 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 1afd6d8..578f41f 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -218,8 +218,10 @@ def on_exit(self): if self.editor_tab_get_editor().GetModify() == 1: return self.ask_exit() + self._store_config_settings() return self.Destroy() except AttributeError: + self._store_config_settings() return self.Destroy() def file_dialog(self, prompt, style): From 9991b4d46812b933a3f870eaac121feb6258870e Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 10:01:05 +0100 Subject: [PATCH 111/141] Code style cleanup --- pythonforumide/gui_lib/ide_mainframe.py | 52 ++++++++++++------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 578f41f..efea1cf 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -47,8 +47,8 @@ def _create_menu(self): def _create_main_panel(self): self._sizer.AddSpacer((-1, 2)) - ctrl= MainFramePanel(self._frame_panel) - self._sizer.Add(ctrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 2) + ctrl = MainFramePanel(self._frame_panel) + self._sizer.Add(ctrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) self.mainframe_panel = ctrl self._sizer.AddSpacer((-1, 2)) self.notebook = ctrl.notebook @@ -57,21 +57,20 @@ def _create_main_panel(self): def _apply_config_settings(self): """ Applys the stored config values""" self.SetSize((self._config["MainFrame.Width"], - self._config["MainFrame.Height"])) + self._config["MainFrame.Height"])) self.MoveXY(self._config["MainFrame.XPos"], - self._config["MainFrame.YPos"]) - self.menu_view_toolbar_show( - self._config["MainMenu.View.Toolbar.Show"]) + self._config["MainFrame.YPos"]) + self.menu_view_toolbar_show(self._config["MainMenu.View.Toolbar.Show"]) def _store_config_settings(self): """ Stores the current config values""" width, height = self.GetSizeTuple() - self._config["MainFrame.Width"]= width - self._config["MainFrame.Height"]= height + self._config["MainFrame.Width"] = width + self._config["MainFrame.Height"] = height xpos, ypos = self.GetPositionTuple() - self._config["MainFrame.XPos"]= xpos - self._config["MainFrame.YPos"]= ypos - self._config["MainMenu.View.Toolbar.Show"]= \ + self._config["MainFrame.XPos"] = xpos + self._config["MainFrame.YPos"] = ypos + self._config["MainMenu.View.Toolbar.Show"] = \ self.MenuBar.IsChecked(ID_SHOW_TOOLBAR) def editor_tab_get_editor(self): @@ -80,12 +79,12 @@ def editor_tab_get_editor(self): def editor_open_file(self): """ Opens a dialof to chose the file to open""" - dirname, filename = self.file_dialog('Open a file', wx.OPEN) + dirname, filename = self.file_dialog("Open a file", wx.OPEN) self.notebook.editor_tab_open_file(dirname, filename) def editor_save(self): """Saves the currently active editor file""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor.filepath: active_editor.save_file() else: @@ -93,8 +92,7 @@ def editor_save(self): def editor_save_as(self): """Save as for the currently active editor file""" - dirname, filename = self.file_dialog('Save file as', - wx.SAVE) + dirname, filename = self.file_dialog("Save file as", wx.SAVE) if dirname and filename: path = os.path.join(dirname, filename) if path: @@ -105,25 +103,25 @@ def editor_save_as(self): def editor_undo(self): """Undo changes in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.Undo() def editor_redo(self): """Redo changes in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.Redo() def editor_cut(self): """Cut changes in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.Cut() def editor_copy(self): """Copy changes in active editor""" - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() if active_editor: active_editor.Copy() @@ -149,7 +147,7 @@ def editor_search_and_replace(self): """ Search and replace in active editor""" # Create a search frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. - active_editor= self.editor_tab_get_editor() + active_editor = self.editor_tab_get_editor() replace_frame = ReplaceFrame(active_editor, self, title = "Find and Replace", size = (410, 150)) @@ -166,8 +164,8 @@ def on_run(self): # run_editor.sizer.Add(run_panel, 1, wx.EXPAND) # run_editor.Layout() - run_panel= self.console - active_editor= self.editor_tab_get_editor() + run_panel = self.console + active_editor = self.editor_tab_get_editor() if active_editor.filepath: if active_editor.GetModify(): @@ -176,8 +174,8 @@ def on_run(self): run_panel.WriteText("Running %s" % filename) run_panel.Newline() reactor.spawnProcess(PythonProcessProtocol(run_panel), - get_python_exe(), - ["python", str(active_editor.filepath)]) + get_python_exe(), + ["python", str(active_editor.filepath)]) else: run_panel.WriteText("Running unsaved script.") @@ -185,11 +183,11 @@ def on_run(self): script = StringIO() script.write(active_editor.GetText()) script.seek(0) - scr = script.read().replace("\r",'') + scr = script.read().replace("\r", '') reactor.spawnProcess(PythonProcessProtocol(run_panel), - get_python_exe(), - ["python", "-c", scr]) + get_python_exe(), + ["python", "-c", scr]) return run_panel #=============================================================================== From e01381b8b8368e1c8aa19dec2d9bd768c66fe66d Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 10:22:24 +0100 Subject: [PATCH 112/141] Code cleanup, minimal changes --- pythonforumide/config/Ide_Config.cfg | 4 +- pythonforumide/gui_lib/ide_console.py | 22 +-- pythonforumide/gui_lib/ide_headered_panel.py | 163 +++++++----------- pythonforumide/gui_lib/ide_mainframe.py | 16 +- pythonforumide/gui_lib/ide_mainframe_panel.py | 40 ++--- pythonforumide/gui_lib/ide_notebook.py | 42 ++--- pythonforumide/gui_lib/ide_notebook_events.py | 20 ++- pythonforumide/gui_lib/ide_simple_frame.py | 17 +- pythonforumide/gui_lib/ide_toolbar.py | 32 ++-- 9 files changed, 145 insertions(+), 211 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 1b9596d..a5c5d0b 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>False indent = int>4 -mainframe.ypos = int>86 +mainframe.ypos = int>97 usetab = int>0 -mainframe.xpos = int>779 +mainframe.xpos = int>721 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_console.py b/pythonforumide/gui_lib/ide_console.py index 91d7698..6a67d00 100644 --- a/pythonforumide/gui_lib/ide_console.py +++ b/pythonforumide/gui_lib/ide_console.py @@ -1,14 +1,18 @@ -''' +""" Created on 4 Aug 2011 -@author: D.W. -''' +@author: D.W., David +@reviewer: David +""" import wx from wx.richtext import RichTextCtrl class ConsolePanel(wx.Panel): + """Creates the Panel GUI""" + def __init__(self, *args, **kwargs): + """Super the panel and create GUI""" super(ConsolePanel, self).__init__(*args, **kwargs) self.sizer = wx.BoxSizer(wx.VERTICAL) self._create_rich_text_ctrl() @@ -17,19 +21,9 @@ def __init__(self, *args, **kwargs): self.Layout() def _create_rich_text_ctrl(self): + """Creates the textbox for the console""" self._rt_ctrl = RichTextCtrl(self, style = wx.TE_READONLY) monospace_font = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL, False, u"Monospace") self._rt_ctrl.SetFont(monospace_font) self.sizer.Add(self._rt_ctrl, 1, wx.EXPAND | wx.ALL, 1) - -if __name__ == '__main__': - import ide_test_app as wx_app - import ide_simple_frame - app = wx_app.Wx_App(False) - frame = ide_simple_frame.SimpleFrame(None, - title="Testing console without events") - panel = ConsolePanel(frame) - frame.sizer.Add(panel, 1, wx.EXPAND) - frame.Layout() - app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_headered_panel.py b/pythonforumide/gui_lib/ide_headered_panel.py index da5c34f..a407388 100644 --- a/pythonforumide/gui_lib/ide_headered_panel.py +++ b/pythonforumide/gui_lib/ide_headered_panel.py @@ -1,12 +1,14 @@ -''' +""" Created on 3 Aug 2011 -@author: Dave Wilson -''' +@author: Dave Wilson, David +""" import wx class HeaderPanel(wx.Panel): + """Header Panel Class, with events and GUI creation""" + def __init__(self, *args, **kwargs): """ Creates headered panel with Minimize/Maximize & Close buttons """ super(HeaderPanel, self).__init__(*args, **kwargs) @@ -19,11 +21,10 @@ def __init__(self, *args, **kwargs): self.SetSizer(sizer) self.Layout() - def _create_variables(self): """ Creates variables for internal use only""" self._button_font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, - u'Webdings') + u'Webdings') def _create_statictxt(self, sizer): """ Create header label""" @@ -33,9 +34,9 @@ def _create_statictxt(self, sizer): ctrl.SetFont(font) sizer.Add(ctrl, 0, flag=wx.ALIGN_CENTER_VERTICAL) self.header_caption = ctrl - + def _create_minimize(self, sizer): - """ Creates the Minimize button""" + """Creates the Minimize button""" sizer.AddStretchSpacer(1) ctrl = wx.Button(self, label="0", size=(19, 19)) ctrl.SetFont(self._button_font) @@ -44,7 +45,7 @@ def _create_minimize(self, sizer): self.btn_minimize = ctrl def _create_maximize(self, sizer): - """ Creates the Maximize button""" + """Creates the Maximize button""" ctrl = wx.Button(self, label="1", size=(19, 19)) ctrl.SetFont(self._button_font) ctrl.SetToolTipString("Maximize") @@ -53,15 +54,16 @@ def _create_maximize(self, sizer): # restore = 2 def _create_close(self, sizer): - """ Creates the Close button""" + """Creates the Close button""" ctrl = wx.Button(self, label="r", size=(19, 19)) ctrl.SetFont(self._button_font) ctrl.SetToolTipString("Close") sizer.Add(ctrl, 0, wx.ALIGN_RIGHT) self.btn_close = ctrl - class HeaderedPanel(wx.Panel): + """Headered Panel class""" + def __init__(self, *args, **kwargs): """ Creates a panel for displaying text """ super(HeaderedPanel, self).__init__(*args, **kwargs) @@ -70,20 +72,22 @@ def __init__(self, *args, **kwargs): self._create_header_panel() self.SetSizer(self.sizer) self.Layout() - + def _create_variables(self): + """Creates GUI variables for components""" self.child_panel = None self._proportion = None self._minimized = False self._parent_frame = None - + def _create_header_panel(self): """ Creates the header panel""" ctrl = HeaderPanel(self, style=wx.BORDER_THEME) self.sizer.Add(ctrl, 0, wx.EXPAND | wx.ALL, 1) self.header_panel = ctrl - + def add_panel_ctrl(self, panel_class, caption="", proportion=1): + """Adds a panel to the GUI""" self.Freeze() ctrl = panel_class(self) self.sizer.Add(ctrl, 1, wx.EXPAND | wx.ALL, 0) @@ -94,54 +98,62 @@ def add_panel_ctrl(self, panel_class, caption="", proportion=1): self.set_caption(caption) self.Thaw() return ctrl - + def _update_layout(self): + """Refresh/Update layout of the IDE""" parent = self.GetParent() parent.Layout() parent.Update() parent.Refresh() self.Layout() - + def _set_restore_state(self): + """Defines the restore state""" self.header_panel.btn_maximize.SetLabel("2") - + def _set_after_restore(self): + """Defines state after restore""" self.header_panel.btn_maximize.SetLabel("1") - + def _get_items_sizer(self, item): + """Gets the items in a sizer""" sizer = item.GetContainingSizer() return sizer.GetItem(self) - + def _set_own_proportion(self, proportion): + """Defines the proportion of a sizer""" self._get_items_sizer(self).SetProportion(proportion) - - def show(self, show=True): + """Shows a component""" self.Show(show) self._update_layout() - + def show_child(self, show=True): + """Show a child component""" self.child_panel.Show(show) - + def _set_no_child_state(self): + """Sets a no_child_state to a component""" self.header_panel.btn_maximize.SetLabel("2") self._set_own_proportion(0) self._update_layout() self._minimized = True - + def minimized_state(self): + """Checks the minimized state""" if not self.child_panel: return self.show_child(False) self.header_panel.btn_maximize.SetLabel("2") self.header_panel.btn_maximize.SetToolTipString("Restore") - + self._set_own_proportion(0) self._update_layout() self._minimized = True - + def restore_state(self): + """Restores state of component""" if not self.child_panel: return if self._minimized: @@ -153,55 +165,63 @@ def restore_state(self): self._minimized = False else: self._move_child_to_a_frame() - + def close(self): + """Closes a component""" if not self.child_panel: return self.show(False) - + def _move_child_to_a_frame(self): + """Move a child component to a frame""" self.sizer.Detach(self.child_panel) self.show(False) self._parent_frame = ParentFrame(self, title=self._get_caption()) self._parent_frame.add_child(self.child_panel, self) - def set_caption(self, text): + """Define the caption of a component""" self.header_panel.header_caption.SetLabel(" %s" % (text)) - + def _get_caption(self): + """Get caption of a component""" return self.header_panel.header_caption.GetLabel() - - class HeaderedPanelEvents(object): - def __init__(self, view, model=""): + """Headered Panel Events class, handles events""" + + def __init__(self, view, model=''): + """Initiates the panel, calls event binding functions""" self.view = view self.model = model self._create_variables() self._create_binds() - + def _create_variables(self): + """Creates GUI_related variables""" header_panel = self.view.header_panel self.btn_minimize = header_panel.btn_minimize self.btn_maximize = header_panel.btn_maximize self.btn_close = header_panel.btn_close -# self.header_panel= self.view.header_panel - +# self.header_panel= self.view.header_panel + def _create_binds(self): + """Defines binds""" self.btn_minimize.Bind(wx.EVT_BUTTON, self._on_btn_minimize) self.btn_maximize.Bind(wx.EVT_BUTTON, self._on_btn_maximize) self.btn_close.Bind(wx.EVT_BUTTON, self._on_btn_close) - + def _on_btn_minimize(self, event): + """Handles minimize button""" self.view.minimized_state() - + def _on_btn_maximize(self, event): + """Handles maximizing button""" self.view.restore_state() - + def _on_btn_close(self, event): + """Handles closing event with button""" self.view.close() - class ParentFrame(wx.Frame): """Frame to use for testing""" @@ -213,77 +233,26 @@ def __init__(self, *args, **kwargs): self.sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.Bind(wx.EVT_CLOSE, self._on_close) - + def add_child(self, child_panel, parent_window): + """Handles child_panel to parent_window""" self._child_panel = child_panel self._parent_window = parent_window child_panel.Reparent(self) self.sizer.Add(child_panel, 1, wx.EXPAND) self.Layout() self.Show() -# self.Maximize() - +# self.Maximize() + def _move_child_back(self): + """Removes a child_panel back""" self.sizer.Detach(self._child_panel) self._child_panel.Reparent(self._parent_window) self._parent_window.sizer.Add(self._child_panel, 1, wx.EXPAND | wx.ALL, 0) self._parent_window.show(True) self._parent_window._parent_frame = None - - + def _on_close(self, event): + """Handles closing event""" self._move_child_back() event.Skip() - - - -class TestTextPanel(wx.Panel): - def __init__(self, *args, **kwargs): - """ Creates a panel for with a TextCtrl """ - super(TestTextPanel, self).__init__(*args, **kwargs) - self.sizer = wx.BoxSizer(wx.VERTICAL) - self._create_textctrl() - - - self.SetSizer(self.sizer) - self.Layout() - - def _create_textctrl(self): - ctrl = wx.TextCtrl(self, - size=(-1, 300) - ) - self.sizer.Add(ctrl, 1, wx.EXPAND | wx.ALL, 1) - - -if __name__ == '__main__': - from ide_simple_frame import SimpleFrame, SimplePanel - app = wx.App(None) - frame = SimpleFrame(None, title="Test frame") - - s_panel = SimplePanel(frame) - frame.sizer.Add(s_panel, 1, wx.EXPAND) - - parent = s_panel - - panel = HeaderedPanel(parent, style=wx.BORDER_THEME) - HeaderedPanelEvents(panel) - parent.sizer.Add(panel, 0, wx.EXPAND) - panel.add_panel_ctrl(TestTextPanel, "Panel1", 1) - panel.minimized_state() - panel.set_caption("Panel1 has proportion 1 and set minimized") - - panel = HeaderedPanel(parent, style=wx.BORDER_THEME) - HeaderedPanelEvents(panel) - parent.sizer.Add(panel, 1, wx.EXPAND) - panel.add_panel_ctrl(TestTextPanel, "Panel2", 2) - panel.set_caption("Panel1 has proportion 2") - - panel = HeaderedPanel(parent, style=wx.BORDER_THEME) - HeaderedPanelEvents(panel) - parent.sizer.Add(panel, 1, wx.EXPAND) - panel.add_panel_ctrl(TestTextPanel, "Panel3", 3) - panel.set_caption("Panel1 has proportion 3") - - - frame.Layout() - app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index efea1cf..05d9b59 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -159,10 +159,10 @@ def on_run(self): # Allows this frame to be destroyed by the main window on close. reactor = wx.GetApp().this_reactor -# run_editor = SimpleFrame(wx.GetApp().TopWindow, title="") -# run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) -# run_editor.sizer.Add(run_panel, 1, wx.EXPAND) -# run_editor.Layout() +# run_editor = SimpleFrame(wx.GetApp().TopWindow, title="") +# run_panel = wx.richtext.RichTextCtrl(run_editor, style=wx.TE_READONLY) +# run_editor.sizer.Add(run_panel, 1, wx.EXPAND) +# run_editor.Layout() run_panel = self.console active_editor = self.editor_tab_get_editor() @@ -248,11 +248,3 @@ def menu_view_toolbar_show(self, enable = True): """Hides or shows the toolbar""" self.MenuBar.Check(ID_SHOW_TOOLBAR, enable) self.mainframe_panel.toolbar_show(enable) - -if __name__ == "__main__": - import ide_test_app as wx_app - app = wx_app.Wx_App(False) - main_frame= MainFrame(None, title= "Testing main frame with no events") - main_frame.Layout() - main_frame.mainframe_panel.toolbar_show(False) - app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_mainframe_panel.py b/pythonforumide/gui_lib/ide_mainframe_panel.py index 12998d5..43bebdc 100644 --- a/pythonforumide/gui_lib/ide_mainframe_panel.py +++ b/pythonforumide/gui_lib/ide_mainframe_panel.py @@ -1,8 +1,8 @@ -''' +""" Created on 4 Aug 2011 -@author: Main -''' +@author: Main, David +""" import wx @@ -16,7 +16,9 @@ class MainFramePanel(wx.Panel): """ Creates the mainframe panel""" + def __init__(self, *args, **kwargs): + """Initializes the panel""" super(MainFramePanel, self).__init__(*args, **kwargs) self._sizer = wx.BoxSizer(wx.VERTICAL) self._create_toolbar() @@ -25,51 +27,39 @@ def __init__(self, *args, **kwargs): self._create_first_tab() self.SetSizer(self._sizer) self.Layout() - + def _create_toolbar(self): """Creates a toolbar""" ctrl= ToolBarPanel(self) self._sizer.Add(ctrl, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 3) self.toolbar= ctrl - + def _create_notebook_panel(self): - """ Create the notebook panel """ + """Create the notebook panel""" self._sizer.AddSpacer((-1, 2)) - headered_panel = HeaderedPanel(self, style=wx.BORDER_THEME) + headered_panel = HeaderedPanel(self, style = wx.BORDER_THEME) HeaderedPanelEvents(headered_panel) self._sizer.Add(headered_panel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) ctrl = headered_panel.add_panel_ctrl(NoteBookPanel, "Editor", 100) NotebookEvents(ctrl) self.notebook = ctrl.notebook - + def _create_console(self): - """ Create a console window """ + """Create a console window""" self._sizer.AddSpacer((-1, 4)) - headered_panel = HeaderedPanel(self, style=wx.BORDER_THEME) + headered_panel = HeaderedPanel(self, style = wx.BORDER_THEME) HeaderedPanelEvents(headered_panel) self._sizer.Add(headered_panel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) ctrl = headered_panel.add_panel_ctrl(ConsolePanel, "Console", 30) self.console_rich_text = ctrl._rt_ctrl self._sizer.AddSpacer((-1, 2)) - + def _create_first_tab(self): """Adds a new blank editor tab perhaps open last edited in the future, for now just open new.""" self.notebook.editor_tab_new() - + def toolbar_show(self, enable= True): - """ Show/hide the toolbar""" + """Show/hide the toolbar""" self.toolbar.Show(enable) self.Layout() - - -if __name__ == '__main__': - import ide_test_app as wx_app - import ide_simple_frame - app = wx_app.Wx_App(False) - frame = ide_simple_frame.SimpleFrame(None, - title="Testing mainframe_panel without events") - panel = MainFramePanel(frame) - frame.sizer.Add(panel, 1, wx.EXPAND) - frame.Layout() - app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 84fb230..6bd264d 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -12,23 +12,28 @@ from ide_editor import EditorPanel class NoteBookPanel(wx.Panel): + """Creates the GUI inside the notebook panel""" + def __init__(self, *args, **kwargs): """ Create a panel with a containing NoteBook control""" super(NoteBookPanel, self).__init__(*args, **kwargs) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self._create_notebook(sizer) - + def _create_notebook(self, sizer): """ Creates the NoteBook control""" - ctrl = NoteBook(self, agwStyle=fnb.FNB_X_ON_TAB | + ctrl = NoteBook(self, agwStyle=fnb.FNB_X_ON_TAB | fnb.FNB_NO_X_BUTTON | fnb.FNB_NO_TAB_FOCUS | fnb.FNB_VC8, pos=(-100, -100)) sizer.Add(ctrl, 1 , wx.EXPAND | wx.ALL, 0) self.notebook = ctrl class NoteBook(fnb.FlatNotebook): + """Handles notebook-related events""" + def __init__(self, *args, **kwargs): + """Supers the notebook and sets variables""" super(NoteBook, self).__init__(*args, **kwargs) self._active_editor = None self._active_tab_index = None @@ -41,29 +46,29 @@ def editor_tab_new(self, page_name=""): self.editor_tab_name_untitled_tabs() wx.CallAfter(self.SetSelection, self.GetPageCount() - 1) return editor_panel.editor - + def editor_tab_open_file(self, dirname, filename): """Loads a slected file into a new editor tab""" - + if dirname and filename: editor = self.editor_tab_new(filename) path = os.path.join(dirname, filename) editor.load_file(path) - + def editor_tab_close_active(self): """Closes the currently active editor tab""" self.DeletePage(self._active_tab_index) wx.CallAfter(self.editor_tab_name_untitled_tabs) - + def editor_tab_get_editor(self): - """ Returns the currently active editor instance or None""" + """ Returns the currently active editor instance or None""" return self._active_editor - + def editor_tab_set_active_tab_text(self, text): """Rename the currently active tab text""" if self._active_tab_index > -1: self.SetPageText(self._active_tab_index, text) - + def editor_tab_name_untitled_tabs(self): """Renumbers the untitled pages""" self.Freeze() @@ -85,7 +90,7 @@ def active_editor_can_cut(self): return False except AttributeError: return True - + def active_editor_can_copy(self): """Returns True if the active editor can copy""" try: @@ -95,14 +100,14 @@ def active_editor_can_copy(self): return False except AttributeError: return True - + def active_editor_can_paste(self): """Returns True if the active editor can paste""" if self._active_editor: return self._active_editor.CanPaste() else: return False - + def active_editor_can_delete(self): """Returns True if the active editor can delete""" try: @@ -112,16 +117,3 @@ def active_editor_can_delete(self): return False except AttributeError: return True - - -if __name__ == '__main__': - import ide_test_app as wx_app - import ide_simple_frame - app = wx_app.Wx_App(False) - frame = ide_simple_frame.SimpleFrame(None, - title="Testing notebook without events") - panel = NoteBookPanel(frame) - frame.sizer.Add(panel, 1, wx.EXPAND) - panel.notebook.editor_tab_new() - frame.Layout() - app.MainLoop() diff --git a/pythonforumide/gui_lib/ide_notebook_events.py b/pythonforumide/gui_lib/ide_notebook_events.py index 4f7b56f..8d00dcd 100644 --- a/pythonforumide/gui_lib/ide_notebook_events.py +++ b/pythonforumide/gui_lib/ide_notebook_events.py @@ -12,23 +12,25 @@ class NotebookEvents(object): """ Creates the events for the notebook""" + def __init__(self, view, model=None): + """Calls the functions to bind events""" self.view = view self.model = model self.notebook= self.view.notebook self._create_binds() - + def _create_binds(self): """Create binds""" self.notebook.Bind(EVT_FLATNOTEBOOK_PAGE_CLOSED, - self._on_page_closed) - + self._on_page_closed) + self.notebook.Bind(EVT_FLATNOTEBOOK_PAGE_CHANGED, - self._on_page_changed) - + self._on_page_changed) + self.notebook.Bind(EVT_FLATNOTEBOOK_PAGE_CLOSING, self._on_page_closing) - + def _on_page_changed(self, event): """sets the currently active page index and editor page""" nb_obj = event.GetEventObject() @@ -36,15 +38,15 @@ def _on_page_changed(self, event): active_editor_panel = nb_obj.GetCurrentPage() self.notebook._active_editor = active_editor_panel.editor event.Skip() - + def _on_page_closed(self, event): """Sets currently active page to none and renames the untitled pages""" event.Skip() wx.CallAfter(self.notebook.editor_tab_name_untitled_tabs) - + def _on_page_closing(self, event): + """Called when tab is closed""" new_index= event.GetSelection() if not new_index: self.notebook._active_editor = None event.Skip() - diff --git a/pythonforumide/gui_lib/ide_simple_frame.py b/pythonforumide/gui_lib/ide_simple_frame.py index 6d0c5ed..2c517bc 100644 --- a/pythonforumide/gui_lib/ide_simple_frame.py +++ b/pythonforumide/gui_lib/ide_simple_frame.py @@ -8,28 +8,21 @@ class SimpleFrame(wx.Frame): """Frame to use for testing""" + def __init__(self, *args, **kwargs): """Initiates the frame and the GUI""" super(SimpleFrame, self).__init__(*args, **kwargs) - self.SetInitialSize(kwargs.get("size", (600,600))) + self.SetInitialSize(kwargs.get("size", (600, 600))) self.Center(wx.BOTH) self.sizer= wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) self.Show() - + class SimplePanel(wx.Panel): """Panel to use for testing""" + def __init__(self, *args, **kwargs): """Creates the GUI for the test panel""" super(SimplePanel, self).__init__(*args, **kwargs) self.sizer= wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.sizer) - -if __name__ == '__main__': - """Adds the test panel to the test frame""" - app = wx.PySimpleApp(False) - frame = SimpleFrame(None, title= "Testing SimpleFrame") - panel = SimplePanel(frame) - frame.sizer.Add(panel, 1, wx.EXPAND) - frame.Layout() - app.MainLoop() + self.SetSizer(self.sizer) \ No newline at end of file diff --git a/pythonforumide/gui_lib/ide_toolbar.py b/pythonforumide/gui_lib/ide_toolbar.py index ad8639d..ecac790 100644 --- a/pythonforumide/gui_lib/ide_toolbar.py +++ b/pythonforumide/gui_lib/ide_toolbar.py @@ -1,7 +1,7 @@ """ Created on 31 Jul 2011 -@author: D.W. +@author: D.W., David """ import wx @@ -10,7 +10,9 @@ class ToolBarPanel(wx.Panel): """ Creates a panel to hold the toolbar""" + def __init__(self, *args, **kwargs): + """Creates the GUI""" super(ToolBarPanel, self).__init__(*args, **kwargs) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) @@ -19,46 +21,46 @@ def __init__(self, *args, **kwargs): self.toolbar = ctrl self.Layout() - - class ToolBar(wx.ToolBar): + """Toolbar class, creates the toolbar and binds events""" + def __init__(self, *args, **kwargs): """Create the toolbar""" super(ToolBar, self).__init__(*args, **kwargs) self.SetToolBitmapSize((16, 16)) self._add_toolbar_btn(ide.ID_NEW, ide.id_text_new, menu_icons.get_icon_new()) - + self._add_toolbar_btn(ide.ID_OPEN, ide.id_text_open, menu_icons.get_icon_open()) - + self._add_toolbar_btn(ide.ID_SAVE, ide.id_text_save, menu_icons.get_icon_save()) - + self._add_toolbar_btn(ide.ID_SAVEAS, ide.id_text_saveas, menu_icons.get_icon_saveas()) - + self.AddSeparator() - + self._add_toolbar_btn(ide.ID_CUT, ide.id_text_cut, menu_icons.get_icon_cut()) - + self._add_toolbar_btn(ide.ID_COPY, ide.id_text_copy, menu_icons.get_icon_copy()) - + self._add_toolbar_btn(ide.ID_PASTE, ide.id_text_paste, menu_icons.get_icon_paste()) - + self.AddSeparator() - + self._add_toolbar_btn(ide.ID_UNDO, ide.id_text_undo, menu_icons.get_icon_undo()) - + self._add_toolbar_btn(ide.ID_REDO, ide.id_text_redo, menu_icons.get_icon_redo()) - + self.Realize() - + def _add_toolbar_btn(self, id, id_text, icon_bmp=None): """ Creates tool bar buttons""" self.AddLabelTool(id=id, label=id_text.toolbar, bitmap=icon_bmp, From 3b40b95a10ab2283113ca14f2cccc706d5f848bc Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 10:55:52 +0100 Subject: [PATCH 113/141] Added dialog when closing page, Issue #35 --- pythonforumide/config/Ide_Config.cfg | 4 +-- pythonforumide/gui_lib/ide_notebook_events.py | 30 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index a5c5d0b..c3f1a32 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>False indent = int>4 -mainframe.ypos = int>97 +mainframe.ypos = int>125 usetab = int>0 -mainframe.xpos = int>721 +mainframe.xpos = int>779 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_notebook_events.py b/pythonforumide/gui_lib/ide_notebook_events.py index 8d00dcd..6eb5934 100644 --- a/pythonforumide/gui_lib/ide_notebook_events.py +++ b/pythonforumide/gui_lib/ide_notebook_events.py @@ -44,9 +44,31 @@ def _on_page_closed(self, event): event.Skip() wx.CallAfter(self.notebook.editor_tab_name_untitled_tabs) + def ask_close(self, index): + dial = wx.MessageDialog(None, "Do you really want to close this page?", + "Close page", wx.YES_NO | wx.ICON_QUESTION) + + if dial.ShowModal() == wx.ID_YES: + self.close_accepted = True + return self.notebook.DeletePage(index) + def _on_page_closing(self, event): """Called when tab is closed""" - new_index= event.GetSelection() - if not new_index: - self.notebook._active_editor = None - event.Skip() + + #close_accepted == True means no asking to close page + + try: + if self.close_accepted: + self.close_accepted = False + return + except AttributeError: + index = event.GetSelection() + + if self.notebook._active_editor.GetModify() == 1: + event.Veto() + return self.ask_close(index) + + if not index: + self.notebook._active_editor = None + + event.Skip() From ea40b53fbfeaacce665c2f90466f1a3c5ddd1508 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 12:49:37 +0100 Subject: [PATCH 114/141] Remvoed text saying 'Editor' above the editor --- pythonforumide/config/Ide_Config.cfg | 8 ++++---- pythonforumide/gui_lib/ide_mainframe_panel.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index c3f1a32..578b181 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>False indent = int>4 -mainframe.ypos = int>125 +mainframe.ypos = int>24 usetab = int>0 -mainframe.xpos = int>779 -mainframe.height = int>600 -mainframe.width = int>600 +mainframe.xpos = int>0 +mainframe.height = int>744 +mainframe.width = int>1366 diff --git a/pythonforumide/gui_lib/ide_mainframe_panel.py b/pythonforumide/gui_lib/ide_mainframe_panel.py index 43bebdc..6a969e5 100644 --- a/pythonforumide/gui_lib/ide_mainframe_panel.py +++ b/pythonforumide/gui_lib/ide_mainframe_panel.py @@ -40,7 +40,7 @@ def _create_notebook_panel(self): headered_panel = HeaderedPanel(self, style = wx.BORDER_THEME) HeaderedPanelEvents(headered_panel) self._sizer.Add(headered_panel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 2) - ctrl = headered_panel.add_panel_ctrl(NoteBookPanel, "Editor", 100) + ctrl = headered_panel.add_panel_ctrl(NoteBookPanel, "", 100) NotebookEvents(ctrl) self.notebook = ctrl.notebook From 4d4838dbc73e21c71e636ac01e8ec7e432b80476 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 13:03:50 +0100 Subject: [PATCH 115/141] Alt + N now goes to page N --- pythonforumide/config/Ide_Config.cfg | 8 ++++---- pythonforumide/gui_lib/ide_mainframe_panel.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 578b181..3da8f68 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] -mainmenu.view.toolbar.show = bool>False +mainmenu.view.toolbar.show = bool>True indent = int>4 mainframe.ypos = int>24 usetab = int>0 -mainframe.xpos = int>0 -mainframe.height = int>744 -mainframe.width = int>1366 +mainframe.xpos = int>56 +mainframe.height = int>600 +mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_mainframe_panel.py b/pythonforumide/gui_lib/ide_mainframe_panel.py index 6a969e5..4aa3350 100644 --- a/pythonforumide/gui_lib/ide_mainframe_panel.py +++ b/pythonforumide/gui_lib/ide_mainframe_panel.py @@ -20,6 +20,7 @@ class MainFramePanel(wx.Panel): def __init__(self, *args, **kwargs): """Initializes the panel""" super(MainFramePanel, self).__init__(*args, **kwargs) + self.Bind(wx.EVT_KEY_UP, self.on_key_up) self._sizer = wx.BoxSizer(wx.VERTICAL) self._create_toolbar() self._create_notebook_panel() @@ -28,6 +29,19 @@ def __init__(self, *args, **kwargs): self.SetSizer(self._sizer) self.Layout() + def on_key_up(self, event): + key_code = event.GetKeyCode() + + number_keys = [49, 50, 51, 52, 53, 54, 56, 57, 58] + + if event.AltDown(): + if key_code in number_keys: + index = number_keys.index(key_code) + #~ print index + self.notebook.SetSelection(index) + + event.Skip() + def _create_toolbar(self): """Creates a toolbar""" ctrl= ToolBarPanel(self) From 2fb52b3980016daf2b8edcce59f268d1058deae4 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 13:51:27 +0100 Subject: [PATCH 116/141] Alt+N now working with EVT_KEY_DOWN --- pythonforumide/config/Ide_Config.cfg | 4 +- pythonforumide/gui_lib/ide_editor.py | 64 ++++++++------- .../gui_lib/ide_mainframe_events.py | 79 +++++++++---------- pythonforumide/gui_lib/ide_mainframe_panel.py | 14 ---- 4 files changed, 75 insertions(+), 86 deletions(-) diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg index 3da8f68..0b19eba 100644 --- a/pythonforumide/config/Ide_Config.cfg +++ b/pythonforumide/config/Ide_Config.cfg @@ -1,9 +1,9 @@ [ide] mainmenu.view.toolbar.show = bool>True indent = int>4 -mainframe.ypos = int>24 +mainframe.ypos = int>140 usetab = int>0 -mainframe.xpos = int>56 +mainframe.xpos = int>713 mainframe.height = int>600 mainframe.width = int>600 diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index ad6ae83..2607c7e 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -42,13 +42,13 @@ def __init__(self, *args, **kwargs): sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self._create_editor(sizer) - + def _create_editor(self, sizer): """ Creates the Editor control""" ctrl = Editor(self, style=wx.BORDER_THEME, pos=(-100, -100)) sizer.Add(ctrl, 1 , wx.EXPAND | wx.ALL, 1) self.editor = ctrl - + class Editor(stc.StyledTextCtrl): """Inherits wxStyledTextCtrl and handles all editor functions""" @@ -59,14 +59,14 @@ def __init__(self, *args, **kwargs): self.conf = wx.GetApp().config self.filepath = '' - self.indent_level = 0 - - self.SetGenerics() - self.SetMargins() + self.indent_level = 0 + + self.SetGenerics() + self.SetMargins() self.SetStyles() self.SetBindings() self.SetAutoComplete() - + def SetAutoComplete(self): self.autocomp = CodeCompletion() self.autocomp.add_builtins() @@ -75,7 +75,7 @@ def SetAutoComplete(self): def SetBindings(self): """Sets the key events bindings""" self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - + def SetGenerics(self): """Rather than do it in the __init__ and to help debugging the styles and settings are split into seperate SetOptions, this sets the generic @@ -87,10 +87,10 @@ def SetGenerics(self): self.SetIndentationGuides(1) #Indentation will only use space characters if useTabs is false self.SetUseTabs(False) - + def SetMargins(self): """This is specifically for the margins. Like the other Set methods it - is only really to be called in the __init__ its here more for + is only really to be called in the __init__ its here more for readability purpsoses than anything else.""" # margin 0 for breakpoints self.SetMarginSensitive(0, True) @@ -103,20 +103,20 @@ def SetMargins(self): # margin 2 for line numbers self.SetMarginType(2, stc.STC_MARGIN_NUMBER) self.SetMarginWidth(2, 28) - + def SetStyles(self, lang='python'): """This is different from the other Set methods that are called in the __init__ this one is for the highlighting and syntax of the langauge, - this will eventually be callable with different langauge styles. + this will eventually be callable with different langauge styles. For the moment, leave the lang kwarg in. """ - + #INDICATOR STYLES FOR ERRORS (self.errorMark) self.IndicatorSetStyle(2, stc.STC_INDIC_SQUIGGLE) self.IndicatorSetForeground(2, wx.RED) self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) # Python styles - + # White space self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) # Comment @@ -145,15 +145,15 @@ def SetStyles(self, lang='python'): self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "face:%(mono)s,fore:#990000,back:#C0C0C0,italic,size:%(size)d" % faces) # End of line where string is not closed self.StyleSetSpec(stc.STC_P_STRINGEOL, "face:%(mono)s,fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) - + def SmartIndent(self): """Handles smart indentation for the editor""" # Read settings from the config file - + # Suggestion: instead of indent_amount and use_tab, maybe just # have one config value, specifying what is to be used as indent. # -bunburya - + indent_amount = int(self.conf["indent"]) usetab = self.conf["usetab"] @@ -161,10 +161,10 @@ def SmartIndent(self): last_line = split_comments(self.GetLine(last_line_no))[0] self.NewLine() indent_level = self.GetLineIndentation(last_line_no) // indent_amount - + if last_line.rstrip().endswith(':'): indent_level += 1 - elif any(last_line.lstrip().startswith(token) + elif any(last_line.lstrip().startswith(token) for token in ["return", "break", "yield"]): indent_level = max([indent_level - 1, 0]) @@ -175,14 +175,14 @@ def SmartIndent(self): self.AddText(indent) print self.conf - + def AutoComp(self, event, keycode): """TODO: - If you indent (via tab or SmartIndent) and then autocomplete, it seems that the program automatically indents again after printing the word. - Properly handle uppercase; the current implementation ignores - caps lock. + caps lock. """ if keycode == wx.WXK_BACK: self.autocomp.back() @@ -201,13 +201,19 @@ def AutoComp(self, event, keycode): if choices: choices.sort() self.AutoCompShow(self.autocomp.len_entered-1, ' '.join(choices)) - - + def OnKeyDown(self, event): """Defines events for when the user presses a key""" key = event.GetKeyCode() control = event.ControlDown() alt = event.AltDown() + + number_keys = [49, 50, 51, 52, 53, 54, 56, 57, 58] + + if alt and key in number_keys: + index = number_keys.index(key) + self.GetParent().GetParent().SetSelection(index) + if key == wx.WXK_RETURN and not control and not alt: self.SmartIndent() else: @@ -219,12 +225,12 @@ def CanCut(self): if self.GetSelectedText(): return True return False - + def CanCopy(self): """ Returns true if there's a selection that can be copied""" """ Uses CanCut could be altered for its own checking if required""" return self.CanCut() - + def CanPaste(self): """ Returns ture if the clipboard contains text""" """ Note: I dont know at present how to check clipboard for contents""" @@ -234,16 +240,16 @@ def CanDelete(self): """ Returns true if there's a selection that can be deleted""" """ Uses CanCut could be altered for its own checking if required""" return self.CanCut() - + def load_file(self, path): """Loads a new file""" self.LoadFile(path) self.filepath= path - + def save_file(self): """Saves the current file""" return self.SaveFile(self.filepath) - + def save_file_as(self, filepath): """Save the current file as a new filepath""" self.filepath= filepath @@ -252,7 +258,7 @@ def save_file_as(self, filepath): if __name__=='__main__': """Adds the editor to the frame""" import ide_test_app as wx_app - import ide_simple_frame + import ide_simple_frame app = wx_app.Wx_App(False) frame= ide_simple_frame.SimpleFrame(None, title= "Testing editor") panel= ide_simple_frame.TestPanel(frame) diff --git a/pythonforumide/gui_lib/ide_mainframe_events.py b/pythonforumide/gui_lib/ide_mainframe_events.py index 4ccce9e..34d68b4 100644 --- a/pythonforumide/gui_lib/ide_mainframe_events.py +++ b/pythonforumide/gui_lib/ide_mainframe_events.py @@ -15,9 +15,9 @@ def __init__(self, view, model= None): """Sets the binds and does other stuff""" self.view = view self.model = model - self.mainframe= self.view - self.mainframe_panel= self.mainframe.mainframe_panel - self.notebook= self.view.mainframe_panel.notebook + self.mainframe = self.view + self.mainframe_panel = self.mainframe.mainframe_panel + self.notebook = self.view.mainframe_panel.notebook self._create_menu_file_binds() self._create_menu_edit_binds() self._create_menu_view_binds() @@ -28,32 +28,32 @@ def __init__(self, view, model= None): def _create_menu_file_binds(self): """ Creates the binds for the file menu""" self.mainframe.Bind(wx.EVT_MENU, self._on_new, id=ide.ID_NEW) - self.mainframe.Bind(wx.EVT_MENU, self._on_open, id=ide.ID_OPEN) + self.mainframe.Bind(wx.EVT_MENU, self._on_open, id=ide.ID_OPEN) self.mainframe.Bind(wx.EVT_MENU, self._on_save, id=ide.ID_SAVE) self.mainframe.Bind(wx.EVT_MENU, self._on_save_as, id=ide.ID_SAVEAS) self.mainframe.Bind(wx.EVT_MENU, self._on_close_tab, id=ide.ID_CLOSETAB) self.mainframe.Bind(wx.EVT_MENU, self._on_exit_app, id=ide.ID_EXITAPP) - + def _on_new(self, event): """Opens a new tab with a new editor instance""" self.notebook.editor_tab_new() - + def _on_open(self, event): """Opens a new tab and ask for a file to load""" self.mainframe.editor_open_file() - + def _on_save(self, event): """Saves the currently active file""" self.mainframe.editor_save() - + def _on_save_as(self, event): """Save as required filename""" self.mainframe.editor_save_as() - + def _on_close_tab(self, event): """Closes the current editor tab""" self.notebook.editor_tab_close_active() - + def _on_exit_app(self, event): """ application wants to exit""" self.mainframe.on_exit() @@ -66,48 +66,48 @@ def _create_menu_edit_binds(self): self.mainframe.Bind(wx.EVT_MENU, self._on_editor_copy, id=ide.ID_COPY) self.mainframe.Bind(wx.EVT_MENU, self._on_editor_paste, id=ide.ID_PASTE) self.mainframe.Bind(wx.EVT_MENU, self._on_editor_delete, id=ide.ID_DELETE) - self.mainframe.Bind(wx.EVT_MENU, self._on_editor_selectall, + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_selectall, id=ide.ID_SELECTALL) - + def _on_editor_undo(self, event): """Undo for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor.CanUndo(): self.mainframe.editor_undo() - + def _on_editor_redo(self, event): """Redo for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor.CanRedo(): self.mainframe.editor_redo() - + def _on_editor_cut(self, event): """Cut for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor.CanCut(): self.mainframe.editor_cut() - + def _on_editor_copy(self, event): """Copy for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor.CanCopy(): self.mainframe.editor_copy() - + def _on_editor_paste(self, event): """paste for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor.CanPaste(): self.mainframe.editor_paste() - + def _on_editor_delete(self, event): """Clear for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor.CanDelete(): self.mainframe.editor_delete_selection() - + def _on_editor_selectall(self, event): """Selectall for the current editor tab""" - active_editor= self.notebook.editor_tab_get_editor() + active_editor = self.notebook.editor_tab_get_editor() if active_editor: self.mainframe.editor_selectall() #-------------------------------------------------------------------- Menu View @@ -115,15 +115,15 @@ def _create_menu_view_binds(self): """ Creates the binds for the view menu""" self.mainframe.Bind(wx.EVT_MENU, self._on_view_toolbar_state, id= ide.ID_SHOW_TOOLBAR) - + def _on_view_toolbar_state(self, event): self.mainframe_panel.toolbar_show(event.Checked()) #------------------------------------------------------------------ Menu search def _create_menu_search_binds(self): """ Creates the binds for the search menu""" - self.mainframe.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, + self.mainframe.Bind(wx.EVT_MENU, self._on_editor_search_and_replace, id=ide.ID_SEARCH) - + def _on_editor_search_and_replace(self, event): """Replace for the current editor tab""" self.mainframe.editor_search_and_replace() @@ -132,7 +132,7 @@ def _create_menu_run_binds(self): """ Creates the binds for the run menu""" self.mainframe.Bind(wx.EVT_MENU, self._on_editor_run, id=ide.ID_RUNFILE) - + def _on_editor_run(self, event): """Runs selected code in a new window.""" self.mainframe.on_run() @@ -141,38 +141,38 @@ def _create_remaining_binds(self): """ Creates the non menu binds""" self.view.Bind(wx.EVT_UPDATE_UI, self._evt_update_ui) self.mainframe.Bind(wx.EVT_CLOSE, self._on_exit_app) - + def _evt_update_ui(self, event): """Events to update the view""" - event_id = event.GetId() - active_editor= self.notebook.editor_tab_get_editor() - if event_id== ide.ID_CUT: + + active_editor = self.notebook.editor_tab_get_editor() + if event_id == ide.ID_CUT: if active_editor: event.Enable(active_editor.CanCut()) else: event.Enable(False) - elif event_id== ide.ID_COPY: + elif event_id == ide.ID_COPY: if active_editor: event.Enable(active_editor.CanCopy()) else: event.Enable(False) - elif event_id== ide.ID_PASTE: + elif event_id == ide.ID_PASTE: if active_editor: event.Enable(active_editor.CanPaste()) else: event.Enable(False) - elif event_id== ide.ID_DELETE: + elif event_id == ide.ID_DELETE: if active_editor: event.Enable(active_editor.CanDelete()) else: event.Enable(False) - elif event_id== ide.ID_UNDO: + elif event_id == ide.ID_UNDO: if active_editor: event.Enable(active_editor.CanUndo()) else: event.Enable(False) - elif event_id== ide.ID_REDO: + elif event_id == ide.ID_REDO: if active_editor: event.Enable(active_editor.CanRedo()) else: @@ -185,6 +185,3 @@ def _evt_update_ui(self, event): event.Enable(False) else: event.Skip() - - - diff --git a/pythonforumide/gui_lib/ide_mainframe_panel.py b/pythonforumide/gui_lib/ide_mainframe_panel.py index 4aa3350..6a969e5 100644 --- a/pythonforumide/gui_lib/ide_mainframe_panel.py +++ b/pythonforumide/gui_lib/ide_mainframe_panel.py @@ -20,7 +20,6 @@ class MainFramePanel(wx.Panel): def __init__(self, *args, **kwargs): """Initializes the panel""" super(MainFramePanel, self).__init__(*args, **kwargs) - self.Bind(wx.EVT_KEY_UP, self.on_key_up) self._sizer = wx.BoxSizer(wx.VERTICAL) self._create_toolbar() self._create_notebook_panel() @@ -29,19 +28,6 @@ def __init__(self, *args, **kwargs): self.SetSizer(self._sizer) self.Layout() - def on_key_up(self, event): - key_code = event.GetKeyCode() - - number_keys = [49, 50, 51, 52, 53, 54, 56, 57, 58] - - if event.AltDown(): - if key_code in number_keys: - index = number_keys.index(key_code) - #~ print index - self.notebook.SetSelection(index) - - event.Skip() - def _create_toolbar(self): """Creates a toolbar""" ctrl= ToolBarPanel(self) From 63bb7665be181696e9d412bc9865d3e98c5528f3 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 13 Sep 2011 13:55:32 +0100 Subject: [PATCH 117/141] Removed testing code from master --- pythonforumide/gui_lib/ide_editor.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 2607c7e..117220f 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -254,16 +254,3 @@ def save_file_as(self, filepath): """Save the current file as a new filepath""" self.filepath= filepath return self.save_file() - -if __name__=='__main__': - """Adds the editor to the frame""" - import ide_test_app as wx_app - import ide_simple_frame - app = wx_app.Wx_App(False) - frame= ide_simple_frame.SimpleFrame(None, title= "Testing editor") - panel= ide_simple_frame.TestPanel(frame) - frame.sizer.Add(panel, 1, wx.EXPAND) - editor= Editor(panel) - panel.sizer.Add(editor, 1, wx.EXPAND) - frame.Layout() - app.MainLoop() From 5d6cf4a6d5e8530ef22146847daf25986c1305d6 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 27 Sep 2011 19:35:47 +0100 Subject: [PATCH 118/141] Fixed (I think) issue #45, needs testing --- pythonforumide/gui_lib/ide_mainframe.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 05d9b59..3ccf1e2 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -155,6 +155,15 @@ def editor_search_and_replace(self): def on_run(self): """Runs selected code in a new window.""" + + # Check if pywin32 is installed on Windows users. If not, alert them. + if os.name == "nt": #Check if the user is running Windows. + try: + import win32api + except ImportError: + return wx.MessageBox("You have to install win32", + "pywin32 missing") + # Create a test frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. reactor = wx.GetApp().this_reactor From d4a1d32757746af9c1e36dd1a8be4f72f6395785 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Thu, 29 Sep 2011 09:03:12 +0200 Subject: [PATCH 119/141] Changed pywin32 error message. --- pythonforumide/gui_lib/ide_mainframe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 3ccf1e2..289d6f3 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -161,7 +161,7 @@ def on_run(self): try: import win32api except ImportError: - return wx.MessageBox("You have to install win32", + return wx.MessageBox("To run files you need to install win32api", "pywin32 missing") # Create a test frame and hook into the caller. From b2a82490130a52de2e9cb140af3ecdcb29d99283 Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 29 Sep 2011 17:09:34 +0200 Subject: [PATCH 120/141] Moved the pywin32 stuff from on_run to the entry point --- pythonforumide/wx_app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 82f1dce..b13c170 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -75,5 +75,10 @@ class ListenFactory(Factory): protocol = ListenProtocol if __name__ == '__main__': + if os.name == 'nt': + try: + import win32api + except ImportError: + raise Exception("Pywin32 required.") wx_app = Wx_App(False) wx_app.start_reactor() From 93716f5e1121fc6259b51578268dbb120a68cd4b Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 29 Sep 2011 17:10:53 +0200 Subject: [PATCH 121/141] removed the win32 stuff from onrun. --- pythonforumide/gui_lib/ide_mainframe.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 289d6f3..1a0bbae 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -156,14 +156,6 @@ def editor_search_and_replace(self): def on_run(self): """Runs selected code in a new window.""" - # Check if pywin32 is installed on Windows users. If not, alert them. - if os.name == "nt": #Check if the user is running Windows. - try: - import win32api - except ImportError: - return wx.MessageBox("To run files you need to install win32api", - "pywin32 missing") - # Create a test frame and hook into the caller. # Allows this frame to be destroyed by the main window on close. reactor = wx.GetApp().this_reactor From b651cf8b6fb0e7bc4c64d251fe48dface41c287f Mon Sep 17 00:00:00 2001 From: David Gomes Date: Fri, 30 Sep 2011 20:42:47 +0100 Subject: [PATCH 122/141] JBoywer made master not run (error). This should never happen again. Fixed --- pythonforumide/wx_app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index b13c170..0606b65 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -5,6 +5,8 @@ @reviewer: David """ +import os + import wx import gui_lib.ide_mainframe as ide_mainframe import gui_lib.ide_mainframe_events as ide_mainframe_events From ad047b35c6cee5e85a3f938b411eae1b4bfe1460 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Fri, 30 Sep 2011 22:26:03 +0100 Subject: [PATCH 123/141] Cleaned some whitespace --- pythonforumide/wx_app.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 0606b65..73f2986 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -21,6 +21,7 @@ class Wx_App(wx.App): """Sets the editor up and the ports""" #Not sure if correct description + def __init__(self, *args, **kwargs): """Creates a wx App""" super(Wx_App, self).__init__(*args, **kwargs) @@ -28,54 +29,52 @@ def __init__(self, *args, **kwargs): self._create_port() self._set_up_reactor() self._create_mainframe() - + def _create_config(self): """Set up config""" self.config = IdeConfig() - + def _create_port(self): """Creates a free port""" self._port = get_free_port() - + def get_port(self): return self._port - + def _set_up_reactor(self): """Set's up the reactor""" reactor.registerWxApp(self) reactor.listenTCP(self._port, ListenFactory()) - #frame.Maximize() #Left commented to stop it getting on my nerves. - + def start_reactor(self): """ Starts the reactor, bind a reference to it locally.""" print "Port: %s" % (self.get_port()) self.this_reactor = reactor reactor.run() - + def _create_mainframe(self): """Creates the mainframe""" self.mainframe = ide_mainframe.MainFrame(None, title='PF-IDE - 0.1a') ide_mainframe_events.MainFrameEvents(self.mainframe) - + def OnExit(self): """Handles the event that closes the IDE""" print ("App closing") self.config.update_configfile() - - + class ListenProtocol(Protocol): """Handles connections""" def connectionMade(self): print "Got connection!!!!" - + def connectionLost(self, reason): print "Connection closed." class ListenFactory(Factory): """Handles Twisted listen protocols""" protocol = ListenProtocol - + if __name__ == '__main__': if os.name == 'nt': try: From 422f66c4eafeb91e41a9a367275fc97e9685d482 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 11 Oct 2011 16:10:01 +0100 Subject: [PATCH 124/141] Console goes automatically viewable when the user tries to run a file --- pythonforumide/config/Ide_Config.cfg | 12 ------------ pythonforumide/gui_lib/ide_console.py | 2 +- pythonforumide/gui_lib/ide_mainframe.py | 5 +++++ pythonforumide/wx_app.py | 4 ++-- 4 files changed, 8 insertions(+), 15 deletions(-) delete mode 100644 pythonforumide/config/Ide_Config.cfg diff --git a/pythonforumide/config/Ide_Config.cfg b/pythonforumide/config/Ide_Config.cfg deleted file mode 100644 index 9bfe695..0000000 --- a/pythonforumide/config/Ide_Config.cfg +++ /dev/null @@ -1,12 +0,0 @@ -<<<<<<< HEAD -======= -[ide] -mainmenu.view.toolbar.show = bool>True -indent = int>4 -mainframe.ypos = int>140 -usetab = int>0 -mainframe.xpos = int>713 -mainframe.height = int>600 -mainframe.width = int>600 - ->>>>>>> 63bb7665be181696e9d412bc9865d3e98c5528f3 diff --git a/pythonforumide/gui_lib/ide_console.py b/pythonforumide/gui_lib/ide_console.py index 6a67d00..df7ffd4 100644 --- a/pythonforumide/gui_lib/ide_console.py +++ b/pythonforumide/gui_lib/ide_console.py @@ -22,7 +22,7 @@ def __init__(self, *args, **kwargs): def _create_rich_text_ctrl(self): """Creates the textbox for the console""" - self._rt_ctrl = RichTextCtrl(self, style = wx.TE_READONLY) + self._rt_ctrl = RichTextCtrl(self, style=wx.TE_READONLY) monospace_font = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL, False, u"Monospace") self._rt_ctrl.SetFont(monospace_font) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 1a0bbae..fed411c 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -166,6 +166,11 @@ def on_run(self): # run_editor.Layout() run_panel = self.console + + #If the Console is not viewable, make it viewable to run the file + if run_panel.GetParent().GetParent()._minimized: + run_panel.GetParent().GetParent().restore_state() + active_editor = self.editor_tab_get_editor() if active_editor.filepath: diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 73f2986..0a6ced3 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -55,7 +55,7 @@ def start_reactor(self): def _create_mainframe(self): """Creates the mainframe""" - self.mainframe = ide_mainframe.MainFrame(None, title='PF-IDE - 0.1a') + self.mainframe = ide_mainframe.MainFrame(None, title="PF-IDE - 0.1a") ide_mainframe_events.MainFrameEvents(self.mainframe) def OnExit(self): @@ -82,4 +82,4 @@ class ListenFactory(Factory): except ImportError: raise Exception("Pywin32 required.") wx_app = Wx_App(False) - wx_app.start_reactor() + wx_app.start_reactor() \ No newline at end of file From d8818a2e6fac7cf6ed8ff7f697aa3a5f45e4a8bc Mon Sep 17 00:00:00 2001 From: David Gomes Date: Tue, 11 Oct 2011 16:51:25 +0100 Subject: [PATCH 125/141] Fully fixed issue #49 and now if the console is closed, it is showed to run the file --- pythonforumide/gui_lib/ide_mainframe.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index fed411c..67221d3 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -167,9 +167,12 @@ def on_run(self): run_panel = self.console - #If the Console is not viewable, make it viewable to run the file - if run_panel.GetParent().GetParent()._minimized: - run_panel.GetParent().GetParent().restore_state() + # If the console is closed or minimized, it should be made viewable + # and maximized to run the file + run_headered_panel = run_panel.GetParent().GetParent() + run_headered_panel.Show(True) + if run_headered_panel._minimized: + run_headered_panel.restore_state() active_editor = self.editor_tab_get_editor() @@ -183,7 +186,7 @@ def on_run(self): get_python_exe(), ["python", str(active_editor.filepath)]) - else: + else: # If the current file is not saved, run unsaved script run_panel.WriteText("Running unsaved script.") run_panel.Newline() script = StringIO() From 8c7e7fbd87f0c0898764981c8937b16638d04727 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 31 Oct 2011 23:23:11 +0000 Subject: [PATCH 126/141] Fixed README typo. --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 2c90216..35e62de 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ PythonForumIDE is a cross-platform Python IDE, and it is a Python-Forum Project (www.python-forum.org/pythonforum/). -Try it at your own rish, since it is still in development, even though it can be considered to be at Alpha stage. +Try it at your own risk, since it is still in development, even though it can be considered to be at Alpha stage. Report all bugs to https://github.com/Python-Forum/PythonForumIDE/issues From d7796c7758b8ac8ad9fcad234a42b9cc570b180b Mon Sep 17 00:00:00 2001 From: tralala Date: Sun, 29 Jul 2012 23:01:01 +0200 Subject: [PATCH 127/141] Fixed import error when Ini file is chosen when yaml is installed. --- pythonforumide/config/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index e1cf8aa..e0f4823 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -2,19 +2,20 @@ """ Created on Sat Jul 16 21:01:15 2011 @author: Jakob, Somelauw -@reviewer: David, SomeLauw +@reviewer: David, Somelauw """ from __future__ import print_function import os.path +import ConfigParser #Ugly config file :( try: import yaml #Pretty config file :D has_yaml = True except ImportError: - import ConfigParser #Ugly config file :( has_yaml = False + def file_config(profile): """ Returns the name of the configuration file. From adebe83fd9385f3c30f5876a58a8ba95625d1cb7 Mon Sep 17 00:00:00 2001 From: tralala Date: Sun, 29 Jul 2012 23:33:58 +0200 Subject: [PATCH 128/141] Made smartindentation do the right thing --- pythonforumide/gui_lib/ide_editor.py | 44 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 117220f..b0e6deb 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -1,5 +1,5 @@ """ -@author: Jakob, David, bunburya, confab +@author: Jakob, David, bunburya, confab, Somelauw @reviewer: Somelauw, ghoulmaster, David """ @@ -9,21 +9,21 @@ # from utils.textutils import split_comments -from utils.interpreter import PythonProcessProtocol -from utils.version import get_python_exe, introduction +#from utils.interpreter import PythonProcessProtocol +#from utils.version import get_python_exe, introduction from utils.autocomplete import CodeCompletion import wx.richtext import wx.stc as stc -import os.path +#import os.path import wx -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO +#try: + #from cStringIO import StringIO +#except ImportError: + #from StringIO import StringIO -from ide_simple_frame import SimpleFrame -from ide_replace_frame import ReplaceFrame +#from ide_simple_frame import SimpleFrame +#from ide_replace_frame import ReplaceFrame #TODO: make customisable font and sizes. Perhaps maked this named tuple? faces = { 'times': 'Times', @@ -153,28 +153,34 @@ def SmartIndent(self): # Suggestion: instead of indent_amount and use_tab, maybe just # have one config value, specifying what is to be used as indent. # -bunburya + # ^^ Please make a ticket on github. - indent_amount = int(self.conf["indent"]) + # Determine how to indent usetab = self.conf["usetab"] + if usetab: + indent = "\t" + else: + indent_amount = int(self.conf["indent"]) + indent = indent_amount * " " + + # The column in which we can find the cursor + cursorpos = self.GetColumn(self.GetCurrentPos()) last_line_no = self.GetCurrentLine() last_line = split_comments(self.GetLine(last_line_no))[0] self.NewLine() indent_level = self.GetLineIndentation(last_line_no) // indent_amount - if last_line.rstrip().endswith(':'): + # Should we increase or decrease the indent level + colonpos = last_line.find(":") + if colonpos >= 0 and cursorpos > colonpos: indent_level += 1 elif any(last_line.lstrip().startswith(token) for token in ["return", "break", "yield"]): indent_level = max([indent_level - 1, 0]) - if usetab: - indent = "\t" * indent_level - else: - indent = indent_amount * " " * indent_level - - self.AddText(indent) - print self.conf + self.AddText(indent * indent_level) + #print self.conf def AutoComp(self, event, keycode): """TODO: From bb4f380349a762cf5e416a37786ee10af78f527f Mon Sep 17 00:00:00 2001 From: tralala Date: Sun, 29 Jul 2012 23:42:58 +0200 Subject: [PATCH 129/141] Fixed a bug when saving a file that has not been saved before. --- pythonforumide/gui_lib/ide_editor.py | 1 - pythonforumide/gui_lib/ide_mainframe.py | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index b0e6deb..a1ac96c 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -180,7 +180,6 @@ def SmartIndent(self): indent_level = max([indent_level - 1, 0]) self.AddText(indent * indent_level) - #print self.conf def AutoComp(self, event, keycode): """TODO: diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 67221d3..0c7bae3 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -88,7 +88,8 @@ def editor_save(self): if active_editor.filepath: active_editor.save_file() else: - self.save_as_active_editor() + #self.save_as_active_editor() # Function doesn't exist? + self.editor_save_as() def editor_save_as(self): """Save as for the currently active editor file""" @@ -214,7 +215,7 @@ def ask_exit(self): def on_exit(self): """Handles the event triggered by the user to exit""" try: - current_text = self.editor_tab_get_editor().GetText() + #current_text = self.editor_tab_get_editor().GetText() #Right now, this check if the current open file (the viewable tab) #has been saved or not. If not, prompt the user about quitting From fd5230f268f23b9f38aa34007649be4748faac3f Mon Sep 17 00:00:00 2001 From: Somelauw Date: Sun, 29 Jul 2012 23:57:33 +0200 Subject: [PATCH 130/141] Fixed error when autoindenting while using tabs --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index a1ac96c..7f67d90 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -153,11 +153,11 @@ def SmartIndent(self): # Suggestion: instead of indent_amount and use_tab, maybe just # have one config value, specifying what is to be used as indent. # -bunburya - # ^^ Please make a ticket on github. # Determine how to indent usetab = self.conf["usetab"] if usetab: + indent_amount = 1 indent = "\t" else: indent_amount = int(self.conf["indent"]) From f3fa688b29f0b3a5b837369029cd7410b2a3e2d6 Mon Sep 17 00:00:00 2001 From: Somelauw Date: Mon, 30 Jul 2012 00:06:53 +0200 Subject: [PATCH 131/141] When using tabs, indent_amount should be tabwidth --- pythonforumide/gui_lib/ide_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 7f67d90..c0c5caf 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -157,7 +157,7 @@ def SmartIndent(self): # Determine how to indent usetab = self.conf["usetab"] if usetab: - indent_amount = 1 + indent_amount = self.GetTabWidth() indent = "\t" else: indent_amount = int(self.conf["indent"]) From e6e787c56da79b15735e87913772a63a317ef6ca Mon Sep 17 00:00:00 2001 From: bunburya Date: Sun, 29 Jul 2012 23:28:59 +0100 Subject: [PATCH 132/141] removed hardcoded list of keywords --- pythonforumide/utils/autocomplete.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 6289e82..5c5ba21 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -2,12 +2,9 @@ @author: bunburya """ -# maybe move these to a separate file eventually -keywords = set(['and ', 'elif ', 'is ', 'global ', 'pass', 'if ', - 'from ', 'raise ', 'for ', 'except ', 'finally', 'print ', - 'import ', 'return ', 'exec ', 'else', 'break', 'not ', 'class ', - 'assert ', 'in ', 'yield ', 'try', 'while ', 'continue', 'del ', - 'or ', 'def ', 'lambda ']) +from keyword import kwlist + +keywords = set(kwlist) builtins = set(__builtins__.keys()) class CodeCompletion(object): From 2d097e43c0fb4968bbfbf6b855d52aff86cde2a0 Mon Sep 17 00:00:00 2001 From: bunburya Date: Sun, 29 Jul 2012 23:52:15 +0100 Subject: [PATCH 133/141] improved how autocomplete recognises builtin commands/modules --- pythonforumide/gui_lib/ide_editor.py | 2 -- pythonforumide/utils/autocomplete.py | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index c0c5caf..6e29251 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -69,8 +69,6 @@ def __init__(self, *args, **kwargs): def SetAutoComplete(self): self.autocomp = CodeCompletion() - self.autocomp.add_builtins() - self.autocomp.add_keywords() def SetBindings(self): """Sets the key events bindings""" diff --git a/pythonforumide/utils/autocomplete.py b/pythonforumide/utils/autocomplete.py index 5c5ba21..a9185b2 100644 --- a/pythonforumide/utils/autocomplete.py +++ b/pythonforumide/utils/autocomplete.py @@ -1,31 +1,32 @@ """ @author: bunburya """ - +from sys import builtin_module_names from keyword import kwlist -keywords = set(kwlist) -builtins = set(__builtins__.keys()) class CodeCompletion(object): """A backend class for code completion. """ valid_ch = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' + + keywords = set(kwlist) + builtins = set(__builtins__.keys()) + builtin_modules = set(builtin_module_names) def __init__(self, *modules): self._suggestions = set() self._cache = set() self._key = [] + self._suggestions.update( + self.keywords, + self.builtins, + self.builtin_modules + ) for mod in modules: self.add_module(mod) - def add_builtins(self): - self._suggestions.update(builtins) - - def add_keywords(self): - self._suggestions.update(keywords) - def add_module(self, module): """Adds the variable and method names from a module to the pool of potential suggestions. From 01ae8d62022f790edc6b3bbcbbec300ec4e18942 Mon Sep 17 00:00:00 2001 From: bunburya Date: Mon, 30 Jul 2012 01:43:53 +0100 Subject: [PATCH 134/141] allowed addition of .cfg and .yaml files --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4738d04..c11bd69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ *.pyc -*.cfg -*.yaml .*.swp *~ From 18a22b2a2d0c053895f624b379fd6c74f5be1a49 Mon Sep 17 00:00:00 2001 From: bunburya Date: Mon, 30 Jul 2012 01:44:17 +0100 Subject: [PATCH 135/141] simplified config: got rid of yaml, made cfg file prettier. --- pythonforumide/config/config.py | 290 ++++-------------------- pythonforumide/config/pf_ide.cfg | 11 + pythonforumide/gui_lib/ide_editor.py | 9 +- pythonforumide/gui_lib/ide_mainframe.py | 28 ++- pythonforumide/wx_app.py | 8 +- 5 files changed, 76 insertions(+), 270 deletions(-) create mode 100644 pythonforumide/config/pf_ide.cfg diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index e0f4823..52a89d3 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -1,251 +1,39 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jul 16 21:01:15 2011 -@author: Jakob, Somelauw -@reviewer: David, Somelauw -""" - -from __future__ import print_function -import os.path -import ConfigParser #Ugly config file :( - -try: - import yaml #Pretty config file :D - has_yaml = True -except ImportError: - has_yaml = False - - -def file_config(profile): - """ - Returns the name of the configuration file. - It does not guarantee existence. - Side effect: - Tells the user if can't load the configuration file if it is not supported. - """ - - # We will need some fuzzy logic to accomplish this - profile, ext = os.path.splitext(profile) - yaml_exists = os.path.exists(''.join((profile, ".yaml"))) - cfg_exists = os.path.exists(''.join((profile, ".cfg"))) - - # If no extension could be found, - # guess what configuration type is likely to be preferred. - if not ext: - if yaml_exists or (has_yaml and not cfg_exists): - ext = ".yaml" - else: - ext = ".cfg" - - # We can't load yaml if it doesn't exist - if ext == ".yaml" and not has_yaml: - print("yaml configuration could not be loaded", - "because python-yaml is not installed.") - ext = ".cfg" - - return ''.join((profile, ext)) - -def load_config(configfile): - """Loads a config file""" - - profile, ext = os.path.splitext(configfile) - if ext == ".yaml": - return _BeautifulConfig(profile) - else: - return _UglyConfig(profile) - -class _BeautifulConfig(object): - """This is the config object if we can import yaml.""" - - def __init__(self, profile): - """ - Load the yaml config from file, - if this fails write an empty new one. - """ - - filename = ''.join((profile, ".yaml")) - - try: - self.config = yaml.load(open(filename)) - except IOError: - self.config = None - - if self.config is None: - self.config = {} - - self.file_config = open(filename, 'w') - - def __setitem__(self, option, value): - """Set an option to a value inside the config, does not write.""" - - self.config[option] = value - - def __getitem__(self, option): - """ - Gets the option from the config. - Return None, if the option is not there - """ - - return self.config.get(option, None) - - def set_default(self, option, value): - - self.config.set_default(option, value) - - def save(self): - """Writes the config as yaml out to the config file.""" - - yaml.dump(self.config, self.file_config) - -class _UglyConfig(object): - """ - This is the config object created if we can't use YAML and have to - rely on ConfigParser. - """ - - def __init__(self, profile): - - filename = ''.join((profile, ".cfg")) - self.config = ConfigParser.ConfigParser() - self.config.read(filename) - - if not self.config.has_section('ide'): - self.config.add_section('ide') - - self.file_config = open(filename, 'w') - - def __setitem__(self, option, value): - """Set the value to the option inside the default section.""" - - self.config.set('ide', option, value) - - def __getitem__(self, option): - """Return the values to the option given, or return None""" - - try: - return self.config.get('ide', option) - except ConfigParser.NoOptionError: - return None - - def set_default(self, option, value): - - if not self.config.has_option('ide', option): - self[option] = value - - def save(self): - """Write config to file.""" - - self.config.write(self.file_config) - - -#If we have yaml then the ConfigObject will point to the cooler config object -if has_yaml: - Config = _BeautifulConfig -# Otherwise we point to the ugly one -else: - Config = _UglyConfig -#This way the importers use the config the same way, same api but under the -#hood they are different. - - -class IdeConfig(object): - """ Store's config values in a dict, and to file - converts to and from python type""" - def __init__(self, filepath="", filename="Ide_Config"): - """ Initialize""" - if not filepath: - filepath = os.path.dirname(__file__) - self._fullpath = None - self._data = {} - self._get_file_fullpath(filepath, filename) - self.set_defaults() - self.read_from_config_file() - - def _get_file_fullpath(self, filepath, filename): - """ Works out which config file type and creates if not exists""" - _temp_path = os.path.join(filepath, filename) - self._fullpath= file_config(_temp_path) - with open(self._fullpath, "a") as _: - # Opens file as append to ensure safe creation of the file. - pass - print ("Config using filepath: %s" % (self._fullpath)) - - def read_from_config_file(self): - """ reads the config file and over writes defaults if theres as entry""" - """ if theres a type flag it converts to the python type""" - confile = load_config(self._fullpath) - for key, value in self._data.iteritems(): - config_value = confile[key] - if config_value: - self._data[key] = self.convert_to_pytype(config_value) - confile.file_config.close() - - def convert_to_pytype(self, config_value): - """ Converts the file stored string to a few python types""" - try: - get_type, get_value = config_value.split(">") - if get_type == "int": - return int(get_value) - elif get_type == "float": - return float(get_value) - elif get_type == "bool" and get_value == "True": - return True - elif get_type == "bool" and get_value == "False": - return False - else: - return str(get_value) - except Exception, exception: - print ("Exception when convert_to_pytype", exception.message) - return str(config_value) - - def update_configfile(self): - """ Writes back the current values to the config file""" - confile = load_config(self._fullpath) - for key, value in self._data.iteritems(): - confile[key] = self.convert_pytype_to_str(value) - confile.save() - confile.file_config.close() - - def convert_pytype_to_str(self, value): - """ Adds a pytype flag to the entry""" - get_type = str(type(value)) - get_type = get_type[:-2].split("'")[1] - if get_type == "int": - return "int>%s" % (value) - elif get_type == "float": - return "float>%s" % (value) - elif get_type == "bool" and value == True: - return "bool>%s" % (value) - elif get_type == "bool" and value == False: - return "bool>%s" % (value) - else: - return "str>%s" % (value) - - def __setitem__(self, key, value): - """ Set the current config value""" - self._data[key] = value - - def __getitem__(self, key): - """ Get the current config value""" - return self._data[key] - - - def set_defaults(self): - """ Sets default values""" - self._data["indent"] = 4 - self._data["usetab"] = 0 - self._data["MainFrame.Height"] = 600 - self._data["MainFrame.Width"] = 600 - self._data["MainFrame.XPos"]= 0 - self._data["MainFrame.YPos"]= 0 - self._data["MainMenu.View.Toolbar.Show"]= True - - -if __name__ == '__main__': - ide_config = IdeConfig() - - value = (ide_config["MainFrame.Width"]) - print (value) - ide_config.update_configfile() - - +from os.path import join, dirname +from ConfigParser import ConfigParser + +def get_config_filename(): + """This is just a placeholder for now.""" + return join(dirname(__file__), 'pf_ide.cfg') + +def get_default_config(): + c = ConfigParser() + c.add_section('interface') + c.set('interface', 'y_pos', '17') + c.set('interface', 'x_pos', '383') + c.set('interface', 'height', '779') + c.set('interface', 'width', '683') + c.set('interface', 'show_toolbar', 'yes') + c.add_section('editing') + c.set('editing', 'indent', '4') + c.set('editing', 'usetab', 'yes') + return c + +def read_config_from(fname): + c = get_default_config() + c.read(fname) + return c + +def read_config(): + fname = get_config_filename() + return read_config_from(fname) + +def write_config_to(c, fname): + with open(fname, 'w') as f: + c.write(f) + +def write_config(c): + fname = get_config_filename() + write_config_to(c, fname) + +def write_default_config(): + write_config(get_default_config()) diff --git a/pythonforumide/config/pf_ide.cfg b/pythonforumide/config/pf_ide.cfg new file mode 100644 index 0000000..044eb5f --- /dev/null +++ b/pythonforumide/config/pf_ide.cfg @@ -0,0 +1,11 @@ +[interface] +y_pos = 17 +x_pos = 383 +height = 779 +width = 683 +show_toolbar = True + +[editing] +indent = 4 +usetab = no + diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 6e29251..8bb77f8 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -80,11 +80,12 @@ def SetGenerics(self): options like Tabwidth, expandtab and indentation guides + others.""" self.SetLexer(stc.STC_LEX_PYTHON) self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) #set mono spacing here! - # Presumably we should be reading this stuff from config? - self.SetTabWidth(4) - self.SetIndentationGuides(1) + + # Some values obtained from config + self.SetTabWidth(self.conf.getint('editing', 'indent')) + self.SetIndentationGuides(1) # what is this? #Indentation will only use space characters if useTabs is false - self.SetUseTabs(False) + self.SetUseTabs(self.conf.getboolean('editing', 'usetab')) def SetMargins(self): """This is specifically for the margins. Like the other Set methods it diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index 0c7bae3..cd928ce 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -56,22 +56,28 @@ def _create_main_panel(self): def _apply_config_settings(self): """ Applys the stored config values""" - self.SetSize((self._config["MainFrame.Width"], - self._config["MainFrame.Height"])) - self.MoveXY(self._config["MainFrame.XPos"], - self._config["MainFrame.YPos"]) - self.menu_view_toolbar_show(self._config["MainMenu.View.Toolbar.Show"]) + self.SetSize(( + self._config.getint('interface', 'width'), + self._config.getint('interface', 'height') + )) + self.MoveXY( + self._config.getint('interface', 'x_pos'), + self._config.getint('interface', 'y_pos') + ) + self.menu_view_toolbar_show( + self._config.getboolean('interface', 'show_toolbar') + ) def _store_config_settings(self): """ Stores the current config values""" width, height = self.GetSizeTuple() - self._config["MainFrame.Width"] = width - self._config["MainFrame.Height"] = height + self._config.set('interface', 'width', width) + self._config.set('interface', 'height', height) xpos, ypos = self.GetPositionTuple() - self._config["MainFrame.XPos"] = xpos - self._config["MainFrame.YPos"] = ypos - self._config["MainMenu.View.Toolbar.Show"] = \ - self.MenuBar.IsChecked(ID_SHOW_TOOLBAR) + self._config.set('interface', 'x_pos', xpos) + self._config.set('interface', 'y_pos', ypos) + self._config.set('interface', 'show_toolbar', + self.MenuBar.IsChecked(ID_SHOW_TOOLBAR)) def editor_tab_get_editor(self): """Returns the currently active editor instance from notebook""" diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 0a6ced3..8ed917e 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -10,7 +10,7 @@ import wx import gui_lib.ide_mainframe as ide_mainframe import gui_lib.ide_mainframe_events as ide_mainframe_events -from config.config import IdeConfig +from config.config import read_config, write_config from twisted.internet import wxreactor wxreactor.install() @@ -32,7 +32,7 @@ def __init__(self, *args, **kwargs): def _create_config(self): """Set up config""" - self.config = IdeConfig() + self.config = read_config() def _create_port(self): """Creates a free port""" @@ -61,7 +61,7 @@ def _create_mainframe(self): def OnExit(self): """Handles the event that closes the IDE""" print ("App closing") - self.config.update_configfile() + write_config(self.config) class ListenProtocol(Protocol): """Handles connections""" @@ -82,4 +82,4 @@ class ListenFactory(Factory): except ImportError: raise Exception("Pywin32 required.") wx_app = Wx_App(False) - wx_app.start_reactor() \ No newline at end of file + wx_app.start_reactor() From 9c8527733fa2ddef2755b9d78acb4da128e37683 Mon Sep 17 00:00:00 2001 From: bunburya Date: Mon, 30 Jul 2012 01:55:37 +0100 Subject: [PATCH 136/141] changed metadata on foot of previous commit --- pythonforumide/config/config.py | 5 +++++ pythonforumide/gui_lib/ide_mainframe.py | 2 +- pythonforumide/wx_app.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index 52a89d3..f0eb664 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -1,3 +1,8 @@ +""" +Created on Mon Jul 16 2012 +@author: bunburya +""" + from os.path import join, dirname from ConfigParser import ConfigParser diff --git a/pythonforumide/gui_lib/ide_mainframe.py b/pythonforumide/gui_lib/ide_mainframe.py index cd928ce..79bafe8 100644 --- a/pythonforumide/gui_lib/ide_mainframe.py +++ b/pythonforumide/gui_lib/ide_mainframe.py @@ -2,7 +2,7 @@ """ Created on Wed Jul 27 17:36:42 2011 -@author: jakob, David, confab +@author: jakob, David, confab, bunburya """ import wx import os diff --git a/pythonforumide/wx_app.py b/pythonforumide/wx_app.py index 8ed917e..adc60f0 100644 --- a/pythonforumide/wx_app.py +++ b/pythonforumide/wx_app.py @@ -1,7 +1,7 @@ """ Created on 31 Jul 2011 -@author: D.W., David, confab +@author: D.W., David, confab, bunburya @reviewer: David """ From 63f12410d4342c37fc227f81af218dc6aaff5886 Mon Sep 17 00:00:00 2001 From: bunburya Date: Mon, 30 Jul 2012 02:31:08 +0100 Subject: [PATCH 137/141] update cfg file to properly reflect default --- pythonforumide/config/pf_ide.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforumide/config/pf_ide.cfg b/pythonforumide/config/pf_ide.cfg index 044eb5f..e3b8d04 100644 --- a/pythonforumide/config/pf_ide.cfg +++ b/pythonforumide/config/pf_ide.cfg @@ -3,9 +3,9 @@ y_pos = 17 x_pos = 383 height = 779 width = 683 -show_toolbar = True +show_toolbar = yes [editing] indent = 4 -usetab = no +usetab = yes From 26b4b0d61eb81db21fab56a2e57cec53eae6c0ff Mon Sep 17 00:00:00 2001 From: Somelauw Date: Mon, 30 Jul 2012 10:53:02 +0200 Subject: [PATCH 138/141] Old config file was used in ide_editor, causing errors when indenting. --- pythonforumide/gui_lib/ide_editor.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/pythonforumide/gui_lib/ide_editor.py b/pythonforumide/gui_lib/ide_editor.py index 8bb77f8..227af4d 100644 --- a/pythonforumide/gui_lib/ide_editor.py +++ b/pythonforumide/gui_lib/ide_editor.py @@ -149,25 +149,18 @@ def SmartIndent(self): """Handles smart indentation for the editor""" # Read settings from the config file - # Suggestion: instead of indent_amount and use_tab, maybe just - # have one config value, specifying what is to be used as indent. - # -bunburya - # Determine how to indent - usetab = self.conf["usetab"] + usetab = self.conf.getboolean("editing", "usetab") if usetab: indent_amount = self.GetTabWidth() indent = "\t" else: - indent_amount = int(self.conf["indent"]) + indent_amount = self.conf.getint("editing", "indent") indent = indent_amount * " " - # The column in which we can find the cursor cursorpos = self.GetColumn(self.GetCurrentPos()) - last_line_no = self.GetCurrentLine() last_line = split_comments(self.GetLine(last_line_no))[0] - self.NewLine() indent_level = self.GetLineIndentation(last_line_no) // indent_amount # Should we increase or decrease the indent level @@ -178,6 +171,8 @@ def SmartIndent(self): for token in ["return", "break", "yield"]): indent_level = max([indent_level - 1, 0]) + # Perform the actual smartindent + self.NewLine() self.AddText(indent * indent_level) def AutoComp(self, event, keycode): From ff9b0329ac87e996b64d71c1a0c9a33eb820282c Mon Sep 17 00:00:00 2001 From: Somelauw Date: Mon, 30 Jul 2012 11:08:05 +0200 Subject: [PATCH 139/141] Removed config file since this file can be easily regenerated. --- .gitignore | 1 + pythonforumide/config/pf_ide.cfg | 11 ----------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 pythonforumide/config/pf_ide.cfg diff --git a/.gitignore b/.gitignore index c11bd69..921376e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc .*.swp *~ +*.cfg diff --git a/pythonforumide/config/pf_ide.cfg b/pythonforumide/config/pf_ide.cfg deleted file mode 100644 index e3b8d04..0000000 --- a/pythonforumide/config/pf_ide.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[interface] -y_pos = 17 -x_pos = 383 -height = 779 -width = 683 -show_toolbar = yes - -[editing] -indent = 4 -usetab = yes - From 0cebb9157bd1b506a81184bed8c5f390828dfa89 Mon Sep 17 00:00:00 2001 From: David Gomes Date: Mon, 30 Jul 2012 10:51:20 +0100 Subject: [PATCH 140/141] Fixed issue #55. --- pythonforumide/gui_lib/ide_mainframe_panel.py | 1 + pythonforumide/gui_lib/ide_notebook.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pythonforumide/gui_lib/ide_mainframe_panel.py b/pythonforumide/gui_lib/ide_mainframe_panel.py index 6a969e5..3f8e69c 100644 --- a/pythonforumide/gui_lib/ide_mainframe_panel.py +++ b/pythonforumide/gui_lib/ide_mainframe_panel.py @@ -43,6 +43,7 @@ def _create_notebook_panel(self): ctrl = headered_panel.add_panel_ctrl(NoteBookPanel, "", 100) NotebookEvents(ctrl) self.notebook = ctrl.notebook + self.notebook.parent = headered_panel def _create_console(self): """Create a console window""" diff --git a/pythonforumide/gui_lib/ide_notebook.py b/pythonforumide/gui_lib/ide_notebook.py index 6bd264d..ad552dc 100644 --- a/pythonforumide/gui_lib/ide_notebook.py +++ b/pythonforumide/gui_lib/ide_notebook.py @@ -40,6 +40,7 @@ def __init__(self, *args, **kwargs): def editor_tab_new(self, page_name=""): """Opens a new editor tab""" + self.parent.show(True) editor_panel = EditorPanel(self) self.AddPage(editor_panel, page_name) self._active_editor = editor_panel.editor From f98fd9912ea8fc445dddd57cadaaeb21bc4a3d7c Mon Sep 17 00:00:00 2001 From: bunburya Date: Mon, 30 Jul 2012 13:37:35 +0100 Subject: [PATCH 141/141] changing default conf behaviour to reflect PEP8 --- pythonforumide/config/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforumide/config/config.py b/pythonforumide/config/config.py index f0eb664..5d6463d 100644 --- a/pythonforumide/config/config.py +++ b/pythonforumide/config/config.py @@ -20,7 +20,7 @@ def get_default_config(): c.set('interface', 'show_toolbar', 'yes') c.add_section('editing') c.set('editing', 'indent', '4') - c.set('editing', 'usetab', 'yes') + c.set('editing', 'usetab', 'no') return c def read_config_from(fname):