コマンドの出力をひたすら表示するウィンドウ
Python付属ライブラリに、CGIHTTPServerと言う物があります。
python -m CGIHTTPServer
を、HTMLなどを置いたディレクトリで実行し、
ブラウザで"http://localhost:8000/"を開くと、ローカルでCGIやHTMLをテスト出来るというものです。
大変重宝なのですが、Windowsだと、CGIHTTPServerのためにDOS窓が開くと邪魔です。
かといって、pythonwでDOS窓なしだと、ちょっとさみしい。
そこで、普段はタスクトレイに格納し、必要なときだけCGIHTTPServerの結果を表示するスクリプトを作ってみました。
#iconizebat.pyw #本体 #encoding:shift-jis from __future__ import with_statement, division, print_function from myutil import * import wx import yaml class IconizeFrame(wx.Frame): def __init__(self, parent, cfg): self.cfg = copy.copy(cfg) wx.Frame.__init__(self, parent) # タスクトレイに入れるアイコン self.ico = wx.Icon(cfg.icon, wx.BITMAP_TYPE_ICO) self.tb_ico=wx.TaskBarIcon() self.tb_ico.SetIcon(self.ico, self.cfg.tips) # タスクトレイアイコンのイベント関連付け self.tb_ico.Bind(wx.EVT_TASKBAR_LEFT_UP, self.OnTbiLeftUp) self.Bind(wx.EVT_ICONIZE, self.OnIconized) self.Bind(wx.EVT_CLOSE, self.OnClose) def OnIconized(self, evt): self.Hide() self.tb_ico.SetIcon(self.ico, self.cfg.tips) def OnTbiLeftUp(self, evt): self.Iconize(False) # <= 必須!! Very Important self.Show(True) self.Raise() ## self.tb_ico.RemoveIcon() def OnClose(self, evt): self.Iconize() class ReadingThread(threading.Thread): def __init__(self, istream, estream, write): attributesFromDict(locals()) threading.Thread.__init__(self) self.keep_going = True def stop(self): self.keep_going = False self.join() def run(self): while self.keep_going: if self.istream.CanRead(): text = self.istream.read() self.write(text) if self.estream.CanRead(): text = self.estream.read() self.write(text) time.sleep(0.15) class PopenFrame(IconizeFrame): ID_KILL = wx.NewId() ID_EXIT = wx.ID_EXIT def __init__(self, parent, cfg): IconizeFrame.__init__(self, parent, cfg) title = "[running]{0}".format(self.cfg.title) self.SetTitle(title) style = ( wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL ) self.logText = wx.TextCtrl(self, style=style) sz = wx.BoxSizer() sz.Add(self.logText, 1, wx.EXPAND) self.SetSizer(sz) self.menu = wx.Menu() self.menu.Append(self.ID_KILL, "&Kill", "Kill") self.menu.Append(self.ID_EXIT, "&Exit", "Exit") self.Bind(wx.EVT_MENU, self.OnMenu) mbar = wx.MenuBar() mbar.Append(self.menu, "File") self.SetMenuBar(mbar) self.tb_ico.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTbiRightUp) self.Bind(wx.EVT_END_PROCESS, self.OnProcessEnded) #プロセス作成 self.process = wx.Process.Open( self.cfg.cmdline, wx.EXEC_ASYNC ) if self.cfg.log: self.logfp = open(self.cfg.logpath, "w") else: self.logfp = Null() def write(s): self.logfp.write(s) self.logfp.flush() self.logText.AppendText(s) self.readingthread = ReadingThread( self.process.GetInputStream(), self.process.GetErrorStream(), write ) self.readingthread.start() self.Show(self.cfg.show) def OnTbiRightUp(self, event): self.PopupMenu(self.menu) def ExitMainLoop(self): self.tb_ico.RemoveIcon() self.logfp.close() wx.GetApp().ExitMainLoop() def OnMenu(self, event): id = event.GetId() if id == self.ID_EXIT: self.Kill() self.ExitMainLoop() elif id == self.ID_KILL: self.Kill() def Kill(self): if self.process is not None: self.process.CloseOutput() title = "[killed]{0}".format(self.cfg.title) self.SetTitle(title) self.readingthread.stop() def OnProcessEnded(self, event): self.readingthread.stop() self.process.Destroy() self.process = None ret = event.GetExitCode() title = "[finished:{1}]{0}".format(self.cfg.title, ret) self.SetTitle(title) quit = { "show" : partial(self.quitWithMessage, False), "show_if_error" : partial(self.quitWithMessage, True), "stay" : self.stay, }[self.cfg.quit_type] quit(ret) def quitWithMessage(self, only_if_error, return_code): if not only_if_error or return_code!=0: self.Show() wx.MessageBox( "return code={0}".format(return_code), parent=self ) self.ExitMainLoop() def stay(self, return_code): pass def loadcfg(cfgpath): path = unicode class choice(object): def __init__(self, t, values): attributesFromDict(locals()) def __call__(self, v): v = self.t(v) if v not in self.values: raise ValueError(v) return v types = dict( cmdline = path, show = bool, icon = path, cwd = path, log = bool, logpath=path, quit_type=choice(unicode, "show show_if_error stay".split()), title=unicode, tips=unicode ) with open(cfgpath) as fp: d = yaml.load(fp.read()) r = {} for k, t in types.iteritems(): r[k] = t(d[k]) return Bunch(**d) def main(): cfgpath = sys.argv[1] cfg = loadcfg(cfgpath) os.chdir(cfg.cwd) app=wx.PySimpleApp() frame=PopenFrame(None, cfg) app.MainLoop() if __name__ == "__main__": main()
#CGIHTTPServer.yaml #設定ファイル cmdline : 'python -u -m CGIHTTPServer' show : True icon : "C:\\Python26\\DLLs\\pyc.ico" cwd : "d:\\Owner\\My Documents\\WebPage\\dist" log : False logpath : "test_log.txt" quit_type : "show" title : "CGIHTTPServer" tips : "CGIHTTPServer"
使用例
python iconizebat.pyw CGIHTTPServer.yaml
最初はCGIHTTPServerのプロセスを、subprocess.Popen
で作ろうとしたのですが、ご覧の通りwx.Process
を使っています。
Windows版のsubprocess.Popenではサブプロセスの標準出力を、
リアルタイムでは手に入れられないみたいでした。
サブプロセスが終了すると、一気に入ってくるみたいです。