class SelectItem(BaseDialog): ''' Create a list of items called 'name' from a table and return the database ID of the item in item_id. ''' def __init__(self, master, table, column, thing=None): self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug('SelectItem enter constructor') self.table = table self.column = column if thing is None: self.thing = 'Item' else: self.thing = thing self.item_id = -1 super().__init__(master) self.wait_window(self) self.logger.debug('SelectItem leave constructor') @debugger def body(self, master): self.title("Select %s"%(self.thing)) self.data = Database.get_instance() padx = 6 pady = 2 frame = tk.Frame(master, bd=1, relief=tk.RIDGE) frame.grid(row=0, column=0, padx=4, pady=7) tk.Label(frame, text="Select %s"%(self.thing), font=("Helvetica", 14)).grid(row=0, column=0, columnspan=2) ###################### # Populate the combo boxes lst = self.data.populate_list(self.table, self.column) lst.sort() ###################### # Show the boxes tk.Label(frame, text='Name:').grid(row=1, column=0) self.cbb = ttk.Combobox(frame, values=lst) self.cbb.grid(row=1, column=1, padx=padx, pady=pady) try: self.cbb.current(0) except tk.TclError: mb.showerror("TCL ERROR", "No records are available to select for this table.") @debugger def validate(self): # Since the name was selected from the list, there is no need to # validate. return True @debugger def apply(self): ''' Populate the form with the selected data. ''' id = self.data.get_id_by_name(self.table, self.column, self.cbb.get()) self.item_id = id
class SetupPurchaseForm(supplimental_form): def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.PURCHASE_FRAME, 'PurchaseRecord') self.add_title('Purchase Setup Form') self.add_indirect_label('Vendor', 'vendor_ID', 'Vendor', 'name') self.add_dynamic_label('Gross', 'gross', width=20) self.add_dynamic_label('Tax', 'tax', width=20) self.add_dynamic_label('Shipping', 'shipping', width=20) self.add_combo('Purchase Type', 'PurchaseType', 'type_ID', inc_row=False, width=20) self.add_combo('Purchase Status', 'PurchaseStatus', 'status_ID', width=20) self.add_text('Notes', 'notes', height=10) self.add_button('Prev') self.add_button('Next') self.add_button('Save') self.add_button('Delete') self.add_commit_btn()
class SetupNotebook(NotebookBase): BUSINESS_FRAME = 0 CUSTOMERS_FRAME = 1 VENDORS_FRAME = 2 ACCOUNTS_FRAME = 3 INVENTORY_FRAME = 4 SALES_FRAME = 5 PURCHASE_FRAME = 6 IMPORT_FRAME = 7 def __init__(self, master): self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(master, ['Business', 'Customers', 'Vendors', 'Accounts', 'Inventory', 'Sales', 'Purchase', 'Import']) SetupBusinessForm(self) SetupCustomersForm(self) SetupVendorsForm(self) SetupAccountsForm(self) SetupInventoryForm(self) SetupSalesForm(self) SetupPurchaseForm(self) SetupImportForm(self) self.show_frame(self.BUSINESS_FRAME)
class SingleButtonBox(tk.Frame): ''' Make the button box and register the events. ''' def __init__(self, master, form, text=None, *args, **kargs): ''' master = The frame to bind the widgets to. form = Name of the form to bind the events to. ''' self.logger = Logger(self, level=Logger.INFO) self.logger.debug("NotesBox enter constructor") super().__init__(master, *args, **kargs) self.form = form self.name = text.lower() self.events = EventHandler.get_instance() tk.Button(self, text=text, command=self.btn_callback).grid(row=1, column=2, padx=5, pady=5) self.logger.debug("NotesBox leave constructor") @debugger def register_events(self, save): ''' save = callback for the save button ''' self.events.register_event( '%s_btn_callback_%s' % (self.name, self.form), save) @debugger def btn_callback(self): self.events.raise_event('%s_btn_callback_%s' % (self.name, self.form))
class MainHomeForm(Form): ''' Create the form for the Business tab under Setup. ''' def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.HOME_FRAME, 'Business') self.add_title('Home Form') self.add_entry('Name', 'name', str) self.add_entry('Address1', 'address1', str) self.add_entry('Address2', 'address2', str) self.add_entry('City', 'city', str, inc_row=False, span=1, width=20) self.add_entry('State', 'state', str, width=20, span=1) self.add_entry('Zip Code', 'zip', str, width=20, span=1, inc_row=False) self.add_entry('Country', 'country', str, width=20, span=1) self.add_entry('Email', 'email_address', str, width=20, span=1, inc_row=False) self.add_entry('Phone', 'phone_number', str, width=20, span=1) self.add_entry('Web Site', 'web_site', str) self.add_entry('Description', 'description', str) self.add_text('Terms', 'terms') self.add_text('Returns', 'returns') self.add_text('Warranty', 'warranty') self.add_button('Save')
class ComboBox(tk.Frame): ''' Implement a Combobox. Note that this requires that the table that it uses to populate the list must have a column called "name". ''' def __init__(self, master, table, column, *args, **kargs): ''' master = the frame to bind this frame to name = the text of the label table = the name of the database table that is associated with this widget column = the name of the database column this widget associates with ''' self.logger = Logger(self, level=Logger.INFO) self.logger.debug("ComboBox enter constructor") super().__init__(master, *args, **kargs) self.table = table self.column = column self.data = Database.get_instance() self.content = ttk.Combobox(self, state='readonly') self.content.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W) self.row = { 'table': self.table, 'column': self.column, 'self': self, 'hasid': None } self.logger.debug("ComboBox leave constructor") @debugger def populate(self, table, column): self.content_list = self.data.populate_list(table, column) self.content.configure(values=self.content_list) @debugger def read(self): return self.content.current() + 1 @debugger def write(self, val): self.content.current(int(val) - 1) @debugger def clear(self): try: self.content.current(0) except tk.TclError: pass @debugger def get_line(self): ''' Return the form entry to update the form. ''' return self.row
class SetupImportForm(supplimental_form): def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.IMPORT_FRAME, 'RawImport') self.add_title('Import Setup Form') self.add_dir_browser() self.add_import_btn()
class LabelBox(tk.Frame): ''' Implement a consistent interface to the entry widget ''' def __init__(self, master, table, column, *args, **kargs): ''' master = the frame to bind this frame to name = the text of the label table = the name of the database table that is associated with this widget column = the name of the column this widget associates with ''' self.logger = Logger(self, level=Logger.INFO) self.logger.debug("LabelBox enter constructor") super().__init__(master, *args, **kargs) self.table = table self.column = column self.content = tk.StringVar(master) self.entry = tk.Label(self, textvariable=self.content) self.entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W) self.row = { 'table': self.table, 'column': self.column, 'self': self, 'hasid': None } self.logger.debug("LabelBox leave constructor") @debugger def set_id(self, table, column, id=0): ''' When the column has a ID, rather than a value, use this to get the record that the ID references. ''' self.row['hasid'] = {'table': table, 'column': column, 'id': id} return id @debugger def read(self): return self.content.get() @debugger def write(self, val): self.content.set(val) @debugger def clear(self): self.content.set('') @debugger def get_line(self): ''' Return the form entry to update the form. ''' return self.row
class MainSetupForm(Form): ''' Create the form for the Business tab under Setup. ''' def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.SETUP_FRAME, 'Business') self.add_notebook(SetupNotebook)
class MainReportsForm(Form): ''' Create the form for the Business tab under Setup. ''' def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.REPORTS_FRAME, 'Business') self.add_title('Reports Form')
class SetupCustomersForm(Form): def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.CUSTOMERS_FRAME, 'Customer') self.add_title('Customers Setup Form') self.add_dynamic_label('Date', 'date_created', width=20) self.add_entry('Name', 'name', str) self.add_entry('Address1', 'address1', str) self.add_entry('Address2', 'address2', str) self.add_entry('City', 'city', str, inc_row=False, span=1, width=20) self.add_entry('State', 'state', str, width=20, span=1) self.add_entry('Zip Code', 'zip', str, width=20, span=1, inc_row=False) self.add_combo('Country', 'Country', 'country_ID', width=20) self.add_entry('Email', 'email_address', str, width=20, span=1, inc_row=False) self.add_combo('Email Status', 'EmailStatus', 'email_status_ID', width=20) self.add_entry('Phone', 'phone_number', str, width=20, span=1, inc_row=False) self.add_combo('Phone Status', 'PhoneStatus', 'phone_status_ID', width=20) self.add_entry('Web Site', 'web_site', str, width=20, inc_row=False) self.add_combo('Class', 'ContactClass', 'class_ID', width=20) self.add_entry('Description', 'description', str) self.add_text('Notes', 'notes', height=10) self.add_button('Prev') self.add_button('Next') self.add_button('Select') self.add_button('New') self.add_button('Save') self.add_button('Delete')
def __init__(self, configuration): CREATED_TEMPDIR_MESSAGE = "Created temporary directory at {tempdir}" self.configuration = configuration # A temporary directory used for this updater over runtime. self.tempdir = tempfile.mkdtemp() Logger.debug(CREATED_TEMPDIR_MESSAGE.format(tempdir=self.tempdir)) # must switch context before instantiating updater # because updater depends on some module (tuf.conf) variables self.switch_context() self.updater = tuf.client.updater.Updater( self.configuration.hostname, self.configuration.repository_mirrors)
def __init__(self, configuration): CREATED_TEMPDIR_MESSAGE = "Created temporary directory at {tempdir}" self.configuration = configuration # A temporary directory used for this updater over runtime. self.tempdir = tempfile.mkdtemp() Logger.debug(CREATED_TEMPDIR_MESSAGE.format(tempdir=self.tempdir)) # must switch context before instantiating updater # because updater depends on some module (tuf.conf) variables self.switch_context() self.updater = tuf.client.updater.Updater(self.configuration.hostname, self.configuration.repository_mirrors)
class MainFrame(tk.Frame): ''' Entry point of application. ''' def __init__(self): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) self.master = tk.Tk() self.master.geometry('1000x800') #self.master.resizable(0, 0) self.master.wm_title("Accounting") MainNotebook(self.master) #self.main_notebook = MainNotebook(self.master) #self.setup_nb = self.main_notebook.set_form(self.main_notebook.SETUP_FRAME, SetupNotebook) #self.main_notebook.show_frame(self.main_notebook.HOME_FRAME) #self.setup_nb.show_frame(self.setup_nb.SALES_FRAME) @debugger def main(self): try: self.logger.debug("start main loop") self.master.mainloop() self.logger.debug('close database') #self.data.close() self.logger.debug("end main loop") except Exception: traceback.print_exception(*sys.exc_info())
class helpDialog: def __init__(self, parent): self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug("enter constructer") self.top = tkinter.Toplevel(parent) self.tx = tkinter.Text(self.top, height=25, width=80) self.sb = tkinter.Scrollbar(self.top) self.sb.pack(side=tkinter.RIGHT, fill=tkinter.Y) self.tx.pack(side=tkinter.LEFT) self.sb.config(command=self.tx.yview) self.tx.config(yscrollcommand=self.sb.set) self.tx.insert(tkinter.END, help_text) self.tx.config(state='disabled') self.logger.debug("leave constructer")
class LineWidget(tk.Frame): ''' This is a special widget used to show and edit product entries for a sale form. It implements a single line in the list. ''' def __init__(self, master, form, line, *args, **kargs): ''' master = The frame to bind the widgets to. form = Name of the form to bind the events to. line = The line number of the line, displayed as a label ''' self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug("Line Widget enter constructor") self.data = Database.get_instance() super().__init__(master, *args, **kargs) self.form = form self.events = EventHandler.get_instance() #self.data = Database.get_instance() self.values = self.data.populate_list('InventoryItem', 'name') self.values.insert(0, '') # first line is blank tk.Label(self, text='%d' % (int(line))).grid(row=0, column=0) self.quan = tk.Spinbox(self, from_=1, to=99, width=2) self.quan.grid(row=0, column=1, padx=5, pady=5) self.prod = ttk.Combobox(self, values=self.values, width=40) self.prod.grid(row=0, column=2) self.logger.debug("Line Widget leave constructor") @debugger def get_str(self): return self.prod.get() @debugger def read(self): val = self.prod.get() if not val is None and val != '': return (int(self.quan.get()), val) else: return None
class SetupAccountsForm(Form): def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.ACCOUNTS_FRAME, 'Account') self.add_title('Accounts Setup Form') self.add_entry('Name', 'name', str, width=20) self.add_entry('Number', 'number', str, width=20) self.add_combo('Type', 'AccountTypes', 'type_ID', width=20) self.add_entry('Total', 'total', float, width=20) self.add_entry('Description', 'description', str) self.add_text('Notes', 'notes', height=10) self.add_button('Prev') self.add_button('Next') self.add_button('Select') self.add_button('New') self.add_button('Save') self.add_button('Delete')
class CommitBase: def __init__(self, form_content, id): self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug("Commit Base enter constructor") self.data = Database.get_instance() self.id = id self.form_content = form_content @debugger def get_account(self, name): ''' Returns the (id, total). ''' row = self.data.get_row_list_by_col('Account', 'name', name)[0] total = float(row['total']) id = row['ID'] return (id, total) @debugger def make_generic_entry(self, gross, to_acc, from_acc, msg): row = { 'date_committed': time.strftime('%m/%d/%Y'), 'gross': gross, 'description': msg, 'to_account_ID': to_acc, 'from_account_ID': from_acc, } id = self.data.insert_row('GenericTransaction', row) return id @debugger def connect_sale(self, id): row = {'generic_trans_ID': id, 'sale_trans_ID': self.id} self.data.insert_row('SGenericTransaction', row) @debugger def connect_purchase(self, id): row = {'generic_trans_ID': id, 'purchase_trans_ID': self.id} self.data.insert_row('PGenericTransaction', row)
class SetupInventoryForm(Form): def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.INVENTORY_FRAME, 'InventoryItem') self.add_title('Inventory Setup Form') self.add_entry('Name', 'name', str, width=20) self.add_entry('Stock Num', 'stock_num', int, width=20) self.add_entry('Stock', 'num_stock', int, width=20) self.add_entry('Retail', 'retail', float, width=20) self.add_entry('Wholesale', 'wholesale', float, width=20) self.add_entry('Description', 'description', str) self.add_text('Notes', 'notes', height=10) self.add_button('Prev') self.add_button('Next') self.add_button('Select') self.add_button('New') self.add_button('Save') self.add_button('Delete')
class MainNotebook(NotebookBase): HOME_FRAME = 0 SALES_FRAME = 1 PURCHASE_FRAME = 2 REPORTS_FRAME = 3 SETUP_FRAME = 4 def __init__(self, master): self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(master, ['Home', 'Sales', 'Purchase', 'Reports', 'Setup']) MainHomeForm(self) MainSalesForm(self) MainPurchaseForm(self) MainReportsForm(self) MainSetupForm(self) self.show_frame(self.HOME_FRAME)
class NotebookBase(Notebook): def __init__(self, master, names=None, height=700, width=1000): self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug("enter constructor") super().__init__(master, height=height, width=width) self.names = names if not names is None: for name in self.names: self.add_tab(name) self.form_class = [] @debugger def get_name(self, index): return self.names[index] @debugger def get_index(self, name): for index, item in enumerate(self.names): if item == name: return index return None @debugger def get_form_class(self, index): return self.form_class[index] @debugger def set_form(self, index, form_class, **kargs): idx = 0 if type(index) == type(''): idx = self.get_name() elif type(index) == type(0): idx = index self.form_class.append(form_class(self.get_frame(idx), **kargs)) return self.form_class[-1]
class MainFrame(tk.Frame): ''' This is the main frame that "contains" the other frames. ''' def __init__(self): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) self.master = tk.Tk() self.master.wm_title("Accounting") self.data = Database.get_instance() notebook = NoteBk(self.master, height=700, width=1050) notebook.add_tab('Sales', DummyClass) notebook.add_tab('Purchase', DummyClass) notebook.add_tab('Reports', DummyClass) notebook.add_tab('Setup', SetupNotebook) # activate a frame for initial display notebook.show_frame('Sales') @debugger def main(self): try: self.logger.debug("start main loop") self.master.mainloop() self.logger.debug('close database') self.data.close() self.logger.debug("end main loop") # ev = EventHandler.get_instance() # ev.dump_events() except Exception: traceback.print_exception(*sys.exc_info())
class SetupSalesForm(supplimental_form): def __init__(self, notebook): self.logger = Logger(self, Logger.DEBUG) self.logger.debug(sys._getframe().f_code.co_name) super().__init__(notebook, notebook.SALES_FRAME, 'SaleRecord') self.add_title('Sales Setup Form') self.add_indirect_label('Customer', 'customer_ID', 'Customer', 'name') self.add_dynamic_label('Gross', 'gross', width=20) self.add_dynamic_label('Fees', 'fees', width=20) self.add_dynamic_label('Shipping', 'shipping', width=20) self.add_combo('Status', 'SaleStatus', 'status_ID', width=20) # TODO make the products widget and add it here self.add_products_widget() self.add_text('Notes', 'notes', height=10) self.add_button('Prev') self.add_button('Next') self.add_button('Save') self.add_button('Delete') self.add_commit_btn() # TODO override the load and save forms to support products widget @debugger def save_callback(self): self.logger.debug('supplimental form save callback') if askyesno('Save record?', 'Are you sure you want to save this?'): self.commit_form() self.controls['Products']['obj'].save_btn() self.data.commit() @debugger def delete_callback(self): self.logger.debug('supplimental form delete callback') if askyesno('Delete record?', 'Are you sure you want to delete this?'): self.data.delete_row(self.table, self.row_list[self.row_index]) self.data.delete_where( 'ProductList', 'sale_record_ID=%d' % (self.controls['Products']['obj'].sale_id)) self.row_list = self.data.get_id_list(self.table) self.load_form() self.data.commit()
class NoteBkBtn(tk.Button): ''' This Button class keeps track of the state is relation to the NoteBk class. It makes no sense outside of that context. ''' def __init__(self, master, title, uuid, *args, **kargs): self.logger = Logger(self, level=Logger.INFO) self.logger.debug("enter constructor") super().__init__(master, *args, **kargs) self.configure(width=10) self.configure(command=self.btn_cmd) self.configure(text=title) self.last_state = True self.title = title self.uuid = uuid self.events = EventHandler.get_instance() self.logger.debug("leave constructor") @debugger def btn_cmd(self): #print('here', self.title) self.events.raise_event('clearButtons_%s'%(self.uuid)) self.set_state(False) self.events.raise_event('show_frame_%s'%(self.title), self.title) @debugger def set_state(self, state): self.logger.debug('button: %s, state: %s'%(self.title, str(state))) if state: self.configure(relief=tk.RAISED) else: self.configure(relief=tk.SUNKEN) self.last_state = state @debugger def set_last_state(self): #print('here1') self.set_state(self.last_state)
class MainFrame(tkinter.Frame): ''' This is the main frame that "contains" the other frames. ''' def __init__(self, master=None): self.logger = Logger(self, Logger.INFO) self.logger.debug(sys._getframe().f_code.co_name) tkinter.Frame.__init__(self, master) self.master = master self.master.protocol("WM_DELETE_WINDOW", self.close_window) self.general_params = tkinter.LabelFrame(self.master, text="General Parameters") self.general_params.pack() self.output_params = tkinter.LabelFrame(self.master, text="Output Parameters") self.output_params.pack(fill="both", expand="yes") # set up some default values #self.current_file_name = os.path.join(os.getcwd(), "untitled.wis") self.data = DataStore.get_instance() self.logger.debug("data store: %s"%(str(self.data))) self.calc = Calculator() self.upper_frame = UpperFrame(self.general_params) self.lower_frame = LowerFrame(self.output_params) menu = tkinter.Menu(self.master, tearoff=0) self.master.config(menu=menu) #self.master.geometry("750x450") fileMenu = tkinter.Menu(menu, tearoff=0) fileMenu.add_command(label="Load", command=self.loadCommand) fileMenu.add_command(label="Save", command=self.saveCommand) fileMenu.add_command(label="Save As", command=self.saveasCommand) fileMenu.add_command(label="Export", command=self.exportCommand) fileMenu.add_separator() fileMenu.add_command(label="Quit", command=self.close_window) menu.add_cascade(label="File", menu=fileMenu) settingsMenu = tkinter.Menu(menu, tearoff=0) settingsMenu.add_command(label="Constants", command=self.constCommand) settingsMenu.add_command(label="Embouchure", command=self.emboCommand) settingsMenu.add_command(label="Notes", command=self.notesCommand) menu.add_cascade(label="Settings", menu=settingsMenu) editMenu = tkinter.Menu(menu, tearoff=0) editMenu.add_command(label="Help", command=self.helpCommand) editMenu.add_command(label="About", command=self.aboutCommand) editMenu.add_command(label="Dump", command=self.dumpInternalData) menu.add_cascade(label="Help", menu=editMenu) tkinter.Label(self.master, text="Tilbury Woodwinds Whistle Calculator", font=("Helvetica", 14)).pack() self.upper_frame.create_frame() self.lower_frame.update_frame() @debugger def set_state(self): self.upper_frame.set_state() self.lower_frame.set_state() @debugger def get_state(self): self.upper_frame.get_state() self.lower_frame.get_state() @debugger def close_window(self): if self.data.get_change_flag(): if messagebox.askyesno("Quit", "Do you want to save the changes before quitting?"): self.logger.debug('save') self.saveCommand() else: self.logger.debug('ignore') self.master.destroy() @debugger def loadCommand(self): f = filedialog.askopenfilename(initialfile=self.data.get_file_name(), filetypes=(("Whistle Files","*.wis"), ("all files", "*.*"))) if f != '': self.logger.debug("loading file: %s"%(f)) self.data.load(f) self.data.set_file_name(f) raise_event("UPDATE_LOWER_FRAME_EVENT") self.set_state() else: self.logger.debug("cancel") @debugger def saveCommand(self): #d = filedialog.askdirectory(initialdir=os.getcwd(), mustexist=True) #p = os.path.join(d, self.data.get_file_name()) p = os.path.join(os.getcwd(), self.data.get_file_name()) if p != '': self.logger.debug("saving file = " + p ) self.data.save(p) else: self.logger.debug("cancel") @debugger def saveasCommand(self): f = filedialog.asksaveasfilename(initialfile=self.data.get_file_name(), filetypes=(("Whistle Files","*.wis"), ("all files", "*.*"))) if f != '': self.logger.debug("file save as = " + f) self.data.save(f) self.data.set_file_name(f) else: self.logger.debug("cancel") @debugger def aboutCommand(self): messagebox.showinfo( "About", "Tilbury Woodwinds Company\nWhistle Calculator\nChuck Tilbury (c) 2019\nVersion: 1.0\nData Version: %s"%(self.data.get_version())) @debugger def dumpInternalData(self): self.data.print_data() utility.dump_events() @debugger def helpCommand(self): #messagebox.showinfo( # "Help", dialogs.helpDialog(self.master) @debugger def exportCommand(self): name = self.data.get_file_name().replace('.wis', '.txt') f = filedialog.asksaveasfilename(initialfile=name, filetypes=(("Text Files","*.txt"), ("all files", "*.*"))) if f != '': self.logger.debug("export file as = " + f) with open(f, 'w') as fh: fh.write("\n%s\n"%("-"*60)) fh.write("%s\n"%(self.data.get_title())) fh.write("%s\n\n"%("-"*60)) fh.write("BELL: %s (%0.3f Hz)\n"%( self.data.note_table[self.data.get_bell_note_select()]['note'], self.data.note_table[self.data.get_bell_note_select()]['frequency'])) fh.write("ID: %0.3f\n"%(self.data.get_inside_dia())) fh.write("WALL: %0.3f\n"%(self.data.get_wall_thickness())) fh.write("NUM HOLES: %d\n"%(self.data.get_number_holes())) if self.data.get_units() == False: fh.write("UNITS: inches\n") else: fh.write("UNITS: millimeters\n") fh.write("LENGTH: %0.4f\n"%(self.data.get_length())) fh.write("\n%s\n"%("-"*60)) fh.write(" Drill Location Note Frequency\n") for x in range(self.data.get_number_holes()): fh.write("%-10s"%("Hole %d"%(x+1))) if not self.data.get_units(): if self.data.get_disp_frac(): fh.write("%-10s"%(utility.reduce(self.data.get_hole_size(x)))) else: fh.write("%-10s"%("%0.4f"%(self.data.get_hole_size(x)))) else: fh.write("%-10s"%("%0.4f"%(self.data.get_hole_size(x)))) fh.write("%-12s"%("%0.4f"%(self.data.get_hole_xloc(x)))) fh.write("%-10s "%(self.data.get_hole_note(x))) fh.write("%0.4f Hz\n"%(self.data.get_hole_freq(x))) fh.write("\n%s\n"%("-"*60)) fh.write("Notes:\n\n") fh.write("%s"%(self.data.get_notes())) fh.write("\n%s\n"%("-"*60)) fh.write("\nCut sheet generated on %s\nby Tilbury Woodwinds Whistle Calculator\n\n"%(time.ctime())) else: self.logger.debug("cancel") @debugger def emboCommand(self): dialogs.EmbouchureDialog(self.master) raise_event("UPDATE_LOWER_FRAME_EVENT") raise_event("UPDATE_UPPER_EVENT") @debugger def constCommand(self): dialogs.ConstDialog(self.master) raise_event("UPDATE_LOWER_FRAME_EVENT") raise_event("UPDATE_UPPER_EVENT") @debugger def notesCommand(self): dialogs.NotesDialog(self.master)
class Database(object): ''' The goal of this class is to move all SQL composition out of the program logic and place it here. ''' __instance = None @staticmethod def get_instance(): ''' This static method is used to get the singleton object for this class. ''' if Database.__instance == None: Database() return Database.__instance def __init__(self): # gate the access to __init__() if Database.__instance != None: raise Exception( "Database class is a singleton. Use get_instance() instead.") else: Database.__instance = self # Continue with init exactly once. self.logger = Logger(self, Logger.DEBUG) self.logger.debug("enter constructor") self.data_version = '1.0' self.database_name = 'accounting.db' self.db_create_file = 'database.sql' self.db_pop_file = 'populate.sql' self.open() locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') self.logger.debug("leave constructor") @debugger def open(self): if not os.path.isfile(self.database_name): self.create_database() self.db = sql.connect(self.database_name) self.db.row_factory = sql.Row @debugger def close(self): self.db.commit() self.db.close() @debugger def read_statement(self, fh): ''' Read a statement from the *.sql file and return it. This skips comments and concatinates lines until a ';' is read. A comment is text that starts with a '#' and continues to the end of the line. ''' retv = '' for line in fh: # strip comments from the line idx = line.find('#') line = line[0:idx].strip() # If there is anything left, append it to the return value. if len(line) > 0: retv += " %s" % (line) if line[-1] == ';': break return retv @debugger def run_file(self, db, name): ''' Execute all of the statements in a *.sql file. ''' with open(name) as fh: while True: line = self.read_statement(fh) if len(line) > 0: db.execute(line) else: break @debugger def create_database(self): ''' Create the database if it does not exist already. ''' # Load the DB creation file and create the database from that. self.logger.info("creating database") c = sql.connect(self.database_name) db = c.cursor() self.run_file(db, self.db_create_file) self.run_file(db, self.db_pop_file) c.commit() c.close() @debugger def get_columns(self, table): ''' Return a dict where all column names are keys with blank data. ''' # TODO: make the data the type of element that the column uses. retv = {} cols = self.execute('PRAGMA table_info(%s);' % (table)) for item in cols: retv[item[1]] = '' return cols @debugger def get_column_list(self, table): ''' Return a list with all of the column names. ''' retv = [] cols = self.execute('PRAGMA table_info(%s);' % (table)) for item in cols: retv.append(item[1]) return retv @debugger def execute(self, sql): ''' Execute an arbitrary SQL statement. ''' self.logger.debug("SQL=%s" % (sql)) return self.db.execute(sql) @debugger def commit(self): ''' Commit the database to disk. ''' self.db.commit() @debugger def populate_list(self, table, column): ''' Return a list with all of the items then the column of the table. ''' curs = self.execute('select %s from %s;' % (column, table)) retv = [] for item in curs: retv.append(' '.join(item)) #retv.append(item) return retv @debugger def get_row_by_id(self, table, ID): ''' Return a dict of all of the columns in the row that has the specified ID. ''' curs = self.execute('select * from %s where ID = %d;' % (table, ID)).fetchall() try: retv = dict(curs[0]) return retv except IndexError: return None @debugger def get_id_by_row(self, table, col, val): ''' Return a dictionary of the columns in the row where a data element matches the value given. ''' if type(val) is str: sql = 'SELECT ID FROM %s WHERE %s = \"%s\";' % (table, col, val) else: sql = 'SELECT ID FROM %s WHERE %s = %s;' % (table, col, val) row = self.execute(sql).fetchall() if len(row) == 0: return None else: return dict(row[0])['ID'] @debugger def get_cursor(self): ''' Return the current database cursor. ''' return self.db.cursor() @debugger def get_id_list(self, table, where=None): ''' Get a list of all of the IDs in the table ''' retv = [] if where is None: sql = 'SELECT ID FROM %s;' % (table) else: sql = 'SELECT ID FROM %s WHERE %s;' % (table, where) cur = self.execute(sql) for item in cur: retv.append(item[0]) return retv @debugger def get_row_list(self, table, where): ''' Get a generic list of rows based on more than one criteria ''' retv = [] sql = 'SELECT * FROM %s WHERE %s' % (table, where) cur = self.execute(sql) for item in cur: retv.append(dict(item)) if len(retv) == 0: return None else: return retv @debugger def get_row_list_by_col(self, table, col, val): ''' Get the list of all rows where the column has a certain value ''' retv = [] if type(val) is str: sql = 'SELECT * FROM %s WHERE %s = \"%s\";' % (table, col, val) else: sql = 'SELECT * FROM %s WHERE %s = %s;' % (table, col, val) self.logger.debug("SQL=%s" % (sql)) cur = self.execute(sql) for item in cur: retv.append(dict(item)) if len(retv) == 0: return None else: return retv @debugger def get_id_by_name(self, table, col, val): ''' Return the ID where the data in the column matches the value. Only returns the first match. ''' if type(val) is str: sql = 'select ID from %s where %s = \"%s\";' % (table, col, val) else: sql = 'select ID from %s where %s = %s;' % (table, col, str(val)) curs = self.execute(sql) recs = curs.fetchall() retv = None for row in recs: retv = row[0] break return retv @debugger def get_single_value(self, table, col, row_id): ''' Retrieve a single value where the table, column and row ID are known. ''' sql = 'SELECT %s FROM %s WHERE ID=%d;' % (col, table, row_id) self.logger.debug("SQL=%s" % (sql)) curs = self.execute(sql) recs = curs.fetchall() retv = None for row in recs: retv = row[0] break return retv @debugger def set_single_value(self, table, col, row_id, val): ''' Retrieve a single value where the table, column and row ID are known. ''' vals = tuple([val]) sql = 'UPDATE %s SET %s=? WHERE ID=%d;' % (table, col, row_id) self.logger.debug("SQL=%s (%s)" % (sql, vals)) return self.db.execute(sql, vals) @debugger def insert_row(self, table, rec): ''' Insert a row from a dictionary. This expects a dictionary where the keys are the column names and the values are to be inserted in to the columns. ''' keys = ','.join(rec.keys()) qmks = ','.join(list('?' * len(rec))) vals = tuple(rec.values()) sql = 'INSERT INTO %s (%s) VALUES (%s);' % (table, keys, qmks) self.logger.debug("SQL=%s (%s)" % (sql, vals)) return self.db.execute(sql, vals).lastrowid @debugger def update_row(self, table, rec, where): ''' Update a row from a dictionary. This expects a dictionary where the keys are the column names and the data is the value to place in those columns. A condition must be specified, such as ID=123. Otherwise the database will have incorrect data placed in it. ''' keys = '=?,'.join(rec.keys()) keys += '=?' vals = tuple(rec.values()) sql = 'UPDATE %s SET %s WHERE %s;' % (table, keys, where) self.logger.debug("SQL=%s (%s)" % (sql, vals)) return self.db.execute(sql, vals) @debugger def update_row_by_id(self, table, rec, id): ''' Update a row using a dictionary and the id of the row. This expects a dictionary where the keys are the column names and the data is the value to be placed in the columns. ''' keys = '=?,'.join(rec.keys()) keys += '=?' vals = tuple(rec.values()) sql = 'UPDATE %s SET %s WHERE ID = %d;' % (table, keys, id) self.logger.debug("SQL=%s (%s)" % (sql, vals)) return self.db.execute(sql, vals) @debugger def delete_row(self, table, id): ''' Delete the row given by the ID ''' sql = 'DELETE FROM %s WHERE ID = %d;' % (table, id) self.logger.debug("SQL=%s" % (sql)) return self.db.execute(sql) @debugger def delete_where(self, table, where): ''' Delete rows that conform to the "where" clause. ''' sql = 'DELETE FROM %s WHERE %s;' % (table, where) self.logger.debug("SQL=%s" % (sql)) return self.db.execute(sql) @debugger def if_rec_exists(self, table, column, value): ''' Return True if there is a row that has the column with the value ''' if type(value) is int or type(value) is float: sql = 'SELECT %s FROM %s WHERE %s = %s;' % (column, table, column, str(value)) else: sql = 'SELECT %s FROM %s WHERE %s = \"%s\";' % (column, table, column, value) cursor = self.db.execute(sql) if cursor.fetchone() is None: return False return True @debugger def convert_value(self, val, value_type, abs_val=True): ''' Convert the value to the specified type. The value_type is an actual python type name. ''' retv = None self.logger.debug('val type: %s, value: %s, target type: %s' % (type(val), val, value_type)) #try: if type(val) is value_type: retv = val elif value_type is str: retv = str(val) else: if value_type is float: if type(val) is str: if val == '': retv = 0.0 else: if abs_val: retv = abs(locale.atof(val)) else: retv = locale.atof(val) else: if abs_val: retv = abs(locale.atof(val)) else: retv = locale.atof(val) elif value_type is int: if abs_val: retv = int(abs(locale.atof(val))) else: retv = int(locale.atof(val)) # except: # self.logger.error('Cannot convert value') # exit(1) self.logger.debug('made it here: %s' % (str(retv))) return retv
class Database(object): __instance = None @staticmethod def get_instance(): ''' This static method is used to get the singleton object for this class. ''' if Database.__instance == None: Database() return Database.__instance def __init__(self): # gate the accress to __init__() if Database.__instance != None: raise Exception( "Database class is a singleton. Use get_instance() instead.") else: Database.__instance = self # Continue with init exactly once. self.logger = Logger(self, Logger.DEBUG) self.logger.debug("enter constructor") self.data_version = '1.0' self.database_name = 'shop-timer.db' self.open() self.logger.debug("leave constructor") @debugger def open(self): if not os.path.isfile(self.database_name): self.create_database() self.db = sql.connect(self.database_name) @debugger def close(self): self.db.commit() self.db.close() @debugger def create_database(self): c = sql.connect(self.database_name) db = c.cursor() db.execute('''create table assembly ( ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, number TEXT NOT NULL, description TEXT, notes TEXT, created REAL NOT NULL, modified REAL NOT NULL );''') db.execute('''create table part ( ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, number TEXT NOT NULL, description TEXT, notes TEXT, created REAL NOT NULL, modified REAL NOT NULL, assembly_ID INT NOT NULL );''') db.execute('''create table action ( ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, seq_number INTEGER NOT NULL, description TEXT, notes TEXT, created REAL NOT NULL, modified REAL NOT NULL, part_ID INT NOT NULL );''') db.execute('''create table timer ( ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT, notes TEXT, created REAL NOT NULL, modified REAL NOT NULL, action_ID INT NOT NULL );''') db.execute('''create table timer_instance ( ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, start REAL NOT NULL, end REAL NOT NULL, total REAL NOT NULL, timer_ID INT NOT NULL );''') db.execute('''create table housekeeping ( ID INTEGER PRIMARY KEY AUTOINCREMENT, last_timer_id INTEGER, data_version TEXT );''') db.execute("insert into housekeeping (data_version) values ('1.0');") c.commit() c.close() @debugger def _insert(self, table, fields, values): ''' Do a SQL "insert" command. Create a row in the database. table = the table name as a string fields = [field names as strings] values = [values for the fields] The values can be any arbitrary type that can be converted to a string. ''' sv = [] for v in values: if type(v) == str: sv.append("\'%s\'" % (v)) else: sv.append(str(v)) sql = 'insert into %s (%s) values (%s);' % (table, ','.join(fields), ','.join(sv)) self.execute(sql) self.commit() @debugger def _select(self, table, fields=None, where=None): ''' Do a SQL "select" command. Returns one or more rows as a database cursor. table = the table name as a string fields = [field names as strings] where = an optional expression presented as a string ''' if fields is None: if not where is None: sql = "select * from %s where %s;" % (table, where) else: sql = "select * from %s;" % (table) elif type(fields) is str: if not where is None: sql = "select %s from %s where %s;" % (fields, table, where) else: sql = "select %s from %s;" % (fields, table) else: if not where is None: sql = "select %s from %s where %s;" % (','.join(fields), table, where) else: sql = "select %s from %s;" % (','.join(fields), table) return self.execute(sql) @debugger def _update(self, table, field, value, where): ''' Do a SQL "update" command. Modifies a row. table = the table name as a string field = a single field name as a string, or a list of field names value = the value to put in the field, or a list of values. may be any type where = a required expression presented as a string If the fields and values are not the same size of array, it will throw an IndexError exception. ''' if type(field) is list: # convert a list of field, value variables arr = [] for idx, item in enumerate(field): if type(value[idx]) is str: arr.append('%s = \'%s\'' % (item, str(value[idx]))) else: arr.append('%s = %s' % (item, str(value[idx]))) val = ','.join(arr) sql = "update %s set %s where %s;" % (table, val, where) else: if type(value) is str: val = "\'%s\'" % (value) else: val = str(value) sql = "update %s set %s = %s where %s;" % (table, field, val, where) self.execute(sql) self.commit() @debugger def _delete(self, table, id): ''' Do a SQL "delete" command. Delete a row from the database. table = the table name id = the row ID to delete ''' sql = 'delete from %s where ID = %d;' % (table, id) self.execute(sql) self.commit() @debugger def get_columns(self, table): ''' Return a list of all column names for the table specified. ''' cols = self.execute('PRAGMA table_info(%s);' % (table)) # for item in cols: # print(item) return cols @debugger def convert_cursor(self, cursor, names): ''' Returns a list of dicts where each row of data is in the dict with the name given in the order that it was read from the database. cursor = Sqlite3 database cursor names = list of names in the order they are expected ''' ret_lst = [] for row in cursor: val = {} for idx, item in enumerate(names): if not item is None: val[item] = row[idx] ret_lst.append(val) return ret_lst @debugger def get_version(self): return self.data_version # Utilities @debugger def validate_type(self, var, t): ''' Validate the type of the srguement. If it cannot be converted, then the program cannot continue. This is considered a developer error. The exceptions here only happen if the input validation from the GUI has failed. ''' if type(var) != t: if t is float: try: tmp = float(var) return tmp except ValueError: mbox.showerror( "FATAL ERROR", "expected type %s but got type %s" % (str(t), str(type(var)))) self.logger.fatal("expected type %s but got type %s" % (str(t), str(type(var)))) except: mbox.showerror("FATAL ERROR", "float type error.") self.logger.fatal("float type error.") elif t is int: try: tmp = int(var) return tmp except ValueError: mbox.showerror( "FATAL ERROR", "expected type %s but got type %s" % (str(t), str(type(var)))) self.logger.fatal("expected type %s but got type %s" % (str(t), str(type(var)))) except: mbox.showerror("FATAL ERROR", "int type error.") self.logger.fatal("int type error.") elif t is bool: try: tmp = bool(var) return tmp except ValueError: mbox.showerror( "FATAL ERROR", "expected type %s but got type %s" % (str(t), str(type(var)))) self.logger.fatal("expected type %s but got type %s" % (str(t), str(type(var)))) except: mbox.showerror("FATAL ERROR", "bool type error.") self.logger.fatal("bool type error.") elif t is str: # anything can be converted to a str() return str(var) else: mbox.showerror( "FATAL ERROR", "attempt to validate an unexpected type %s as type %s." % (str(type(var)), str(t))) self.logger.fatal( "attempt to validate an unexpected type %s as type %s." % (str(type(var)), str(t))) else: return var @debugger def execute(self, sql): self.logger.debug("SQL=%s" % (sql)) return self.db.execute(sql) @debugger def commit(self): self.db.commit()
def cleanup(self): """Clean up after certain side effects, such as temporary directories.""" DELETED_TEMPDIR_MESSAGE = "Deleted temporary directory at {tempdir}" shutil.rmtree(self.tempdir) Logger.debug(DELETED_TEMPDIR_MESSAGE.format(tempdir=self.tempdir))
class EntryBox(tk.Frame): ''' Implement a consistent interface to the entry widget ''' def __init__(self, master, table, column, width=None, readonly=False, *args, **kargs): ''' master = the frame to bind this frame to name = the text of the label table = the name of the database table that is associated with this widget column = the name of the column this widget associates with lw = label width cw = control width ''' self.logger = Logger(self, level=Logger.INFO) self.logger.debug("EntryBox enter constructor") super().__init__(master, *args, **kargs) self.table = table self.column = column self.readonly = readonly self.content = tk.StringVar(master) self.entry = tk.Entry(self, textvariable=self.content, width=width) self.entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W) if self.readonly: self.entry.configure(state='readonly') self.row = { 'table': self.table, 'column': self.column, 'self': self, 'hasid': None } self.logger.debug("EntryBox leave constructor") @debugger def read(self): return self.content.get() @debugger def write(self, val): if self.readonly: self.entry.configure(state='normal') self.content.set(val) if self.readonly: self.entry.configure(state='readonly') @debugger def clear(self): self.content.set('') @debugger def get_line(self): ''' Return the form entry to update the form. ''' return self.row
class LineBox(tk.Frame): ''' This is a container for the line widgets. It holds a variable number of line boxes and has the ability to write them all to the database when the write method is called. ''' def __init__(self, master, form=None, *args, **kargs): ''' master = The frame to bind the widgets to. name_id = The id of the line containing the customer to associate form = Name of the form to bind the events to. ''' self.logger = Logger(self, level=Logger.DEBUG) self.logger.debug("Line Widget enter constructor") super().__init__(master, bd=1, relief=tk.RIDGE, *args, **kargs) #self.name_id = int(name_id) self.form = form self.events = EventHandler.get_instance() self.data = Database.get_instance() self.line_list = [] self.crnt_index = 0 self.events.register_event('next_button', self.clear) self.events.register_event('prev_button', self.clear) # add button tk.Button(self, text="Add", command=self.add).grid(row=0, column=0) # reset button tk.Button(self, text="Reset", command=self.clear).grid(row=0, column=1) # add one line widget self.add() # self.row = {'table': None, 'column':None, 'self':self, 'hasid':None} self.logger.debug("Line Widget leave constructor") @debugger def add(self): ''' Method that actually adds the line to the widget. ''' line = LineWidget(self, None, self.crnt_index + 1) line.grid(row=self.crnt_index + 1, column=0, columnspan=2, padx=5) self.line_list.append(line) self.crnt_index += 1 # @debugger # def get_line(self): # return self.row @debugger def clear(self): ''' Reset the widget to having one blank line. ''' for item in self.line_list: item.grid_forget() self.line_list = [] self.crnt_index = 0 self.add() @debugger def read(self, sale_id): ''' Read method saves the contects to the database ''' for item in self.line_list: (quan, name) = item.read() if name != '': row = { 'sale_record_ID': sale_id, 'quantity': quan, 'inventory_ID': self.data.get_id_by_name('InventoryItem', name) } self.data.insert_row('ProductList', row) self.data.commit() @debugger def write(self): ''' This function does nothing for this widget ''' self.clear()
class ButtonBox(tk.Frame): ''' Make the button box and register the events. ''' def __init__(self, master, form, disable_select=False, disable_new=False, *args, **kargs): ''' master = The frame to bind the widgets to. form = Name of the form to bind the events to. ''' self.logger = Logger(self, level=Logger.INFO) self.logger.debug("NotesBox enter constructor") super().__init__(master, *args, **kargs) row = 0 col = 0 self.form = form self.events = EventHandler.get_instance() tk.Button(self, text='Prev', command=self.prev_btn).grid(row=row, column=col, padx=5, pady=5) col += 1 tk.Button(self, text='Next', command=self.next_btn).grid(row=row, column=col, padx=5, pady=5) if not disable_select: col += 1 tk.Button(self, text='Select', command=self.select_btn).grid(row=row, column=col, padx=5, pady=5) if not disable_new: col += 1 tk.Button(self, text='New', command=self.new_btn).grid(row=row, column=col, padx=5, pady=5) col += 1 tk.Button(self, text='Save', command=self.save_btn).grid(row=row, column=col, padx=5, pady=5) col += 1 tk.Button(self, text='Delete', command=self.delete_btn).grid(row=row, column=col, padx=5, pady=5) @debugger def register_events(self, next, prev, select, new, save, delete): ''' next = callback for the next button prev = callback for the prev button select = callback for the select button new = callback for the new button save = callback for the save button delete = callback for the delete button ''' self.events.register_event('next_btn_%s' % (self.form), next) self.events.register_event('prev_btn_%s' % (self.form), prev) self.events.register_event('select_btn_%s' % (self.form), select) self.events.register_event('new_btn_%s' % (self.form), new) self.events.register_event('save_btn_%s' % (self.form), save) self.events.register_event('delete_btn_%s' % (self.form), delete) @debugger def next_btn(self): self.events.raise_event('next_btn_%s' % (self.form)) @debugger def prev_btn(self): self.events.raise_event('prev_btn_%s' % (self.form)) @debugger def select_btn(self): self.events.raise_event('select_btn_%s' % (self.form)) @debugger def new_btn(self): self.events.raise_event('new_btn_%s' % (self.form)) @debugger def save_btn(self): self.events.raise_event('save_btn_%s' % (self.form)) @debugger def delete_btn(self): self.events.raise_event('delete_btn_%s' % (self.form))