Code view in Configure tab of SUSI.AI Bot Wizard

The purpose of configure tab in SUSI.AI bot wizard is to provide the bot creator various options on how the bot will interact with different websites. It currently provides an option of enabling or disabling the chatbot on different websites.
The configure tab has two parts. One is the code view which allows the users to write websites on which they want to enable/disable the chatbot and a table below it which lists those websites.

The default code in code view is passed in a state in the Configure.js file. The following code demonstrates that:

this.state = {
  code: this.props.code,
};

Fetching data from code view:

After writing the websites on which the user wants the chatbot to be enabled/disabled and pressing on the ‘Save’ button, a functiongenerateConfigData is called.
This function takes the code in code view and stores in inside of a variable. Then it splits the code and makes two arrays:

  • enabledSites: This array contains all the websites that are written in  enabled field.
  • disabledSites: This array contains all the websites that are written in disabled field.

This process can be easily understood from the following code snippet:

let newCode = this.state.code;
let websiteData = newCode
 .split('::sites_enabled')[1]
 .split('::sites_disabled');
let enabledSites = websiteData[0].split(',');
let disabledSites = websiteData[1].split(',');

This data is stored inside an object.

Displaying the data:

The data fetched from the code view now has to be displayed in form of a table. This data is looks like this:

let configData = [
 {
   id: '1',
   name: 'website1.com',
   last: 'Jan 12, 2018 20:08 hrs',
   status: 1,
 },
 {
   id: '2',
   name: 'website2.com',
   last: 'Feb 19, 2018 13:00 hrs',
   status: 1,
 },
 {
   id: '3',
   name: 'website3.com',
   last: 'Mar 14, 2018 10:15 hrs',
   Status: 2,
 }
];

Status is 1 if the website is in enabled column and 2 if the website is in disabled column.
To display this data on the screen, we simply map through the data and display the rows of the table. The following code demonstrates it:

{configData.map((item, index) => {
 if (item.name) {
   return (
    <TableRow key={index}>
      <TableRowColumn style={{ fontSize: '16px' }}>
         {item.name}
     </TableRowColumn>
      <TableRowColumn style={{ fontSize: '16px' }}>
         {item.last}
     </TableRowColumn>
     <TableRowColumn>
       <SelectField
         floatingLabelText="Status"
         fullWidth={true}
         value={item.status}
       >
         <MenuItem value={1} primaryText="Enable" />
         <MenuItem value={2} primaryText="Disable" />
       </SelectField>
     </TableRowColumn>
    </TableRow>
   );
 }
})}

References:

Continue ReadingCode view in Configure tab of SUSI.AI Bot Wizard

Creating GUI for configuring SUSI Linux Settings

SUSI Linux app provides access to SUSI on Linux distributions on desktop as well as hardware devices like Raspberry Pi. The settings for SUSI Linux are controlled with the use of a config.json file. You may edit the file manually, but to provide safe configurations, we have a config generator script. You may run the script to configure settings like TTS Engine, STT Engine, authentication, choice about the hotword engine etc. Generally, it is easier to configure application settings through a GUI. Thus, we added a GUI for it using PyGTK and Glade.

Glade is a GUI designer for GNOME based Linux systems. I wrote a blog about how to create user interfaces in Glade and access it from Python code in SUSI Linux. Now, for creating UI for Configuration screen, we need to choose an ideal layout. Glade provides various choices like BoxLayout, GridLayout, FlowBox, ListBox , Notebook etc. Since, we need to display only basic settings options, we select the BoxLayout for this purpose.

BoxLayout as the name suggests, forms a box like arrangement for widgets. You can arrange the widgets in either Landscape or Horizontal Layout. We select Application Window as a top-level container and add a BoxLayout container in it. Now, in each box of the BoxLayout, we need to add the widgets like ComboBox and Switch for user’s choice and a Label. This can be done by using a horizontal BoxLayout with corresponding widgets. After arranging the UI in above described manner, we have a GUI like below.

