mainapp.py module - wb8tyw/D-Rats GitHub Wiki

Overview

mainapp.py is called by the d-rats program to do most of the work.

This module is over 2100 lines long, and pylint recommends all modules be less than 1000 lines.

Loading the mainapp module causes stdout on win32 to be redirected to a debug.log file. On non-win32, it causes the debug_log file to be deleted.

We need to make sure that internationalization is setup in MainApp init() module before the first internationalized message is declared in it.

We also have to move the setup of the debut log to before the first message is written to it, and integrate its function into the logging module.

Anything in a module that is outside of a function and is not an import is to be treated with suspicion that it is probably not the best way to do something.

This code is run when the module is imported the first time.

Next we find a call for GObject.threading_init, again outside of a program, and that is likely going to cause problems. GObject.threading_init has been deprecated and is now Gtk.threading_init, which the GObject documentation tells us should also be removed.

See also https://wiki.gnome.org/Attic/GdkLock

Then some exception classes. Have not seen anything catch any of the exceptions yet, and currently nothing seems to be specifically trying to handle them if they occur.

Next we find two ping functions outside of a class, and both appear to be potentially dangerous. They are used to craft the default ping response to a remote station.

CallList class

This appears to be a work in progress as it is updated when a station is heard by d-rats. I can not find anything that ever reads it or clears it, so eventually if you hear enough stations, it will use up all the memory in your computer. I expect that to take years though.

MainApp class externally referenced methods and properties

MainApp is an extension of the GTK.Application class, so it can not run in a thread, and none of its methods should ever be called from a thread.

Only one instance of the MainApp should ever exist in a program.

Curiously the MainApp instance is stored in a global variable for other modules to access. That type of access can cause confusion in trying to understand how this works, and is again something to look at improving.

In this section only the functions of the MainApp class will be documented. The Signal handlers all behave similar to delayed interrupts, so need to be each treated as the start of a new chain of execution.

init method

A class init method should usually be a lightweight as possible. It is responsible for creating the structure for the class.

  • Initializes the class logger
  • Initializes class variables
  • Connects up a shutdown handler
  • Sets up the gettext translations - This can be deferred to the main() method in the class.
  • It then checks to see if the user has done a d-rats setup. This also can be deferred to the main() method.
  • gps position is loaded from config. This also can be deferred to the main() method.
  • mainwindow object is created and its "_signal" are connected to handlers in the MainApp Class.
  • mainwindow object created tab property that has a number of tabs.
  • For each tab, the "_signals" for it are connected to handlers.
  • Checks to see if it should report that is is going online. This also can be deferred to the main method.
  • Starts up a periodic timed trigger every 3 seconds for the self._refresh_location handler.

config property

This returns the instance of the config Class used by the MainApp instance.

Referenced by qst.py, wl2k.py.

get_mainapp method

This returns the instance of the MainApp class.

Referenced by qst.py, formgui.py.

get_position

This gets the current position of the D-Rats station.

Referenced by qst.py, formgui.py, and mainapp.py

  • gets position from self.gps.get_position()
  • For the above, the self.gps defaults to StaticGPSSource and get_position returns a GPSPosition object.
  • This updates that position with the current configured call sign.
  • It attempts to add the default_gps_comment, if the GPS DPRS Checksum matches. Does the config GUI make sure that the GPS DPRS Checksum matches?
  • A GPSPosition object is returned.

main method

This is the method that should start any actions for d-rats.

  • Copies the default forms from internal directory to user form directory if not present.
  • Clears any stale message locks from a previous session
  • Migrates configuration from single port to multi-port if needed.
  • Attempts to start the Plugin Server
  • Loads in the static routes from the configuration, which may be needed by the plugin server
  • Attempts to start the message router
  • Runs application windows and waits for the application window to be closed.
  • Until that window closes, everything is now handled by background threads and events.
  • After the close, save the configuration. Need to look into saving configuration as it is changed.
  • If configured, send a signoff message to each port that is enabled.

Referenced by d-rats.py.

map property

This is used to access the map window class instance.

Referenced by qst.py.

active_sessions property

This is used to access the dict of tuples of SessionManager and SessionCoordinator Pairs.

Referenced by wl2k.py.

Virtual methods overrides

A virtual method is a method that overrides a method in a parent class, and the parent class methods are what call these routines instead of their internal routines.

Generally a virtual method should call the parent method at some point in its execution, usually as the last step.

do_activate

This is called as part of the activation of a Application class.

  • Creates the Map.Window window
  • Sets a handler to reload map overlays on a reload sources.
  • Sets default settings for the the map.
  • Does a config refresh

do_quit_mainloop

Called when the main loop is ending.

Currently just being monitored for for diagnostic.

do_shutdown

Called when the application is being shutdown.

Currently just being monitored for for diagnostic.

Handlers

ev_shutdown

This is a shutdown handler that is signaled when d-rats main program is exiting.

It is to signal all holders of a reference that may be ignoring or not otherwise receiving the shutdown event. In d-rats case, the Map window normally hides when it gets this signal instead of closing.

_refresh_location

This is handler is triggered every 3 seconds. It looks like one of its purposes is for a mobile station to be updating its location for when that location is queried or broadcast.

  • calls self.get_position()
  • sets the self.__map_point with the position information.
  • Sets the aprs_icon for the self.__map_point
  • adds the point to the stations_overlay object.
  • Updates the status box or the map display

__status

This is signaled to update the status window with status text.

__user_stop_session

Signaled to stop a specific session.

  • This looks up the session object for the port and session id passed.
  • calls RPC session.close(), Longer option is state full close, otherwise just calls base close method is so skip some steps.
  • If the session is already closed, then exit, Added because I saw a case of recursion earlier while debugging python3 changes.
  • RPC Session close sets a threading event to notify the thread the state full connection.
  • RPC Session close clears any outstanding block events.
  • Joins the thread for state full connection, waits for the worker thread to end.
  • Calls the base.close method.
  • The base close method calls the session_manager stop_session() method.
  • Session_manager stop_session method looks for the rpc session name in the active_sessions dict.
  • if the Session_manager stop_session method does not find the rpc_session name it just exits.
  • Otherwise it flushes any blocks left for it.
  • Details of flushing blocks?
  • If the rpc session is not yet closed, calls session manager control.end_session()
  • The session manager control.end_session does nothing for stateless sessions.
  • The session manager control_end_session for state full sessions attempts to send an T_END frame and get a response to it. Three retries.
  • The session manager _deregister_session is called.
  • The session manager _deregister_session calls "fire_session_cb(ident, "end")
  • The fire_session_cb goes though dict of registered callbacks and calls them.
  • At this time, the potential registered callbacks are unknown.
  • The session manager _deregister_session tries to remove the session identification from the self.sessions dict.
  • The RPC session close() is called. Note that this was originally called by that method, So we are now recursing.

As I currently understand this:

  • mainapp.active_sessions is a dict of tuples of [SessionManager, SessionCoordinator] objects, indexed by radio port.
  • Each Session Manager object has a dict of rpc sessions indexed by a session number.
  • The rpc session object has these properties that are set when it is registered.
  1. property _sm, which is the session manager object.
  2. property _id, which is the session identification number.
  3. property _st, which is the destination station.

So in the SessionManager object, self and self.sessions[index]._sm are always the same.

__user_cancel_session

Signaled to cancel a specific session, just seems to invoke __user_stop_session.

__user_send_form

Signaled to cause a form to be sent via the session coordinator

  • Looks up the session_coordinator object for the port
  • Looks like the session_coordinator object contains a reference to the session_manager object.
  • The session_coordinator object adds the filename to list of outgoing forms.
  • The session_coordinator object creates a reference to the form.FormTransferSession class
  • A form_thread is started with a self.session_manager.start_session target.

form_thread

  • Parameter target = self.session_manager.start_session. This is the function that is used for running the thread.
  • Parameter name: "Form".
  • Parameter dest: Destination stationid.
  • Parameter cls: Class form.FormTransferSession.
  • Parameter blocksize: block size of transfer, not set by form, overridden by file.
  • Parameter outlimit: Block outlimit, default 8, not set by Form, overridden by file.
  • Class form.FormTransferSession is a child class of FileTransferSession with a class attribute "type" set to base.T_FORMXFER.
  • Creates a form.FormTransferSession object with name="Form", and **kwargs) as variable name session.
  • FormTransferSession is child of FileTransferSession, only difference is type attribute.
  • FileTransferSession is a child of stateful.StateFulSession.
  • StateFulSession is a child of base.Session
  • base.Session initializes some class variables, _sm, _id, _st, rs, and type to none. These are shared with all sessions and just appear to be constant initializers to be overridden by class instances.
  • base.Session creates a transport.BlockQueue object as "inq" property.
  • base.Session creates a threading.Event object as "state_event" property.
  • state is set to base.ST_CLSD
  • stateful.Session creates a transport.BlockQueue object as "data" property.
  • stateful.Session creates a treading.Condition object as "data_waiting" property.
  • stateful.Session creates a threading.Event object as "event" property.
  • stateful.Session creates a threading.Thread(target=self.worker) thread.
  • FileTransferSession because optional status_callback is not specified, the self.internal_status is used. internal_status just logs the status to the console.
  • FileTransferSession creates a NotifyDict object as "stats" property.
  • NotifyDict is a child of dict, that calls a callback function every time it is updated.
  • The session state is set to base.ST_SYNC.
  • A session_id is created by register_session(session, dest, reason="new,out").
  • The session_id is limited from 0 to 255.
  • Create a dict with _sm (session_manager), _id (session_id), and _st (destination station) members.
  • above dict is added to the session_manager object session dict property.
  • if dest == "CQCQCQ", a string constant repeated all through d-rats, then return.
  • If this is not a self.control.new_session(session), then deregister the session.

form worker thread in sessions/stateful.py

  • Loops wile self.enabled is true, ends thread when false.
  • call self.send_blocks.
  • send_blocks does the following
  • If self.outstanding and not self.is_timeout, return from send blocks below.
  • calls self.queue_next
  • return from self.queue_next
  • if not self.outstanding, then return from send blocks below.
  • If too many retries, set state base.ST_CLSD, self.enabled False, and return from send_blocks below.
  • if self.waiting_for_ack, call self.send_reqack, and return from send blocks below.
  • Loop for block in self.outstanding
  • variable last_block contains the last block sent.
  • if block sent_event.is_set, increments self.stats['retries'], and block.sent_event.clear.
  • call self._sm.outgoing(self, b_block)
  • Make sure that the frame(block) has a destination station_id.
  • Sets source station to our station_id.
  • Uses either remote session id number or local session id per setup.
  • calls self.tport.send_frame for the block.
  • transport.send_frame checks to see if the transport is active, if not it returns (No status return)
  • transport.send_frame calls self.outq.enqueue, which adds the frame to the list of going frame.
  • return from self.outq.enqueue.
  • return from self.tport.send_frame.
  • return from _sm.outgoing
  • Append block.seq to list to be acked.
  • if not the first block to be sent, then block.sent_event_wait(forever), call self.update_xmit()
  • set current block to last block
  • end of loop for self.outstanding
  • call self.send_reqack.
  • builds a DDT2EncodedFrame of type T_REQACK
  • calls self._sm.outgoing(self, frame) as above.
  • return from send_reqack.
  • call last_block.send_event_wait(forever).
  • return from send_event_wait.
  • call self.update_xmt that updates xmit stats. (Used for calculating timeout.
  • return from send_blocks.
  • call self.recv_blocks.
  • recv_blocks does ?
  • Does route trip calculation.
  • Chekcs self.outstanding and self.outq.peek
  • self.outq.peek does ?
  • If nothing outstanding peek returns true, got to start of loop
  • lf self.outstanding then wait 1 second, call self.event_clear and goto start of loop.
  • wait for self.IDLE_TIMEOUT (?)
  • if session times out set session state to base.ST_CLSD and disable thread.
  • call self.event.clear.
  • go to start of loop.

Data flow summary for form transfer:

  • form file name is added to list of outgoing forms.
  • Unknown what starts processing the file at this time.
  • Once the file starts being processed, blocks put on transport outgoing queue.
  • responses come from transport incoming queue.

__user_send_file

Signaled to cause a file to be sent via the session coordinator

  • Looks up the session_coordinator object for the port
  • Looks like the session_coordinator object contains a reference to the session_manager object.
  • The session_coordinator object adds the filename to list of outgoing files.
  • The session_coordinator object creates a reference to the form.FileTransferSession class
  • A file_thread is started with a self.session_manager.start_session target.

Common code for the __user_send_form signal for threads.

__user_send_chat

Signaled to cause a chat message to be sent via the chat_session property of mainapp.

  • Has two options, write, and write_raw.
  • The session.chat object for the port is looked up.
  • for the write_raw option, the chat.session object _sm.outgoing sends a frame and returns. See __user_send_form.
  • for the write option, the signal outgoing-chat-message is emitted, with parameters self._sm.station and self_st, and data)
  • stateless.StatelessSession.write(self, data, dest), the parent write method is called.
  • StatelessSession.write creates a DDT2EncodedFrame, populates it, sets the compress attribute.
  • calls chat.session object _sm.outgoing sends a frame and returns.
  • return from stateless.StatelessSession and return.

__incoming_chat_message

Signaled by an incoming chat message to be handled via the __chat method of mainapp

  • Check to see if destination station is either "CQCQCQ" or the "callsign" actually stationid of the d-rats user. If not either, return.
  • calls self.__chat(src, dst, data, True, port)
  • self.__chat check if pluginsrv is active and calls pluginsrv.incoming_chat_message.
  • pluginsrv.incoming_chat_message only processes the messages for any remote applications listening for chat messages.
  • return from pluginsrv.incoming_chat_message.
  • if src is not "CQCQCQ", call self.seen_callsigns.set_call_time
  • self.seen_callsigns records the time. Have not found anything reading this data.
  • return from self.seen_callsigns.set_call_time
  • kwargs['priv_src'] set if src is not "CQCQCQ"
  • defines a do_incoming function to be "@run_gtk_locked".
  • run_gtk_locked is using deprecated calls for Gdk.threads routines, so decorator is being removed.
  • GLib.idle_add is used to call the do_incoming function when GTK is otherwise idle.
  • return from self.__chat.

do_incoming delayed call

  • call self.mainwindow.tabs["chat"].display_line()
  • This figures out how to display the line on the chat window.
  • return from display_line()

__outgoing_chat_message

Signaled to cause a chat message to be sent via the __chat method of mainapp

Signaled from session.chat.write() method.

Processed the same as __incoming_chat_message above, except that the __chat() method changes the color the message is displayed as.

__get_station_list

Signaled to get the current reachable heard station list returned.

  • Builds a dict of stations indexed by radio port from the active_stations property.
  • calls self.mainwindow.tabs["stations"].get_stations() which returns to a station_list variable.
  • The main_stations.get_stations object gets stations stored the object and returns a list of Station objects.
  • return from get_stations
  • Loops through the stations in the station list.
  • Add stations heard from the station_list variable that there is an active port for.
  • End of loop through the stations
  • returns the stations dict

__get_message_list

Signaled to get the shared message list for a destination

  • Calls self.mainwindow.tabs["messages"].get_shared_message(station)
  • get_shared_messages gets a list of shared messages from the inbox directory.
  • returns a list of shared messages.

__submit_rpc_job

Signaled to submit an rpc job.

  • Calls self.rpc_session(port).submit(job)
  • sessions.rpc.submit calls self.__getseq to get the next job id for that object instance.
  • sessions.rpc.submit calls self.__send_job.
  • __send_job calls self.__job_to_frame to create a frame for the RPC job.
  • __send_job calls _sm.outgoing to send the frame.
  • returns from __send_job.
  • records the job in the rpc instance.
  • returns from submit(job).

__event

Signaled it looks like to add an event to the event window.

  • Calls self.mainwindow.tabs["messages"].event(event)
  • main_events.EventTab.event calls Glib.idle_add to display the event.

main_events.EventTab.self._event delayed routine

  • Stores the event in the widget storage.
  • calls self._notice.
  • self._notice emits a "notice" signal, which triggers the mainwindow._maybe_blink handler.
  • self._notice adjusts the color of the message as needed.
  • returns from self.notice.
  • emits a ("status", event._message) signal, which triggers the MainApp.__status handler above.

__config_changed

Signaled when the the configuration is changed.

  • calls self.refresh_config
  • Lookup current callsign
  • set the current GPS units.
  • refresh comms
  • refresh gps
  • refresh_mail_threads
  • refresh_map
  • return from self.refresh_config

__show_map_station

Signaled to show the map window by executing the mapwindow show method()

  • Calls self.map.show() to make the map visible.

__ping_station

Signaled to ping a station

  • Calls self.chat_session(port).ping_station(station)

__ping_station_echo

Signaled to respond to a ping

  • Calls self.chat_session(port).ping_echo_station(station, data, callback, cb_data)

__ping_request

Signaled when a ping request comes in

  • Creates ping request message string
  • Creates a main_events.PingEvent object
  • Calls self.mainwindow.tabs["event"].event(event)

__ping_response

Signaled on response to a ping?

  • Creates a ping response message string
  • Creates a main_events.PingEvent object
  • Calls self.mainwindow.tabs["event"].event(event)

__incoming_gps_fix

Signaled when an incoming GPS location is received.

Signaled by d_rats.sessions.chat.incoming_data method. Nothing using the returned data.

  • Looks up self.mainwindow.tabs["event"].last_event_time for fix.station.
  • last_event_time returns timestamp for an event group with this stationid or 0.
  • return from last_event_time method.
  • if more than 300 seconds, call self.mainwindow.tabs["event"].finalize_last for fix.station
  • finalize_last sees if there is an event_group for the stationid and sets it to 0 with an empty string.
  • return from finalize_event.
  • call fix.set_relative_to_current with self.get_position.
  • get_position documented above.
  • return from get_position passed to set_relative_to_current.
  • set_relative_to_current does stores the current position in fix as the current position.
  • return from set_relative_to_current
  • creates a main_events.PosReportEvent object for the gps fix stationid and str(fix)
  • PosReportEvent creates an Event object with group_id, message, and type EVENT_POS_REPORT.
  • Event object create an object top store the initial event information.
  • return from PosReportEvent.
  • updates self.mainwindow.tabs["event"].event with event.
  • self.mainwindow...event method updates the event to the event window when idle.
  • return from self.mainwindow... event method.
  • updates self.mainwindow.tabs["stations].saw_station with the GPS fix.
  • saw_station updates the station window with the new information for the station seen.
  • return from saw_station.
  • if configured for time_stamping positions:
  • Use source_for_station to find the source for the station.
  • calls self.map.get_map_source to return a map source object or None.
  • returns form self.get_map_source.
  • if we got a map source object return it.
  • Attempt to create a MapFileSource.open_source_by_name for the station.
  • MapFileSource.open_source_by_name does opens a static source file with the name of the stationid.
  • return from MapFileSource.open_source_by_name.
  • If create fails, return a self.station_overlay map source object.
  • if create succeeds, call self.add_map_source to add the source.
  • add_map_source adds the map source to the list of sources.
  • add_map_source adds the map source to the marker list.
  • add_map_source adds the points from the source to the map.
  • return from add_map_source.
  • return from source_for_station with map source.
  • Append the current time as a string to the fix.station member.
  • If not configured for time_stamping positions:
  • Use the self.stations_overlay for the source for the station.
  • end of if block
  • Create a map_sources.MapStation object from fix in a variable named point.
  • map_sources.MapStation object inherits from MapPoint class and stores Station position information.
  • If the fix does not have an APRS icon then force a default.
  • call point.set_icon_from_aprs_sym with fix.aprs_icon.
  • set_icon_from_aprs_sym looks up the icon to use.
  • return from set_icon_from_aprs_sym.
  • Call source.add_point with point
  • add_point updates the point information and signals either a point-updated or point-added event.
  • signal triggers a map update for that point information change.
  • return from add_point.
  • call source.save
  • source.save does saves the point information to a file.
  • return from source.save.
  • Tests if a mapserver is active
  • if a map server is active, then calls self.callback_gps
  • callback_gps attempts to connect to the mapserver.
  • callback_gps sends a json formatted message and closes the connection to the mapserver
  • message format is: '{ "lat": "%f", "lng": "%f", "station": "%s", "comments": "%s","timestamp": "%s" }' %
    (flat, flng, station, comments, strftime("%Y-%m-%d %H:%M:%S", gmtime()))
  • return from callback_gps
  • end of it map server is active.
  • creates a gps.StaticGPSSource object for fix.
  • gps.StaticGPSSource is a GPS source that just returns a stored value with no thread.
  • return from gps.StaticGPSSource
  • return from signal, with the gps.StaticGPSSource, nothing uses this return.

__station_status

Signaled when a station status changes.

  • calls self.mainwindow.tabs["stations"].saw_stations to update station widget
  • Tries to get a status from the message, default is to report the code.
  • Valid status codes are 0: UNKNOWN, 1: ONLINE, 2: UNATTENDED, 9: OFFLINE
  • Creates an Event for the status
  • calls self.window.tabs["event"].event to update event window.

__get_current_status

Signaled to return what is currently a constant dict.

Appears to be signaled once in ui.sessions.chat.

  • calls self.mainwindow_tabs["stations"].get_status to get a dict of status codes indexed by status text.
  • This is done by processing another static dict and then returning the result.
  • This effectively constant data is then returned.

__get_current_position

Signaled to return a gps.GPSPosition for a station.

What happens to a raised exception in a signal handler?

  • If station is None, return current position for this station.
  • Look up station in known stationids.
  • raise an exception if the station is not known.

__session_started

Signaled to create and display an event for a session start and return the event for it

__session_status_update

Signaled create and report an event for the session status It just calls __session_started. Does not return a result.

__session_ended

Signaled to when a session has ended unexpectedly?

  • Calls __session_started to update the event display
  • calls event.set_restart_info((stationid, filename) to set the restart info for the event.
  • calls event.set_as_final which marks this as the end of a series of events for a given group.
  • calls self.msgrouter.form_xfer_done(filename, port, True), True indicates transfer failed.
  • form_xfer_done makes sure message is unlocked.
  • form_xfer_done looks up the stationid that the filename was for.
  • form_xfer_done increments the failed count for that stationid, as noted above.
  • form_xfer_done removes the stationid from the __sent_call dict.
  • form_xfer_done removes the filename from the __file_to_call dict.
  • form_xfer_done removes the port from the __sent_port dictionary.
  • return from form_xfer_done

__form_received

Signaled when a form is received.

  • Creates a formgui.FormFile object from the filename as 'ffile'
  • Looks up some information from the form
  • If form is to be forwarded?
  • Move form to outbox for transfer to next station.
  • Else if form is not forwarded?
  • Move form to inbox.
  • End if.
  • Make sure the form is unlocked.
  • calls self.mainwindow.tabs["messages"].refresh_if_folder on the updated folder.
  • creates a main_events.FormEvent for that ID num and port.
  • call event.set_as_final
  • calls self.mainwindow.tabs["event"].event for the event.

__file_received

Signaled when a file is received.

  • creates a main_events.FileEvent for that ID num and port.
  • call event.set_as_final
  • calls self.mainwindow.tabs["messages"].refresh_local
  • calls self.mainwindow.tabs["event"].event for the event.

__form_sent

Signaled when a form is successfully sent.

  • creates a main_events.FormEvent for that ID num and port.
  • call event.set_as_final
  • calls self.mainwindow.tabs["messages"].message_sent for the form filename.
  • message_sent moves the message file sent from the outbox to the sent box.
  • return from message_sent
  • calls self.mainwindow.tabs["event"].event for the event.

__file_sent

Signaled when a file is successfully sent.

  • creates a main_events.FileEvent for that ID num and port.
  • call event.set_as_final
  • calls self.mainwindow.tabs["messages"].file_sent for the form filename.
  • file_sent has if self._remote is a RemoteFileView object.
  • file_sent moves the file sent from the remote outstanding list.
  • Store the file in the list of the RemoteFileView object.
  • file_sent endif.
  • return from file_sent
  • calls self.mainwindow.tabs["event"].event for the event.

__get_chat_port

Signaled to get the chat port

  • calls self.mainwindow.tabs["chat"].get_selected_port
  • get_selected_port looks the "chat_destination" Gtk.GtkComboBoxText object.
  • from the Gtk.ComboBoxText object get and returns the active text.
  • returns the port

__trigger_msg_router

Signal to trigger message routing.

  • if an account is not given, call self.msgrouter.trigger
  • msgrouter.trigger if the thread is not alive will start it.
  • if the thread was already alive, then call its __event.set().
  • return from msgrouter.trigger
  • else if account is "@WL2K"?
  • calls wl2k.wl2k_auto_thread to create a WinLInkTelnetThread or WinLinkAGWThread object.
  • if AGW thread connection?
  • Check for an active radio port is done.
  • pipe.get_agw_connection is called.
  • get_agw_connection does returns an agw.AGWConnection object.
  • return from get_agw_connection.
  • WinLinkAGWThread object is started with the agw_connection.
  • self.__connect_object to connect signals to the thread object
  • signals are "mail-thread-complete", "event", "form-received", "form-sent".
  • start the thread object.
  • else if account is in list of known threads?
  • call self.mail_threads[account].trigger method.
  • return from self.mail_threads[account].trigger method.
  • else
  • Create an AccountMailThread object for account name.
  • Start the thread object.
  • end of if blocks

__register_object

A signaled to connect other handlers?

Internal methods

session_coordinator

This is simply an accessor for session coordinator for an active session indicated by a portname. It is only called two places in the mainapp routine. Looks like something for optimizing away.