Amarisoft

Remote Control GUI - Python

 

The purpose of this tutorial is to show to provide a Python template showing various components to control the equipment in GUI (Graphic User Interface).  Motivations for this tutorial are as follows :

NOTE :  The purpose of this tutorial is to provide proof of concept examples for Python Script to implements various remote control components in GUI. We do not provide any technical support for Python script or troubleshoot Python installation.

NOTE :  I would suggest the readers to use this script just as an example (template) to show/explain on various ways to control PC remotely. This is not a mature and stable program. You may revise and extend the shared source code in any way you like to make it better fit for your purpose.

NOTE :  For now, the script is developed and tested on Windows PC only. Even though Python should be compatible across multiple different OS, it is not tested (not guranteed) to work on Operating System other than Windows).

 

Table of Contents

 

Test Setup

Following is the hardware setup in which the script is tested. In this environment, Amarisoft Callbox, Amarisoft UEsim and a control PC are connected over WiFi with same IP subnet.

RemoteAPI GUI TestSetup 01

 

Python and Development Enviorment

Following is the version of the python and package that are used for this tutorial. (I tested this on Windows 11 Home Edition). I don't think there is any specific dependencies for Windows Powershell version and Windows Commad Line Windows version. For python, first try with whatever version you are using as long as it is ver 3.x and upgrade it to latest version if it does not work.

In this script, so many different Python libraries are used. I am using ANACONDA and VS Code. (NOTE : You would not need to use ANACONDA, VS Code for this scripting, but I used ANACONDA framework since it is easier for easy management and installation of python libraries and used VS Code for easy scripting).

When you use ANACONDA and VS Code, it is important to run VS Code within ANACONDA NAVIGATOR as highlighted below. If you use VS Code independantly and run Python, it may fail to import libraries by ANACONDA.

RemoteAPI GUI DevEnv 01

Execution of the script is done within the TERMINAL within VS Code environment.

RemoteAPI GUI DevEnv 02

Once Python and coding environment is setup, install all the python packages that are required for the following import. I would not explain about how to install each of these packages since googling would give you the better instruction. I insalled all of these packages with pip within VS Code TERMINAL.

 

Python Script

Here I share the full source code here.  Download the zip file and unzip into any location where your python enviroment can recognize.

If you set up all the necessary Python enviroment on your Windows PC, it should run as explained in this tutorial. I will explain about the source code at high level function level in later sections, but there would not be line by line comments about the code assuming that the readers have a certain level of python skills.

 

Highlevel Overview

In this program, there are 6 tabs. Each of tab has a set of related functions as is explained below.  In this section, I will go over the screenshot of each tabs and high level descriptions for the function. The detailed operations for each tab will be explained in later section.

In terms of programming, basically fundamental of each tabs are based on various socket libraries as summarized below.

The software interface for each of these sockets are supported by Python libraries and the details will be explained in later section.

 

Callbox (I)

Basically this tab is for implementing RemoteAPI in GUI. So the basic frame of the script is very similar to the tutorial RemoteAPI Python .  The difference is that the RemoteAPI is implemented in GUI.  In addition to Remote API, [Stop LTE Service] and [Restart LTE Service] are added and these functions are not supported by Remote API web socket. The detailed usage will be explained in later section.

RemoteAPI GUI Overview 01

 

Callbox (II)

This is an extension to Callbox(I) tab. The purpose of this tap is to send several most commonly used and PHY related remote API command just by clicking on a few options without typing in command mannually. As an additional information, there is a log viewer that shows RRC / NAS messages.  The details on usage will be explained in later section.

RemoteAPI GUI Overview 02

 

UEsim

UEsim tab does same thing as Callbox(I) + Callbox(II). The difference is that this tab is to implement Remote API for UEsim instead of the callbox.  To avoid the situation of having too many tabs, I tried to pack UEsim Remote API within a single tab. I think the single tab is good enough since we use relatively small and simple remote API commands in UEsim comparing to Callbox.

RemoteAPI GUI Overview 03

 

File Browser(Callbox)

File Browser (Callbox) tab is to browse files on local PC (control PC, the Windows PC in this case) and the remote PC (Callbox PC) and transfer files between local PC and callbox PC.

RemoteAPI GUI Overview 04

 

File Browser(UEsim)

File Browser (Callbox) tab is to browse files on local PC (control PC, the Windows PC in this case) and the remote PC (UEsim PC) and transfer files between local PC and UEsim PC.

RemoteAPI GUI Overview 05

 

Screen(Callbox)

Screen(Callbox) is to run 'screen -r' command on Callbox. The basic idea is to capture and display the output of screen command and run some of the frequently used command with button click rather than typing in the command. You can manually type in a specify command and run if you like .

NOTE : Due to the difficulties of processing the screen command output over SSH socket, often the resulting text is not properly aligned, but it is good enough to see what's going on for now.

RemoteAPI GUI Overview 06

 

 

User Operation

In this section, I will describe on the detailed operation of each tabs. Most of the explanation will be commented right on the screen capture rather than writing in separate text. In GUI program, there is no single path of operation. There would be almost infinite different path to follow. The flow that I show here is the way that I personally follow most of the time. Just try to catch the overall flow and you will come up with your own way of operation flow.

 

Callbox (I)

Basically this tab is for implementing RemoteAPI in GUI. So the basic frame of the script is very similar to the tutorial RemoteAPI Python .  The difference is that the RemoteAPI is implemented in GUI.  In addition to Remote API, [Stop LTE Service] and [Restart LTE Service] are added and these functions are not supported by Remote API web socket. The detailed usage will be explained in later section.

In terms of usage, the only requirement is to put the correct IP, port, user id and password and to start lte service. Once these information are correctly input and lte service is running, all the remaining workflow is up to you.

RemoteAPI GUI UserOperation CallboxI 01

 

RemoteAPI GUI UserOperation CallboxI 02

 

RemoteAPI GUI UserOperation CallboxI 03

 

RemoteAPI GUI UserOperation CallboxI 04

 

RemoteAPI GUI UserOperation CallboxI 05

 

RemoteAPI GUI UserOperation CallboxI 06

 

RemoteAPI GUI UserOperation CallboxI 07

 

Callbox (II)

Basically this is an extension to Callbox (I).  There are many things that I wanted to implment in GUI for callbox remote API, but the space on single tab is limited. So I justed created another tap.

RemoteAPI GUI UserOperation CallboxII 01

 

RemoteAPI GUI UserOperation CallboxII 02

 

RemoteAPI GUI UserOperation CallboxII 03

 

RemoteAPI GUI UserOperation CallboxII 04

 

RemoteAPI GUI UserOperation CallboxII 05

 

RemoteAPI GUI UserOperation CallboxII 06

 

RemoteAPI GUI UserOperation CallboxII 07

 

UEsim

RemoteAPI GUI UserOperation UEsim 01

 

RemoteAPI GUI UserOperation UEsim 02

 

RemoteAPI GUI UserOperation UEsim 03

 

RemoteAPI GUI UserOperation UEsim 04

 

RemoteAPI GUI UserOperation UEsim 05

 

RemoteAPI GUI UserOperation UEsim 06

 

File Browser (Callbox)

RemoteAPI GUI UserOperation FileBrowser Callbox 01

 

RemoteAPI GUI UserOperation FileBrowser Callbox 02

 

RemoteAPI GUI UserOperation FileBrowser Callbox 03

 

RemoteAPI GUI UserOperation FileBrowser Callbox 04

 

RemoteAPI GUI UserOperation FileBrowser Callbox 05

 

File Browser (UEsim)

RemoteAPI GUI UserOperation FileBrowser UEsim 01

 

RemoteAPI GUI UserOperation FileBrowser UEsim 02

 

RemoteAPI GUI UserOperation FileBrowser UEsim 03

 

RemoteAPI GUI UserOperation FileBrowser UEsim 04

 

RemoteAPI GUI UserOperation FileBrowser UEsim 05

 

Screen (Callbox)

NOTE : This works without any problem and is enough to check out the functionality, but text print in screen is a little messy. It would be because handling incoming data through shell socket is not perfect. This will be something to be improved later.

RemoteAPI GUI UserOperation Screen Callbox 01

 

RemoteAPI GUI UserOperation Screen Callbox 02

If you hit on [Connect] button, you will get a prompt as shown below.

RemoteAPI GUI UserOperation Screen Callbox 03

Hit [Run Screen] button and you will get the initial screen with (mme) mode.

RemoteAPI GUI UserOperation Screen Callbox 04

If you hit [go to (enb)] button, the screen switches to (enb) and print out the initial text of (enb) screen.

RemoteAPI GUI UserOperation Screen Callbox 05

Now you can run various (enb) commands by clicking on ready made buttons, for example, [cell phy] button as shown below (NOTE : If you want to run any commands that is not made with buttons, you can type the command into "Command" text box and hit [ENTER] button)

RemoteAPI GUI UserOperation Screen Callbox 06

 

 

Code Description

In this section, I will describe on the source code of the program, but I will not go through each and every line of the code. I would go through chunks(block) of code which are associated with certain user interactions (e.g, poping up a window, clicking a button or route mouse click for a popup menu etc).  For further details, you can refer to the source code that you downloaded and go to the block of the code mentioned here and check the details.

 

Overall Layout

As you may notice, this program is made up of single window with single notebook GUI component (Multiple tabs that you see in this program is just components of the Notebook component). So the first part of the program is to create a window (a dialogbox) and a Notebook component within the window. That is done by the code shown below.

    root = tk.Tk()  # this creates a window (main window) of this program

    root.title("Remote Control GUI")

    root.geometry("1200x600")

    root.grid_columnconfigure(0, weight=1)

    root.grid_rowconfigure(0, weight=1)

    root.config(cursor="")

 

    # Create a style object

    style = ttk.Style()

 

    # Modify the TNotebook.Tab style (this is the style for the tabs)

    style.configure('TNotebook.Tab',

                    font=('Arial', '10', 'bold'),

                    padding=[5, 10],  # [left/right padding, top/bottom padding]

                    )

 

    # Increase the size of the tabs to accommodate the new font size

    style.configure('TNotebook', tabposition='nw')  # 'n' is the default: 'n' (north), 's' (south), 'w' (west), 'e' (east)

 

    notebook = ttk.Notebook(root, style='TNotebook')  # this creates a Notebook and put it into 'root' (main window).

    notebook.grid(row=0, column=0, sticky="nsew")

 

Callbox (I)

Now let's look into the code for the Callbox (I) tab. There are quite a lot of GUI components in this tab. For easy association for each GUI components and corresponding source code, I split this tab into multiple sections and labeled as shown below.

RemoteAPI GUI CodeDescription CallboxI 01

 

GUI Component Layout