If you see the current window in the preview now, you will find that the ComboBox do not have any items. We need to define items in the ComboBox using a GTKListStore. You may refer to this video tutorial to see how this can be done.

Now, when we see the preview, our GUI is fully functional. We have options for Speech Recognition Service, Text to Speech Service in ComboBox. Other simple settings are available as switches.

Now, we need to add functionality to our UI. We want our code to be modular and structured, therefore, we declare a ConfigurationWindow class. Though the ideal way to handle such cases is inheriting from the Gtk.Window class, but reading the documentation of PyGTK+ 3, I could not find a way to do this for windows created through Glade. Thus, we will use composition for storing the window object. We add window and other widgets present in the UI as properties of ConfigurationWindow class like this.

class ConfigurationWindow:
   def __init__(self) -> None:
       super().__init__()
       builder = Gtk.Builder()
       builder.add_from_file(os.path.join("glade_files/configure.glade"))

       self.window = builder.get_object("configuration_window")
       self.stt_combobox = builder.get_object("stt_combobox")
       self.tts_combobox = builder.get_object("tts_combobox")
       self.auth_switch = builder.get_object("auth_switch")
       self.snowboy_switch = builder.get_object("snowboy_switch")
       self.wake_button_switch = builder.get_object("wake_button_switch")

Now, we need to connect the Signals from our configuration window to the Handler. We declare the Handler as a nested class in the ConfigurationWindow class because its scope of usage is inside the ConfigurationWindow object. Then you may connect signals to an object of the Handler class.

builder.connect_signals(ConfigurationWindow.Handler(self))

Since we may need to modify the state of the widgets, we hold a reference of the parent ConfigurationWindow object in the Handler and pass the self as a parameter to the Handler. You may read more about using the handlers in my previous blog.

In the Handler, we connect to the config.json file and change the parameters of the the file based on the user inputs on the GUI. We handle it for the Text to Speech selection comboBox in the following manner. We also declare two addition Dialogs for handling the input of credentials by the users for the Watson and Bing services.

def on_stt_combobox_changed(self, combo: Gtk.ComboBox):
   selection = combo.get_active()

   if selection == 0:
       config['default_stt'] = 'google'

   elif selection == 1:
       credential_dialog = WatsonCredentialsDialog(self.config_window.window)
       response = credential_dialog.run()

       if response == Gtk.ResponseType.OK:
           username = credential_dialog.username_field.get_text()
           password = credential_dialog.password_field.get_text()
           config['default_stt'] = 'watson'
           config['watson_stt_config']['username'] = username
           config['watson_stt_config']['password'] = password
       else:
           self.config_window.init_stt_combobox()

       credential_dialog.destroy()

   elif selection == 2:
       credential_dialog = BingCredentialDialog(self.config_window.window)
       response = credential_dialog.run()

       if response == Gtk.ResponseType.OK:
           api_key = credential_dialog.api_key_field.get_text()
           config['default_stt'] = 'bing'
           config['bing_speech_api_key']['username'] = api_key
       else:
           self.config_window.init_stt_combobox()

       credential_dialog.destroy()

Now, we declare two more methods to show and exit the Window.

def show_window(self):
   self.window.show_all()
   Gtk.main()

def exit_window(self):
   self.window.destroy()
   Gtk.main_quit()

Now, we may use the ConfigurationWindow class object anywhere from our code. This modularized approach is better when you need to manage multiple windows as you can just declare the Window of a particular type and show it whenever need in your code.

Resources

  • Glade usage Youtube tutorial: https://www.youtube.com/watch?v=vOGK3TveDDk
  • Creating GUI using PyGTK for SUSI Linux: https://blog.fossasia.org/making-gui-for-susi-linux-with-pygtk/
  • PyGObject Documentation: http://pygobject.readthedocs.io/en/latest/getting_started.html
Continue ReadingCreating GUI for configuring SUSI Linux Settings