Bear with me as I'm fairly new to programming. My basic question is this. I have a program written in Haskell whose stdout I want to connect to the stdin of a Python program (which will manage the GUI related stuff). Similarly, I want to connect the stdout of the Python program to the stdin of the Haskell program so that it can send information about what the user clicked/typed in to the Haskell program.
The first question is this, if I set up a pipeline between the two, assuming the stdout of the Python program is hooked up to the Haskell program, if I use Tkinter to create widgets and stuff, will they be displayed on the screen nonetheless?
The second question is how exactly would I establish this pipeline? Consider the following example code..
main :: IO ()main = do
-- putStrLn "Enter a number." <- this will be displayed in Python
string <- getLine
putStrLn $ 5 + read string::Int -- or any equivalent function to send to stdout
The Python code would look something like this.
from Tkinter import *root = Tk()
label = Label(root, text = "Enter a number.")
label.pack()
enternum = Entry(root)
enternum.pack()
enternum.bind("<Return>", print_num)
-- print_num would essentially be a function to send the Haskell program the number
-- which would be received by the getLine function the way I have it.
I'm sorry if this has already been asked before, but thanks for helping me out!
I did it using Twisted since it provides great abstraction over polling. Basically you need to first define the ways (called protocol in Twisted) how Python and Haskell programs communicate with each other, E.g., how long a data packet is, how to handle errors etc. Then you just code them up.
Here is the haskell code:
-- File "./Hs.hs"
import Control.Concurrent
import System.IO
main = do
-- Important
hSetBuffering stdout NoBuffering
-- Read a line
line <- getLine
-- parse the line and add one and print it back
putStrLn (show (read line + 1))
-- Emphasize the importance of hSetBuffering :P
threadDelay 10000000
And here is the Python code:
# File "./pyrun.py"
import os
here = os.path.dirname(os.path.abspath(__file__))
from twisted.internet import tksupport, reactor, protocol
from twisted.protocols.basic import LineReceiver
from Tkinter import Tk, Label, Entry, StringVar
# Protocol to handle the actual communication
class HsProtocol(protocol.ProcessProtocol):
def __init__(self, text):
self.text = text
def connectionMade(self):
# When haskell prog is opened
towrite = self.text + '\n'
# Write a line to the haskell side
self.transport.write(towrite)
def outReceived(self, data):
# When haskell prog write something to the stdout
# Change the label in the tk window to be the received data
label_var.set(data[:-1])
def send_num_to_hs(_event):
content = enternum.get()
# The abspath of the haskell program
prog = os.path.join(here, 'Hs')
reactor.spawnProcess(HsProtocol(content), # communication protocol to use
prog, # path
[prog] # args to the prog
)
# Setting up tk
root = Tk()
# On main window close, stop the tk reactor
root.protocol('WM_DELETE_WINDOW', reactor.stop)
# Since I'm going to change that label..
label_var = StringVar(root, 'Enter a number')
# Label whose content will be changed
label = Label(root, textvariable=label_var)
label.pack()
# Input box
enternum = Entry(root)
enternum.pack()
enternum.bind('<Return>', send_num_to_hs)
# Patch the twisted reactor
tksupport.install(root)
# Start tk's (and twisted's) mainloop
reactor.run()
You can also establish the pipeline from a command shell:
mypython.py | myhaskell.hs
The Haskell program will respond the same as it would to any other type of standard input, as in:
myhaskell.hs