Home
  Software
    HowTo
      GTK+
        Gio.IOChannel

Idioma:
  English

En esta página
  GUI
  Código cliente
  Código servidor
  Gracias

Temas
  GIOChannel
  Gio.IOChannel
  FIFO chat
  Socket chat

GTK+ - GIOChannel en Python3

El ejemplo que signe muestra como conectar algunos eventos con rutinas utilizando IOChannels. En ejemplo utilizamos el reloj de tiempo real (RTC) disponible en cada PC para generar llamadas cadad segundo, y nos conectamos con internet con zocalos con la internet.

El programa brinde lo siguiente:

NOTAS:

Código fuente en Python3

#!/usr/bin/env python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 8 -*- 
#
# main.py
# Copyright (C) 2019 John Coppens <john@jcoppens.com>
# 
# test_multi is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# test_multi is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gio
import os, sys
import pdb
import socket
import fcntl
import subprocess as sp


UI_FILE = "test_multi.ui"
RTC_DEV = "/dev/rtc0"
IP      = "127.0.0.1"
PORT    = 9999
XTERM   = "xterm"

RTC_UIE_ON = 0x00007003

class RTC():
    """ RTC connects a handler to the RTC device (normally /dev/rtc0)
            <callback> parameter    if defined, will be called on each tick
            <viewer> parameter      if defined, must be a Gtk.TextView, and a
                                    character will be inserted on each tick

        NOTE: At the moment, the 'tick' signal is the default 1 second period.
            The rate can be changed with ioctl commands, but this is NOT
            implemented here for simplicity.
    """
    def __init__(self, parent, viewer = None, callback = None):
        self.parent = parent
        self.callback = callback
        self.viewer = viewer
        
        self.rtc = open(RTC_DEV, "rb")
        if not self.rtc:
            print("Can't open file:", RTC_DEV)
            exit(1)
        self.ioch = GLib.IOChannel.unix_new(self.rtc.fileno())
        
        # Datos leidos del RTC son del tipo 'long': No son caracteres validos
        # y es preciso de deshabilitar la codificacion:
        self.ioch.set_encoding(None)
        
        GLib.io_add_watch(self.ioch, GLib.IO_IN, self.read)
        
        self.enable_interrupts()


    def read(self, ioch, par2):
        try:
            data = self.ioch.read(8)    # Cada 'long' tiene 8 bytes
            self.append_text("@")
                
        except Exception as e:
            print(e)
            print("Data read contains strange things")
            
        return True


    def enable_interrupts(self):
        r = fcntl.ioctl(self.rtc, RTC_UIE_ON, 0)
        if r == -1:
            print("Could not enable interrupts")


    def append_text(self, txt):
        if self.callback != None:
            self.callback(txt)
            
        bff = self.viewer.get_buffer()
        bff.insert(bff.get_end_iter(), txt)


    def disable_interrupts(self):
        fcntl.ioctl(self.rtc, RTC_UIE_OFF, 0);



class TCP_client():
    """ The TCP client tries to connect to the server on instantiation of the
        class. So the server must be running!
            <callback> parameter    if defined, will be called with the data
                                    received on the TCP port.
            <viewer> parameter      if defined, must be a Gtk.TextView, and
                                    will be updated with the received data.
            semd method             can be called to send a message to the
                                    TCP port
    """
    def __init__(self, host, port,
                callback = None,
                viewer = None):
        self.callback = callback
        self.viewer = viewer

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.socket.connect((host, port))
        self.socket.setblocking(False)
        self.fd = self.socket.fileno()             # Obtener el file descriptor
        
        print("Connected to {}".format(host))
        GLib.io_add_watch(self.fd, GLib.IO_IN, self.process_input)
                                                   # Instalar la funcion en caso
                                                   # de datos recibidos


    def process_input(self, skt, cond):
        """ This function is called asynchronously when data is received.
        """
        msg = self.socket.recv(100)                # Recibir el mensage de la red
        if self.callback != None:
            self.callback(msg)
            
        bff = self.viewer.get_buffer()
        bff.insert(bff.get_end_iter(), msg.decode("latin-1"))
        
        return True                                # Queremos quedar activos


    def send(self, msg):
        self.socket.send(msg.encode("latin-1"))