This is to create all the graphical components within the first tab (Callbox (I)). Overall idea is to create a frame(boundary is not shown though) filling out the whole tab area and put all the remaining components within the frame.  I would not cover the code for every sections labeled above. I would just go over the part that are associated with user interactions.

    # Create a new frame for the first tab

    frmTab1 = ttk.Frame(notebook)

 

    # Based on this block, only the section I would get resized as the main window size changes in both horizontal and vertical direction. All other components resizes only in horizontal direction and does not

    # change size in vertical direction.

    frmTab1.grid_columnconfigure(8, weight=1) # weight=0 --> not resizable, weight=1 --> resizable , Make Section I resizable in horizontal direction

    frmTab1.grid_rowconfigure(0, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(1, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(2, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(3, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(4, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(5, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(6, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(7, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

    frmTab1.grid_rowconfigure(8, weight=1)  # weight=0 --> not resizable, weight=1 --> resizable  , Make Section I resizable in vertical direction

    frmTab1.grid_rowconfigure(9, weight=0)  # weight=0 --> not resizable, weight=1 --> resizable

 

    notebook.add(frmTab1, text="Callbox (I)")

 

    # Add your components to the first tab

    #create_row1(frmTab1,"10.0.0.185") # create txtIP, cboPort

    create_row1(frmTab1,"192.168.100.17") # create txtIP, cboPort

    create_row2(frmTab1) # create cboCellID, cboUEID

    create_row5(frmTab1) # create txtCommand

    create_row7(frmTab1) # create txtReturn

    create_row3(frmTab1) # create cboCommand, cboSubCommand

    create_row4(frmTab1) # create

    create_row6(frmTab1) # create btnSend

    create_row9(frmTab1) # create tblProcess

    create_row8(frmTab1) # create btnProcess

    create_row10(frmTab1) # create btnExpandAll, btnCollapseAll,btnExpandLevel1

    create_row11(frmTab1) # create btnServiceStop,btnServiceRestart

 

Section C (Row 3) - Command, SubCommand Dropdown box

This is for the components in the section C.  The important part of this section is two dropdown box labeled as 'Command' and 'Sub Command'.  The important functionalities for this section are

 

def create_row3(root):

 

    # Read the JSON file

    with open('remoteApi.txt', 'r') as f:

        data = json.load(f)

 

    # Create the 'Command' drop-down

    command_var = tk.StringVar()

    cboCommand = ttk.Combobox(root, textvariable=command_var)

    cboCommand['values'] = [item['message'] for item in data]

    cboCommand.current(0)  # Select the first item by default

 

    # Create the 'SubCommand' drop-down

    subcommand_var = tk.StringVar()

    cboSubCommand = ttk.Combobox(root, textvariable=subcommand_var)

 

    # Function to update the 'SubCommand' drop-down

    def update_subcommand(*args):

        message = command_var.get()

        for item in data:

            if item['message'] == message:

                cboSubCommand['values'] = item['settings']    

                item_count = len(cboSubCommand['values'])

                if item_count > 0  :

                    cboSubCommand.current(0)  # Select the first item by default after updating values

                break

 

    # Function to update the command string in txtCommand

    def update_command_string(*args):

        if not subcommand_var.get()   :

            cmdString = "{}".format('{"message"'+':'+'"'+command_var.get()+'"' + '}')

        else  :  

            if subcommand_var.get() != "{}" :

                cmdString = "{},{}".format('{"message"'+':'+'"'+command_var.get()+'"', subcommand_var.get()[1:-1] + ' }')

                cmdString = cmdString.replace("'", '"')

                cmdString = cmdString.replace("False", 'false')

                cmdString = cmdString.replace("True", 'true')

            else :

                cmdString = "{}".format('{"message"'+':'+'"'+command_var.get()+'"' + '}')

            

        txtCommand.delete("1.0", tk.END)  # Clear the current text

        txtCommand.insert("1.0", cmdString)  # Insert the new command string

 

    # Update the 'SubCommand' drop-down when the 'Command' selection changes

    command_var.trace('w', update_subcommand)

    command_var.trace('w', update_command_string)  # Update the command string when command_var changes

    subcommand_var.trace('w', update_command_string)  # Update the command string when subcommand_var changes

 

    # Arrange the widgets using grid (or use pack/place, depending on your layout)

    lbl_command = tk.Label(root, text="Command")

    lbl_command.grid(row=2, column=0, sticky="w",padx=(10, 0))

    cboCommand.grid(row=2, column=1, sticky="w")

 

    lbl_sub_command = tk.Label(root, text="SubCommand")

    lbl_sub_command.grid(row=2, column=2, sticky="w",padx=(10, 0))

    cboSubCommand.grid(row=2, column=3, columnspan=7, sticky="ew",padx=(0, 10))

 

NOTE : The items shown in Command and SubCommand dropdown box is stored remoteApi.txt.  You can add, remove, edit those items as you like by editing remoteApi.txt. The default contents of the file is as follows. The "message" item will show up in Command dropbox and "settings" item will show up in SubCommand dropdown box.

 

[

  {

    "message": "config_get",

    "settings": [ ]

  },

  {

    "message": "log_get",

    "settings": [ ]

  },

  {

    "message": "config_set",

    "settings": [

       {"logs": { "bcch": false } } ,

       {"logs": { "bcch": true } } ,

       {"logs": { "layers": { "ALL": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"logs": { "layers": { "PHY": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"logs": { "layers": { "MAC": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"logs": { "layers": { "RLC": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"logs": { "layers": { "PDCP": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"logs": { "layers": { "RRC": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"logs": { "layers": { "GTPU": { "level": "debug", "max_size": 0, "payload": true } }, "bcch": false }},

       {"cells": {"1":{"inactivity_timer":60000} } },

       {"cells": {"1":{"force_dl_schedule":true, "pdsch_fixed_rb_alloc":true,"pdsch_fixed_rb_start":0,"pdsch_fixed_l_crb":20 }}},

       {"cells": {"1":{"pdsch_mcs":4} } },

       {"cells": {"1":{"force_full_bsr":true, "pusch_fixed_rb_alloc":true,"pusch_fixed_rb_start":0,"pusch_fixed_l_crb":20 }}},

       {"cells": {"1":{"pusch_mcs":4} } }

       

    ]

  },

  {

    "message": "rf",

    "settings": [

      {},

      {"tx_gain": 80},

      {"tx_gain": [80, 70]},

      {"tx_gain": [80, 80, 70, 70]},

      {"tx_gain": [80, 80, 70, 70, 60, 60]},

      {"tx_gain": [80, 80, 80, 80, 70, 70, 70, 70]},

      {"rx_gain": 50},

      {"rx_gain": [60, 50]},

      {"rx_agc": -10}

    ]

  },

  {

    "message": "stats",

    "settings": [

      {},

      {"samples": true},

      {"samples": true, "rf": true},

      {"samples": true, "rf": true, "Initial_delay": 0.7}

    ]

  },

  {

    "message": "ue_get",

    "settings": [

      {},

      {"stats":true}

    ]

  }

]

 

Section F (Row 6) - [Send] button

This is the code for the Section F. High level code is very simple as shown below. What it does is just to create a button labeled 'Send' (btnSend) and link it with the function send_command(). As you may guess, the important part is the implementation of send_command() function. Basically send_command() is super simple version of RemoteAPI program.

If you want to understand the details of send_command() function, check out this tutorial first. If you don't have any problem with understanding Remote API Python tutorial, you should have no problem with understanding send_command() function.

def create_row6(root):    

 

    btnSend = ttk.Button(root, text="Send", command=lambda: send_command())

    btnSend.grid(row=5, column=0, columnspan=9, sticky="ew", padx=(400, 400), pady=(5, 5))

 

Section H (Row 8) - [Process] button

This is the code for the Section H. What it does is just to create a button labeled 'Process' (btnProcess) and link it with the function process_txtReturn(). What process_txtReturn() does is to parse the json format data printed in txtReturn textbox and display it in tree form as shown in Section I.

As you may guess, process_txtReturn() would implement the process of json parsing and manipulation of the treeview GUI component. I would not go into the details on json parsing and treeview manipulation here. Following note in sharetechnote may help of understanding the process.

 

def create_row8(root):

 

    btnProcess = ttk.Button(root, text="Process", command=lambda: process_txtReturn())

    btnProcess.grid(row=7, column=0, columnspan=9, sticky="ew", padx=(400, 400), pady=(5, 5))

 

Section J (Row 10) - [Expand All], [Collpase All], [Expand Level 1] button

This is the code for the Section J. What it does is just to create three buttons labeled 'Expand All' (btnExpandAll), 'Collapse All' (btnCollapseAll) and  'Expand Level 1' (btn btnExpandLevel1) respectively, and link them with the corresponding function(callback) as commented below. Bascially all these three buttons are to change the display pattern of treeview (Section (I))

def create_row10(root):    

 

    frame = tk.Frame(root,borderwidth=2, relief="groove")

    frame.grid_rowconfigure(0, weight=1)

    frame.grid_columnconfigure(0, weight=0)

 

    colOffset = 0

    btnExpandAll = ttk.Button(frame, text="Expand All", width=30, command=lambda: ExpandAll(tblProcess))   # if you click btnExpandAll button, ExpandAll(tblProcess) will be executed

    btnExpandAll.grid(row=10, column=0+colOffset, sticky="n", padx=(10, 0), pady=(5, 5))

 

    btnCollapseAll = ttk.Button(frame, text="Collapse All", width=30, command=lambda: CollapseAll(tblProcess)) # if you click btnCollapseAll button, CollapseAll(tblProcess) will be executed

    btnCollapseAll.grid(row=10, column=1+colOffset, sticky="n", padx=(10, 0), pady=(5, 5))

 

    btnExpandLevel1 = ttk.Button(frame, text="Expand Level 1", width=30, command=lambda: ExpandLevel1(tblProcess)) # if you click btnExpandLevel1 button, ExpandLevel1(tblProcess) will be executed

    btnExpandLevel1.grid(row=10, column=2+colOffset, sticky="n", padx=(10, 0), pady=(5, 5))

 

    frame.grid(row=10, columnspan=9, sticky="nsew", padx=(10, 10), pady=(0, 10))

 

Section K (Row 11) - [Stop LTE Service], [Restart LTE Service] button

This is the code for the Section K. What it does is just to create three buttons labeled 'Stop LTE service' (btnServiceStop) and  'Restart LTE service' (btnServiceRestart ) respectively, and link them with the corresponding function(callback) as commented below. Bascially the two buttons are to setup an ssh socket and send 'service lte stop' or 'service lte restart' command.

def create_row11(root):    

 

    frame = tk.Frame(root,borderwidth=2, relief="groove")

    frame.grid_rowconfigure(0, weight=1)

    frame.grid_columnconfigure(0, weight=0)

 

    colOffset = 0

 

    btnServiceStop = ttk.Button(frame, text="Stop LTE service", width=30, command=lambda: lteServiceStop(txtIP)) # if you click btnServiceStop button, lteServiceStop(txtIP) will be executed

    btnServiceStop.grid(row=11, column=3+colOffset, sticky="n", padx=(10, 0), pady=(5, 5))

 

    btnServiceRestart = ttk.Button(frame, text="Restart LTE service", width=30, command=lambda: lteServiceRestart(txtIP)) # if you click  btnServiceRestart button, lteServiceRestart(txtIP) will be executed

    btnServiceRestart.grid(row=11, column=4+colOffset, sticky="n", padx=(10, 0), pady=(5, 5))

 

    frame.grid(row=11, columnspan=9, sticky="nsew", padx=(10, 10), pady=(0, 10))

 

Callbox (II)

Now let's look into the code for the Callbox (II) tab. There are also quite a lot of GUI components in this tab. For easy association for each GUI components and corresponding source code, I split this tab into multiple sections and labeled as shown below.

RemoteAPI GUI CodeDescription CallboxII 01

 

GUI Component Layout

This is to create all the graphical components within the second tab (Callbox (II)). Overall idea is to create a frame(boundary is not shown though) filling out the whole tab area and put all the remaining components within the frame.  I would not cover the code for every sections labeled above. I would just go over the part that are associated with user interactions.

    # Create a new frame for the second tab

    frmTab2 = ttk.Frame(notebook)

    frmTab2.grid_columnconfigure(0, weight=0)

    frmTab2.grid_rowconfigure(0, weight=0)

    notebook.add(frmTab2, text="Callbox (II)")

 

    chkVarTxGainAll = tk.IntVar(value=1)

    chkVarTxGain = []  # create list to store IntVars

    create_row1_tab2(frmTab2)

    chkVarRxGainAll = tk.IntVar(value=1)

    chkVarRxGain = []  # create list to store IntVars

    create_row2_tab2(frmTab2) # Create txtRxGain

 

    chkVarForceDlSchedule = tk.IntVar(value=0)

    chkVarPdschFixedRbAlloc = tk.IntVar(value=0)

    create_row3_tab2(frmTab2) # Create txtForceDlScheduleCells, txtForceDlScheduleMCS , txtForceDlScheduleRbStart, txtForceDlScheduleRbN

 

    chkVarForceFullBsr = tk.IntVar(value=0)

    chkVarPuschFixedRbAlloc = tk.IntVar(value=0)

    create_row4_tab2(frmTab2) # create txtorceFullBsrCells, txtorceFullBsrMCS , txtorceFullBsrRbStart, txtorceFullBsrRbN

    create_row5_tab2(frmTab2) # create txtInactivityTimer

 

    create_row7_tab2(frmTab2) # create tblLogUpdate

    frmTab2.grid_rowconfigure(6, weight=1) # weight=0 --> not resizable, weight=1 --> not resizable

 

    create_row6_tab2(frmTab2) # create btnUpdateLog, btnClearLog

    frmTab2.grid_columnconfigure(6, weight=1) # weight=0 --> not resizable, weight=1 --> not resizable tion

 

Section A (Row 1) - Set button, Tx Gain Value setting field

This is the code for the Section A. For most part, you would not need any specific comments because those are just putting a specific GUI components at a specific position. The only thing that I want to comment is about process_btnTxGain() without showing the code details. You just check out the code details of this function in the source code you can download in this tutorial.

What process_btnTxGain() does is :

Followings are examples of RemoteAPI command generated by 'Set' button

def create_row1_tab2(root):

 

    global txtTxGain

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=0, column=0, columnspan=18,sticky="we", padx=(10,10), pady=(5,5))

 

    # creating a label with the text "Tx Gain" and check box

    lblTxGain = ttk.Label(nested_frame , text="tx_gain", width=10)

    lblTxGain.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

    chkTxGainAll = ttk.Checkbutton(nested_frame, text="", variable=chkVarTxGainAll)

    chkTxGainAll.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    # creating a textbox with the text "Tx Gain"

    txtTxGain = ttk.Spinbox(nested_frame , from_=0, to=100, width=5)

    txtTxGain.set(90)  # Set the default value

    txtTxGain.grid(row=0, column=2, sticky="ew", padx=(10,0), pady=(5,5))

 

    # creating the button labeled as "Set". When this button is clicked, the function process_btnTxGain() gets executed

    btnTxGain = ttk.Button(nested_frame, text="Set", command=process_btnTxGain)

    btnTxGain.grid(row=0, column=3, sticky="ew", padx=(0,10), pady=(5,5))

 

   # creating the 8 labels and spinbox(a textbox with up/down button).

    for i in range(8):

        spinVarTxGain.append(tk.IntVar(value=90))  

        lblTxGain = ttk.Label(nested_frame , text="TX{}".format(i))

        lblTxGain.grid(row=0, column=4+2*i, sticky="w", padx=(10,0), pady=(5,5))

        spinTxGain = ttk.Spinbox(nested_frame, from_=0, to=90, textvariable=spinVarTxGain[i], width=5, name='txtTxGainNo{}'.format(i))

        spinTxGain.grid(row=0, column=4+2*i+1, sticky="w", padx=(0,10), pady=(5,5))

 

Section B (Row 2) - Set button, Rx Gain Value setting field

This is the code for the Section B. For most part, you would not need any specific comments because those are just putting a specific GUI components at a specific position. The only thing that I want to comment is about process_btnRxGain() without showing the code details. You just check out the code details of this function in the source code you can download in this tutorial.

What process_btnRxGain() does is :

Followings are examples of RemoteAPI command generated by 'Set' button

 

def create_row2_tab2(root):

 

    global txtRxGain

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=1, column=0, columnspan=18,sticky="we", padx=(10,10), pady=(5,5))

 

    # creating a label with the text "Rx Gain" and check box

    lblRxGain = ttk.Label(nested_frame , text="rx_gain", width=10)

    lblRxGain.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

    chkRxGainAll = ttk.Checkbutton(nested_frame, text="", variable=chkVarRxGainAll)

    chkRxGainAll.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    # creating a textbox with the text "Rx Gain"

    txtRxGain = ttk.Spinbox(nested_frame , from_=0, to=100, width=5)

    txtRxGain.set(60)  # Set the default value

    txtRxGain.grid(row=0, column=2, sticky="ew", padx=(10,0), pady=(5,5))

 

    # creating the button labeled as "Set". When this button is clicked, the function process_btnRxGain() gets executed

    btnRxGain = ttk.Button(nested_frame , text="Set", command = process_btnRxGain)

    btnRxGain.grid(row=0, column=3, sticky="ew", padx=(0,10), pady=(5,5))

 

  # creating the 8 labels and spinbox(a textbox with up/down button).

    for i in range(8):

        spinVarRxGain.append(tk.IntVar(value=60))  

        lblRxGain = ttk.Label(nested_frame , text="TX{}".format(i))

        lblRxGain.grid(row=0, column=4+2*i, sticky="w", padx=(10,0), pady=(5,5))

        spinRxGain = ttk.Spinbox(nested_frame, from_=0, to=60, textvariable=spinVarRxGain[i], width=5, name='txtRxGainNo{}'.format(i))

        spinRxGain.grid(row=0, column=4+2*i+1, sticky="w", padx=(0,10), pady=(5,5))

 

Section C (Row 3) - DL Phy Scheduling

This is the code for the Section C. For most part, you would not need any specific comments because those are just putting a specific GUI components at a specific position. The only thing that I want to comment is about process_btnPhyDL() without showing the code details. You just check out the code details of this function in the source code you can download in this tutorial.   

What process_btnPhyDL() does is to create a Remote API command based on options selected by checking / unchecking checkboxes. For example, if you check all the options the generated remoteAPI command is like this.

 

def create_row3_tab2(root):    

 

    global txtForceDlScheduleCells, txtForceDlScheduleMCS , txtForceDlScheduleRbStart, txtForceDlScheduleRbN

    

    #nested_frame = tk.Frame(root)

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=2, column=0, columnspan=14,sticky="we", padx=(10,10), pady=(5,5))

 

    lblForceDlScheduleCells = ttk.Label(nested_frame, text="cells", width=10)

    lblForceDlScheduleCells.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceDlScheduleCells = ttk.Spinbox(nested_frame, from_=0, to=100, width = 5)

    txtForceDlScheduleCells.set(1)  # Set the default value

    txtForceDlScheduleCells.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    btnPhyDL = ttk.Button(nested_frame, text="Set", command=process_btnPhyDL)

    btnPhyDL.grid(row=0, column=2, sticky="ew", padx=(0,10), pady=(5,5))

 

    chkForceDlSchedule = ttk.Checkbutton(nested_frame, text="force_dl_schedule", variable=chkVarForceDlSchedule)

    chkForceDlSchedule.grid(row=0, column=3, sticky="w", padx=(10,10), pady=(5,5))

 

    chkPdschFixedRbAlloc= ttk.Checkbutton(nested_frame, text="fixed_rb_alloc", variable=chkVarPdschFixedRbAlloc)

    chkPdschFixedRbAlloc.grid(row=0, column=4, sticky="w", padx=(10,10), pady=(5,5))

 

    lblForceDlScheduleMCS = ttk.Label(nested_frame, text="MCS")

    lblForceDlScheduleMCS.grid(row=0, column=5, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceDlScheduleMCS = ttk.Spinbox(nested_frame, from_=0, to=28, width = 5)

    txtForceDlScheduleMCS.set(5)  # Set the default value

    txtForceDlScheduleMCS.grid(row=0, column=6, sticky="ew", padx=(10,0), pady=(5,5))

 

    lblForceDlScheduleRbStart = ttk.Label(nested_frame, text="Start RB")

    lblForceDlScheduleRbStart.grid(row=0, column=7, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceDlScheduleRbStart = ttk.Spinbox(nested_frame, from_=0, to=273, width = 5)

    txtForceDlScheduleRbStart.set(0)  # Set the default value

    txtForceDlScheduleRbStart.grid(row=0, column=8, sticky="ew", padx=(10,0), pady=(5,5))

 

    lblForceDlScheduleRbN = ttk.Label(nested_frame , text="N RB")

    lblForceDlScheduleRbN.grid(row=0, column=9, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceDlScheduleRbN = ttk.Spinbox(nested_frame, from_=1, to=273, width = 5)

    txtForceDlScheduleRbN.set(51)  # Set the default value

    txtForceDlScheduleRbN.grid(row=0, column=10, sticky="ew", padx=(10,0), pady=(5,5))

 

Section D (Row 4) - UL Phy Scheduling

This is the code for the Section D. For most part, you would not need any specific comments because those are just putting a specific GUI components at a specific position. The only thing that I want to comment is about process_btnPhyUL() without showing the code details. You just check out the code details of this function in the source code you can download in this tutorial.   

What process_btnPhyUL() does is to create a Remote API command based on options selected by checking / unchecking checkboxes. For example, if you check all the options the generated remoteAPI command is like this.

 

def create_row4_tab2(root):    

    

    global txtForceFullBsrCells, txtForceFullBsrMCS , txtForceFullBsrRbStart, txtForceFullBsrRbN

    #nested_frame = tk.Frame(root)

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=3, column=0, columnspan=14,sticky="we", padx=(10,10), pady=(5,5))

 

    lblForceFullBsrCells = ttk.Label(nested_frame, text="cells", width=10)

    lblForceFullBsrCells.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceFullBsrCells = ttk.Spinbox(nested_frame, from_=0, to=100, width = 5)

    txtForceFullBsrCells.set(1)  # Set the default value

    txtForceFullBsrCells.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    btnPhyUL = ttk.Button(nested_frame, text="Set", command=process_btnPhyUL)

    btnPhyUL.grid(row=0, column=2, sticky="ew", padx=(0,10), pady=(5,5))

 

    chkForceFullBsr = ttk.Checkbutton(nested_frame, text="force_full_bsr", variable=chkVarForceFullBsr)

    chkForceFullBsr.grid(row=0, column=3, sticky="w", padx=(10,10), pady=(5,5))

 

    chkPuschFixedRbAlloc= ttk.Checkbutton(nested_frame, text="fixed_rb_alloc", variable=chkVarPuschFixedRbAlloc)

    chkPuschFixedRbAlloc.grid(row=0, column=4, sticky="w", padx=(10,10), pady=(5,5))

 

    lblForceFullBsrMCS = ttk.Label(nested_frame, text="MCS")

    lblForceFullBsrMCS.grid(row=0, column=5, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceFullBsrMCS = ttk.Spinbox(nested_frame, from_=0, to=100, width = 5)

    txtForceFullBsrMCS.set(5)  # Set the default value

    txtForceFullBsrMCS.grid(row=0, column=6, sticky="ew", padx=(10,0), pady=(5,5))

 

    lblForceFullBsrRbStart = ttk.Label(nested_frame, text="Start RB")

    lblForceFullBsrRbStart.grid(row=0, column=7, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceFullBsrRbStart = ttk.Spinbox(nested_frame, from_=0, to=273, width = 5)

    txtForceFullBsrRbStart.set(0)  # Set the default value

    txtForceFullBsrRbStart.grid(row=0, column=8, sticky="ew", padx=(10,0), pady=(5,5))

 

    lblForceFullBsrRbN = ttk.Label(nested_frame , text="N RB")

    lblForceFullBsrRbN.grid(row=0, column=9, sticky="w", padx=(10,10), pady=(5,5))

 

    txtForceFullBsrRbN = ttk.Spinbox(nested_frame, from_=1, to=273, width = 5)

    txtForceFullBsrRbN.set(51)  # Set the default value

    txtForceFullBsrRbN.grid(row=0, column=10, sticky="ew", padx=(10,0), pady=(5,5))

 

Section E (Row 5) - Inactivity Timer Setting

This is the code for the Section E.  It is relatively simple GUI components and relatively simple remote API command. Again, the only thing I want to note is the 'Set' button and process_btnInactivityTimer() function. What this function does is to generate remote API command as follows :

def create_row5_tab2(root):

    

    global txtInactivityTimer, txtInactivityTimerCells

    #nested_frame = tk.Frame(root)

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=4, column=0, columnspan=14,sticky="we", padx=(10,10), pady=(5,5))

 

    lblInactivityTimerCells = ttk.Label(nested_frame, text="cells", width=10)

    lblInactivityTimerCells.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

 

    txtInactivityTimerCells = ttk.Spinbox(nested_frame, from_=0, to=100, width = 5)

    txtInactivityTimerCells.set(1)  # Set the default value

    txtInactivityTimerCells.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    lblInactivityTimer = ttk.Label(nested_frame, text="Inactivity Timer", width=15)

    lblInactivityTimer.grid(row=0, column=2, sticky="e", padx=(30,0), pady=(5,5))

 

    txtInactivityTimer = ttk.Spinbox(nested_frame, from_= 0, to = 600000, width = 10, increment=1000)

    txtInactivityTimer.set(300000)  # Set the default value

    txtInactivityTimer.grid(row=0, column=3, sticky="ew", padx=(0,0), pady=(5,5))

 

    btnInactivityTimer = ttk.Button(nested_frame, text="Set",command=process_btnInactivityTimer)

    btnInactivityTimer.grid(row=0, column=4, sticky="ew", padx=(0,10), pady=(5,5))

 

Section F (Row 6) - Update and Clear Log button

This is the code for the Section E. GUI component in this section is very simple. Only two buttons. The important part of this sections is process_LogUpdate() which is executed by clicking on [Update Log] button. This function may be one of the most complicated part in this program. Overview of what this function does is :

NOTE : Currently this functions does not seem to much robust. It works relatively well for the small sized log, but does not seem to work reliably when the size of the log very large (e.g, log with a lot of lower layer log or a lot of SIB messages). There might be some issues as below :

 

def create_row6_tab2(root):    

    

    # Configure the root's grid to expand horizontally

    root.grid_columnconfigure(0, weight=1)

 

    nested_frame = tk.Frame(root)

    nested_frame.grid(row=5, column=0, columnspan=14, sticky="ew") # make the frame expand to fill its grid cell in the horizontal direction

 

    # Configure the nested_frame's grid to expand horizontally

    nested_frame.grid_columnconfigure(0, weight=1)

 

    btnUpdateLog = ttk.Button(nested_frame, text="Update Log", command=lambda: process_LogUpdate())

    btnUpdateLog.grid(row=0, column=6) # the button is placed in the center of the frame

    btnClearLog = ttk.Button(nested_frame, text="Clear Log", command=lambda: process_LogClear())

    btnClearLog.grid(row=0, column=7) # the button is placed in the center of the frame

 

    # Add padding around the button to center it in the frame

    for i in range(1, 14):

        nested_frame.grid_columnconfigure(i, weight=1)

 

UEsim

Now let's look into the code for the UEsim tab. This is to control UEsim via Remote API and ssh. This is equivalent to Callbox(I) + Callbox(II). Since I don't do much of operations on UEsim, I tried to put every operations on a single tab.

RemoteAPI GUI CodeDescription UEsim 01

 

GUI Component Layout

This is to create all the graphical components within the UEsim tab. Overall idea is to create a frame(boundary is not shown though) filling out the whole tab area and put all the remaining components within the frame.  I would not cover the code for every sections labeled above. I would just go over the part that are associated with user interactions.

 # Create a new frame for the third tab

    frmTabUEsim = ttk.Frame(notebook)

    frmTabUEsim.grid_columnconfigure(0, weight=0)

    frmTabUEsim.grid_rowconfigure(0, weight=0)

    notebook.add(frmTabUEsim, text="UEsim")

 

    #create_row1_tabUEsim(frmTabUEsim,"10.0.0.35") # create txtIP, cboPort

    create_row1_tabUEsim(frmTabUEsim,"192.168.100.15") # create txtIP, cboPort

    create_row2_tabUEsim(frmTabUEsim) # create cboCellID, cboUEID

    create_row3_tabUEsim(frmTabUEsim) # create txtCommand

 

    chkVarTxGainAll_uesim = tk.IntVar(value=1)

    chkVarTxGain_uesim = []  # create list to store IntVars

    create_row4_tabUEsim(frmTabUEsim) # Create RxGain Settings

 

    chkVarRxGainAll_uesim = tk.IntVar(value=1)

    chkVarRxGain_uesim = []  # create list to store IntVars

    create_row5_tabUEsim(frmTabUEsim) # Create RxGain Settings

 

    create_row6_tabUEsim(frmTabUEsim) # create btnPowerOn_uesim, btnPowerOff_uesim,

    create_row7_tabUEsim(frmTabUEsim)

    create_row8_tabUEsim(frmTabUEsim) # create txtCommand_uesim

    create_row9_tabUEsim(frmTabUEsim) # create btnSend_uesim

    create_row10_tabUEsim(frmTabUEsim) # create txtReturn_uesim

    create_row11_tabUEsim(frmTabUEsim) # create btnServiceStop_uesim, btnServiceRestart_uesim

 

    frmTabUEsim.grid_columnconfigure(5, weight=1) # weight=0 --> not resizable, weight=1 --> resizable

    frmTabUEsim.grid_rowconfigure(10, weight=1) # weight=0 --> not resizable, weight=1 --> resizable

 

Section D (Row 4) - Set button, Tx Gain Value setting field

This is the code for the Section D. For most part, you would not need any specific comments because those are just putting a specific GUI components at a specific position. The only thing that I want to comment is about process_btnTxGain_uesim() without showing the code details. You just check out the code details of this function in the source code you can download in this tutorial.

What process_btnTxGain_uesim() does is :

Followings are examples of RemoteAPI command generated by 'Set' button

def create_row4_tabUEsim(root):

 

    global txtTxGain_uesim

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=4, column=0, columnspan=18,sticky="we", padx=(10,10), pady=(15,5))

 

    # i)

    lblTxGain = ttk.Label(nested_frame , text="tx_gain", width=10)

    lblTxGain.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

    chkTxGainAll_uesim = ttk.Checkbutton(nested_frame, text="", variable=chkVarTxGainAll_uesim)

    chkTxGainAll_uesim.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    # ii)

    txtTxGain_uesim = ttk.Spinbox(nested_frame , from_=0, to=100, width=5)

    txtTxGain_uesim.set(90)  # Set the default value

    txtTxGain_uesim.grid(row=0, column=2, sticky="ew", padx=(10,0), pady=(5,5))

 

    # iii)

    btnTxGain_uesim = ttk.Button(nested_frame, text="Set", command=process_btnTxGain_uesim)

    btnTxGain_uesim.grid(row=0, column=3, sticky="ew", padx=(0,10), pady=(5,5))

 

 

    for i in range(8):

        spinVarTxGain_uesim.append(tk.IntVar(value=90))  

        lblTxGain = ttk.Label(nested_frame , text="TX{}".format(i))

        lblTxGain.grid(row=0, column=4+2*i, sticky="w", padx=(10,0), pady=(5,5))

        spinTxGain_uesim = ttk.Spinbox(nested_frame, from_=0, to=90, textvariable=spinVarTxGain_uesim[i], width=5, name='txtTxGainNo_uesim{}'.format(i))

        spinTxGain_uesim.grid(row=0, column=4+2*i+1, sticky="w", padx=(0,10), pady=(5,5))

 

Section E (Row 5) - Set button, Rx Gain Value setting field

This is the code for the Section E. For most part, you would not need any specific comments because those are just putting a specific GUI components at a specific position. The only thing that I want to comment is about process_btnRxGain_uesim() without showing the code details. You just check out the code details of this function in the source code you can download in this tutorial.

What process_btnRxGain_uesim() does is :

Followings are examples of RemoteAPI command generated by 'Set' button

 

def create_row5_tabUEsim(root):

 

    global txtRxGain_uesim

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=5, column=0, columnspan=18,sticky="we", padx=(10,10), pady=(5,5))

 

    # i)

    lblRxGain = ttk.Label(nested_frame , text="rx_gain", width=10)

    lblRxGain.grid(row=0, column=0, sticky="w", padx=(10,10), pady=(5,5))

    chkRxGainAll_uesim = ttk.Checkbutton(nested_frame, text="", variable=chkVarRxGainAll_uesim)

    chkRxGainAll_uesim.grid(row=0, column=1, sticky="ew", padx=(10,0), pady=(5,5))

 

    # ii)

    txtRxGain_uesim = ttk.Spinbox(nested_frame , from_=0, to=100, width=5)

    txtRxGain_uesim.set(60)  # Set the default value

    txtRxGain_uesim.grid(row=0, column=2, sticky="ew", padx=(10,0), pady=(5,5))

 

    # iii)

    btnRxGain_uesim = ttk.Button(nested_frame, text="Set", command=process_btnRxGain_uesim)

    btnRxGain_uesim.grid(row=0, column=3, sticky="ew", padx=(0,10), pady=(5,5))

 

 

    for i in range(8):

        spinVarRxGain_uesim.append(tk.IntVar(value=60))  

        lblRxGain = ttk.Label(nested_frame , text="RX{}".format(i))

        lblRxGain.grid(row=0, column=4+2*i, sticky="w", padx=(10,0), pady=(5,5))

        spinRxGain_uesim = ttk.Spinbox(nested_frame, from_=0, to=90, textvariable=spinVarRxGain_uesim[i], width=5, name='txtRxGainNo_uesim{}'.format(i))

        spinRxGain_uesim.grid(row=0, column=4+2*i+1, sticky="w", padx=(0,10), pady=(5,5))

 

Section F (Row 6) - Power On/Power Off button

This is the code for the Power On and Power Off button. Main points are process_btnPowerOn_uesim() and process_btnPowerOff_uesim() functions. process_btnPowerOn_uesim() is executed when btnPowerOn_uesim is clicked process_btnPowerOff_uesim() is executed when btnPowerOff_uesim button is clicked.

What process_btnPowerOn_uesim() button does is to send remote API message '{"message": "power_on", "ue_id": 1 }' and what process_btnPowerOff_uesim() button does is to send remote API message '{"message": "power_off", "ue_id": 1 }'

 

def create_row6_tabUEsim(root):

 

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=6, column=0, columnspan=18,sticky="we", padx=(10,10), pady=(5,5))

 

    title = tk.Label(nested_frame, text="Frequently Used Functions")

    title.grid(row=0, column=0, sticky='w', padx=(10,10), pady=(5,5))

 

    btnPowerOn_uesim = ttk.Button(nested_frame, text="Power On", command=lambda: process_btnPowerOn_uesim())

    btnPowerOn_uesim.grid(row=0, column=1, sticky='w', padx=(10,10), pady=(5,5))

    

    btnPowerOff_uesim = ttk.Button(nested_frame, text="Power Off", command=lambda: process_btnPowerOff_uesim())

    btnPowerOff_uesim.grid(row=0, column=2, sticky='w', padx=(10,10), pady=(5,5))

 

Section I (Row 9) - Send button

This is the code for the Section I. High level code is very simple as shown below. What it does is just to create a button labeled 'Send' (btnSend_uesim) and link it with the function send_command_uesim(). As you may guess, the important part is the implementation of send_command_uesim() function. Basically send_command_uesim() is super simple version of RemoteAPI program.

If you want to understand the details of send_command_uesim() function, check out this tutorial first. If you don't have any problem with understanding Remote API Python tutorial, you should have no problem with understanding send_command_uesim() function.

def create_row9_tabUEsim(root):    

 

    btnSend_uesim = ttk.Button(root, text="Send", command=lambda: send_command_uesim())

    btnSend_uesim.grid(row=9, column=0, columnspan=6, sticky="ew", padx=(400, 400), pady=(5, 5))

 

Section K (Row 11) - Stop LTE Service/Restart LTE Service

This is the code for the Section K. What it does is just to create three buttons labeled 'Stop LTE service' (btnServiceStop_uesim) and  'Restart LTE service' (btnServiceRestart_uesim ) respectively, and link them with the corresponding function(callback) as commented below. Bascially the two buttons are to setup an ssh socket and send 'service lte stop' or 'service lte restart' command.

def create_row11_tabUEsim(root):

 

    nested_frame = tk.Frame(root, bd=2, relief="groove")

    nested_frame.grid(row=11, column=0, columnspan=18,sticky="we", padx=(10,10), pady=(5,5))

 

    #title = tk.Label(nested_frame, text="Frequently Used Functions")

    #title.grid(row=0, column=0, sticky='w', padx=(10,10), pady=(5,5))

 

    btnServiceStop_uesim = ttk.Button(nested_frame, text="Stop LTE Service", command=lambda: lteServiceStop_uesim(txtIP_uesim))

    btnServiceStop_uesim.grid(row=0, column=0, sticky='w', padx=(10,10), pady=(5,5))

 

    btnServiceRestart_uesim = ttk.Button(nested_frame, text="Restart LTE Service", command=lambda: lteServiceRestart_uesim(txtIP_uesim))

    btnServiceRestart_uesim.grid(row=0, column=1, sticky='w', padx=(10,10), pady=(5,5))

 

 

FileBrowser (Callbox)

Now let's look into the code for the FileBrowser(Callbox) tab. This is to make two file browsers : one for file browser on local PC and the other one for the file browser on remotePC (callbox) and let them upload/download files between them.

RemoteAPI GUI CodeDescription FileBrowser Callbox 01

 

GUI Component Layout

This is to create all the graphical components within the FileBrowser(Callbox). This is to create the sub section (A),(B),(C) within this tab.

frmTabFiles = ttk.Frame(notebook)

frmTabFiles.grid_columnconfigure(0, weight=0)

frmTabFiles.grid_rowconfigure(0, weight=0)

notebook.add(frmTabFiles, text="File Browser(Callbox)")

 

frameLeft, frameRight = create_frame_tabFiles(frmTabFiles)  # create three vertical frames on frmTabFiles

fb = FileBrowser(master=frameLeft, path='C:\\') # create Local File Brwoser on frameLeft

fbSCP  = FileBrowserSCP(master=frameRight , initType = 'Callbox',  initpath='/root',initRemoteIP='192.168.100.17')

Following is to creates three sub sections in this tab : left section, right section and middle section.

def create_frame_tabFiles(root):

    # Create PanedWindow for vertical frames

    panedwindow = tk.PanedWindow(root, orient='horizontal')

    panedwindow.pack(fill='both', expand=True)

 

    # Create the frames

    frameLeft = ttk.Frame(panedwindow, relief='groove', borderwidth=1)

    frameMiddle = ttk.Frame(panedwindow, relief='groove', borderwidth=1)

    frameRight = ttk.Frame(panedwindow, relief='groove', borderwidth=1)

 

    # Add the frames to the PanedWindow

    panedwindow.add(frameLeft, stretch='always')

    panedwindow.add(frameMiddle)

    panedwindow.add(frameRight, stretch='always')

 

    # Set the width of frameMiddle to 100 pixels

    frameMiddle.config(width=100)

 

    # Add buttons to frameMiddle

    btnDownload = ttk.Button(frameMiddle, text='<-Download', command=lambda: process_Download(fb, fbSCP))

    btnUpload = ttk.Button(frameMiddle, text='Upload->',command=lambda:process_Upload(fb, fbSCP))

 

    # Calculate the vertical center position for the buttons

    btnDownload.place(relx=0.5, rely=0.45, anchor='center')

    btnUpload.place(relx=0.5, rely=0.55, anchor='center')

 

    return frameLeft, frameRight

Following is for the GUI component layout for local PC file browser layout which is a method of FileBrowser Class.  This function creates all the gui components within the section (A).

    def create_widgets(self):

 

        # Load the icons

        self.folder_icon = tk.PhotoImage(file="icon_dir.png")

        self.file_icon = tk.PhotoImage(file="icon_file.png")

 

        # Create a Menu

        self.popup_menu = tk.Menu(self, tearoff=0)

 

        # Add commands to the menu

        self.popup_menu.add_command(label="Rename", command=self.rename_file)

        self.popup_menu.add_command(label="Copy", command=self.copy_file)

        self.popup_menu.add_command(label="Paste", command=self.paste_file)

        self.popup_menu.add_command(label="Delete", command=self.delete_file)

        self.popup_menu.add_command(label="Create Directory", command=self.create_dir)

        self.popup_menu.add_separator()

        self.popup_menu.add_command(label="Open File", command=self.open_with_simple_notepad)

        self.popup_menu.add_separator()

        self.popup_menu.add_command(label="Refresh", command=self.update_file_list)

 

        self.navigation_frame = tk.Frame(self)  # Frame to hold the Entry and Button

        self.navigation_frame.pack(fill='x')

        self.directory = tk.StringVar(value=self.path)

 

        self.entry = ttk.Entry(self.navigation_frame, textvariable=self.directory)

        self.button = ttk.Button(self.navigation_frame, text="Navigate", command=self.update_file_list)

 

        self.entry.pack(side='left', fill='x', padx=[5,0], pady=[5,5], expand=True)

        self.button.pack(side='right',padx=[5,5])

 

        # Bind the Enter key to the update_file_list function

        self.entry.bind('<Return>', lambda event: self.update_file_list())

 

        # Creating button frame

        self.button_frame = tk.Frame(self)

        self.button_frame.pack(fill='x')

 

        self.rename_button = ttk.Button(self.button_frame, text="Rename", command=self.rename_file)

        self.copy_button = ttk.Button(self.button_frame, text="Copy", command=self.copy_file)

        self.paste_button = ttk.Button(self.button_frame, text="Paste", command=self.paste_file)

        self.delete_button = ttk.Button(self.button_frame, text="Delete", command=self.delete_file)

        self.create_dir_button = ttk.Button(self.button_frame, text="Create Dir", command=self.create_dir)

 

        self.rename_button.pack(side='left', padx=[5,0], pady=[0,5])

        self.copy_button.pack(side='left', pady=[0,5])

        self.paste_button.pack(side='left', pady=[0,5])

        self.delete_button.pack(side='left', pady=[0,5])

        self.create_dir_button.pack(side='left', padx=[0,5], pady=[0,5])

 

        self.file_frame = tk.Frame(self)  # Frame to hold the Treeview and Scrollbar

        self.file_frame.pack(fill='both', expand=True)

 

        self.file_list = ttk.Treeview(self.file_frame, columns=('fullpath', 'type', 'size'), show='tree headings')  # Note the change here

        self.file_list.column("#1", width=100)

        self.file_list.column("#2", width=150)

        self.file_list.column("#3", width=100)

        self.file_list.heading("#0", text="File Name")

        self.file_list.heading("#1", text="Size (bytes)")

        self.file_list.heading("#2", text="Modified Time")

        self.file_list.heading("#3", text="File Type")

        self.file_list.bind('<Double-1>', self.navigate)  # Bind the function navigate to double click event

        self.file_list.tag_configure('parent_directory', font=('TkDefaultFont', 14, 'bold'))

        self.file_list.bind("<Button-3>", self.show_popup_menu)  # Button-3 is right-click

 

        # Add binding for 'Ctrl + C' to copy_file

        self.file_list.bind('<Control-c>', self.copy_file)

 

        # Add binding for 'Ctrl + V' to paste_file

        self.file_list.bind('<Control-v>', self.paste_file)

 

        # Add binding for 'File Selection' to paste_file

        self.file_list.bind('<<TreeviewSelect>>', self.select_file)

 

        self.scrollbar = ttk.Scrollbar(self.file_frame, orient="vertical", command=self.file_list.yview)

        self.file_list.configure(yscrollcommand=self.scrollbar.set)

 

        self.file_list.pack(side='left', fill='both', expand=True, padx=[5,0], pady=[0,5]) # Use side option to pack Treeview and Scrollbar horizontally

        self.scrollbar.pack(side='right', fill='y')

Following is for the GUI component layout for local PC file browser layout which is a method of FileBrowserSCP Class.  This function creates all the gui components within the section (C).

    def create_widgets(self):

 

        # Load the icons

        self.folder_icon = tk.PhotoImage(file="icon_dir.png")

        self.file_icon = tk.PhotoImage(file="icon_file.png")

 

        # Create a Menu

        self.popup_menu = tk.Menu(self, tearoff=0)

 

        # Add commands to the menu

        self.popup_menu.add_command(label="Rename", command=self.rename_file)

        self.popup_menu.add_command(label="Copy", command=self.copy_file)

        self.popup_menu.add_command(label="Paste", command=self.paste_file)

        self.popup_menu.add_command(label="Delete", command=self.delete_file)

        self.popup_menu.add_command(label="Create Directory", command=self.create_dir)

        self.popup_menu.add_separator()

        self.popup_menu.add_command(label="Open File", command=self.open_with_simple_notepad)

        self.popup_menu.add_separator()

        self.popup_menu.add_command(label="Refresh", command=self.update_file_list)

 

        self.navigation_frame = tk.Frame(self)  # Frame to hold the Entry and Button

        self.navigation_frame.pack(fill='x')

        self.directory = tk.StringVar(value=self.path)

 

        self.entry = ttk.Entry(self.navigation_frame, textvariable=self.directory)

        self.button = ttk.Button(self.navigation_frame, text="Navigate", command=self.update_file_list)

 

        self.entry.pack(side='left', fill='x', padx=[5,0], pady=[5,5], expand=True)

        self.button.pack(side='right',padx=[5,5])

 

        # Bind the Enter key to the update_file_list function

        self.entry.bind('<Return>', lambda event: self.update_file_list())

 

        # Creating button frame

        self.button_frame = tk.Frame(self)

        self.button_frame.pack(fill='x')

 

        self.rename_button = ttk.Button(self.button_frame, text="Rename", command=self.rename_file)

        self.copy_button = ttk.Button(self.button_frame, text="Copy", command=self.copy_file)

        self.paste_button = ttk.Button(self.button_frame, text="Paste", command=self.paste_file)

        self.delete_button = ttk.Button(self.button_frame, text="Delete", command=self.delete_file)

        self.create_dir_button = ttk.Button(self.button_frame, text="Create Dir", command=self.create_dir)

 

        self.rename_button.pack(side='left', padx=[5,0], pady=[0,5])

        self.copy_button.pack(side='left', pady=[0,5])

        self.paste_button.pack(side='left', pady=[0,5])

        self.delete_button.pack(side='left', pady=[0,5])

        self.create_dir_button.pack(side='left', padx=[0,5], pady=[0,5])

 

        self.file_frame = tk.Frame(self)  # Frame to hold the Treeview and Scrollbar

        self.file_frame.pack(fill='both', expand=True)

 

        self.file_list = ttk.Treeview(self.file_frame, columns=('fullpath', 'type', 'size'), show='tree headings')  # Note the change here

        self.file_list.column("#1", width=100)

        self.file_list.column("#2", width=150)

        self.file_list.column("#3", width=100)

        self.file_list.heading("#0", text="File Name")

        self.file_list.heading("#1", text="Size (bytes)")

        self.file_list.heading("#2", text="Modified Time")

        self.file_list.heading("#3", text="File Type")

        self.file_list.bind('<Double-1>', self.navigate)  # Bind the function navigate to double click event

        self.file_list.tag_configure('parent_directory', font=('TkDefaultFont', 14, 'bold'))

        self.file_list.bind("<Button-3>", self.show_popup_menu)  # Button-3 is right-click

 

        # Add binding for 'Ctrl + C' to copy_file

        self.file_list.bind('<Control-c>', self.copy_file)

 

        # Add binding for 'Ctrl + V' to paste_file

        self.file_list.bind('<Control-v>', self.paste_file)

 

        # Add binding for 'File Selection' to paste_file

        self.file_list.bind('<<TreeviewSelect>>', self.select_file)

 

        self.scrollbar = ttk.Scrollbar(self.file_frame, orient="vertical", command=self.file_list.yview)

        self.file_list.configure(yscrollcommand=self.scrollbar.set)

 

        self.file_list.pack(side='left', fill='both', expand=True, padx=[5,0], pady=[0,5]) # Use side option to pack Treeview and Scrollbar horizontally

        self.scrollbar.pack(side='right', fill='y')

 

Section B  - Download/Upload button

Most of this code is for creating and placing GUI components which are straightforward. The code to pay attention tos are following two functions.

def create_frame_tabFiles(root):

    # Create PanedWindow for vertical frames

    panedwindow = tk.PanedWindow(root, orient='horizontal')

    panedwindow.pack(fill='both', expand=True)

 

    # Create the frames

    frameLeft = ttk.Frame(panedwindow, relief='groove', borderwidth=1)

    frameMiddle = ttk.Frame(panedwindow, relief='groove', borderwidth=1)

    frameRight = ttk.Frame(panedwindow, relief='groove', borderwidth=1)

 

    # Add the frames to the PanedWindow

    panedwindow.add(frameLeft, stretch='always')

    panedwindow.add(frameMiddle)

    panedwindow.add(frameRight, stretch='always')

 

    # Set the width of frameMiddle to 100 pixels

    frameMiddle.config(width=100)

 

    # Add buttons to frameMiddle

    btnDownload = ttk.Button(frameMiddle, text='<-Download', command=lambda: process_Download(fb, fbSCP))

    btnUpload = ttk.Button(frameMiddle, text='Upload->',command=lambda:process_Upload(fb, fbSCP))

 

    # Calculate the vertical center position for the buttons

    btnDownload.place(relx=0.5, rely=0.45, anchor='center')

    btnUpload.place(relx=0.5, rely=0.55, anchor='center')

 

    return frameLeft, frameRight

 

Section D  - Navigate button on Local PC

What this code does is to move the path specified in path textbox and list all the directories and files within the path. This procedure is done by the method update_file_list().

    # In FileBrowser() Class

    def create_widgets(self):

        ....

        self.navigation_frame = tk.Frame(self)  # Frame to hold the Entry and Button

        self.navigation_frame.pack(fill='x')

        self.directory = tk.StringVar(value=self.path)

 

        self.entry = ttk.Entry(self.navigation_frame, textvariable=self.directory)

        self.button = ttk.Button(self.navigation_frame, text="Navigate", command=self.update_file_list)

        ....

 

Section E  - Menu Buttons (Copy, Paste, Delete, Rename, Create Dir) on Local PC

What this code does is to perform the basic operations in file browser, which are Copy, Paste, Delete, Rename, Create Dir. These operations are done by following methods. If you want to check out the detailed implementation, look into the definition of these functions.

    # In FileBrowser() Class

    def create_widgets(self):

        ....

        # Creating button frame

        self.button_frame = tk.Frame(self)

        self.button_frame.pack(fill='x')

 

        self.rename_button = ttk.Button(self.button_frame, text="Rename", command=self.rename_file)

        self.copy_button = ttk.Button(self.button_frame, text="Copy", command=self.copy_file)

        self.paste_button = ttk.Button(self.button_frame, text="Paste", command=self.paste_file)

        self.delete_button = ttk.Button(self.button_frame, text="Delete", command=self.delete_file)

        self.create_dir_button = ttk.Button(self.button_frame, text="Create Dir", command=self.create_dir)

        ....

 

Section E-1  - Popup Menu  (Copy, Paste, Delete, Rename, Create Directory, Open File) on Local PC

This is a mirror functionality of the buttons explained in previous section except 'Open File' and 'Refresh' menu item. If you want to check out the detailed implementation, look into the definition of these functions.

    # In FileBrowser() Class

    def create_widgets(self):

        ....

        # Create a Menu

        self.popup_menu = tk.Menu(self, tearoff=0)

 

        # Add commands to the menu

        self.popup_menu.add_command(label="Rename", command=self.rename_file)

        self.popup_menu.add_command(label="Copy", command=self.copy_file)

        self.popup_menu.add_command(label="Paste", command=self.paste_file)

        self.popup_menu.add_command(label="Delete", command=self.delete_file)

        self.popup_menu.add_command(label="Create Directory", command=self.create_dir)

        self.popup_menu.add_separator()

        self.popup_menu.add_command(label="Open File", command=self.open_with_simple_notepad)

        self.popup_menu.add_separator()

        self.popup_menu.add_command(label="Refresh", command=self.update_file_list)

        ....

 

Section F  - File Browser Table on Local PC

This is the code to populate the table with file / directory list within the path specified in 'Navigate' input box. It also responds to mouse double click to move to parent or child directory and respond to mouse right click to show the popup menu. Followings are the two import lines to look into if you want to get the details.

    # In FileBrowser() Class

    def create_widgets(self):

        ....

        self.file_list = ttk.Treeview(self.file_frame, columns=('fullpath', 'type', 'size'), show='tree headings')  # Note the change here

        self.file_list.column("#1", width=100)

        self.file_list.column("#2", width=150)

        self.file_list.column("#3", width=100)

        self.file_list.heading("#0", text="File Name")

        self.file_list.heading("#1", text="Size (bytes)")

        self.file_list.heading("#2", text="Modified Time")

        self.file_list.heading("#3", text="File Type")

        self.file_list.bind('<Double-1>', self.navigate)  # Bind the function navigate to double click event

        self.file_list.tag_configure('parent_directory', font=('TkDefaultFont', 14, 'bold'))

        self.file_list.bind("<Button-3>", self.show_popup_menu)  # Button-3 is right-click

        ....

 

Section G  - Connection to Remote PC

This is to establish ssh connection to Remote PC. Most of the code is to create and place GUI components which does not require any explanation. For the details of the implementation of the operation (i.e, establishing ssh connection), look into the following function.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # IP address widget

        self.ip_label = ttk.Label(self.connection_frame, text="IP Address:")

        self.ip_label.grid(row=0, column=0, sticky='w')

        self.ip_entry = ttk.Entry(self.connection_frame)

        self.ip_entry.grid(row=0, column=1, sticky='ew')

        self.ip_entry.insert(0, self.remote_ip)

 

        # Username widget

        self.username_label = ttk.Label(self.connection_frame, text="Username:")

        self.username_label.grid(row=0, column=2, sticky='w')

        self.username_entry = ttk.Entry(self.connection_frame)

        self.username_entry.grid(row=0, column=3, sticky='ew')

        self.username_entry.insert(0, self.remote_username)

 

        # Password widget

        self.password_label = ttk.Label(self.connection_frame, text="Password:")

        self.password_label.grid(row=0, column=4, sticky='w')

        self.password_entry = ttk.Entry(self.connection_frame, show="*")

        self.password_entry.grid(row=0, column=5, sticky='ew')

        self.password_entry.insert(0, self.remote_password)

 

        self.connected = False

 

        # Connect/Disconnect button

        self.connect_button = ttk.Button(self.connection_frame, text="Connect", command=self.toggle_connection)

        self.connect_button.grid(row=0, column=6, sticky='ew')

        ....

 

Section H  - Navigate button on Remote PC

What this does is to change directory to the specified path (specified in the inputbox) and populate the file list with the subdirectories and files within the directory. Following function is the one that performs this operation.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        self.navigation_frame = tk.Frame(self)  

        self.navigation_frame.grid(row=1, column=0, sticky='we')

        self.navigation_frame.grid_columnconfigure(0, weight=1)

 

        self.directory = tk.StringVar(value=self.path)

        self.entry = ttk.Entry(self.navigation_frame, textvariable=self.directory)

        self.button = ttk.Button(self.navigation_frame, text="Navigate", command=self.navigate_to_directory)

        ....

 

Section I  - Menu Buttons (Copy, Paste, Delete, Rename, Create Dir, Link enb, Link mme) on Remote PC

What this code does is to perform the basic operations in file browser on remote PC, which are Copy, Paste, Delete, Rename, Create Dir. These operations are done by following methods. If you want to check out the detailed implementation, look into the definition of these functions.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Creating button frame

        self.button_frame = tk.Frame(self)

        #self.button_frame.pack(fill='x')

        self.button_frame.grid(row=2, column=0, sticky='nsew')

 

        self.rename_button = ttk.Button(self.button_frame, text="Rename", command=self.rename_file)

        self.copy_button = ttk.Button(self.button_frame, text="Copy", command=self.copy_file)

        self.paste_button = ttk.Button(self.button_frame, text="Paste", command=self.paste_file)

        self.delete_button = ttk.Button(self.button_frame, text="Delete", command=self.delete_file)

        self.create_dir_button = ttk.Button(self.button_frame, text="Create Dir", command=self.create_dir)

        if self.Type == 'CALLBOX' :

            self.linkenb_button = ttk.Button(self.button_frame, text="Link enb", command=self.link_enb)

            self.linkmme_button = ttk.Button(self.button_frame, text="Link mme", command=self.link_mme)

        if self.Type == 'UESIM' :

            self.linkue_button = ttk.Button(self.button_frame, text="Link ue", command=self.link_ue)

    

        self.rename_button.pack(side='left', padx=[5,0], pady=[0,5])

        self.copy_button.pack(side='left', pady=[0,5])

        self.paste_button.pack(side='left', pady=[0,5])

        self.delete_button.pack(side='left', pady=[0,5])

        self.create_dir_button.pack(side='left', pady=[0,5])

        if self.Type == 'CALLBOX' :

            self.linkenb_button.pack(side='left', padx=[10,0], pady=[0,5])

            self.linkmme_button.pack(side='left', pady=[0,5])

        if self.Type == 'UESIM' :

            self.linkue_button.pack(side='left', padx=[10,0], pady=[0,5])    

        ....

 

Section I-1  - Popup Menu  (Copy, Paste, Delete, Rename, Create Directory, Link enb, Link mme) on Remote PC

This is a mirror functionality of the buttons explained in previous section except 'Open File' and 'Refresh' menu item. If you want to check out the detailed implementation, look into the definition of these functions.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Create a Menu

        self.popup_menu = tk.Menu(self, tearoff=0)

        self.popup_menu.add_command(label="Rename", command=self.rename_file)

        self.popup_menu.add_command(label="Copy", command=self.copy_file)

        self.popup_menu.add_command(label="Paste", command=self.paste_file)

        self.popup_menu.add_command(label="Delete", command=self.delete_file)

        self.popup_menu.add_command(label="Create Directory", command=self.create_dir)

        self.popup_menu.add_separator()

        if self.Type == 'CALLBOX' :

            self.popup_menu.add_command(label="Link enb", command=self.link_enb)

            self.popup_menu.add_command(label="Link mme", command=self.link_mme)

        if self.Type == 'UESIM' :

            self.popup_menu.add_command(label="Link ue", command=self.link_ue)

        self.popup_menu.add_separator()    

        self.popup_menu.add_command(label="Refresh", command=self.update_file_list)

        ....

 

Section J  - File Browser Table on Remote PC

This is the code to populate the table with file / directory list within the path specified in 'Navigate' input box. It also responds to mouse double click to move to parent or child directory and respond to mouse right click to show the popup menu. Followings are the two import lines to look into if you want to get the details.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Creating Fileview frame

        self.file_frame = tk.Frame(self)

        self.file_frame.grid(row=3, column=0, sticky='nsew')

        self.file_frame.rowconfigure(0, weight=1)

        self.file_frame.columnconfigure(0, weight=1)

        self.rowconfigure(3, weight=1)

        self.columnconfigure(0, weight=1)   

 

        self.file_list = ttk.Treeview(self.file_frame, columns=('fullpath', 'type', 'size'), show='tree headings')

        self.file_list.column("#1", width=100)

        self.file_list.column("#2", width=150)

        self.file_list.column("#3", width=100)

        self.file_list.heading("#0", text="File Name")

        self.file_list.heading("#1", text="Size (bytes)")

        self.file_list.heading("#2", text="Modified Time")

        self.file_list.heading("#3", text="File Type")

        self.file_list.bind('<Double-1>', self.navigate)

        self.file_list.tag_configure('parent_directory', font=('TkDefaultFont', 14, 'bold'))

        self.file_list.bind("<Button-3>", self.show_popup_menu)

 

        self.file_list.bind('<Control-c>', self.copy_file)

        self.file_list.bind('<Control-v>', self.paste_file)

 

        self.file_list.bind('<<TreeviewSelect>>', self.select_file)

 

        self.file_list.grid(row=0, column=0, padx=5, pady=5, sticky='nsew')

        self.file_frame.columnconfigure(0, weight=1)

        self.file_frame.rowconfigure(0, weight=1)

 

        self.scrollbar = ttk.Scrollbar(self.file_frame, orient="vertical", command=self.file_list.yview)

        self.scrollbar.grid(row=0, column=1, sticky='ns')

        ....

 

Section K - Menu Buttons (goto Log, goto Log backup, goto enb config, go to mme config) on Remote PC

This code is to change directories to a couple of predefined directories which are the most frequently used for callbox operation. Following functions are what you need to look into for further details.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # create the button frame at the bottom

        self.button_frame = tk.Frame(self)

        self.button_frame.grid(row=4, column=0, sticky='we')

 

        self.goto_log_button = ttk.Button(self.button_frame, text="goto Log", command=self.goto_log, width=15)

        self.goto_log_backup_button = ttk.Button(self.button_frame, text="goto Log Backup", command=self.goto_log_backup, width=15)

        if self.Type == 'CALLBOX' :

            self.goto_enb_config_button = ttk.Button(self.button_frame, text="goto enb config", command=self.goto_enb_config, width=15)

            self.goto_mme_config_button = ttk.Button(self.button_frame, text="goto mme config", command=self.goto_mme_config, width=15)

        if self.Type == 'UESIM' :

            self.goto_ue_config_button = ttk.Button(self.button_frame, text="goto ue config", command=self.goto_ue_config, width=15)

 

        self.goto_log_button.grid(row=0, column=0, padx=5, pady=5, sticky='ew')

        self.goto_log_backup_button.grid(row=0, column=1, padx=5, pady=5, sticky='ew')

        if self.Type == 'CALLBOX' :

            self.goto_enb_config_button.grid(row=0, column=2, padx=5, pady=5, sticky='ew')

            self.goto_mme_config_button.grid(row=0, column=3, padx=5, pady=5, sticky='ew')

        if self.Type == 'UESIM' :

            self.goto_ue_config_button.grid(row=0, column=2, padx=5, pady=5, sticky='ew')   

        ....

 

 

FileBrowser (UEsim)

Now let's look into the code for the FileBrowser(UEsim) tab. This is to make two file browsers : one for file browser on local PC and the other one for the file browser on remotePC (UEsim) and let them upload/download files between them.

RemoteAPI GUI CodeDescription FileBrowser UEsim 01

 

GUI Component Layout

This is to create all the graphical components within the FileBrowser(UEsim). This is to create the sub section (A),(B),(C) within this tab.

   # Create a new frame for the File Browser (UEsim)

    frmTabFiles_UEsim = ttk.Frame(notebook)

    frmTabFiles_UEsim .grid_columnconfigure(0, weight=0)

    frmTabFiles_UEsim .grid_rowconfigure(0, weight=0)

    notebook.add(frmTabFiles_UEsim , text="File Browser(UEsim)")

 

    frameLeft_UEsim , frameRight_UEsim  = create_frame_tabFiles_UEsim(frmTabFiles_UEsim)  # create three vertical frames on frmTabFiles

    fb_UEsim  = FileBrowser(master=frameLeft_UEsim , path='C:\\') # create Local File Brwoser on frameLeft

    fbSCP_UEsim  = FileBrowserSCP(master=frameRight_UEsim , initType = 'UEsim',  initpath='/root',initRemoteIP='192.168.100.15')

 

All other Sections : (D)~(K)

The code for these sections are identical to the part used for FileBrowser (Callbox).

 

 

Screen (Callbox)

This is the code for the Screen(Callbox) tab.

RemoteAPI GUI CodeDescription Screen Callbox 01

 

GUI Component Layout

This is to create all the graphical components within the Screen(Callbox).  

    # Create a new frame for the Screen (Callbox)

    frmTabScreen_Callbox = ttk.Frame(notebook)

    frmTabScreen_Callbox.grid_columnconfigure(0, weight=0)

    frmTabScreen_Callbox.grid_rowconfigure(0, weight=0)

    notebook.add(frmTabScreen_Callbox, text="Screen(Callbox)")

 

    frameScreen_Callbox = create_frame_tabScreen_Callbox(frmTabScreen_Callbox)  # create three vertical frames on frmTabFiles

    screen_CallBox  = ScreenSSH(master=frameScreen_Callbox , initType = 'Callbox',  initpath='/root',initRemoteIP='192.168.100.17')

Following is for the GUI component layout for local PC file browser layout which is a method of FileBrowser Class.  This function creates all the gui components within the section (A).

    #In ScreenSSH() class

    def create_widgets(self):

 

        self.grid(sticky='nsew')

        self.master.grid_rowconfigure(0, weight=1)

        self.master.grid_columnconfigure(0, weight=1)

 

        # Initialize SFTP client as None

        self.ssh = None

        self.sftp_client = None

 

        self.paths_to_copy = []

 

        # Load the icons

        self.folder_icon = tk.PhotoImage(file="icon_dir.png")

        self.file_icon = tk.PhotoImage(file="icon_file.png")

        self.link_icon = tk.PhotoImage(file="icon_link.png")

 

        # Create connection frame

        self.connection_frame = ttk.Frame(self)

        self.connection_frame.grid(row=0, column=0, sticky='ew')

 

        # IP address widget

        self.ip_label = ttk.Label(self.connection_frame, text="IP Address:")

        self.ip_label.grid(row=0, column=0, sticky='w')

        self.ip_entry = ttk.Entry(self.connection_frame)

        self.ip_entry.grid(row=0, column=1, sticky='ew')

        self.ip_entry.insert(0, self.remote_ip)

 

        # Username widget

        self.username_label = ttk.Label(self.connection_frame, text="Username:")

        self.username_label.grid(row=0, column=2, sticky='w')

        self.username_entry = ttk.Entry(self.connection_frame)

        self.username_entry.grid(row=0, column=3, sticky='ew')

        self.username_entry.insert(0, self.remote_username)

 

        # Password widget

        self.password_label = ttk.Label(self.connection_frame, text="Password:")

        self.password_label.grid(row=0, column=4, sticky='w')

        self.password_entry = ttk.Entry(self.connection_frame, show="*")

        self.password_entry.grid(row=0, column=5, sticky='ew')

        self.password_entry.insert(0, self.remote_password)

 

        self.connected = False

 

        # Connect/Disconnect button

        self.connect_button = ttk.Button(self.connection_frame, text="Connect", command=self.toggle_connection)

        self.connect_button.grid(row=0, column=6, sticky='ew')

 

        # Launch Screen

        self.RunScreen_button = ttk.Button(self.connection_frame, text="Run Screen", command=self.process_RunScreen)

        self.RunScreen_button.grid(row=0, column=7, sticky='ew')

 

        # Quit Screen

        self.RunScreen_button = ttk.Button(self.connection_frame, text="Quit Screen", command=self.process_QuitScreen)

        self.RunScreen_button.grid(row=0, column=8, sticky='ew')

 

        # Clear Screen

        self.ClearScreen_button = ttk.Button(self.connection_frame, text="Clear Screen", command=self.process_ClearScreen)

        self.ClearScreen_button.grid(row=0, column=9, sticky='ew')

 

        for col in range(10):

            self.connection_frame.grid_columnconfigure(col, weight=1)

 

        # Creating button frame for eNB screen command

        self.button_frame = tk.Frame(self, bd=2, relief="groove")

        self.button_frame.grid(row=2, column=0, sticky='nsew')

 

        self.enb_label = tk.Label(self.button_frame, text="ENB Command:")

        self.enb_cell_phy_button = ttk.Button(self.button_frame, text="cell phy", command=self.process_Cmd_cell_phy)

        self.enb_cell_button = ttk.Button(self.button_frame, text="cell", command=self.process_Cmd_cell)

        self.enb_t_button = ttk.Button(self.button_frame, text="t", command=self.process_Cmd_t)

        self.enb_t_spl_button = ttk.Button(self.button_frame, text="t spl", command=self.process_Cmd_t_spl)

        self.enb_t_spl_dbm_button = ttk.Button(self.button_frame, text="t spl dbm", command=self.process_Cmd_t_spl_dbm)

        self.enb_t_cpu_button = ttk.Button(self.button_frame, text="t cpu", command=self.process_Cmd_t_cpu)

        self.enb_t_g_button = ttk.Button(self.button_frame, text="t g", command=self.process_Cmd_t_g)

        self.enb_ue_button = ttk.Button(self.button_frame, text="ue", command=self.process_Cmd_enb_ue)

        self.enb_rf_info_button = ttk.Button(self.button_frame, text="rf_info", command=self.process_Cmd_rf_info)

    

        self.enb_label.pack(side='left', padx=[5,0], pady=[5,5])

        self.enb_cell_phy_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.enb_cell_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.enb_t_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.enb_t_spl_button.pack(side='left', pady=[5,5])

        self.enb_t_spl_dbm_button.pack(side='left', pady=[5,5])

        self.enb_t_cpu_button.pack(side='left', pady=[5,5])

        self.enb_t_g_button.pack(side='left', pady=[5,5])

        self.enb_ue_button.pack(side='left', pady=[5,5])

        self.enb_rf_info_button.pack(side='left', pady=[5,5])

 

        # Creating button frame for mme screen command

        self.button_frame = tk.Frame(self, bd=2, relief="groove")

        self.button_frame.grid(row=3, column=0, sticky='nsew')

 

        self.mme_label = tk.Label(self.button_frame, text="MME Command:")

        self.mme_ue_button = ttk.Button(self.button_frame, text="ue", command=self.process_mme_ue)

        self.mme_ng_ran_button = ttk.Button(self.button_frame, text="ng ran", command=self.process_mme_ng_ran)

 

        self.mme_label.pack(side='left', padx=[5,0], pady=[5,5])

        self.mme_ue_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.mme_ng_ran_button.pack(side='left', padx=[5,0], pady=[5,5])

      

 

        # Creating button frame for ims screen command

        self.button_frame = tk.Frame(self, bd=2, relief="groove")

        self.button_frame.grid(row=4, column=0, sticky='nsew')

 

        self.ims_label = tk.Label(self.button_frame, text="IMS Command:")

        self.ims_t_button = ttk.Button(self.button_frame, text="t", command=self.process_ims_t)

        self.ims_users_button = ttk.Button(self.button_frame, text="users", command=self.process_ims_users)

        self.ims_ipsec_button = ttk.Button(self.button_frame, text="ipsec", command=self.process_ims_ipsec)

        self.ims_mt_call_button = ttk.Button(self.button_frame, text="mt_call", command=self.process_ims_mt_call)

    

        self.ims_label.pack(side='left', padx=[5,0], pady=[5,5])

        self.ims_t_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.ims_users_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.ims_ipsec_button.pack(side='left', padx=[5,0], pady=[5,5])

        self.ims_mt_call_button.pack(side='left', pady=[5,5])

 

        # Creating terminal frame

        self.terminal_frame = tk.Frame(self)

        self.terminal_frame.grid(row=5, column=0, sticky='nsew')

        self.terminal_frame.rowconfigure(0, weight=1)

        self.terminal_frame.columnconfigure(0, weight=1)

        self.rowconfigure(3, weight=1)

        self.columnconfigure(0, weight=1)   

 

        self.terminal = scrolledtext.ScrolledText(self.terminal_frame,

                                          font=("Lucida Console", 10),

                                          bg="black",

                                          fg="white")

        self.terminal.grid(row=0, column=0, padx=5, pady=5, sticky='nsew')

        self.terminal.columnconfigure(0, weight=1)

        self.terminal.rowconfigure(0, weight=1)

 

        # create the button frame at the bottom

        self.button_frame = tk.Frame(self)

        self.button_frame.grid(row=6, column=0, sticky='we')

 

        self.screen_label = tk.Label(self.button_frame, text="Command:")

        self.screen_label.grid(row=0, column=0, padx=[5,0], pady=[5,5],sticky='w')

 

        self.entry = tk.Entry(self.button_frame)

        self.entry.grid(row=0, column=1, columnspan=3,padx=[5,0], pady=[5,5], sticky='we')  # sticky='we' makes the Entry expand horizontally

        self.entry.bind('<Return>', self.on_enter_key)

        self.entry.bind('<Control-a>', self.send_ctrl_a)

 

        self.run_button = ttk.Button(self.button_frame, text="ENTER", command=self.process_Run)

        self.run_button.grid(row=0, column=4, padx=[5,0], pady=[5,5],sticky='w')

 

        self.goto_enb_button = ttk.Button(self.button_frame, text="goto (enb)", command=self.process_goto_enb, width=15)

        self.goto_mme_button = ttk.Button(self.button_frame, text="goto (mme)", command=self.process_goto_mme, width=15)

        self.goto_ims_button = ttk.Button(self.button_frame, text="goto (ims)", command=self.process_goto_ims, width=15)

 

        self.goto_enb_button.grid(row=0, column=5, padx=[5,0], pady=[5,5],sticky='w')

        self.goto_mme_button.grid(row=0, column=6, padx=[5,0], pady=[5,5],sticky='w')

        self.goto_ims_button.grid(row=0, column=7, padx=[5,0], pady=[5,5],sticky='w')

 

Section A - SSH Connection and Screen Run/Stop

This is to establish ssh connection to Remote PC and Start/Stop screen command. Most of the code is to create and place GUI components which does not require any explanation. For the details of the implementation of the operation (i.e, establishing ssh connection), look into the following functions.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Create connection frame

        self.connection_frame = ttk.Frame(self)

        self.connection_frame.grid(row=0, column=0, sticky='ew')

 

        # IP address widget

        self.ip_label = ttk.Label(self.connection_frame, text="IP Address:")

        self.ip_label.grid(row=0, column=0, sticky='w')

        self.ip_entry = ttk.Entry(self.connection_frame)

        self.ip_entry.grid(row=0, column=1, sticky='ew')

        self.ip_entry.insert(0, self.remote_ip)

 

        # Username widget

        self.username_label = ttk.Label(self.connection_frame, text="Username:")

        self.username_label.grid(row=0, column=2, sticky='w')

        self.username_entry = ttk.Entry(self.connection_frame)

        self.username_entry.grid(row=0, column=3, sticky='ew')

        self.username_entry.insert(0, self.remote_username)

 

        # Password widget

        self.password_label = ttk.Label(self.connection_frame, text="Password:")

        self.password_label.grid(row=0, column=4, sticky='w')

        self.password_entry = ttk.Entry(self.connection_frame, show="*")

        self.password_entry.grid(row=0, column=5, sticky='ew')

        self.password_entry.insert(0, self.remote_password)

 

        self.connected = False

 

        # Connect/Disconnect button

        self.connect_button = ttk.Button(self.connection_frame, text="Connect", command=self.toggle_connection)

        self.connect_button.grid(row=0, column=6, sticky='ew')

 

        # Launch Screen

        self.RunScreen_button = ttk.Button(self.connection_frame, text="Run Screen", command=self.process_RunScreen)

        self.RunScreen_button.grid(row=0, column=7, sticky='ew')

 

        # Quit Screen

        self.RunScreen_button = ttk.Button(self.connection_frame, text="Quit Screen", command=self.process_QuitScreen)

        self.RunScreen_button.grid(row=0, column=8, sticky='ew')

 

        # Clear Screen

        self.ClearScreen_button = ttk.Button(self.connection_frame, text="Clear Screen", command=self.process_ClearScreen)

        self.ClearScreen_button.grid(row=0, column=9, sticky='ew')

        ....

 

Section B - Command Buttons for (enb) screen

This is to implement the functionalities of each (enb) screen command. Followings are the list of the functions and the screen command. Basic logic for each of these button is to type int to a command to 'Command' textbox and hit [ENTER] button.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Creating button frame for eNB screen command

        self.button_frame = tk.Frame(self, bd=2, relief="groove")

        self.button_frame.grid(row=2, column=0, sticky='nsew')

 

        self.enb_label = tk.Label(self.button_frame, text="ENB Command:")

        self.enb_cell_phy_button = ttk.Button(self.button_frame, text="cell phy", command=self.process_Cmd_cell_phy)

        self.enb_cell_button = ttk.Button(self.button_frame, text="cell", command=self.process_Cmd_cell)

        self.enb_t_button = ttk.Button(self.button_frame, text="t", command=self.process_Cmd_t)

        self.enb_t_spl_button = ttk.Button(self.button_frame, text="t spl", command=self.process_Cmd_t_spl)

        self.enb_t_spl_dbm_button = ttk.Button(self.button_frame, text="t spl dbm", command=self.process_Cmd_t_spl_dbm)

        self.enb_t_cpu_button = ttk.Button(self.button_frame, text="t cpu", command=self.process_Cmd_t_cpu)

        self.enb_t_g_button = ttk.Button(self.button_frame, text="t g", command=self.process_Cmd_t_g)

        self.enb_ue_button = ttk.Button(self.button_frame, text="ue", command=self.process_Cmd_enb_ue)

        self.enb_rf_info_button = ttk.Button(self.button_frame, text="rf_info", command=self.process_Cmd_rf_info)

        ....

 

Section C - Command Buttons for (mme) screen

This is to implement the functionalities of each (mme) screen command. Followings are the list of the functions and the screen command. Basic logic for each of these button is to type int to a command to 'Command' textbox and hit [ENTER] button.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Creating button frame for mme screen command

        self.button_frame = tk.Frame(self, bd=2, relief="groove")

        self.button_frame.grid(row=3, column=0, sticky='nsew')

 

        self.mme_label = tk.Label(self.button_frame, text="MME Command:")

        self.mme_ue_button = ttk.Button(self.button_frame, text="ue", command=self.process_mme_ue)

        self.mme_ng_ran_button = ttk.Button(self.button_frame, text="ng ran", command=self.process_mme_ng_ran)

        ....

 

Section D - Command Buttons for (ims) screen

This is to implement the functionalities of each (ims) screen command. Followings are the list of the functions and the screen command. Basic logic for each of these button is to type int to a command to 'Command' textbox and hit [ENTER] button.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Creating button frame for ims screen command

        self.button_frame = tk.Frame(self, bd=2, relief="groove")

        self.button_frame.grid(row=4, column=0, sticky='nsew')

 

        self.ims_label = tk.Label(self.button_frame, text="IMS Command:")

        self.ims_t_button = ttk.Button(self.button_frame, text="t", command=self.process_ims_t)

        self.ims_users_button = ttk.Button(self.button_frame, text="users", command=self.process_ims_users)

        self.ims_ipsec_button = ttk.Button(self.button_frame, text="ipsec", command=self.process_ims_ipsec)

        self.ims_mt_call_button = ttk.Button(self.button_frame, text="mt_call", command=self.process_ims_mt_call)

        ....

 

Section E - Screen Terminal

This is for the big textbox (black) to display the text from remote PC. This is just to display text from the remote PC and does not do any interactions with users. So the code here is just to create a GUI component for the text box.

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # Creating terminal frame

        self.terminal_frame = tk.Frame(self)

        self.terminal_frame.grid(row=5, column=0, sticky='nsew')

        self.terminal_frame.rowconfigure(0, weight=1)

        self.terminal_frame.columnconfigure(0, weight=1)

        self.rowconfigure(3, weight=1)

        self.columnconfigure(0, weight=1)   

 

        self.terminal = scrolledtext.ScrolledText(self.terminal_frame,

                                          font=("Lucida Console", 10),

                                          bg="black",

                                          fg="white")

        ....

 

Section F - Run Command Text/Screen mode switching

This is for the buttons to swich the screen to (enb), (mme) or (ims). What this does is

    # In FileBrowserSCP() Class

    def create_widgets(self):

        ....

        # create the button frame at the bottom

        self.button_frame = tk.Frame(self)

        self.button_frame.grid(row=6, column=0, sticky='we')

 

        self.screen_label = tk.Label(self.button_frame, text="Command:")

        self.screen_label.grid(row=0, column=0, padx=[5,0], pady=[5,5],sticky='w')

 

        self.entry = tk.Entry(self.button_frame)

        self.entry.grid(row=0, column=1, columnspan=3,padx=[5,0], pady=[5,5], sticky='we')  # sticky='we' makes the Entry expand horizontally

        self.entry.bind('<Return>', self.on_enter_key)

        self.entry.bind('<Control-a>', self.send_ctrl_a)

 

        self.run_button = ttk.Button(self.button_frame, text="ENTER", command=self.process_Run)

        self.run_button.grid(row=0, column=4, padx=[5,0], pady=[5,5],sticky='w')

 

        self.goto_enb_button = ttk.Button(self.button_frame, text="goto (enb)", command=self.process_goto_enb, width=15)

        self.goto_mme_button = ttk.Button(self.button_frame, text="goto (mme)", command=self.process_goto_mme, width=15)

        self.goto_ims_button = ttk.Button(self.button_frame, text="goto (ims)", command=self.process_goto_ims, width=15)

        ....