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.")
enternum = Entry(root)
-- 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: