Search code examples
pythonsocketsuser-interfacewxpython

How to display an jpg image and change it on the same panel every x sec using wxPython?


I need to display an screen shot of a far away computer on a GUI panel using wx Python. Moreover, I need to replace the screen shot with a new screen shot every 0.04 sec for example. I'm using threading for this. The problem begins when the code does not display any of the images except the last screen shot(when I stop the program). My question is what I have to change in my code so that it will display the screen shot at the same platform and change the image every 0.04 sec. I'm adding the code of my server side(the far away computer that sends me screen shots of it) and the client side(my computer). client side: import wx import socket

import threading
import time
import cStringIO


PORT = 1729
IP = '127.0.0.1'
LOCATION = 'C:\stam\stam1.jpg'
class PhotoCtrl(wx.App):
    def __init__(self, redirect=False, filename=None):
        wx.App.__init__(self, redirect, filename)
        self.frame = wx.Frame(None, title='Photo Control')

        self.panel = wx.Panel(self.frame)

        self.PhotoMaxSize = 240




def createWidgets(self):
    imageFile = LOCATION
    data = open(imageFile, "rb").read()
    stream = cStringIO.StringIO(data)
    self.imageCtrl = wx.StaticBitmap(self.panel, wx.ID_ANY, wx.BitmapFromImage( wx.ImageFromStream( stream )))
    self.mainSizer = wx.BoxSizer(wx.VERTICAL)
    self.sizer = wx.BoxSizer(wx.HORIZONTAL)
    self.mainSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY), 0, wx.ALL|wx.EXPAND, 5)
    self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
    self.panel.SetSizer(self.mainSizer)
    self.mainSizer.Fit(self.frame)
    self.panel.Layout()
    self.panel.Center()
    self.frame.Show()



class User(wx.Frame):

def __init__(self, parent, id):
    wx.Frame.__init__(self, parent, id, 'MiniViewer2017', size=(500, 500))
    panel = wx.Panel(self)
    button_scan = wx.Button(panel, label='start scanning!')
    button_exit = wx.Button(panel, label='exit', pos=(400, 300))
    self.Bind(wx.EVT_BUTTON, self.Scan, button_scan)

    self.Bind(wx.EVT_BUTTON, self.closebutton, button_exit)
    self.Bind(wx.EVT_CLOSE, self.closewindow)

def Scan(self, event):

    client_socket = self.connetet_socket('127.0.0.1', PORT)

    app = PhotoCtrl()
    t1 = threading.Thread(target=self.ScreenThread(client_socket, app))
    t1.start()
    app.MainLoop()

def recieve_all(self, client_socket):
    """ input:socket output: the data we recieved from the server"""
    str_length = int(client_socket.recv(1024))
    new_data = client_socket.recv(str_length)
    return new_data

def connetet_socket(self, ip, port):
    """ input :  ip and port output : socket """
    try:
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client_socket.connect((ip, port))
    except socket.error as msg:
        print msg
        client_socket = None
    return client_socket


def ask_for_screen_shot(self, client_socket,app):
    client_socket.send("Send me your screen")
    time.sleep(0.0001)
    screen_shot = self.recieve_all(client_socket)
    print "i got it"
    f = open(LOCATION, 'wb')
    f.write(screen_shot)
    f.close()
    app.createWidgets()

def ScreenThread(self, client_socket,app):
    fl=True
    while fl:
        self.ask_for_screen_shot(client_socket,app)
        time.sleep(0.04)
def closebutton(self,event):
    self.Close(True)

def closewindow(self, event):
    self.Destroy()


def main():
    app = wx.PySimpleApp()
    frame = User(parent=None, id=-1)
    frame.Show()
    app.MainLoop()



if __name__ == '__main__':
    main()

server side:

import ImageGrab, socket, threading
PORT = 1729
def print_screen():
    im = ImageGrab.grab()
    im.save('C:\stam\stam.jpg') #!!!before starting, make sure you have a     folder in C drive named "stam"!!!
    with open('C:\stam\stam.jpg', 'rb') as f:
        return f.read()

def create_server_socket(ip, PORT):
    """ input :  ip and port  output : server_socket    """
    try:
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind((ip, PORT))
        server_socket.listen(1)
    except socket.error as msg:
        server_socket.close()
        server_socket = None
        print msg
    return server_socket


def send_message(client_socket, msg):
    #the function sends the client the length of the message(screen shot), and then send the screen shot while the client already set the amount of bits he is about to get.
    try:
        str_msg = str(msg)
        str_msg_length = len(str_msg)
        client_socket.send(str(str_msg_length))
        client_socket.send(msg)
    except socket.error as msg:
        client_socket.close()
        print msg

def ScreenThread(client_socket):

    while True:
        r = client_socket.recv(50)
        print r
        if r == "Send me your screen":
            send_message(client_socket, print_screen())
            print "The image has been sent successfully!"
        else:
            pass
 def main():

    while True:
        server_socket_screen = create_server_socket('0.0.0.0', PORT)

        if server_socket_screen is not None:
            client_socket, address = server_socket_screen.accept()

            t1 = threading.Thread(target=ScreenThread(client_socket))
            t1.start()



if __name__ == '__main__':
     main()

Solution

  • I would recommend starting with a wxpython tutorial so you can understand its basic concepts. See Here

    I changed your picture control to inherit a frame instead of wx.App. I added a few more comments throughout the code to explain some changes.

    There are 2 frames, the 'User' frame and the picture control frame. The user frame runs the thread and then updates the image on the picture control frame.

    import cStringIO
    import wx
    import os
    import threading
    import socket
    import time
    
    PORT = 1729
    IP = '127.0.0.1'
    LOCATION = 'C:\stam\stam1.jpg'
    
    
    class PhotoCtrl(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition,
                              size=wx.Size(500, 300), style=wx.DEFAULT_FRAME_STYLE)
    
            self.panel = wx.Panel(self.frame)
    
            self.PhotoMaxSize = 240
            self.imageCtrl = wx.StaticBitmap(self.panel, wx.ID_ANY, wx.EmptyBitmap())
            self.mainSizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer = wx.BoxSizer(wx.HORIZONTAL)
            self.mainSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY), 0, wx.ALL | wx.EXPAND, 5)
            self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
            self.panel.SetSizer(self.mainSizer)
            self.mainSizer.Fit(self.frame)
            self.panel.Layout()
            self.panel.Center()
    
            self.frame.Show()
    
        def update_image(self):
            imageFile = LOCATION
            if not os.path.exists(imageFile):
                return
    
            with open(imageFile, "rb") as fileobj:
                data = fileobj.read()
            stream = cStringIO.StringIO(data)
            bmp = wx.BitmapFromImage(wx.ImageFromStream(stream))
            self.imageCtrl.SetBitmap(bmp)
    
    
    class User(wx.Frame):
        def __init__(self, parent, id):
            wx.Frame.__init__(self, parent, id, 'MiniViewer2017', size=(500, 500))
            panel = wx.Panel(self)
            button_scan = wx.Button(panel, label='start scanning!')
            button_exit = wx.Button(panel, label='exit', pos=(400, 300))
            self.Bind(wx.EVT_BUTTON, self.Scan, button_scan)
    
            self.Bind(wx.EVT_BUTTON, self.closebutton, button_exit)
            self.Bind(wx.EVT_CLOSE, self.closewindow)
    
            # create the photo ctrl as a child frame
            self.photo_ctrl = PhotoCtrl(self)
    
        def Scan(self, event):
    
            client_socket = self.connetet_socket('127.0.0.1', PORT)
    
            # app = PhotoCtrl()
            t1 = threading.Thread(target=self.ScreenThread(client_socket, app))
            t1.start()
            app.MainLoop()
    
        def recieve_all(self, client_socket):
            """ input:socket output: the data we recieved from the server"""
            str_length = int(client_socket.recv(1024))
            new_data = client_socket.recv(str_length)
            return new_data
    
        def connetet_socket(self, ip, port):
            """ input :  ip and port output : socket """
            try:
                client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                client_socket.connect((ip, port))
            except socket.error as msg:
                print msg
                client_socket = None
            return client_socket
    
        def ask_for_screen_shot(self, client_socket, app):
            client_socket.send("Send me your screen")
            time.sleep(0.0001)
            screen_shot = self.recieve_all(client_socket)
            print "i got it"
            with open(LOCATION, "wb") as fileobj:
                fileobj.write(screen_shot) # you could probably just pass the data directly to update_image instead of writing to a file
            self.photo_ctrl.update_image()
    
        def ScreenThread(self, client_socket, app):
            fl = True
            while fl:
                self.ask_for_screen_shot(client_socket, app)
                time.sleep(0.04)
    
        def closebutton(self, event):
            self.Close(True)
    
        def closewindow(self, event):
            self.Destroy() # when Frames are deleted they automatically call destroy
    
    
    app = wx.App()
    user = User(None, -1)
    user.Show()
    app.MainLoop()