class GUI:
    def __init__(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file(UI_FILE)
        self.builder.connect_signals(self)

        main_window = self.builder.get_object("MainWindow")
        # Make an dictionary of references to widgets we are interested in
        # from the UI file
        self.obj = {}
        for objref in ["from_RTC_buffer",
                       "from_RTC_view",
                       "from_socket_buffer",
                       "from_socket_view"]:
            self.obj[objref] = self.builder.get_object(objref)

        self.rtc = RTC(self,
                    viewer = self.obj["from_RTC_view"],
                    callback = self.process_rtc)

        self.tcp = TCP_client(IP, PORT, 
                    viewer = self.obj["from_socket_view"],
                    callback = self.process_tcp)
                    
        self.tcp.send("Simulated server (netcat) \n")

        main_window.show_all()


    def on_window_destroy(self, window):
        Gtk.main_quit()

    def process_rtc(self, data):
        """ Execute anything which has to be synced with the RTC
        """
        print(data)
        return True

    def process_tcp(self, data):
        """ Process the data coming from the server
        """
        print(data)
        return True


    def kbd_entry_activate_cb(self, entry):
        """ Send the data from the keyboard to the server (and print
            on the terminal
        """
        text = entry.get_text()
        print(text)
        self.tcp.send(text + "\n")
        entry.set_text('')
        

def main():
    sp.Popen([XTERM, '-e', 'nc', '-l', '-p', '9999'])
    gui = GUI()
    Gtk.main()
		
if __name__ == "__main__":
    sys.exit(main())
Code: test_multi.py

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.0"/>
  <object class="GtkTextBuffer" id="from_RTC_buffer"/>
  <object class="GtkTextBuffer" id="from_kbd_buffer"/>
  <object class="GtkTextBuffer" id="from_socket_buffer"/>
  <object class="GtkWindow" id="MainWindow">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">window</property>
    <property name="default_width">500</property>
    <property name="default_height">400</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_left">4</property>
        <property name="margin_right">4</property>
        <property name="margin_top">4</property>
        <property name="margin_bottom">4</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkFrame" id="from_RTC">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">2</property>
            <property name="margin_bottom">2</property>
            <property name="label_xalign">0.05000000074505806</property>
            <property name="shadow_type">none</property>
            <child>
              <object class="GtkAlignment">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="left_padding">12</property>
                <child>
                  <object class="GtkScrolledWindow">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="shadow_type">in</property>
                    <child>
                      <object class="GtkTextView" id="from_RTC_view">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="wrap_mode">char</property>
                        <property name="buffer">from_RTC_buffer</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child type="label">
              <object class="GtkLabel">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">From RTC</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkFrame" id="from_kbd_frame">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">2</property>
            <property name="margin_bottom">2</property>
            <property name="vexpand">False</property>
            <property name="label_xalign">0.05000000074505806</property>
            <child>
              <object class="GtkAlignment">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="left_padding">12</property>
                <child>
                  <object class="GtkEntry" id="kbd_entry">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="has_focus">True</property>
                    <signal name="activate" handler="kbd_entry_activate_cb" swapped="no"/>
                  </object>
                </child>
              </object>
            </child>
            <child type="label">
              <object class="GtkLabel">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">From keyboard</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkFrame" id="from_socket_frame">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_top">2</property>
            <property name="margin_bottom">2</property>
            <property name="label_xalign">0.05000000074505806</property>
            <child>
              <object class="GtkAlignment">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="left_padding">12</property>
                <child>
                  <object class="GtkTextView" id="from_socket_view">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="wrap_mode">char</property>
                    <property name="buffer">from_socket_buffer</property>
                  </object>
                </child>
              </object>
            </child>
            <child type="label">
              <object class="GtkLabel">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">From socket</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
Code: test_multi.ui

2569
(c) John Coppens ON6JC/LW3HAZ correo