Advertisement
Guest User

main.py

a guest
Dec 2nd, 2012
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.12 KB | None | 0 0
  1. import os
  2. import sys
  3. import pickle
  4.  
  5. import wx
  6. import wx.aui
  7. import appdirs
  8.  
  9. import broadpy.addrtools as addrtools
  10.  
  11. import address_cleaner.const as const
  12. import address_cleaner.shared as shared
  13. from address_cleaner.shared import vprint
  14. from address_cleaner.addrlookup import AddressLookupPanel
  15. from address_cleaner.tablecleaner import TableCleanerPanel
  16.  
  17. def run():
  18.     app = App(redirect=False)
  19.     app.MainLoop()
  20.  
  21. class App(wx.App):
  22.     def OnInit(self):
  23.         # Initialize the config system.
  24.         initialize_config()
  25.  
  26.         # Try to connect to MySQL.
  27.         if not connect_to_database():
  28.             return False
  29.  
  30.         # Confirm that the cache is loaded.
  31.         load_norway_cache()
  32.  
  33.         # Create the main frame and show it.
  34.         self.frame = MainFrame(None)
  35.         self.SetTopWindow(self.frame)
  36.         self.frame.Show(True)
  37.  
  38.         # Call raise on the frame. This helps on Windows, where the window
  39.         # appears below the CMD it was created from.
  40.         self.frame.Raise()
  41.  
  42.         return True
  43.  
  44.     def MacOpenFile(self, filename):
  45.         vprint(2, 'MacOpenFile:', filename)
  46.  
  47.     def MacReopenApp(self):
  48.         vprint(2, 'MacReopenApp')
  49.  
  50.         self.GetTopWindow().Raise()
  51.  
  52. class MainFrame(wx.Frame):
  53.     MIN_SIZE = (400, 300)
  54.     DEFAULT_SIZE = (1024, 768)
  55.  
  56.     def __init__(self, parent):
  57.         wx.Frame.__init__(self, parent, -1, 'Address cleaner')
  58.  
  59.         # Initialize the controls.
  60.         self.initialize_ui()
  61.  
  62.         # Bind accelerators.
  63.         at = wx.AcceleratorTable([
  64.             (wx.ACCEL_CTRL, ord('W'), wx.ID_CLOSE)
  65.         ])
  66.         self.SetAcceleratorTable(at)
  67.  
  68.         # Set the starting frame size.
  69.         self.initialize_dimensions()
  70.  
  71.         # Set the initial status bar text.
  72.         self.SetStatusText('Connected to MySQL database at {}.'.format(
  73.             addrtools.good_con_info['mysql_con']['host']
  74.         ))
  75.  
  76.         # Bind resize event to save the frame size.
  77.         self.Bind(wx.EVT_MOVE, self.on_rect_change)
  78.         self.Bind(wx.EVT_SIZE, self.on_rect_change)
  79.         self.Bind(wx.EVT_CLOSE, self.on_close)
  80.  
  81.     def initialize_dimensions(self):
  82.         """Loads the cached frame position and size, or uses default values."""
  83.  
  84.         # Load the frame size.
  85.         rect = shared.config_get('main_frame_rect')
  86.  
  87.         self.SetMinSize(self.MIN_SIZE)
  88.  
  89.         if rect:
  90.             self.Rect = rect
  91.         else:
  92.             self.SetSize(self.DEFAULT_SIZE)
  93.             self.CentreOnScreen()
  94.  
  95.         if shared.config_get('main_frame_maximized', False):
  96.             self.Maximize()
  97.  
  98.     def initialize_ui(self):
  99.         """Initializes the UI for this Frame."""
  100.  
  101.         # Create and set the menu bar.
  102.         self.main_menu = self.create_main_menu()
  103.         self.SetMenuBar(self.main_menu)
  104.  
  105.         # Create the AUI notebook. Also add an address lookup page.
  106.         self.notebook = wx.aui.AuiNotebook(self)
  107.         self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_page_close)
  108.         self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_page_changed)
  109.  
  110.         self.notebook.AddPage(
  111.             AddressLookupPanel(self.notebook),
  112.             'Address lookup'
  113.         )
  114.  
  115.         # Add a status bar.
  116.         self.CreateStatusBar()
  117.  
  118.     def create_main_menu(self):
  119.         """Creates and returns the main menu."""
  120.  
  121.         main_menu_bar = wx.MenuBar()
  122.  
  123.         new_al_page_label = 'New &address lookup page\tCTRL+ALT+A'
  124.         file_menu = shared.create_menu([
  125.             (wx.ID_OPEN, '&Open...\tCTRL+O'),
  126.             (const.ID_NEW_ADDRESS_LOOKUP_PAGE, new_al_page_label),
  127.             (),
  128.             (wx.ID_CLOSE, '&Close page'),
  129.             (),
  130.             (wx.ID_EXIT, 'E&xit')
  131.         ])
  132.  
  133.         help_menu = shared.create_menu([
  134.             (wx.ID_ABOUT, 'About Address Cleaner')
  135.         ])
  136.  
  137.         # Add menus to menu bar.
  138.         main_menu_bar.Append(file_menu, '&File')
  139.         main_menu_bar.Append(help_menu, '&Help')
  140.        
  141.         # Event handling.
  142.         self.Bind(wx.EVT_MENU, self.on_main_menu)
  143.         self.Bind(wx.EVT_UPDATE_UI, self.on_main_menu_update_ui)
  144.  
  145.         return main_menu_bar
  146.  
  147.     def create_address_lookup_panel(self, parent):
  148.         """Creates and returns an address lookup panel."""
  149.         return
  150.  
  151.     def add_address_lookup_page(self):
  152.         """Adds an address lookup page."""
  153.         panel = AddressLookupPanel(self.notebook)
  154.         self.notebook.AddPage(panel, 'Address lookup page')
  155.  
  156.         # Select the new page.
  157.         self.notebook.SetSelection(self.notebook.GetPageCount()-1)
  158.  
  159.     def add_table_cleaner_page(self, path):
  160.         """Adds a table cleaner page."""
  161.         panel = TableCleanerPanel(self.notebook, path)
  162.  
  163.         filename = os.path.basename(path)
  164.  
  165.         # Cap the file name to 30 characters.
  166.         filename = len(filename) > 30 and filename[:30 - 3] + '...' or filename
  167.  
  168.         # Add the table cleaner panel to a new page.
  169.         self.notebook.AddPage(panel, u'Table cleaner: "{}"'.format(filename))
  170.  
  171.         # Select the new page.
  172.         self.notebook.SetSelection(self.notebook.GetPageCount()-1)
  173.  
  174.     def open_table_file(self):
  175.         wildcard = 'Excel 2007 file (*.xlsx)|*.xlsx|' \
  176.                    'CSV file (*.csv)|*.csv'
  177.  
  178.         # Load the default directory from settings.
  179.         default_dir = shared.config_get('default_dir', '~')
  180.  
  181.         # Create a file open dialog.
  182.         dialog = wx.FileDialog(self, message='Choose a file', style=wx.OPEN,
  183.                                defaultDir=default_dir, wildcard=wildcard)
  184.  
  185.         if dialog.ShowModal() == wx.ID_OK:
  186.             path = dialog.GetPath()
  187.  
  188.             # Set the default directory setting to the one just used.
  189.             shared.config_set('default_dir', os.path.dirname(path))
  190.  
  191.             self.add_table_cleaner_page(path)
  192.  
  193.     def close_current_page(self):
  194.         """Closes the current focused page."""
  195.         x = self.notebook.GetSelection()
  196.  
  197.         if x > -1:
  198.             self.notebook.DeletePage(x)
  199.  
  200.     def show_about(self):
  201.         info = wx.AboutDialogInfo()
  202.         info.Name = address_cleaner.const.APPNAME
  203.         info.Version = address_cleaner.const.VERSION
  204.         info.Copyright = '(C) 2012 Broadnet AS'
  205.         info.Description = address_cleaner.const.DESCRIPTION
  206.         info.Developers = [address_cleaner.const.AUTHOR]
  207.  
  208.         wx.AboutBox(info)
  209.  
  210.     def handle_page_switch(self, old_page, new_page):
  211.         is_editor = lambda x: isinstance(x, shared.EditorPanel)
  212.  
  213.         # If the old and new page is the same page, which can happen when tabs
  214.         # are reorganized, ignore this function call.
  215.         if old_page is new_page:
  216.             return
  217.  
  218.         if is_editor(old_page):
  219.             old_page.release_main_menu_bar()
  220.  
  221.         if is_editor(new_page):
  222.             new_page.bind_main_menu_bar(self.main_menu)
  223.  
  224.     def on_main_menu(self, event):
  225.         event_id = event.GetId()
  226.  
  227.         if event_id == wx.ID_OPEN:
  228.             self.open_table_file()
  229.         elif event_id == const.ID_NEW_ADDRESS_LOOKUP_PAGE:
  230.             self.add_address_lookup_page()
  231.         elif event_id == wx.ID_CLOSE:
  232.             self.close_current_page()
  233.         elif event_id == wx.ID_EXIT:
  234.             self.Close()
  235.         elif event_id == wx.ID_ABOUT:
  236.             self.show_about()
  237.         else:
  238.             vprint(1, '{}: Unknown menu click ID: {}',
  239.                    shared.function_name(), event_id)
  240.  
  241.     def on_main_menu_update_ui(self, event):
  242.         event_id = event.Id
  243.  
  244.         # Disable the "Close page" button if there are no pages.
  245.         if event_id == wx.ID_CLOSE:
  246.             if self.notebook.PageCount < 1:
  247.                 event.Enable(False)
  248.             else:
  249.                 event.Enable(True)
  250.  
  251.     def on_page_changed(self, event):
  252.         print 'Page changed: {}, {}'.format(event.OldSelection, event.Selection)
  253.         new_page = self.notebook.GetPage(event.Selection)
  254.  
  255.         # If the old page selection is -1, it means the selection didn't change.
  256.         if event.OldSelection > -1:
  257.             old_page = self.notebook.GetPage(event.OldSelection)
  258.         else:
  259.             old_page = None
  260.  
  261.         self.handle_page_switch(old_page, new_page)
  262.  
  263.         event.Skip()
  264.  
  265.     def on_page_close(self, event):
  266.         print 'Page closed', event
  267.  
  268.     def on_rect_change(self, event):
  269.         # Save the dialog rect in the config.
  270.         if not self.IsMaximized():
  271.             shared.config_set('main_frame_rect', self.Rect, silent=True)
  272.  
  273.         # Save the maximized state.
  274.         shared.config_set('main_frame_maximized', self.IsMaximized(),
  275.                          silent=True)
  276.  
  277.         event.Skip()
  278.  
  279.     def on_maximize(self, event):
  280.         print('Maximized')
  281.  
  282.     def on_close(self, event):
  283.         # Save the configuration.
  284.         vprint(1, 'Saving the config.')
  285.         shared.config_save()
  286.  
  287.         event.Skip()
  288.  
  289. # Startup routines -------------------------------------------------------------
  290.  
  291. def initialize_config():
  292.     """Makes sure the config folder and file is created."""
  293.     config_dir = appdirs.user_data_dir(
  294.         const.APPNAME, const.AUTHOR, version=const.VERSION
  295.     )
  296.  
  297.     # Create the application data directory if it doesn't exist.
  298.     if not os.path.isdir(config_dir):
  299.         os.makedirs(config_dir)
  300.  
  301.     # Create the config file if it doesn't exist.
  302.     shared.config_file_path = os.path.join(config_dir, 'config.dat')
  303.  
  304.     # If the config file doesn't exist, create an empty one.
  305.     if not os.path.isfile(shared.config_file_path):
  306.         shared.config = {}
  307.  
  308.         with open(shared.config_file_path, 'w') as f:
  309.             pickle.dump(shared.config, f)
  310.     # Otherwise load the file. If the load fails, reset the file.
  311.     else:
  312.         try:
  313.             with open(shared.config_file_path, 'r') as f:
  314.                 shared.config = pickle.load(f)
  315.         except Exception as e:
  316.             shared.config = {}
  317.             wx.MessageBox('Config file error',
  318.                           'Config file was corrupted and has been reset',
  319.                           style=wx.OK | wx.ICON_ERROR)
  320.             vprint(1, 'Config error: {}', e.message)
  321.  
  322.     vprint(1, 'Initialized config: "{}"', shared.config_file_path)
  323.  
  324. def connect_to_database():
  325.     """Connect to the address database."""
  326.     error_msg = None
  327.  
  328.     # Display a loading dialog.
  329.     loading = shared.show_loading(None, 'Connecting',
  330.                                   'Connecting to address database')
  331.  
  332.     error, shared.con = addrtools.connect_to_database(return_error=True)
  333.  
  334.     loading.done()
  335.  
  336.     if shared.con is None:
  337.         msg = 'Failed to connect to a MySQL address database\n\n{}'
  338.         msg = msg.format(error)
  339.  
  340.         wx.MessageBox(msg, 'MySQL connection error', wx.OK | wx.ICON_ERROR)
  341.  
  342.         # The application is useless without the MySQL connection, so just quit.
  343.         return False
  344.     else:
  345.         vprint(1, 'Connected to address database: "{}"',
  346.                addrtools.good_con_info['mysql_con']['host'])
  347.         return True
  348.  
  349. def load_norway_cache():
  350.     """Loads the addrtools Norway cache. Displays a progress dialog if the cache
  351.    must be downloaded."""
  352.  
  353.     loading = None
  354.  
  355.     # Check if the cache must be updated.
  356.     if not addrtools.check_cache():
  357.         loading = shared.show_loading(None, 'Creating cache',
  358.                                       'Creating a cache for quick lookups')
  359.  
  360.     # Load the cache.
  361.     addrtools.load_cache()
  362.  
  363.     # Remove the loading dialog.
  364.     if loading:
  365.         loading.done()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement