# vim: ts=4 et sts=4 sw=4 autoindent import gtk.glade, gtk import gobject import sys, os.path import math import Pyro.core import Pyro.errors import Pyro.constants from optparse import OptionParser import time import sndcs_common #from sndcs.EmployeeFactory import EmployeeFactory #from sndcs.Employee import Employee #from sndcs.JobHedFactory import JobHedFactory #from sndcs.OperationFactory import OperationFactory #from sndcs.IndirectFactory import IndirectFactory #from sndcs.LaborDtlFactory import LaborDtlFactory from sndcs_common.TrueAndFalseMixin import TrueAndFalseMixin from sndcs_common.MathUtilMixin import MathUtilMixin from sndcs_common.PyroProxyMixin import PyroProxyMixin from sndcs_common.Win32HelperMixin import Win32HelperMixin from sndcs_client.Config import config import sndcs_client.gtk # For required server version #from MiddleKit.Run.ObjectStore import UnknownObjectError from sndcs_common.SndcsExceptions import * from sndcs_common.Logger import logger log = logger.getLogger("sndcs_gtk") gobject.threads_init() def treeview_visible_function(model,iter,widget): #udata is a list of [ column_number, "search for" ] column_number=widget.column_number search_for=widget.search_criteria.lower() # Admin Employee throw off iterator errors, fix this. try: value=str(model.get_value(iter,column_number)) except ValueError: return False if not hasattr(widget,"boolean_only") or not widget.boolean_only: if value.lower().find(search_for) > -1: # Partial query results return True else: return False else: if value.lower()==str(search_for) or search_for=="": return True else: return False def getColumnNumberForName(widget,name): y = 0 numberFound=False for x in widget.get_columns(): if x.get_title() == name: #print "Number Found",y,"!" numberFound = True break y += 1 # If columns aren't defined the glade interface file sometimes the names don't get set in time for this function # but since the rlist defaults to 1, we can return 1 to prevent recursion errors. if numberFound: return y else: return 1 class Application(TrueAndFalseMixin, MathUtilMixin, PyroProxyMixin, Win32HelperMixin): def __init__(self): print print sndcs_common.NAME, sndcs_common.VERSION print sndcs_common.COPYRIGHT print print sndcs_common.LICENSE print # Check if we have correct Pyro version REQUIRED_PYRO_VERSION = '3.5' if Pyro.constants.VERSION < REQUIRED_PYRO_VERSION: log.error("Pyro version must be %s or greater. You have version %s installed.", REQUIRED_PYRO_VERSION, Pyro.constants.VERSION) sys.exit(1) self.config=config #Make Config() instance to all class methods # Parse comand line options self.parser = OptionParser(usage="%prog [options]", version="%prog " + sndcs_common.VERSION) self.parser.add_option("-c", "--config", dest="config_file", help="use FILE as additional config file", metavar="FILE") self.parser.add_option("-H", "--host", dest="ns_hostname", help="use HOST as name server host name", metavar="HOST") self.parser.add_option("-n", "--namespace", dest="namespace", help="use NAMESPACE as namespace", metavar="NAMESPACE") (self.options, args) = self.parser.parse_args() if self.options.config_file: # User specified an additional config file additional_config = config.read(os.path.expanduser(self.options.config_file)) log.info("Using config file(s): %s", self.config.getParsedFilenames()) # Set the Pyro config if self.options.ns_hostname: pyro_ns_hostname = self.options.ns_hostname else: pyro_ns_hostname = self.config.get("pyro", "ns_hostname", None) if pyro_ns_hostname: Pyro.config.PYRO_NS_HOSTNAME = pyro_ns_hostname log.info("Using Pyro Name Server at: %s", pyro_ns_hostname) # Get the Pyro namespace from the config if self.options.namespace: self.namespace = self.options.namespace else: self.namespace = self.config.get("pyro", "namespace", "sndcs") log.info("Using Pyro namespace '%s'", self.namespace) self.callbacks = { "on_window_pininterface_delete_event": self.on_window_pininterface_delete_event, "on_window_pininterface_key_press_event": self.addToPinPad, "on_enter_pinpad_pressed": self.enterPinPad, "on_cancel_pinpad_pressed": self.clearPinPad, "on_backspace_pinpad_pressed": self.backspacePinPad, "on_window_main_delete_event": gtk.main_quit, "on_window_main_key_press_event": self.on_window_main_key_press_event, "on_treeview_employee_list_button_press_event": self.on_treeview_employee_list_button_press_event, "on_window_admin_key_press_event": self.on_window_admin_key_press_event, "on_bttn_resume_tasks_clicked": self.resumeTasks, "on_bttn_clock_out_clicked": self.on_bttn_clock_out_clicked, "on_notebook_main_switch_page": self.on_notebook_main_switch_page, "on_notebook_selected_employee_switch_page": self.on_notebook_selected_employee_switch_page, "on_bttn_return_to_employee_list_clicked": self.on_bttn_return_to_employee_list_clicked, "on_bttn_start_production_activity_clicked": self.on_bttn_start_production_activity_clicked, "on_bttn_start_job_gang_clicked": self.on_bttn_start_job_gang_clicked, "on_bttn_start_setup_activity_clicked": self.on_bttn_start_setup_activity_clicked, "on_bttn_start_indirect_activity_clicked": self.on_bttn_start_indirect_activity_clicked, "on_bttn_end_activity_clicked": self.on_bttn_end_activity_clicked, "on_bttn_lunch_in_out_clicked": self.on_bttn_lunch_in_out_clicked, "on_bttn_break_in_out_clicked": self.on_bttn_break_in_out_clicked, "on_button_start_setup_gang_clicked": self.on_button_start_setup_gang_clicked, "on_button_indirect_cancel_clicked": self.on_button_indirect_cancel_clicked, "on_button_indirect_ok_clicked": self.on_button_indirect_ok_clicked, "on_rlistLookForIn_changed": self.on_rlistLookForIn_changed, "on_searchCriteria_changed": self.on_searchCriteria_changed, "on_rlistLookForIn_admin_changed": self.on_rlistLookForIn_admin_changed, "on_searchCriteria_admin_changed": self.on_searchCriteria_admin_changed, "on_recent_activities_treeview_row_activated": self.on_recent_activities_treeview_row_activated, "on_treeview_indirect_activity_codes_button_press_event": self.on_treeview_indirect_activity_codes_button_press_event, "on_button_available_production_cancel_clicked": self.on_button_available_production_cancel_clicked, "on_button_available_production_ok_clicked": self.on_button_available_production_ok_clicked, "on_treeview_available_activities_button_press_event": self.on_treeview_available_activities_button_press_event, "on_treeview_start_setup_button_press_event": self.on_treeview_start_setup_button_press_event, "on_button_setup_cancel_clicked": self.on_button_setup_cancel_clicked, "on_button_setup_ok_clicked": self.on_button_setup_ok_clicked, "on_button_start_gang_add_clicked": self.on_button_start_gang_add_clicked, "on_button_start_gang_remove_clicked": self.on_button_start_gang_remove_clicked, "on_treeview_start_gang_available_button_press_event": self.on_treeview_start_gang_available_button_press_event, "on_treeview_start_gang_available_drag_data_get": self.on_treeview_start_gang_available_drag_data_get, "on_treeview_start_gang_available_drag_data_received": self.on_treeview_start_gang_available_drag_data_received, "on_treeview_start_gang_current_button_press_event": self.on_treeview_start_gang_current_button_press_event, "on_treeview_start_gang_current_drag_data_received": self.on_treeview_start_gang_current_drag_data_received, "on_treeview_start_gang_current_drag_data_get": self.on_treeview_start_gang_current_drag_data_get, "on_button_start_gang_ok_clicked": self.on_button_start_gang_ok_clicked, "on_button_start_gang_cancel_clicked": self.on_button_start_gang_cancel_clicked, "on_button_end_activity_dynamic_cancel_clicked": self.on_button_end_activity_dynamic_cancel_clicked, "on_button_end_activity_dynamic_ok_clicked": self.on_button_end_activity_dynamic_ok_clicked, "on_menu_main_file_quit_activate": gtk.main_quit, "on_menu_main_help_about_activate": self.on_menu_main_help_about_activate, "on_menu_main_view_admin_activate": self.on_menu_main_view_admin_activate, "on_dialog_about_delete_event": self.on_dialog_about_delete_event, "on_button_dialog_about_ok_clicked": self.on_button_dialog_about_ok_clicked, "on_window_admin_delete_event": self.on_window_admin_delete_event, "on_notebook_admin_switch_page": self.on_notebook_admin_switch_page, "on_menu_admin_file_new_activate": self.on_menu_admin_file_new_activate, "on_button_additional_information_clicked": self.on_button_additional_information_clicked, "on_bttn_additional_information_ok_clicked": self.on_bttn_additional_information_ok_clicked, } if self.main_is_frozen(): filename = os.path.join(self.get_main_dir(), "sndcs2.glade") else: filename = os.path.join(sys.prefix, "share", "sndcs", "sndcs2.glade") assert os.path.exists(filename) self.xml = gtk.glade.XML(filename) self.xml.signal_autoconnect(self.callbacks) # Display the splash screen fixed = self.xml.get_widget("fixed_splash_screen") label_splash_description = gtk.Label() label_splash_description_shadow = gtk.Label() label_splash_action = gtk.Label() label_splash_description.set_justify(gtk.JUSTIFY_LEFT) label_splash_description_shadow.set_justify(gtk.JUSTIFY_LEFT) label_splash_action.set_justify(gtk.JUSTIFY_LEFT) label_splash_description.set_markup("%s %s" % (sndcs_common.NAME, sndcs_common.VERSION)) label_splash_description_shadow.set_markup("%s %s" % (sndcs_common.NAME, sndcs_common.VERSION)) label_splash_action.set_markup("Connecting to server...") fixed.put(label_splash_description_shadow, 225, 161) fixed.put(label_splash_description, 225, 160) fixed.put(label_splash_action, 15, 165) label_splash_description_shadow.show() label_splash_description.show() label_splash_action.show() self.xml.get_widget("splash_screen").show() while gtk.events_pending(): gtk.main_iteration() # Get server information # This need to be after the glade code so that if this fails the flatline can display the gtk error box properly try: self.server = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["DCSServer"])) except Exception, e: self.flatline(e) major_version, minor_version, macro_version, custom_version, version = self.server.getServerVersion() log.info("Connected to SaberNet DCS Server version: %s", version) log.info("SaberNet DCS Server uptime: %s", str(self.server.getUptime()) ) # Does that meet the required version number? if ".".join((str(major_version), str(minor_version), str(macro_version))) < sndcs_client.gtk.REQUIRED_SERVER_VERSION: log.error("Server version does not meet requirement. Client required version: %s Server version: %s", sndcs_client.gtk.REQUIRED_SERVER_VERSION, version) sys.exit(1) for x in [ "one_pinpad", "two_pinpad", "three_pinpad", "four_pinpad", "five_pinpad", "six_pinpad", "seven_pinpad", "eight_pinpad", "nine_pinpad", "zero_pinpad" ]: self.xml.get_widget(x).connect("pressed",self.addToPinPad) # For searching feature self.name,self.admin_name = [ "Employee Name","Number" ] self.vbox_information = self.xml.get_widget("vbox_information_display") window_main = self.xml.get_widget("window_main") window_main.set_title(" ".join((sndcs_common.NAME, sndcs_common.VERSION))) if self.true(self.config.get("gtk", "fullscreen")): window_main.fullscreen() # If search dialog is disabled then hide it if not self.true(self.config.get("gtk", "search_dialog")): if not self.true(self.config.get("gtk", "osd_keyboard")): widget = self.xml.get_widget("keyboard_toolbar") widget.hide() else: widget = self.xml.get_widget("searchdialog") widget.hide() # ...otherwise connect the proper callbacks else: self.searchCriteria = self.xml.get_widget("searchCriteria") self.searchCriteriaAdmin = self.xml.get_widget("searchCriteria_admin") self.searchCriteria.connect("changed", self.on_searchCriteria_changed) self.xml.get_widget("rlistLookForIn").connect("changed", self.on_rlistLookForIn_changed) self.attachHighlightToWidget(self.searchCriteria,True) self.attachHighlightToWidget(self.searchCriteriaAdmin,True) if self.true(self.config.get("gtk", "osd_keyboard")): self.capslock,self.shiftkey = [ False, False ] for x in [ "vbox_keyboard", "vbox_keyboard2", "osdbutton", "osdbutton2" ]: self.xml.get_widget(x).show() self.xml.get_widget("osdbutton").connect("toggled",self.toggleKeyboard) self.xml.get_widget("osdbutton2").connect("toggled",self.toggleKeyboard) # Touchscreen buttons callbacks here allTheKeys = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v" , "w", "x", "y" , "z" ] allTheKeys.extend([ "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "zero" ]) allTheKeys.extend(["enter", "escape", "bs", "spacebar", "colon", "pipe", "comma", "period1", "period2", "tab", "hypen", "fslash1", "fslash2"]) # "" is for main page, _admin is for Admin page keyboards for prefix in [ "" ,"_admin" ]: if prefix=="": emitto="window_main" elif prefix=="_admin": emitto="window_admin" for x in [ "capslock", "shiftkey" ]: self.xml.get_widget("%s%s" % (x,prefix) ).connect("pressed",self.caseShift,x) self.xml.get_widget("%s%s" % (x,prefix) ).set_focus_on_click(False) for x in allTheKeys: if x=="bs": #Backspace y = gtk.keysyms.BackSpace elif x=="spacebar": #Spacebar y = gtk.keysyms.space elif x=="colon": # Shift defines this If shift is on then then [0] is selected y = [ gtk.keysyms.colon, gtk.keysyms.semicolon] elif x=="pipe": # Watch the forward slash going into the eval() y= [ gtk.keysyms.bar, gtk.keysyms.backslash ] elif x.find("fslash")>-1: y = gtk.keysyms.slash elif x=="hypen": # hopefully the same everywhere y = 0x2d elif x=="comma": y = gtk.keysyms.comma elif x.find("period")>-1: y = gtk.keysyms.period elif x=="tab": y = gtk.keysyms.Tab elif x=="escape": y = gtk.keysyms.Escape elif x=="enter": y = gtk.keysyms.Return else: y=self.convertAlphaToNum(x) try: self.xml.get_widget("%skey%s" % (x,prefix)).connect("pressed",self.keystrokes,(y,emitto)) self.xml.get_widget("%skey%s" % (x,prefix)).connect("focus",self.focusCapture) self.xml.get_widget("%skey%s" % (x,prefix)).set_focus_on_click(False) except: log.debug("Keybinding failed for: %s at %s" % (x,prefix) ) # Check if the menu should be visible if self.true(self.config.get("gtk", "menu_bar", True)): menu_main = self.xml.get_widget("menu_main") menu_main.show() # Create window_main tree views from sndcs_client.gtk.AvailableActivitiesTreeView import AvailableActivitiesTreeView from sndcs_client.gtk.IndirectActivitiesTreeView import IndirectActivitiesTreeView from sndcs_client.gtk.CurrentActivitiesTreeView import CurrentActivitiesTreeView from sndcs_client.gtk.ActiveEmployeesTreeView import ActiveEmployeesTreeView label_splash_action.set_markup("Loading available activity data...") while gtk.events_pending(): gtk.main_iteration() tree_view = self.xml.get_widget("treeview_available_activities") tree_view.set_enable_search(False) self.treeview_available_activities = AvailableActivitiesTreeView(tree_view, namespace=self.namespace) activity_model = self.populateTreeViewAvailableActivities() label_splash_action.set_markup("Loading available setup data...") while gtk.events_pending(): gtk.main_iteration() tree_view = self.xml.get_widget("treeview_start_setup") tree_view.set_enable_search(False) self.treeview_start_setup = AvailableActivitiesTreeView(tree_view, multiple=False, namespace=self.namespace) self.populateTreeViewStartSetup(model=activity_model) label_splash_action.set_markup("Loading available gang data...") while gtk.events_pending(): gtk.main_iteration() tree_view = self.xml.get_widget("treeview_start_gang_available") tree_view.set_enable_search(False) self.treeview_start_gang_available = AvailableActivitiesTreeView(tree_view, namespace=self.namespace) self.populateTreeViewStartGangAvailable(model=activity_model) tree_view = self.xml.get_widget("treeview_start_gang_current") tree_view.set_enable_search(False) self.treeview_start_gang_current = AvailableActivitiesTreeView(tree_view, namespace=self.namespace) label_splash_action.set_markup("Loading indirect activities...") while gtk.events_pending(): gtk.main_iteration() tree_view = self.xml.get_widget("treeview_indirect_activity_codes") tree_view.set_enable_search(False) self.treeview_indirect_activity_codes = IndirectActivitiesTreeView(tree_view, namespace=self.namespace) self.populateTreeViewIndirectActivityCodes() label_splash_action.set_markup("Loading active employee data...") while gtk.events_pending(): gtk.main_iteration() tree_view = self.xml.get_widget("treeview_employee_list") tree_view.set_enable_search(False) self.treeview_active_employees = ActiveEmployeesTreeView(tree_view, namespace=self.namespace) self.populateTreeViewActiveEmployees() tree_view = self.xml.get_widget("treeview_current_activities") tree_view.set_enable_search(False) self.treeview_current_activities = CurrentActivitiesTreeView(tree_view, self.treeview_active_employees.model) # Set up drag and drop self.treeview_start_gang_available.view.drag_source_set(gtk.gdk.BUTTON1_MASK, [("text/plain", gtk.TARGET_SAME_APP, 0)], gtk.gdk.ACTION_MOVE) self.treeview_start_gang_current.view.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("text/plain", gtk.TARGET_SAME_APP, 0)], gtk.gdk.ACTION_MOVE) self.treeview_start_gang_current.view.drag_source_set(gtk.gdk.BUTTON1_MASK, [("text/plain", gtk.TARGET_SAME_APP, 0)], gtk.gdk.ACTION_MOVE) self.treeview_start_gang_available.view.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("text/plain", gtk.TARGET_SAME_APP, 0)], gtk.gdk.ACTION_MOVE) # Create window_admin tree views # TODO: see if user has access to admin stuff, if not then skip all this #from sndcs_client.gtk.JobAdminTreeView import JobAdminTreeView #tree_view = self.xml.get_widget("treeview_job_admin") #tree_view.set_enable_search(False) #self.treeview_job_admin = JobAdminTreeView(tree_view) #from sndcs_client.gtk.EmployeeAdminTreeView import EmployeeAdminTreeView #tree_view = self.xml.get_widget("treeview_employee_admin") #tree_view.set_enable_search(False) #self.employee_admin_tree_view = EmployeeAdminTreeView(tree_view) dialog_about = self.xml.get_widget("dialog_about") label_dialog_about_name = self.xml.get_widget("label_dialog_about_name") label_dialog_about_name.set_markup("%s %s" % (sndcs_common.NAME, sndcs_common.VERSION)) label_dialog_about_description = self.xml.get_widget("label_dialog_about_description") label_dialog_about_description.set_markup("%s" % (sndcs_common.DESCRIPTION)) label_dialog_about_copyright = self.xml.get_widget("label_dialog_about_copyright") label_dialog_about_copyright.set_markup("%s" % (sndcs_common.COPYRIGHT)) dialog_about.set_title("%s %s" % (sndcs_common.NAME, sndcs_common.VERSION)) window_admin = self.xml.get_widget("window_admin") window_admin.set_title(" ".join((sndcs_common.NAME, sndcs_common.VERSION, "Admin"))) # Set up the recent activities list self.cached_jobs = gtk.ListStore(int,str) self.recent_activities_treeview = self.xml.get_widget("recent_activities_treeview") if self.false(self.config.get("gtk","recent_activity",True)): self.xml.get_widget("recent_activity_frame").hide() tree_column_1 = gtk.TreeViewColumn("Activities") self.recent_activities_treeview.append_column(tree_column_1) cell = gtk.CellRendererText() tree_column_1.pack_start(cell, True) tree_column_1.add_attribute(cell, "text", 1) self.recent_activities_treeview.set_model(self.cached_jobs) # Globals (if add a new one make sure to set to None in clearAndResetGui) self.currently_selected_employee = None self.barcode_command = "" # Globals for end activity screen # End and continue should be a list of LaborDtl objects, start should be a list of Operation objects self.end_activities_dynamic = [] self.start_activities_dynamic = [] self.continue_activities_dynamic = [] self.start_indirect_dynamic = None self.frame_ending = None self.frame_starting = None # The rows of widgets in the end activity dynamic screen self.end_activities_dynamic_widgets = [] self.start_activities_dynamic_widgets = [] self.continue_activities_dynamic_widgets = [] # TODO: lookup the barcode signifiers from the config file self.main_selection_signifier = '$' self.main_action_signifier = '*' self.main_command_signifier = '/' self.separator_signifier = '|' # Selection command signifiers self.clockin_and_select_selection_signifier = 's' self.lunch_selection_signifier = 'l' self.break_selection_signifier = 'b' self.resume_selection_signifier = 'r' # Resume Selection self.clockout_selection_signifier = 'o' # Action command signifiers self.indirect_action_signifier = 'i' self.production_action_signifier = 'p' self.end_production_action_signifier = 'pc' self.setup_action_signifier = 's' self.end_setup_action_signifier = 'sc' # Setup data for the statusbar clock self.clock_offset = 0.0 gobject.idle_add(self.updateStatusBarClock) gobject.timeout_add(1000, gobject.idle_add, self.updateStatusBarClock) # Start the heartbeat monitor label_splash_action.set_markup("Starting heartbeat monitor...") while gtk.events_pending(): gtk.main_iteration() self.startHeartbeatMonitor() # Set up the event subscriber label_splash_action.set_markup("Starting event subscriber...") while gtk.events_pending(): gtk.main_iteration() self.startEventSubsciber() # Employee Image self.employee_image = self.xml.get_widget("img_employee") # Setup evironment to have search feature self.clear_search_criteria() # Show the "Employee Name" option when app first starts up obj=self.xml.get_widget("rlistLookForIn") obj.set_active(0) # Let's make the treeview_employee_list have the focus when the app starts up treeview_employee_list = self.xml.get_widget("treeview_employee_list") treeview_employee_list.grab_focus() window_main.show() self.xml.get_widget("splash_screen").hide() def set_busy_status(self): """ Set cursor to "busy" status """ self.xml.get_widget("window_main").window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) gtk.gdk.display_get_default().flush() def clear_busy_status(self): self.xml.get_widget("window_main").window.set_cursor(None) # TODO: this currently doesn't work very well. The EventSubscriber will # randomly leave it's threaded self running and even though the thread says # it's stopped and isAlive() returns False it will still process events. # This means a single event will get consumend multiple times def refresh(self): self.stopHeartbeatMonitor() self.stopEventSubscriber() self.startHeartbeatMonitor() self.startEventSubsciber() self.populateTreeViews() def addCachedJob(self,operation_id,jobString): for x in self.cached_jobs: if int(x[0]) == int(operation_id): return # Already on "Recent List" if len(self.cached_jobs) > 20: # Last 20 jobs displayed del self.cached_jobs[0] log.info("Added Operation %s to recent activity list" % operation_id) self.cached_jobs.append([operation_id,jobString]) def addListToCachedJobs(self,job_list): log.debug("addListToCached() called: %s" % job_list ) if not job_list is None: operation_object = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Operation"])) for operation in job_list: if type(operation) == dict: if not operation["job_type"] is "Production": return operation_id = int( operation["operation"] ) else: operation_id = int ( operation ) operation = operation_object.setOperationBySerialNum( operation_id ) self.addCachedJob(operation_id,operation.formattedDescription() ) operation.disconnect() def deleteCachedJob(self,operation_id): p = None # (p)arent i = 0 for x in self.cached_jobs: if int(x[0]) == int(operation_id): p = i break i += 1 if p is not None: iter = self.cached_jobs.get_iter(p) self.cached_jobs.remove(iter) log.info("Removed Operation %s from recent activity list" % operation_id) def delete_operation(self,event): self.deleteCachedJob(event.msg["serialNum"]) self.treeview_available_activities.update(event) # Pass along event to AvailableActivitiesView.py def startEventSubsciber(self): from sndcs_client.gtk.EventSubscriber import EventSubscriber callbacks = {self.namespace + "_heartbeat":[self.heartbeat_monitor.update], self.namespace + "_clock_in":[self.treeview_active_employees.update], self.namespace + "_clock_out":[self.treeview_active_employees.update], self.namespace + "_lunch_in":[self.treeview_active_employees.update], self.namespace + "_lunch_out":[self.treeview_active_employees.update], self.namespace + "_break_in":[self.treeview_active_employees.update], self.namespace + "_break_out":[self.treeview_active_employees.update], self.namespace + "_indirect_add":[self.treeview_indirect_activity_codes.update], self.namespace + "_indirect_delete":[self.treeview_indirect_activity_codes.update], self.namespace + "_indirect_start":[self.treeview_active_employees.update], self.namespace + "_indirect_stop":[self.treeview_active_employees.update], self.namespace + "_indirect_suspend":[self.treeview_active_employees.update], self.namespace + "_indirect_resume":[self.treeview_active_employees.update], self.namespace + "_production_start":[self.treeview_active_employees.update], self.namespace + "_production_stop":[self.treeview_active_employees.update], self.namespace + "_production_suspend":[self.treeview_active_employees.update], self.namespace + "_production_resume":[self.treeview_active_employees.update], self.namespace + "_employee_add":[self.treeview_active_employees.update], self.namespace + "_employee_edit":[self.treeview_active_employees.update], self.namespace + "_employee_delete":[self.treeview_active_employees.update], self.namespace + "_setup_start":[self.treeview_active_employees.update], self.namespace + "_setup_stop":[self.treeview_active_employees.update], self.namespace + "_setup_suspend":[self.treeview_active_employees.update], self.namespace + "_setup_resume":[self.treeview_active_employees.update], self.namespace + "_heartbeat":[self.heartbeat_monitor.update, self.calculateClockOffset], self.namespace + "_scrapcode_add":[self.add_scrapcode], self.namespace + "_scrapcode_delete":[self.delete_scrapcode], self.namespace + "_department_add":[self.add_department], self.namespace + "_department_delete":[self.delete_department], self.namespace + "_clear_settings": [self.clear_settings], self.namespace + "_operation_add":[self.treeview_available_activities.update], #self.namespace + "_operation_delete":[self.treeview_available_activities.update], self.namespace + "_operation_delete":[self.delete_operation], #self.namespace + "_operation_complete":[self.treeview_available_activities.update], self.namespace + "_operation_complete":[self.delete_operation], self.namespace + "_ping":[self.pong] } self.event_subscriber= EventSubscriber(callbacks, namespace=self.namespace) self.event_subscriber.start() def pong(self,event): log.info("Reporting info to namespace...") terminal_id = self.config.get("gtk", "terminal_id", "") self.server.pong({"ip_address": self.server.clientIPAddress() ,"terminal_id": terminal_id,"version": " ".join((sndcs_common.NAME, sndcs_common.VERSION))}) def calculateClockOffset(self, event): """ Calculates the difference between the time on the server and the time on the client. Event will contain the timestamp from the server. """ self.clock_offset = event.time - time.time() def updateStatusBarClock(self): clock_format = self.config.get("gtk", "clock_format", "%I:%M %p") if clock_format: statusbar_main = self.xml.get_widget("statusbar_main") terminal_id = self.config.get("gtk", "terminal_id", "") context_id = statusbar_main.get_context_id("statusbar_main_clock") local_time = time.localtime(time.time() + self.clock_offset) formatted_local_time = time.strftime(clock_format, local_time) # Pop off the previous time statusbar_main.pop(context_id) # Push the new time onto the statusbar along with the terminal_id (if exists) if terminal_id: formatted_local_time = " ".join((formatted_local_time, "[%s]" % (terminal_id))) statusbar_main.push(context_id, formatted_local_time) def stopEventSubscriber(self): print "Stopping the Event Subscriber..." self.event_subscriber.abort() def flatline(self, exception = None, restart = False): self.xml.get_widget("splash_screen").hide() # Just in case the splash sceen is visible and covering up an error message if restart: log.error("Flatlined...restart event service and updating treeviews....") try: self.refresh() except Exception,exception: message = "An error has occured.\nThe application will now terminate." if exception: message = message + "\n\nError: " + str(exception) self.alertBox("ERROR", "OK", message) gtk.main_quit() # TODO: This will occasionally throw an "RuntimeError: called outside of a mainloop" exception else: message = "An error has occured.\nThe application will now terminate." if exception: message = message + "\n\nError: " + str(exception) self.alertBox("ERROR", "OK", message) gtk.main_quit() # TODO: This will occasionally throw an "RuntimeError: called outside of a mainloop" exception def handle_flatline(self, exception = None): gobject.idle_add(self.flatline, exception) def startHeartbeatMonitor(self): from sndcs_client.HeartbeatMonitor import HeartbeatMonitor self.heartbeat_monitor = HeartbeatMonitor(self.handle_flatline) self.heartbeat_monitor.start() def stopHeartbeatMonitor(self): print "Stopping Heartbeat Monitor..." self.heartbeat_monitor.quit = True def clear_search_criteria(self): for x in [ self.treeview_available_activities, self.treeview_start_setup, self.treeview_start_gang_available, self.treeview_start_gang_current, self.treeview_start_gang_current, self.treeview_indirect_activity_codes, self.treeview_active_employees ]: x.column_number = 0 x.search_criteria = "" x.boolean_only = False # Search as you type idea from Gourmet def on_rlistLookForIn_changed(self,widget): self.name=widget.get_model()[widget.get_active()][0] # Emit on_searchCriteria_change to update to new results self.xml.get_widget("searchCriteria").emit("changed") def on_searchCriteria_changed(self,widget): notebook=self.xml.get_widget("notebook_main") if notebook.get_current_page()==0: obj=self.treeview_active_employees obj.column_number=getColumnNumberForName(obj.view,self.name) + 2 obj.search_criteria=widget.get_text() obj.update() elif notebook.get_current_page()==1: notebook=self.xml.get_widget("notebook_selected_employee") page_num=notebook.get_current_page() if page_num==1: obj=self.treeview_available_activities obj.column_number=getColumnNumberForName(obj.view,self.name) + 1 if self.name == "Priority": obj.boolean_only = True else: obj.boolean_only = False obj.search_criteria=widget.get_text() obj.update(event=False) elif page_num==2: obj=self.treeview_start_gang_available obj.column_number=getColumnNumberForName(obj.view,self.name) + 1 if self.name == "Priority": obj.boolean_only = True else: obj.boolean_only = False obj.search_criteria=widget.get_text() obj.update(event=False) elif page_num==3: obj=self.treeview_start_setup obj.column_number=getColumnNumberForName(obj.view,self.name) + 1 obj.search_criteria=widget.get_text() obj.update(event=False) elif page_num==4: obj=self.treeview_indirect_activity_codes obj.column_number=getColumnNumberForName(obj.view,self.name) + 1 obj.search_criteria=widget.get_text() obj.update(event=False) def on_rlistLookForIn_admin_changed(self,widget): self.admin_name=widget.get_model()[widget.get_active()][0] # Emit on_serarchCiteria_admin_change to update to new results self.xml.get_widget("searchCriteria_admin").emit("changed") def on_searchCriteria_admin_changed(self,widget): notebook=self.xml.get_widget("notebook_admin") page_num=notebook.get_current_page() if page_num==0: obj=self.treeview_job_admin obj.column_number=getColumnNumberForName(obj.view,self.admin_name) + 1 obj.search_criteria=widget.get_text() elif page_num==2: obj=self.employee_admin_tree_view obj.column_number=getColumnNumberForName(obj.view,self.admin_name) + 1 obj.search_criteria=widget.get_text() else: return obj.update() # If OSD keyboard is enable then the searchdialog is hide/show, but if not the whole toolbar is hidden/shown def showSearchDialog(self,window=""): if self.true(self.config.get("gtk", "search_dialog", True)): if self.true(self.config.get("gtk", "osd_keyboard", False)): self.xml.get_widget("searchdialog%s" % window).show() else: self.xml.get_widget("keyboard_toolbar%s" % window).show() # window can be "" or "_admin" def hideSearchDialog(self,window=""): if self.true(self.config.get("gtk", "search_dialog", True)): if self.true(self.config.get("gtk", "osd_keyboard")): self.xml.get_widget("searchdialog%s" % window).hide() else: self.xml.get_widget("keyboard_toolbar%s" % window).hide() def convertAlphaToNum(self,input,alphaToKeysyms=False): numbers = { "one" : 0x31, "two" : 0x32, "three" : 0x33, "four" : 0x34, "five" : 0x35, "six" : 0x36, "seven" : 0x37, "eight" : 0x38, "nine" : 0x39, "zero" : 0x30 } reverse_numbers = { 0x31: 1, 0x32: 2, 0x33: 3, 0x34: 4 , 0x35 : 5, 0x36 : 6, 0x37 : 7, 0x38 : 8, 0x39 : 9, 0x30 : 0 } try: if not alphaToKeysyms: return numbers[input] else: for key,value in reverse_numbers.items(): if key == input: return value except KeyError: return input def caseShift(self,widget,whichkey): if str(widget.name).count("capslock"): self.capslock = widget.get_active() == False for x in [ "capslock", "capslock_admin" ]: if not x == widget.name: self.xml.get_widget(x).set_active( widget.get_active() == False) if str(widget.name).count("shiftkey"): self.shiftkey = widget.get_active() == False for x in [ "shiftkey", "shiftkey_admin" ]: if not x == widget.name: self.xml.get_widget(x).set_active( widget.get_active() == False) def toggleKeyboard(self,widget): for obj in [ self.xml.get_widget("vbox_keyboard"), self.xml.get_widget("vbox_keyboard2") ]: if widget.get_active(): self.xml.get_widget("osdbutton").set_active(True) self.xml.get_widget("osdbutton2").set_active(True) obj.show() else: self.xml.get_widget("osdbutton").set_active(False) self.xml.get_widget("osdbutton2").set_active(False) obj.hide() def keystrokes(self,widget,currentkey): emitto = currentkey[1] currentkey = currentkey[0] if type(currentkey) == list: if self.shiftkey or self.capslock: currentkey = currentkey[0] else: currentkey = currentkey[1] if self.shiftkey or self.capslock: if self.shiftkey: shiftkey = self.xml.get_widget("shiftkey") self.caseShift(shiftkey,"shiftkey") shiftkey.set_active(False) if isinstance(currentkey, str): keyval = gtk.gdk.keyval_from_name(currentkey) else: keyval = currentkey currentkey = gtk.gdk.keyval_to_upper(keyval) state = gtk.gdk.SHIFT_MASK else: state = 0 if gtk.keysyms.__dict__.has_key(currentkey) : self.emitKey(eval("gtk.keysyms.%s" % currentkey), emitto, state) #Test is this a real letter/number or keysym else: self.emitKey(currentkey, emitto, state) def emitKey(self, emitthis, emitto="window_main", state=0): widget = self.xml.get_widget(emitto) try: string = chr(emitthis) except: string = "" event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) event.keyval = int(emitthis) event.window = widget.window event.time = 0 event.state = state # Probably a better way to do this but Tab and BackSpace don't seem to work unless we set the hardware_keycode if event.keyval == gtk.keysyms.Tab: event.hardware_keycode = 23 elif event.keyval == gtk.keysyms.BackSpace: event.hardware_keycode = 22 elif event.keyval == gtk.keysyms.space: # OSD keyboard space bar won't select complete check box on end activity screen w/out this event.hardware_keycode = 65 widget.emit("key_press_event",event) def populateTreeViewAvailableActivities(self, model=None): print "Loading Available Activity Data..." try: model = self.treeview_available_activities.populate(model) except (Pyro.errors.ConnectionClosedError, Pyro.errors.ProtocolError, Pyro.errors.PyroError, Pyro.errors.NamingError), e: self.flatline(e) return model def populateTreeViewStartSetup(self, model=None): print "Loading Available Setup Data..." try: model = self.treeview_start_setup.populate(model) except (Pyro.errors.ConnectionClosedError, Pyro.errors.ProtocolError, Pyro.errors.PyroError, Pyro.errors.NamingError), e: self.flatline(e) return model def populateTreeViewStartGangAvailable(self, model=None): print "Loading Available Gang Data..." return self.treeview_start_gang_available.populate(model) def populateTreeViewIndirectActivityCodes(self, model=None): print "Loading Indirect Activities..." return self.treeview_indirect_activity_codes.populate(model) def populateTreeViewActiveEmployees(self, model=None): print "Loading Active Employee Data..." return self.treeview_active_employees.populate(model) def populateTreeViews(self): activity_model = self.populateTreeViewAvailableActivities() self.populateTreeViewStartSetup(model=activity_model) self.populateTreeViewStartGangAvailable(model=activity_model) self.populateTreeViewIndirectActivityCodes() self.populateTreeViewActiveEmployees() def populateCurrentActivitiesList(self, employee): self.treeview_current_activities.populate(employee) def switchPageByName(self, page_name): page_numbers = {"EmployeeList":(0,0), "SelectedEmployee":(1,0), "StartProductionActivity":(1,1), "StartJobGang":(1,2), "StartSetupActivity":(1,3), "StartIndirectActivity":(1,4), "AdditionalInformation":(1,5), "EndActivity":(1,6), "EndActivityDynamic":(1,7)} assert page_numbers.has_key(page_name) notebook_main = self.xml.get_widget("notebook_main") notebook_selected_employee = self.xml.get_widget("notebook_selected_employee") if page_name == "EmployeeList" and self.getCurrentPage() == "EmployeeList": pass else: notebook_selected_employee.set_current_page(page_numbers[page_name][1]) notebook_main.set_current_page(page_numbers[page_name][0]) # Place focus in first widget on end activity screen # Doing this here because setting the focus after building the end # activity screen, then calling switchPageByName to display would # change the focus to one of the buttons if page_name == "EndActivityDynamic": focus_me = None if self.end_activities_dynamic_widgets: focus_me = self.end_activities_dynamic_widgets[0]["activity_percentage"] elif self.continue_activities_dynamic_widgets: focus_me = self.continue_activities_dynamic_widgets[0]["activity_percentage"] if focus_me: focus_me.grab_focus() def getCurrentPage(self): page_numbers = {(0,0):"EmployeeList", (1,0):"SelectedEmployee", (1,1):"StartProductionActivity", (1,2):"StartJobGang", (1,3):"StartSetupActivity", (1,4):"StartIndirectActivity", (1,5):"AdditionalInformation", (1,6):"EndActivity", (1,7):"EndActivityDynamic"} notebook_main = self.xml.get_widget("notebook_main") notebook_selected_employee = self.xml.get_widget("notebook_selected_employee") return page_numbers[(notebook_main.get_current_page()), (notebook_selected_employee.get_current_page())] def getEmployeeProxy(self, employee_number = None, employee_serial_num = None): assert (employee_number or employee_serial_num) and not (employee_number and employee_serial_num) # One or the other.... not both try: proxy_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Employee"])) except Exception, e: self.flatline(e) try: if employee_serial_num: proxy = proxy_factory.setEmployeeBySerialNum(employee_serial_num) else: proxy = proxy_factory.setEmployeeByEmpId(employee_number) except SndcsEmployeeError: if employee_serial_num: self.alertBox("WARNING", "OK", "'%s' is an invalid employee serial number. Please try again." % (employee_serial_num)) else: self.alertBox("WARNING", "OK", "'%s' is an invalid employee number. Please try again." % (employee_number)) return except Pyro.errors.ConnectionClosedError, e: self.flatline(e) return proxy def selectEmployee(self, employee_id = None, employee_proxy = None): """ If employee_id is passed it will get the proxy object. Otherwise pass in an existing proxy object. Returns True if employee clocked in or False if they just selected themselves (or if PIN pad displayed). """ assert (employee_id or employee_proxy) and not (employee_id and employee_proxy) # One or the other.... not both if employee_proxy: employee = employee_proxy employee_id = int(employee.serialNum()) else: employee = self.getEmployeeProxy(employee_serial_num = employee_id) if not employee.isClockedIn(): if employee.pinNumber(): self.employee = employee self.showPinPad() return False terminal_id = self.config.get("gtk", "terminal_id", "") employee.clockIn(terminal_id = terminal_id) self.clearAndResetGui() return True else: lbl_emp_name = self.xml.get_widget("lbl_selected_employee_name") lbl_emp_name.set_text("Employee Name: %s" % employee.properName()) lbl_emp_id = self.xml.get_widget("lbl_selected_employee_id") lbl_emp_id.set_text("Employee ID: %s" % employee.empId()) self.populateCurrentActivitiesList(employee_id) self.switchPageByName("SelectedEmployee") if self.currently_selected_employee: self.currently_selected_employee.disconnect() self.currently_selected_employee = employee pictureDataMD5=employee.picture_md5sum() if os.name == "posix": tmppath = "/tmp/" else: #Windows boxen tmppath = sys.prefix + "\\tmp\\" if not os.path.exists(tmppath): os.mkdir(tmppath) def checkCachedVersion(file,md5sum): import md5 f=open(file,"rb") file_md5sum=md5.new(f.read()).hexdigest() log.debug("Found employee picture at %s with MD5 of [%s]" % (file, file_md5sum) ) return file_md5sum == md5sum if not pictureDataMD5 is None: log.debug("Got MD5 of employee picture: [%s]" % pictureDataMD5) if not os.path.exists("%spicture%s.jpg" % ( tmppath,employee.empId() ) ) or not checkCachedVersion( ( "%spicture%s.jpg" % ( tmppath,employee.empId() ) ), pictureDataMD5 ): pictureData=employee.picture() f=open("%spicture%s.jpg" % ( tmppath,employee.empId() ),"wb") f.write(pictureData) f.close() log.debug("Saving employee picture to [%s]" % ("%spicture%s.jpg" % ( tmppath,employee.empId() ) ) ) pixbuf = gtk.gdk.PixbufAnimation("%spicture%s.jpg" % (tmppath,employee.empId()) ) if pixbuf.is_static_image(): # Non-animated image then we can scale. pixbuf = gtk.gdk.pixbuf_new_from_file("%spicture%s.jpg" % (tmppath,employee.empId()) ) pixbuf = pixbuf.scale_simple(150,150,gtk.gdk.INTERP_BILINEAR) self.employee_image.set_from_pixbuf(pixbuf) else: self.employee_image.set_from_animation(pixbuf) # Set animation. else: self.employee_image.set_from_image(None,None) if self.true(self.config.get("gtk", "department_jobs", False)): self.treeview_available_activities.update( departments=self.currently_selected_employee.departments() ) self.treeview_start_gang_available.update( departments=self.currently_selected_employee.departments() ) self.treeview_start_setup.update( departments=self.currently_selected_employee.departments() ) if employee.isClockedIn() and employee.isSuspended(): # Display resume button and hide everything else self.hideButtons(False) else: self.hideButtons() return False def hideButtons(self,hide=True): if hide: self.xml.get_widget("bttn_resume_tasks").set_sensitive(False) else: self.xml.get_widget("bttn_resume_tasks").set_sensitive(True) widgets = [ "recent_activities_treeview", "bttn_clock_out", "button_additional_information", "bttn_start_production_activity", "bttn_start_job_gang", "bttn_start_setup_gang", "bttn_start_setup_activity", "bttn_start_setup_gang", "bttn_start_indirect_activity", "bttn_end_activity", "bttn_lunch_in_out", "bttn_break_in_out" ] for x in widgets: self.xml.get_widget(x).set_sensitive(hide) def resumeTasks(self,widget=False): terminal_id = self.config.get("gtk", "terminal_id", "") self.currently_selected_employee.stopAllActiveLaborDtls( terminal_id ) #Prevents "Indirect" ganging self.currently_selected_employee.resumeAllLaborDtls( terminal_id ) self.clearAndResetGui() def startIndirect(self, indirect_code = None, indirect_serial_num = None): assert self.currently_selected_employee assert (indirect_code or indirect_serial_num) and not (indirect_code and indirect_serial_num) # If code is passed we need to get the serialNum if indirect_code: try: indirect_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Indirect"])) except Exception, e: self.flatline(e) try: indirect = indirect_factory.setIndirectByCode(indirect_code) except SndcsIndirectError: self.alertBox("WARNING", "OK", "'%s' is an invalid Indirect code." % (indirect_code)) self.clearAndResetGui() return except Pyro.errors.ConnectionClosedError, e: self.flatline(e) indirect_id = indirect.serialNum() indirect.disconnect() else: indirect_id = indirect_serial_num # Check if this is LUNCH or BREAK if indirect_code == "LUNCH": terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.log_on_off_lunch_break(type=1, terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.clearAndResetGui() elif indirect_code == "BREAK": terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.log_on_off_lunch_break(type=2, terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.clearAndResetGui() else: # Check if the user is ganged. If so, we need to run the end_activity_dynamic screen and then start the indirect try: is_ganged = self.currently_selected_employee.isGanged() except Pyro.errors.ConnectionClosedError, e: self.flatline(e) if is_ganged: self.createEndActivityDynamic(indirect_id = indirect_id) else: terminal_id = self.config.get("gtk", "terminal_id", False) try: self.currently_selected_employee.startIndirect(indirect_id, terminal_id = terminal_id) except SndcsIndirectError: self.alertBox("INFO", "OK", "Invalid indirect activity. Please try again.") except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.clearAndResetGui() def alertBox(self, type, buttons, message): if type == "INFO": message_type = gtk.MESSAGE_INFO elif type == "WARNING": message_type = gtk.MESSAGE_WARNING elif type == "QUESTION": message_type = gtk.MESSAGE_QUESTION elif type in ["NON_FATAL_ERROR", "ERROR"]: message_type = gtk.MESSAGE_ERROR else: raise ValueError("Invalid message type '%s'." % (type)) if buttons == "NONE": button_type = gtk.BUTTONS_NONE elif buttons == "OK": button_type = gtk.BUTTONS_OK elif buttons == "CLOSE": button_type = gtk.BUTTONS_CLOSE elif buttons == "CANCEL": button_type = gtk.BUTTONS_CANCEL elif buttons == "YES_NO": button_type = gtk.BUTTONS_YES_NO elif buttons == "OK_CANCEL": button_type = gtk.BUTTONS_OK_CANCEL else: raise ValueError("Invalid button type '%s'." % (buttons)) #TODO: log to file window_main = self.xml.get_widget("window_main") dialog = gtk.MessageDialog(window_main, gtk.DIALOG_DESTROY_WITH_PARENT, message_type, button_type, message) resp = dialog.run() dialog.destroy() return resp def toggleAvailableToCurrentTreeViewRows(self, source_treeview, dest_treeview, paths_to_move): source_model = source_treeview.model dest_model = dest_treeview.model rowrefs = [] for path in paths_to_move: rowrefs.append(gtk.TreeRowReference(source_model, path)) for ref in rowrefs: path = ref.get_path() iter = source_model.get_iter(path) row = source_model[iter] dest_treeview.model.append(row) source_treeview.model.remove(iter) def autoToggleAvailableToCurrentTreeViewRows(self, treeview1, treeview2, data_to_move): """ Just like toggleAvailableToCurrentTreeViewRows but it will automatically figure out which treeview to move from <--> to """ from_list = None to_list = None iter = self.find_liststore_iter_from_data(treeview1.model, data_to_move) if iter: from_list = treeview1 to_list = treeview2 else: iter = self.find_liststore_iter_from_data(treeview2.model, data_to_move) if iter: from_list = treeview2 to_list = treeview1 if not iter: self.alertBox("WARNING", "OK", "Could not toggle the job. The operation may be completed.") return if from_list and to_list: path = from_list.model.get_path(iter) self.toggleAvailableToCurrentTreeViewRows(from_list, to_list, (path,)) def clearAndResetGui(self): if self.currently_selected_employee: try: self.currently_selected_employee.disconnect() except Pyro.errors.ProtocolError: pass #Already Disconnected self.currently_selected_employee = None self.currently_selected_start_gang_current_activity = None self.barcode_command = "" self.job_gang_type = "" self.continue_activities_dynamic = [] self.end_activities_dynamic = [] self.start_activities_dynamic = [] self.start_indirect_dynamic = None self.continue_activities_dynamic_widgets = [] self.end_activities_dynamic_widgets = [] self.start_activities_dynamic_widgets = [] self.switchPageByName("EmployeeList") self.employee_image.set_from_image(None,None) self.hideButtons(True) #Set the buttons to be "sensitive" again if self.true(self.config.get("gtk", "department_jobs", False)): self.treeview_available_activities.update() #In case the available activities were changed. self.treeview_start_gang_available.update() self.treeview_start_setup.update() def createEndActivityDynamic(self, start = None, end = None, indirect_id = None): """ This function will handle everything needed to run the dynamic end activity screen. You can pass it either an array of operation id's to start or end (but not both). If both are None all current jobs will just be closed. This would be used in situations like clock out. Here's the logic... We can always get the employees current activities from the database so all we need to know is what jobs we are stopping or what jobs we are continuing/starting. If we know what jobs we are 'stopping' we can safely assuming that we are continuing everything else (if there is anything else). If we know what jobs we are 'continuing/starting' then anything we are currently logged into that *is not* in that list we are stopping. Anything that we are logged into that *is* in that list we are continuing. And anything in that list that we are not currently logged onto is a new item. The reason that we need the option to only pass items to end is that we may only know the jobnum/asm/op of somthing that we want to end and may not necessarily easily know every other job that we are working on and want to continue on. I'll shut up now. """ assert self.currently_selected_employee assert not (start and end) # Clean up everything from last time self.destroyEndActivityScreen() if start is None and end is None: # stop everything and start the indirect activity end_activities = [] try: activities = self.currently_selected_employee.getActivities() except Pyro.errors.ConnectionClosedError, e: self.flatline(e) try: dtl_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["LaborDtl"])) except Exception, e: self.flatline(e) for x in activities: if x["labordtl_id"]: try: dtl = dtl_factory.setLaborDtlBySerialNum(x["labordtl_id"]) except SndcsLaborDtlError: self.alertBox("WARNING", "OK", "'%s' is an invalid LaborDtl ID." % (x["labordtl_id"])) raise end_activities.append(dtl.getDataDict()) dtl.disconnect() try: indirect_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Indirect"])) except Exception, e: self.flatline(e) try: indirect = indirect_factory.setIndirectBySerialNum(indirect_id) except SndcsIndirectError: self.alertBox("WARNING", "OK", "'%s' is an invalid Indirect ID." % (indirect_id)) raise except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.end_activities_dynamic = end_activities try: self.start_indirect_dynamic = indirect.getDataDict() indirect.disconnect() except Pyro.errors.ConnectionClosedError, e: self.flatline(e) #current_activities = self.currently_selected_employee.getActivities() terminal_id = self.config.get("gtk", "terminal_id", False) # Find out if we are on any production activities currently #prod_activities = [x for x in current_activities if x["type"] == "production"] try: prod_activities = self.currently_selected_employee.getActiveProductionAndSetupLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.flatline(e) if start is not None: # If not working on any production activities, just end everything # (in case we were on an indirect) and start everything passed if not prod_activities: to_start = [] for x in start: to_start.append({"operation":x, "indirect":None, "job_type":self.job_gang_type}) try: self.currently_selected_employee.endAllLaborDtl(terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) try: self.currently_selected_employee.startStopContinueLaborDtls(starts = to_start, terminal_id = terminal_id, jobType = self.job_gang_type) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.clearAndResetGui() return # First we will see if we are stopping or continuing for x in prod_activities: found_match = 0 for y in start: if x["operation_id"] == y: found_match = 1 break if found_match: # We are continuing self.continue_activities_dynamic.append(x) else: # We are stopping self.end_activities_dynamic.append(x) # Now we will see what we are starting fresh for x in start: found_match = 0 for y in prod_activities: if x == y["operation_id"]: found_match = 1 break if found_match: # We are continuing... we already took care of this... skip continue else: # We are starting new self.start_activities_dynamic.append(x) elif end is not None: # See if we are stopping or continuing for x in prod_activities: found_match = 0 for y in end: if x["operation_id"] == y: found_match = 1 break if found_match: # We are ending self.end_activities_dynamic.append(x) else: # We are continuing self.continue_activities_dynamic.append(x) #If we are only continuing a single activity and not starting or stopping others than it means that the user selected the same job they were already on... and that really means they want to end it. (Probably only got here if force_end_activity=True) if len(self.continue_activities_dynamic) == 1 and not self.end_activities_dynamic and not self.start_activities_dynamic: if self.true(self.config.get("gtk", "force_end_activity", False)): self.end_activities_dynamic = self.continue_activities_dynamic self.continue_activities_dynamic = [] self.createEndActivityScreen() self.switchPageByName("EndActivityDynamic") return else: terminal_id = self.config.get("gtk", "terminal_id", "") operation_id = self.continue_activities_dynamic[0] description = self.continue_activities_dynamic[1] try: self.currently_selected_employee.startProduction(operation_id, terminal_id = terminal_id) self.addCachedJob(operation_id,description) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.clearAndResetGui() return #If we are only continuing a single activity (and starting others) then there is no need to display the end_activity screen because there is nothing the user could change about it (has to be 100%) if not self.end_activities_dynamic and len(self.continue_activities_dynamic) == 1: to_continue = [] to_start = [] to_continue.append({"labordtl":self.continue_activities_dynamic[0], "activity_percentage":100.0}) for x in self.start_activities_dynamic: to_start.append({"operation":x, "indirect":None, "job_type":self.job_gang_type}) try: self.addListToCachedJobs( to_start ) self.currently_selected_employee.startStopContinueLaborDtls(starts = to_start, continues = to_continue, terminal_id = terminal_id, jobType = self.job_gang_type) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) self.clearAndResetGui() return else: self.createEndActivityScreen() self.switchPageByName("EndActivityDynamic") def endActivityDynamicFocusIn(self, widget, event, vadj, hadj): # TODO: for some reason this doesn't work when first taken to the end # activity screen. The alloc.x and alloc.y come through as -1 for the # activity percentage on the very first focus in. Can switch to a # window and switch back and it will scroll (if the activty percentage # widget is off screen) or you can tab the first time and it will # scroll but not the very first time the end activity screen is # displayed. The focus event must occur before all ofthe layout is done # or something and I'm not sure how to fix it. alloc = widget.get_allocation() if alloc.y < vadj.value or alloc.y > vadj.value + vadj.page_size: vadj.set_value(min(alloc.y, vadj.upper-vadj.page_size)) if alloc.x < hadj.value or alloc.x > hadj.value + hadj.page_size: hadj.set_value(min(alloc.x, hadj.upper-hadj.page_size)) def createEndActivityScreen(self): scrap_codes = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["ScrapCode"])) hbox_main = self.xml.get_widget("hbox_end_activity_dynamic") self.frame_ending = gtk.Frame("Ending / Continuing") self.frame_starting = gtk.Frame("Starting") options_x = gtk.EXPAND | gtk.SHRINK options_y = gtk.SHRINK | gtk.FILL hbox_main.pack_start(self.frame_ending, True, True, 0) hbox_main.pack_start(self.frame_starting, True, True, 0) table = gtk.Table(1, 6, False) table.set_col_spacings(6) self.frame_ending.add(table) table.show() column_headings = ["Activity", "%", "Curr Comp", "Qty Comp" ] self.job_entry = [ 0, [] ] if self.true(self.config.get("gtk","scrap_codes", False)): for x in scrap_codes.getScrapCodes(): column_headings.extend(["" + x["description"] + ""]) else: column_headings.extend(["Scrap"]) if self.true(self.config.get("gtk","show_notes", False)): column_headings.extend(["Notes"]) column_headings.extend(["Comp?"]) i = 0 for heading in column_headings: label = gtk.Label() label.set_markup(heading) table.attach(label, i, i+1, 0, 1, options_x, options_y, 0, 5) label.show() i += 1 scrolled_window = self.xml.get_widget("scrolledwindow_end_activity_dynamic") vadj = scrolled_window.get_vadjustment() hadj = scrolled_window.get_hadjustment() # Add the rows for all ending activities j = 1 for x in self.end_activities_dynamic: row_of_widgets = {} activity_desc = gtk.Label(x["formattedDescription"]) self.attachHighlightToWidget(activity_desc) table.attach(activity_desc, 0, 1, j, j+1, gtk.FILL, options_y, 3, 0) activity_desc.set_alignment(0, 0.5) activity_desc.show() row_of_widgets["activity_desc"] = activity_desc activity_percentage_adjustment = gtk.Adjustment(0.0, 0.0, 100.0, 0.1, 0.1, 100.0) activity_percentage = gtk.SpinButton(activity_percentage_adjustment, 0.1, 1) activity_percentage.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) table.attach(activity_percentage, 1, 2, j, j+1, options_x, options_y, 0, 0) activity_percentage.show() self.attachHighlightToWidget(activity_percentage) row_of_widgets["activity_percentage"] = activity_percentage curr_complete = gtk.Label(x["currentComplete"]) table.attach(curr_complete, 2, 3, j, j+1, gtk.FILL, options_y, 3, 0) curr_complete.set_alignment(0.5, 0.5) curr_complete.show() qty_complete = gtk.Entry() qty_complete.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) self.attachHighlightToWidget(qty_complete) qty_complete.set_size_request(50, -1) desc = x["unitOfMeasure"] if desc is None: desc = "" uofm_desc = gtk.Label(desc) hbox = gtk.HBox(spacing=3) hbox.pack_start(qty_complete) hbox.pack_start(uofm_desc) table.attach(hbox, 3, 4, j, j+1, options_x, options_y, 5, 0) qty_complete.show() uofm_desc.show() hbox.show() row_of_widgets["qty_complete"] = qty_complete y = 4 if not self.true(self.config.get("gtk","scrap_codes", False)): scrap = gtk.Entry() scrap.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) self.attachHighlightToWidget(scrap) scrap.set_size_request(50, -1) table.attach(scrap, y, y+1, j, j+1, options_x, options_y, 5, 0) scrap.show() row_of_widgets["scrap"] = scrap y += 1 else: for x in scrap_codes.getScrapCodes(): scrap = gtk.Entry() scrap.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) scrap.set_size_request(50,-1) self.attachHighlightToWidget(scrap) table.attach(scrap, y, y+1, j, j+1, options_x, options_y, 5, 0) scrap.show() row_of_widgets["scrap_code%s" % x["code"]] = scrap y += 1 if self.true(self.config.get("gtk","show_notes", True)): notes = gtk.Entry() notes.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) self.attachHighlightToWidget(notes) table.attach(notes, y, y+1, j, j+1, options_x, options_y, 5, 0) notes.show() row_of_widgets["notes"] = notes y+=1 operation_complete = gtk.CheckButton() operation_complete.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) if x["operationComplete"]: operation_complete.set_active(True) self.attachHighlightToWidget(operation_complete) table.attach(operation_complete, y, y+1, j, j+1, options_x, options_y, 0, 0) operation_complete.show() row_of_widgets["operation_complete"] = operation_complete self.end_activities_dynamic_widgets.append(row_of_widgets) j += 1 # Add the rows for all continuing activities for x in self.continue_activities_dynamic: row_of_widgets = {} activity_desc = gtk.Label(x["formattedDescription"]) table.attach(activity_desc, 0, 1, j, j+1, gtk.FILL, options_y, 3, 0) activity_desc.set_alignment(0, 0) activity_desc.show() row_of_widgets["activity_desc"] = activity_desc activity_percentage_adjustment = gtk.Adjustment(0.0, 0.0, 100.0, 0.1, 0.1, 100.0) activity_percentage = gtk.SpinButton(activity_percentage_adjustment, 0.1, 1) activity_percentage.connect('focus_in_event', self.endActivityDynamicFocusIn, vadj, hadj) self.attachHighlightToWidget(activity_percentage) table.attach(activity_percentage, 1, 2, j, j+1, options_x, options_y, 0, 0) activity_percentage.show() row_of_widgets["activity_percentage"] = activity_percentage self.continue_activities_dynamic_widgets.append(row_of_widgets) j += 1 # Calculate the average percentage and set the spin buttons end_and_continue_widgets = (self.end_activities_dynamic_widgets + self.continue_activities_dynamic_widgets) num = len(end_and_continue_widgets) if num: j = 0 # Use the calculated percentage for every one except the last one # For the last one figure out what's left over #percentage = (math.floor(((100.0/num)*10)+0.5)) / 10 #last_percentage = 100.0 -(percentage * (num - 1)) percentage, last_percentage = self.calculateDefaultActivityPercentages(len(end_and_continue_widgets)) for x in end_and_continue_widgets: if j == num - 1: x["activity_percentage"].set_value(last_percentage) self.attachHighlightToWidget(x["activity_percentage"]) break x["activity_percentage"].set_value(percentage) j += 1 # Add the rows for all starting activities j = 1 table_start = gtk.Table() self.frame_starting.add(table_start) table_start.show() if self.start_activities_dynamic: for x in self.start_activities_dynamic: row_of_widgets = {} try: operation_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Operation"])) except Exception, e: self.flatline(e) try: operation = operation_factory.setOperationBySerialNum(x) except SndcsOperationError: self.alertBox("WARNING", "OK", "'%s' is an invalid Operation ID." % (x)) raise activity_desc = gtk.Label(operation.formattedDescription()) operation.disconnect() table_start.attach(activity_desc, 0, 1, j, j+1, gtk.FILL, options_y, 3, 3) activity_desc.set_alignment(0, 0) activity_desc.show() row_of_widgets["activity_desc"] = activity_desc self.start_activities_dynamic_widgets.append(row_of_widgets) j += 1 elif self.start_indirect_dynamic: row_of_widgets = {} activity_desc = gtk.Label(self.start_indirect_dynamic["formattedDescription"]) table_start.attach(activity_desc, 0, 1, j, j+1, gtk.FILL, options_y, 3, 0) activity_desc.set_alignment(0, 0) activity_desc.show() row_of_widgets["activity_desc"] = activity_desc self.start_activities_dynamic_widgets.append(row_of_widgets) j += 1 self.frame_ending.show() self.frame_starting.show() hbox_main.show() def destroyEndActivityScreen(self): if self.frame_ending: self.frame_ending.destroy() self.frame_ending = None if self.frame_starting: self.frame_starting.destroy() self.frame_starting = None self.end_activities_dynamic = [] self.start_activities_dynamic = [] self.continue_activities_dynamic = [] self.start_indirect_dynamic = None self.end_activities_dynamic_widgets = [] self.start_activities_dynamic_widgets = [] self.continue_activities_dynamic_widgets = [] def validateEndActivityDynamic(self): # TODO: Make sure the qty_complete and qty_scrap are valid numbers for x in self.end_activities_dynamic_widgets: qty_complete = x["qty_complete"].get_text().strip() try: qty_scrap = x["qty_scrap"].get_text().strip() except: qty_scrap = False if qty_complete: try: float(qty_complete) except ValueError, e: raise EndActivityQtyCompleteError(qty_complete) if qty_scrap: try: float(qty_scrap) except ValueError, e: raise EndActivityQtyScrapError(qty_scrap) # Make sure the sum of all percentages total 100% spin_total = 0.0 for x in self.end_activities_dynamic_widgets: spin_total += x["activity_percentage"].get_value() for x in self.continue_activities_dynamic_widgets: spin_total += x["activity_percentage"].get_value() spin_total = int(math.floor((spin_total*10) + 0.5)) if spin_total <> 1000: raise EndActivityPercentageError("Percentages total %.1f%%\nPlease adjust percentages until they total 100%% and try again." % (spin_total/10.0)) def processEndActivityDynamic(self, terminal_id = None): stops = [] scrap_codes = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["ScrapCode"])) for x in zip(self.end_activities_dynamic, self.end_activities_dynamic_widgets): scrap_dict = {} if self.true(self.config.get("gtk","scrap_codes", False)): scrap_count=0 for z in scrap_codes.getScrapCodes(): try: scrap_dict.update({"%s" % z["code"]: int( x[1]["scrap_code%s" % z["code"]].get_text() )}) scrap_count += int( x[1]["scrap_code%s" % z["code"]].get_text().strip() ) except ValueError: pass else: try: scrap_count = int( x[1]["scrap"].get_text().strip() ) except ValueError: scrap_count = 0 if self.true(self.config.get("gtk","show_notes", True)): notes = x[1]["notes"].get_text() else: notes = None if self.true(self.config.get("gtk","qty_complete_warning",False)): labor_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["LaborDtl"])) labor = labor_factory.setLaborDtlBySerialNum(x[0]["labordtl_id"]) labor_data = labor.getDataDict() labor.disconnect() try: qty_complete = float(x[1]["qty_complete"].get_text().strip()) except: qty_complete = 0 if qty_complete and ( ( labor_data["jobhed_qty_required"] - labor_data["currentComplete"] ) < qty_complete ): if self.alertBox(type = "QUESTION", buttons = "YES_NO", message = "You have entered more completed than required for '%s' is this correct?" % labor_data["formattedDescription"] ) == gtk.RESPONSE_NO: return True stops.append({"labordtl":x[0], "activity_percentage":x[1]["activity_percentage"].get_value(), "qty_complete":x[1]["qty_complete"].get_text().strip(), "qty_scrap": scrap_count, "notes":notes, "operation_complete":x[1]["operation_complete"].get_active(), "completed_by":self.currently_selected_employee.serialNum(), "scrap_codes": scrap_dict }) continues = [] for x in zip(self.continue_activities_dynamic, self.continue_activities_dynamic_widgets): continues.append({"labordtl":x[0], "activity_percentage":x[1]["activity_percentage"].get_value()}) starts = [] if self.start_activities_dynamic: for x in self.start_activities_dynamic: starts.append({"operation":x, "indirect":None, "job_type":self.job_gang_type}) elif self.start_indirect_dynamic: starts.append({"operation":None, "indirect":self.start_indirect_dynamic, "job_type":"Indirect"}) try: self.currently_selected_employee.startStopContinueLaborDtls(starts = starts, stops = stops, continues = continues, terminal_id = terminal_id) self.addListToCachedJobs( starts ) except Pyro.errors.ConnectionClosedError, e: self.flatline(e) def processBarcode(self): if not self.barcode_command: log.debug("Empty barcode command.") return log.debug("Processing barcode data: %s", self.barcode_command) self.set_busy_status() # Parse out the command and the data from the barcode separator_position = self.barcode_command.find(self.separator_signifier) if separator_position == -1: start_signifier = self.barcode_command[0] data = self.barcode_command[1:] command = None end_signifier = None else: start_signifier = self.barcode_command[0] data = self.barcode_command[1:separator_position] command = self.barcode_command[separator_position+1:] end_signifier = None if command: if command[-1] == start_signifier: end_signifier = command[-1] command = command[:-1] # Selection if start_signifier == self.main_selection_signifier: clock_in = False try: employee_id = int(data) except: # probably not an integer self.clear_busy_status() log.warning("'%s' is an invalid employee number.", data) self.alertBox("WARNING", "OK", "'%s' is an invalid employee number. Please try again." % (data)) self.barcode_command = "" return employee = self.getEmployeeProxy(employee_number = employee_id) if not employee: self.barcode_command = "" self.clear_busy_status() return if command == self.clockin_and_select_selection_signifier: clock_in = self.selectEmployee(employee_proxy = employee) #if employee.isSuspended() and not command == self.resume_selection_signifier: # self.clear_busy_status() # self.barcode_command == "" # self.alertBox("WARNING", "OK", "You must 'Resume' before any other action") # self.clearAndResetGui() # return elif command == self.lunch_selection_signifier: terminal_id = self.config.get("gtk", "terminal_id", "") try: employee.log_on_off_lunch_break(1, terminal_id = terminal_id) except SndcsEmployeeError, e: self.alertBox("WARNING", "OK", str(e)) else: self.clearAndResetGui() elif command == self.break_selection_signifier: terminal_id = self.config.get("gtk", "terminal_id", "") try: employee.log_on_off_lunch_break(2, terminal_id = terminal_id) except SndcsEmployeeError, e: self.alertBox("WARNING", "OK", str(e)) else: self.clearAndResetGui() elif command == self.resume_selection_signifier: try: employee.resumeAllLaborDtls() # Resume LaborDtl's except SndcsEmployeeError, e: self.alertBox("WARNING", "OK", str(e)) else: self.clearAndResetGui() elif command == self.clockout_selection_signifier: # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.alertBox("INFO", "OK", "You must end all production activities before clocking out.") try: stops = [x["operation_id"] for x in employee.getActiveProductionLaborDetails()] # Get list of activities to end except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.currently_selected_employee = employee # Set them as the currently selected employee self.createEndActivityDynamic(end = stops) # Run the end activity screen self.barcode_command = "" self.clear_busy_status() return # so we don't disconnect() the employee (or clock out) if we were to drop off the end of this function terminal_id = self.config.get("gtk", "terminal_id", "") try: if not employee.pinNumber(): employee.clockOut(terminal_id = terminal_id) self.clearAndResetGui() else: self.employee=employee self.showPinPad() except SndcsEmployeeError, e: self.alertBox("WARNING", "OK", str(e)) employee_id = employee.serialNum() found_them = self.treeview_active_employees.scroll_to_employee(employee_id) # Make sure the employee is visible if found_them is None and command == self.clockin_and_select_selection_signifier: # Employee clocked in but they are filtered out of the model... try to add them by setting isClockedIn column of model to 1 self.treeview_active_employees.clock_in_employee(employee_id) self.treeview_active_employees.scroll_to_employee(employee_id) # Try again if clock_in: employee.disconnect() # Action if start_signifier == self.main_action_signifier: # Make sure the employee has already selected themselves if not self.currently_selected_employee: self.clear_busy_status() self.alertBox("WARNING", "OK", "There is no selected employee. Please select yourself first and try again.") self.barcode_command = "" return if command == self.indirect_action_signifier: try: indirect_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Indirect"])) except Exception, e: self.clear_busy_status() self.flatline(e) try: indirect = indirect_factory.setIndirectByCode(data) except SndcsIndirectError: self.clear_busy_status() self.alertBox("WARNING", "OK", "'%s' is an invalid Indirect code." % (data)) self.clearAndResetGui() return except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) indirect_id = indirect.serialNum() indirect.disconnect() # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(indirect_id = indirect_id) self.clear_busy_status() return self.startIndirect(indirect_serial_num = indirect_id) self.clearAndResetGui() if command == self.production_action_signifier: # Get the operation try: operation_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Operation"])) except Exception, e: self.clear_busy_status() self.flatline(e) try: operation = operation_factory.setOperationByJobAssemblyOperation(data) except SndcsOperationError: self.clear_busy_status() self.alertBox("WARNING", "OK", "Could not find the job '%s'. Make sure the job exists and try again." % (data)) self.barcode_command = "" return operation_id = operation.serialNum() operation.disconnect() # TODO: Check if the operation is completed and if it is, check from the configuration if we are allowed to clock onto completed jobs # If ganging, go to gang screen try: if self.currently_selected_employee.isGanged(): self.job_gang_type = "Production" self.switchPageByName("StartJobGang") except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) # If we are on the job ganging screen we do not want to simply start the production activity, # we want to move values from available to current or vice versa if self.getCurrentPage() == "StartJobGang": self.autoToggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_current, self.treeview_start_gang_available, operation_id) else: # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(start = [operation_id]) self.clear_busy_status() return terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.startProduction(operation_id, terminal_id = terminal_id) self.addListToCachedJobs( [ operation_id ] ) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() if command == self.end_production_action_signifier: #TODO pass if command == self.setup_action_signifier: # Get the operation try: operation_factory = Pyro.core.getProxyForURI(self.formatPyronameString(self.namespace, ["Operation"])) except Exception, e: self.clear_busy_status() self.flatline(e) try: operation = operation_factory.setOperationByJobAssemblyOperation(data) except SndcsOperationError: self.clear_busy_status() self.alertBox("WARNING", "OK", "Could not find the job '%s'. Make sure the job exists and try again." % (data)) self.barcode_command = "" return # TODO: Check if the operation is completed and if it is, check from the configuration if we are allowed to clock onto completed jobs # TODO: If ganging, go to gang screen operation_id = operation.serialNum() operation.disconnect() terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.startProduction(operation_id, jobType="Setup", terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() if command == self.end_setup_action_signifier: #TODO pass # Command if start_signifier == self.main_command_signifier: if data.lower() in ["exit", "quit"]: gtk.main_quit() elif data.lower() in ["about", "version"]: self.showDialogAbout() elif data.lower() in ["admin",]: self.showWindowAdmin() #elif data.lower() in ["refresh", "reload"]: # self.refresh() self.clear_busy_status() self.barcode_command = "" def find_liststore_iter_from_data(self, liststore, data, column_to_match=0): iter = liststore.get_iter_first() while iter: if liststore.get_value(iter, column_to_match) == data: return iter iter = liststore.iter_next(iter) return None def on_window_pininterface_delete_event(self, widget, event): self.hidePinPad() return True def on_window_admin_key_press_event(self, widget, event): pass def on_window_main_key_press_event(self, widget, event): # We want to have hot keys that will respond no matter what the user is doing. if event.type == gtk.gdk.KEY_PRESS: if event.keyval == gtk.keysyms.Escape: self.set_busy_status() self.clearAndResetGui() self.clear_busy_status() elif event.keyval == gtk.keysyms.Return: self.processBarcode() return True elif event.keyval == gtk.keysyms.BackSpace: self.barcode_command = self.barcode_command[:-1] else: try: string = chr(event.keyval) except: string = "" self.barcode_command += string return False def on_treeview_employee_list_button_press_event(self, widget, event): # Check from the config file if "Easy Select Employee" is enabled if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: if self.true(self.config.get("gtk", "easy_employee_selection", True)): self.set_busy_status() x = int(event.x) y = int(event.y) path = widget.get_path_at_pos(x, y) if path is None: self.clear_busy_status() return False path, col, cellx, celly = path model = widget.get_model() iter = model.get_iter(path) employee_id = model.get_value(iter, 0) self.selectEmployee(employee_id = employee_id) self.clear_busy_status() def on_bttn_clock_out_clicked(self, widget): assert self.currently_selected_employee self.set_busy_status() # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.clear_busy_status() self.alertBox("INFO", "OK", "You must end all production activities before clocking out.") self.set_busy_status() try: stops = [x["operation_id"] for x in self.currently_selected_employee.getActiveProductionLaborDetails()] # Get list of activities to end except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.createEndActivityDynamic(end = stops) # Run the end activity screen self.clear_busy_status() return terminal_id = self.config.get("gtk", "terminal_id", "") try: if self.currently_selected_employee.pinNumber(): self.employee = self.currently_selected_employee self.showPinPad() self.clear_busy_status() else: self.currently_selected_employee.clockOut(terminal_id = terminal_id) self.clearAndResetGui() self.clear_busy_status() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) def hideShowKeybd(self,yesorno = True): if self.true(self.config.get("gtk", "osd_keyboard", False)): obj = self.xml.get_widget("osdbutton") obj.set_active(yesorno) def reset_gang_current_to_available(self): """ Move previously selected activities back to available """ model = self.treeview_start_gang_current.view.get_model() paths = [] iter = model.get_iter_first() while iter: paths.append(model.get_path(iter)) iter = model.iter_next(iter) if paths: self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_current, self.treeview_start_gang_available, paths) def on_button_start_setup_gang_clicked(self,widget): # NOTE: You cannot setup gang if there are productions jobs active... if [ x for x in self.currently_selected_employee.getActiveLaborDetails() if x["jobType"] == "Production" ]: self.alertBox("INFO", "OK", "Please end all production jobs before setup ganging.") return True self.job_gang_type = "Setup" self.switchPageByName("StartJobGang") def on_notebook_main_switch_page(self, widget, page, page_num): self.xml.get_widget("searchCriteria").set_text("") if page_num == 0: das_modell = gtk.ListStore(str) das_modell.append([ "Employee Name" ]) das_modell.append([ "Activity" ]) self.name = "Employee Name" obj = self.xml.get_widget("rlistLookForIn") obj.set_model(das_modell) obj.set_active(0) self.showSearchDialog() self.hideShowKeybd(True) elif page_num == 1: self.hideSearchDialog() self.hideShowKeybd(False) def on_notebook_selected_employee_switch_page(self, widget, page, page_num): self.xml.get_widget("searchCriteria").set_text("") # Move all previously selected current activities back to available # This is probably overkill to put this here but it should catch all occurances # Since we are sharing the same model with multiple treeviews we need to make # sure and always reset so the models are pristine self.reset_gang_current_to_available() if page_num == 0: #TODO: determine from the config file what buttons should be displayed self.hideSearchDialog() self.hideShowKeybd(False) elif page_num == 1: #self.treeview_available_activities.populate() self.showSearchDialog() das_modell = gtk.ListStore(str) das_modell.append([ "Job" ]) das_modell.append([ "Description" ]) das_modell.append([ "Priority" ]) das_modell.append([ "Department" ]) self.name = "Job" obj =self.xml.get_widget("rlistLookForIn") obj.set_model(das_modell) obj.set_active(1) search_widget = self.xml.get_widget("searchCriteria") if self.true(self.config.get("gtk","persistent_search", False)): self.treeview_available_activities.search_criteria = self.config.get("gtk","search_criteria", "") self.treeview_available_activities.column_number = getColumnNumberForName(self.treeview_available_activities.view,"Description") + 1 search_widget.set_text(self.config.get("gtk","search_criteria", "")) self.treeview_available_activities.update() self.hideShowKeybd(True) elif page_num == 2: assert self.currently_selected_employee #self.treeview_start_gang_current.clear() das_modell = gtk.ListStore(str) das_modell.append([ "Job" ]) das_modell.append([ "Description" ]) das_modell.append([ "Priority" ]) das_modell.append([ "Department" ]) self.name = "Description" search_widget = self.xml.get_widget("searchCriteria") obj = self.xml.get_widget("rlistLookForIn") obj.set_model(das_modell) obj.set_active(1) if self.true(self.config.get("gtk","persistent_search", False)): self.treeview_start_gang_available.column_number = getColumnNumberForName(self.treeview_start_gang_available.view,"Description") + 1 self.treeview_start_gang_available.search_criteria = self.config.get("gtk","search_criteria", "") search_widget.set_text(self.config.get("gtk","search_criteria", "")) self.treeview_start_gang_available.update() # If employee is on production activities, move them to the current activities list try: num_labordtls = [ x for x in self.currently_selected_employee.getActiveLaborDetails() if not x["jobType"] == "Indirect" ] except Pyro.errors.ConnectionClosedError, e: self.flatline(e) if num_labordtls: try: current_production = [x["operation_id"] for x in self.currently_selected_employee.getActiveLaborDetails()] except Pyro.errors.ConnectionClosedError, e: self.flatline(e) model = self.treeview_start_gang_available.model paths = [] for x in current_production: iter = self.find_liststore_iter_from_data(model, x) if iter: paths.append(model.get_path(iter)) if paths: self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_available, self.treeview_start_gang_current, paths) self.showSearchDialog() self.hideShowKeybd(True) elif page_num == 3: #self.treeview_start_setup.populate() self.showSearchDialog() das_modell = gtk.ListStore(str) das_modell.append([ "Job" ]) das_modell.append([ "Description" ]) self.name = "Job" obj = self.xml.get_widget("rlistLookForIn") obj.set_model(das_modell) obj.set_active(0) self.hideShowKeybd(True) elif page_num == 4: self.showSearchDialog() das_modell = gtk.ListStore(str) das_modell.append([ "Code" ]) das_modell.append([ "Description" ]) self.name = "Description" obj = self.xml.get_widget("rlistLookForIn") obj.set_model(das_modell) obj.set_active(1) self.hideShowKeybd(True) elif page_num == 7: self.hideSearchDialog() self.hideShowKeybd(True) def on_bttn_return_to_employee_list_clicked(self, widget): self.clearAndResetGui() def on_bttn_start_production_activity_clicked(self, widget): self.switchPageByName("StartProductionActivity") def on_bttn_start_job_gang_clicked(self, widget): if [ x for x in self.currently_selected_employee.getActiveLaborDetails() if x["jobType"] == "Setup" ]: self.alertBox("INFO", "OK", "Please end all setup jobs before job ganging.") return True self.job_gang_type = "Production" self.switchPageByName("StartJobGang") def on_bttn_start_setup_activity_clicked(self, widget): self.switchPageByName("StartSetupActivity") def on_bttn_start_indirect_activity_clicked(self, widget): self.switchPageByName("StartIndirectActivity") def on_bttn_end_activity_clicked(self, widget): assert self.currently_selected_employee self.set_busy_status() try: num_activities = self.currently_selected_employee.getNumberActiveLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) # If we are ganging go to the gang screen if num_activities > 1: self.job_gang_type = "Production" self.switchPageByName("StartJobGang") self.clear_busy_status() return False #TODO: If we are not logged onto anything the end activty button should be inactive and don't do anything if num_activities == 0: self.clear_busy_status() return False try: active_labordtl = self.currently_selected_employee.getActiveLaborDetails()[0] # Should only be one except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) # If indirect or setup activity return to idle if active_labordtl["jobType"] in [ "Indirect"]: terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.endAllLaborDtl(terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() return False # If single production go to end_activity_dynamic self.createEndActivityDynamic(end = [active_labordtl["operation_id"],]) self.switchPageByName("EndActivityDynamic") self.clear_busy_status() def on_bttn_lunch_in_out_clicked(self, widget): assert self.currently_selected_employee terminal_id = self.config.get("gtk", "terminal_id", "") self.set_busy_status() try: self.currently_selected_employee.log_on_off_lunch_break(1, terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() def on_bttn_break_in_out_clicked(self, widget): assert self.currently_selected_employee terminal_id = self.config.get("gtk", "terminal_id", "") self.set_busy_status() try: self.currently_selected_employee.log_on_off_lunch_break(2, terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() def on_button_indirect_cancel_clicked(self, widget): self.switchPageByName("SelectedEmployee") def on_button_indirect_ok_clicked(self, widget): assert self.currently_selected_employee model, selected = self.treeview_indirect_activity_codes.view.get_selection().get_selected_rows() if not selected: self.alertBox("INFO", "OK", "Please select an indirect activity and try again.") return iter = model.get_iter(selected[0]) indirect_id = model.get_value(iter, 0) self.set_busy_status() # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(indirect_id = indirect_id) self.clear_busy_status() return self.startIndirect(indirect_serial_num = indirect_id) self.clear_busy_status() def on_treeview_indirect_activity_codes_button_press_event(self, widget, event): assert self.currently_selected_employee if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: x = int(event.x) y = int(event.y) path = widget.get_path_at_pos(x, y) if path is None: return False path, col, cellx, celly = path model = widget.get_model() iter = model.get_iter(path) indirect_id = model.get_value(iter, 0) self.set_busy_status() # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(indirect_id = indirect_id) self.clear_busy_status() return self.startIndirect(indirect_serial_num = indirect_id) self.clear_busy_status() def on_button_available_production_cancel_clicked(self, widget): self.switchPageByName("SelectedEmployee") def on_button_available_production_ok_clicked(self, widget): assert self.currently_selected_employee # Make sure a row has been selected model, selected = self.treeview_available_activities.view.get_selection().get_selected_rows() if not selected: self.alertBox("INFO", "OK", "Please select a production activity from the list and try again.") return operations = [] for x in selected: iter = model.get_iter(x) operation_id = model.get_value(iter, 0) operations.append(operation_id) self.set_busy_status() # Check if the user is ganged. If so, we need to run the end_activity_dynamic screen and then start the single job. # Or is several operations were selected, run end_activity_dynamic try: is_ganged = self.currently_selected_employee.isGanged() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if is_ganged or len(operations) > 1: self.createEndActivityDynamic(start = operations) self.clear_busy_status() else: # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(start = operations) self.clear_busy_status() return terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.startProduction(operations[0], terminal_id = terminal_id) self.addListToCachedJobs([ operations[0] ] ) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() def on_treeview_available_activities_button_press_event(self, widget, event): # TODO: see if SNDCS_EZ_START_PRODUCTION_ACTIVITY is set in config file assert self.currently_selected_employee if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: x = int(event.x) y = int(event.y) path = widget.get_path_at_pos(x, y) if path is None: return False path, col, cellx, celly = path model = widget.get_model() iter = model.get_iter(path) operation_id = model.get_value(iter, 0) job_num = model.get_value(iter, 1) assembly_num = model.get_value(iter, 2) operation_num = model.get_value(iter, 3) description = model.get_value(iter, 4) self.set_busy_status() # Check if the user is ganged. If so, we need to run the end_activity_dynamic screen and then start the single job. try: is_ganged = self.currently_selected_employee.isGanged() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if is_ganged: self.createEndActivityDynamic(start = [operation_id]) self.clear_busy_status() else: # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)) and self.currently_selected_employee.getNumberActiveProductionLaborDetails(): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(start = [operation_id]) self.clear_busy_status() return terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.startProduction(operation_id, terminal_id = terminal_id) self.addCachedJob(operation_id,description) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() def on_treeview_start_setup_button_press_event(self, widget, event): # TODO: see if SNDCS_EZ_START_SETUP_ACTIVITY is set in config file assert self.currently_selected_employee if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: x = int(event.x) y = int(event.y) path = widget.get_path_at_pos(x, y) if path is None: return False path, col, cellx, celly = path model = widget.get_model() iter = model.get_iter(path) operation_id = model.get_value(iter, 0) self.set_busy_status() # TODO: Check if the user is ganged. If so, we need to run the end_activity_dynamic screen and then start the single setup (look at on_treeview_available_activities_button_press_event()) # TODO: Check if we are forcing the user to see the end activity screen terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.startProduction(operation_id, jobType = "Setup", terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() def on_button_setup_cancel_clicked(self, widget): self.switchPageByName("SelectedEmployee") def on_button_setup_ok_clicked(self, widget): assert self.currently_selected_employee # Make sure a row has been selected model, selected = self.treeview_start_setup.view.get_selection().get_selected_rows() if not selected: self.alertBox("INFO", "OK", "Please select a setup activity and try again.") return iter = model.get_iter(selected[0]) setup_id = model.get_value(iter, 0) self.set_busy_status() # Check if the user is ganged. If so, we need to run the end_activity_dynamic screen and then start the single setup (look at on_treeview_available_activities_button_press_event()) # TODO: Check if we are forcing the user to see the end activity screen terminal_id = self.config.get("gtk", "terminal_id", False) try: self.currently_selected_employee.startProduction(setup_id, jobType = "Setup", terminal_id = terminal_id) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() def on_button_start_gang_add_clicked(self, widget): model, selected = self.treeview_start_gang_available.view.get_selection().get_selected_rows() if not selected: self.alertBox("INFO", "OK", "Please select a available activity from the list and try again.") return self.set_busy_status() paths_to_move = [] for x in selected: item_number = model[x][0] for y in self.treeview_start_gang_current.model: if y[0] == item_number: # To prevent ganging on the same job. continue # model will be the TreeModelSort... we need to convert paths back to the parent model # We are two (possibly three depending on 'department_jobs') models deep parent --> filter --> sort tmpmodel = model path = tmpmodel.convert_path_to_child_path(x) while hasattr(tmpmodel.get_model(), "get_model"): tmpmodel = tmpmodel.get_model() path = tmpmodel.convert_path_to_child_path(path) paths_to_move.append(path) self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_available, self.treeview_start_gang_current, paths_to_move = paths_to_move) self.clear_busy_status() def on_treeview_start_gang_available_button_press_event(self, widget, event): # TODO: see if SNDCS_EZ_START_GANG_ACTIVITY is set in config file assert self.currently_selected_employee if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: x = int(event.x) y = int(event.y) path = widget.get_path_at_pos(x, y) if path is None: return False path, col, cellx, celly = path model = widget.get_model() iter = model.get_iter(path) self.set_busy_status() operation_id = model.get_value(iter,0) for y in self.treeview_start_gang_current.model: if y[0] == operation_id: # To prevent ganging on the same job. self.clear_busy_status() return # model will be the TreeModelSort... we need to convert paths back to the parent model # We are two (possibly three depending on 'department_jobs') models deep parent --> filter --> sort path = model.convert_path_to_child_path(path) while hasattr(model.get_model(), "get_model"): model = model.get_model() path = model.convert_path_to_child_path(path) self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_available, self.treeview_start_gang_current, (path,)) self.clear_busy_status() def on_treeview_start_gang_available_drag_data_get(self, widget, context, selection, targetType, eventTime): model, selected = widget.get_selection().get_selected_rows() operations = [] for x in selected: iter = model.get_iter(x) operation_id = model.get_value(iter, 0) operations.append(str(operation_id)) selection.set(selection.target, 8, ",".join(operations)) def on_treeview_start_gang_available_drag_data_received(self, widget, context, x, y, selection, targetType, time): selections = selection.data.split(",") model = self.treeview_start_gang_current.model paths = [] for x in selections: iter = self.find_liststore_iter_from_data(model, long(x)) if iter: paths.append(model.get_path(iter)) if paths: self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_current, self.treeview_start_gang_available, paths) def on_button_start_gang_remove_clicked(self, widget): model, selected = self.treeview_start_gang_current.view.get_selection().get_selected_rows() if not selected: self.alertBox("INFO", "OK", "Please select a current activity from the list and try again.") return self.set_busy_status() self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_current, self.treeview_start_gang_available, paths_to_move = selected) self.clear_busy_status() def on_treeview_start_gang_current_button_press_event(self, widget, event): # TODO: see if SNDCS_EZ_START_GANG_ACTIVITY is set in config file assert self.currently_selected_employee if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: x = int(event.x) y = int(event.y) path = widget.get_path_at_pos(x, y) if path is None: return False path, col, cellx, celly = path #model = widget.get_model() #iter = model.get_iter(path) # TODO: check if it is a valid operation #operation_id = model.get_value(iter, 0) #try: # operation = OperationFactory().getOperationBySerialNum(int(operation_id)) #except UnknownObjectError: # self.alertBox("INFO", "OK", "Invalid operation. Please try again.") self.set_busy_status() self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_current, self.treeview_start_gang_available, (path,)) self.clear_busy_status() def on_treeview_start_gang_current_drag_data_received(self, widget, context, x, y, selection, targetType, time): selections = selection.data.split(",") model = self.treeview_start_gang_available.model paths = [] for x in selections: check = False for y in self.treeview_start_gang_current.model: check = ( y[0] == long(x) ) == True if not check: iter = self.find_liststore_iter_from_data(model, long(x)) if iter: paths.append(model.get_path(iter)) if paths: self.toggleAvailableToCurrentTreeViewRows(self.treeview_start_gang_available, self.treeview_start_gang_current, paths) def on_treeview_start_gang_current_drag_data_get(self, widget, context, selection, targetType, eventTime): model, selected = widget.get_selection().get_selected_rows() operations = [] for x in selected: iter = model.get_iter(x) operation_id = model.get_value(iter, 0) operations.append(str(operation_id)) selection.set(selection.target, 8, ",".join(operations)) def on_button_start_gang_ok_clicked(self, widget): model = self.treeview_start_gang_current.view.get_model() #factory = OperationFactory() start = [] iter = model.get_iter_first() while iter: #try: # operation = factory.getOperationBySerialNum(model.get_value(iter, 0)) #except UnknownObjectError: # self.alertBox("INFO", "OK", "Invalid operation. Please try again.") operation_id = model.get_value(iter, 0) start.append(operation_id) iter = model.iter_next(iter) self.set_busy_status() self.createEndActivityDynamic(start) self.clear_busy_status() def on_button_start_gang_cancel_clicked(self, widget): self.switchPageByName("SelectedEmployee") def on_button_end_activity_dynamic_cancel_clicked(self, widget): self.destroyEndActivityScreen() self.switchPageByName("SelectedEmployee") def on_button_end_activity_dynamic_ok_clicked(self, widget): self.set_busy_status() try: self.validateEndActivityDynamic() except EndActivityPercentageError, e: self.clear_busy_status() self.alertBox("ERROR", "OK", str(e)) except EndActivityQtyCompleteError, e: self.clear_busy_status() self.alertBox("ERROR", "OK", "'%s' is not a valid entry for quantity complete. Please try again." % str(e)) except EndActivityQtyScrapError, e: self.clear_busy_status() self.alertBox("ERROR", "OK", "'%s' is not a valid entry for quantity scrap. Please try again." % str(e)) else: terminal_id = self.config.get("gtk", "terminal_id", "") if self.processEndActivityDynamic(terminal_id = terminal_id): self.clear_busy_status() # Probably only get here if qty_complete_warning is True the user wanted to cancel the completion return self.clearAndResetGui() self.clear_busy_status() def on_menu_main_help_about_activate(self, menu_item): self.showDialogAbout() def on_menu_main_view_admin_activate(self, menu_item): pass #NOTE: Comment out because win32client would complain on exit... #if menu_item.get_active(): # self.treeview_job_admin.populate() # self.showWindowAdmin() #else: # self.hideWindowAdmin() ### PIN NUMBER FUNCTIONS ### def backspacePinPad(self,widget = ""): entry_pinpad = self.xml.get_widget("entry_pinpad") pin_number = entry_pinpad.get_text() if pin_number: entry_pinpad.set_text(pin_number[:-1]) def clearPinPad(self,widget = False): entry_pinpad = self.xml.get_widget("entry_pinpad") if entry_pinpad.get_text() == "": window_pinpad = self.xml.get_widget("window_pininterface") window_pinpad.hide() self.employee = None self.clearAndResetGui() else: entry_pinpad.set_text("") def addToPinPad(self,widget,event = False): if not event: charToAdd = widget.get_label() else: if event.type == gtk.gdk.KEY_PRESS: if event.keyval == gtk.keysyms.Return: self.enterPinPad() return elif event.keyval == gtk.keysyms.BackSpace: self.backspacePinPad() return elif event.keyval == gtk.keysyms.Escape: self.clearPinPad() return else: value = self.convertAlphaToNum(event.keyval,True) if value: charToAdd = str(value) else: return entry_pinpad = self.xml.get_widget("entry_pinpad") entry_pinpad.set_text(entry_pinpad.get_text() + charToAdd) def enterPinPad(self,widget = ""): entry_pinpad = self.xml.get_widget("entry_pinpad") if not entry_pinpad.get_text() == self.employee.pinNumber(): self.hidePinPad() self.alertBox("WARNING", "OK", "Invalid PIN number!") self.employee = None self.clearAndResetGui() return terminal_id = self.config.get("gtk", "terminal_id", "") if self.employee.isClockedIn(): self.employee.clockOut(terminal_id = terminal_id) else: self.employee.clockIn(terminal_id = terminal_id) self.employee = None self.hidePinPad() self.clearAndResetGui() ### DIALOG ABOUT ### def showDialogAbout(self): dialog_about = self.xml.get_widget("dialog_about") dialog_about.show() def hideDialogAbout(self): dialog_about = self.xml.get_widget("dialog_about") dialog_about.hide() def on_dialog_about_delete_event(self, widget, event): widget.hide() return True def on_button_dialog_about_ok_clicked(self, widget): self.hideDialogAbout() return True ### DIALOG PIN PAD ### def showPinPad(self): window_pinpad = self.xml.get_widget("window_pininterface") window_main = self.xml.get_widget("window_main") window_pinpad.set_transient_for(window_main) # For win32, to keep pin pad on top window_pinpad.activate_focus() window_pinpad.set_keep_above(True) window_pinpad.set_position(gtk.WIN_POS_CENTER) window_pinpad.show() def hidePinPad(self): window_pinpad = self.xml.get_widget("window_pininterface") entry_pinpad = self.xml.get_widget("entry_pinpad") window_pinpad.hide() entry_pinpad.set_text("") ### WINDOW ADMIN ### def showWindowAdmin(self): window_admin = self.xml.get_widget("window_admin") window_admin.show() def hideWindowAdmin(self): window_admin = self.xml.get_widget("window_admin") window_admin.hide() def on_window_admin_delete_event(self, widget, event): menu_main_view_admin = self.xml.get_widget("menu_main_view_admin") menu_main_view_admin.set_active(False) widget.hide() return True def on_notebook_admin_switch_page(self, widget, page, page_num): self.xml.get_widget("searchCriteria_admin").set_text("") if page_num == 0: das_modell = gtk.ListStore(str) das_modell.append([ "Number" ]) das_modell.append([ "Item" ]) das_modell.append([ "Description" ]) self.admin_name = "Number" obj = self.xml.get_widget("rlistLookForIn_admin") obj.set_model(das_modell) obj.set_active(0) self.treeview_job_admin.restore() elif page_num == 1: das_modell = gtk.ListStore(str) das_modell.append(["(NONE)"]) self.admin_name = False #If self.name is False then don't search at all obj = self.xml.get_widget("rlistLookForIn_admin") obj.set_model(das_modell) obj.set_active(0) elif page_num == 2: das_modell = gtk.ListStore(str) das_modell.append([ "Name" ]) das_modell.append([ "Status" ]) self.admin_name = "Name" obj = self.xml.get_widget("rlistLookForIn_admin") obj.set_model(das_modell) obj.set_active(0) def on_menu_admin_file_new_activate(self, menu_item): # Find out which notebook page we are on notebook_admin = self.xml.get_widget("notebook_admin") page = notebook_admin.get_current_page() if page == 0: # Jobs # Create new job pass def on_recent_activities_treeview_row_activated(self,widget,iter,path): model = widget.get_model() operation_id = model[iter[0]][0] self.set_busy_status() # Check if we are forcing the user to see the end activity screen if self.true(self.config.get("gtk", "force_end_activity", False)): # If so, check if they are on any production activities and force them to end them if so try: num_production = self.currently_selected_employee.getNumberActiveProductionLaborDetails() except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) if num_production: self.createEndActivityDynamic(start = [operation_id]) self.clear_busy_status() return terminal_id = self.config.get("gtk", "terminal_id", "") try: self.currently_selected_employee.startProduction(operation_id, terminal_id = terminal_id) self.addListToCachedJobs( [ operation_id ] ) except Pyro.errors.ConnectionClosedError, e: self.clear_busy_status() self.flatline(e) self.clearAndResetGui() self.clear_busy_status() ### SCRAPCODE FUNCTIONS ### Might not need these def delete_scrapcode(self,event): log.info("%s [%s] scrap_code deleted/inactive", event.msg["code"], event.msg["description"] ) def add_scrapcode(self,event): log.info("%s [%s] scrap_code added", event.msg["code"], event.msg["description"] ) ### DEPARTMENT FUNCTIONS ### def delete_department(self,event): if self.config.get("gtk", "terminal_id", "")==event.msg["terminal_name"] and isinstance(self.treeview_active_employees.display_departments,list): self.treeview_active_employees.display_departments.remove( event.msg["department"] ) log.info("%s [%s] department deleted", event.msg["terminal_name"], event.msg["department"] ) self.treeview_active_employees.populate() def add_department(self,event): if self.treeview_active_employees.display_departments is None: self.treeview_active_employees.display_departments = [] if self.config.get("gtk", "terminal_id", "")==event.msg["terminal_name"] and isinstance(self.treeview_active_employees.display_departments,list): self.treeview_active_employees.display_departments.append( event.msg["department"] ) log.info("%s [%s] department added", event.msg["terminal_name"], event.msg["department"] ) self.treeview_active_employees.populate() def clear_settings(self,event): log.info("Clearing terminal settings: %s..." % event.msg["terminal_name"]) if self.config.get("gtk", "terminal_id", "")==event.msg["terminal_name"]: self.treeview_active_employees.display_departments = None self.treeview_active_employees.populate() ### HIGHLIGHT FUNCTIONS ### def highlight_on(self,widget,event): widget.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("yellow")) try: self.job_entry[0] = self.job_entry[1].index(widget) except AttributeError: pass except: pass return def highlight_off(self,widget,event): widget.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("white")) return def attachHighlightToWidget(self,widget,searchbox = False): widget.connect("focus-in-event",self.highlight_on) widget.connect("focus-out-event",self.highlight_off) widget.set_property("can-default",True) if not searchbox: self.job_entry[1].extend([widget]) ### FOCUS FUNCTIONS ### def focusCapture(self,widget,event): #print "Focus Caught",widget.get_name() return True def on_button_additional_information_clicked(self,widget): activities = self.currently_selected_employee.getActiveProductionLaborDetails() if activities: for child in self.vbox_information.get_children(): child.destroy() for x in activities: label = gtk.Label() label.set_markup("""Job Description:\n %s \nJob Note:\n %s \n\n\n""" % (x["formattedDescription"],x["jobhed_note"]) ) label.set_alignment(0.0,0.0) label.set_padding(5,3) label.set_selectable(True) label.set_line_wrap(True) label.show() self.vbox_information.pack_start(label,expand = False) self.switchPageByName("AdditionalInformation") def on_bttn_additional_information_ok_clicked(self,widget): self.switchPageByName("SelectedEmployee") class EndActivityPercentageError(Exception): """Exceptions raised when activity percentage totals do not total 100%""" pass class EndActivityQtyCompleteError(Exception): """Exception raised when qty complete is not a valid numerical entry""" pass class EndActivityQtyScrapError(Exception): """Exception raised when qty scrap is not a valid numerical entry""" pass