Welcome, guest | Sign In | My Account | Store | Cart

File I/O has many potential uses, and in the case of network file shares, messaging can be conducted by creating files in a directory and scanning for new files to find new messages. In the case where there are restrictions within a network that prevent opening server sockets or creating and using client sockets, the technique in this recipe may be used with file shares instead. The program is simple and was written in about fifty minutes, so there are a great deal of enhancements that could be made to the recipe and its capabilities. Hopefully, this will provide inspiration for other program writers.

If you have any comments or wish to down-vote this recipe, please provide your insight as to what could be improved upon and how you would go about fixing any problems that you might find.

Python, 127 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import tkinter
import tkinter.ttk
import getpass
import uuid
import os
import time
import traceback

# XXX The file will be obsolete by tomorrow.

HOLD = 'Logos Storage'

################################################################################

class Logos(tkinter.ttk.Frame):

    @classmethod
    def main(cls):
        # Create and configure the root.
        tkinter.NoDefaultRoot()
        root = tkinter.Tk()
        root.title('Logos')
        root.minsize(200, 200)
        # Create Logos and setup for resizing.
        view = cls(root)
        view.grid(row=0, column=0, sticky=tkinter.NSEW)
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        # Enter the main GUI event loop.
        root.mainloop()

    ########################################################################

    # These are all instance methods.

    def __init__(self, master):
        super().__init__(master)
        # Get the username and save list of found files.
        self.__user = getpass.getuser()
        self.__dirlist = set()
        # Create widgets.
        self.__log = tkinter.Text(self)
        self.__bar = tkinter.ttk.Scrollbar(self, orient=tkinter.VERTICAL,
                                           command=self.__log.yview)
        self.__ent = tkinter.ttk.Entry(self, cursor='xterm')
        # Configure widgets.
        self.__log.configure(state=tkinter.DISABLED, wrap=tkinter.WORD,
                             yscrollcommand=self.__bar.set)
        # Create binding.
        self.__ent.bind('<Return>', self.create_message)
        # Position widgets.
        self.__log.grid(row=0, column=0, sticky=tkinter.NSEW)
        self.__bar.grid(row=0, column=1, sticky=tkinter.NS)
        self.__ent.grid(row=1, column=0, columnspan=2, sticky=tkinter.EW)
        # Configure resizing.
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        # Focus entry.
        self.__ent.focus_set()
        # Schedule message discovery.
        self.after_idle(self.get_messages)

    def get_messages(self):
        # Schedule method for one second from now.
        self.after(1000, self.get_messages)
        # Get a list of new files.
        files = set(os.listdir(HOLD))
        diff = files - self.__dirlist
        self.__dirlist = files
        # Load each new message.
        messages = []
        for name in diff:
            path = os.path.join(HOLD, name)
            try:
                with open(path) as file:
                    user, clock, message = self.load_message(file)
                messages.append((user, float(clock), message))
            except:
                # Print any error for debugging purposes.
                traceback.print_exc()
                os.remove(path)
        # Sort the messages according to time and display them.
        messages.sort(key=lambda m: m[1])
        for m in messages:
            self.display_message(m[0], m[2])

    def display_message(self, user, text):
        # Put a new message on the screen.
        self.__log['state'] = tkinter.NORMAL
        message = '{} - {}\n'.format(user, text)
        self.__log.insert('1.0', message)
        self.__log['state'] = tkinter.DISABLED

    def create_message(self, event):
        # Get the text, clear it, show on screen, and create file.
        text = event.widget.get()
        event.widget.delete(0, tkinter.END)
        self.display_message(self.__user, text)
        self.save_file(text)

    def save_file(self, message):
        # Save message in HOLD using a UUID for the name.
        name = uuid.uuid1().hex
        path = os.path.join(HOLD, name)
        self.__dirlist.add(name)
        with open(path, 'w') as file:
            self.save_message(file, message)

    ########################################################################

    # If one the format is changed, both methods must be altered.

    def load_message(self, file):
        # Read the file, splitting on newline, and get first three lines.
        user, clock, message = file.read().split('\n')[:3]
        return user, clock, message

    def save_message(self, file, message):
        # Save username, timestamp, and message on separate lines.
        print(self.__user, file=file)
        print(time.time(), file=file)
        print(message, file=file, end='')

################################################################################

if __name__ == '__main__':
    Logos.main()