greg@262: #!/usr/bin/env python greg@262: # -*- coding: utf-8 -*- greg@262: greg@262: #This file is part of Beremiz, a Integrated Development Environment for greg@262: #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. greg@262: # greg@262: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD greg@262: # greg@262: #See COPYING file for copyrights details. greg@262: # greg@262: #This library is free software; you can redistribute it and/or greg@262: #modify it under the terms of the GNU General Public greg@262: #License as published by the Free Software Foundation; either greg@262: #version 2.1 of the License, or (at your option) any later version. greg@262: # greg@262: #This library is distributed in the hope that it will be useful, greg@262: #but WITHOUT ANY WARRANTY; without even the implied warranty of greg@262: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU greg@262: #General Public License for more details. greg@262: # greg@262: #You should have received a copy of the GNU General Public greg@262: #License along with this library; if not, write to the Free Software greg@262: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA greg@262: greg@262: import os, sys, getopt greg@262: greg@262: try: greg@262: import wx, re greg@262: from wx.lib.embeddedimage import PyEmbeddedImage greg@262: from threading import Thread greg@262: from types import * greg@262: havewx = True greg@262: except: greg@262: havewx = False greg@262: greg@262: BeremizIcon = PyEmbeddedImage( greg@262: "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABHNCSVQICAgIfAhkiAAADopJ" greg@262: "REFUaIG1mntwXNV9xz/n3rt79+5KWq1WsiRLWlkyfgnHsiwMtnkUYptQMBGFMjRhCDANJmMe" greg@262: "pYV/gJZkpoHMJJm2NG1JBzKBQNIUGlLbmMG1MWAZ25LAtmwZW5Yta/WyHtZ7tSvt7r2nf1yt" greg@262: "vJJWtmyH386Z3XvuOff+vr/zO7/XWSG4JAlAAzxANlAIFAMBS8oCYF5jY2NhdXV1UWtrq7u9" greg@262: "vV1vbW1VY7GYMAxDulwuy+VyxUpLS0MVFRVdGzZsqPFnZTUBLUAr0AUMAGHAAhyAAehAbKI/" greg@262: "CsiUzF0CgDrxMD+wAFhiSbkUKN2/f3/Ztm3bCj/66CNXfX39HORgk8PhoKysLL5p06bWxx9/" greg@262: "fHtxIFALnAI6gDEgHZg/8X1+AuQgYKZ8oJi9aQKyBJQL+K6U8qdSyg+qq6vb1q9fb05I5Kpa" greg@262: "enq6vP/++wf27NnzCwF/IeA2AQ8I+Acp5Y8feuih1wRsFOCcjc/ZmHcKyBdws4BnpJS/PXr0" greg@262: "aNM999wTF0JcNePTm2EYcsuWLe2jo6P/LqV8OxwO77333ntbASngOQHpswo6xdo7gRxgKbDO" greg@262: "knLt22+/vW7Lli3eUCiUchV1XWfJNYWUzE8jkGtQnGfg0SWmVBmNarR2R2nuGOZ0sJ8zZ4NY" greg@262: "lpXyOcuXL5cvvfTSwCuvvJJ+5MgRB8DKlSv/q/7IkV8Be7H3xBSaDsCBre9lwI3RWGztM888" greg@262: "c8trr73mkVIipUyaKCi/toQ7bsimaq1Bmm4iLROwIDFOKCAUhFARqgNFMwj2G7y/d5D/23eS" greg@262: "YGt7SiDJtGDBghPBlpafA78Fxi8GQAUygcXA2tDo6M1VVVW3V1dXu6WUWJaFZVlIKVlUWsDf" greg@262: "PRBg3VKQVhxkaolOexVCURCqjmb40TMX8Yf9Mf7ll/9NR0fnrLNycnK6z/f2/gz4DyAy/b6S" greg@262: "9O0BCoAyS8qljz766K0HDx50q6pKonncbp7/60r+54dFrF0cR5rROTIPIJGWBdJCCA2Hnsb3" greg@262: "v/stDny2g7zc3FlnjY6Opl+7fHk6tmrPIA17LziALGChJeXCp59+ev2uXbsyVVVFURQsyyI3" greg@262: "x8dPNhdTVmgizRmqODcSAqFoqE4Pqp5JU/A8D27eQld396xTwuGw2+l0uoEMYJhp/kAVtupk" greg@262: "ACVAme5y3fnGG2+UCyFQFAUhBIH5Pn71XDGFWbHp8y8fg6KhqE4+qevggcd/Sld37yXnBAKB" greg@262: "zo6Oji+A3ukMKNhL4wPyjh47ds3rr7++RlVVNE1DVVVystJ59Yn5ZLhmdYaXQRJpjhML91FZ" greg@262: "Ms4/PnUTq1cUX3KW1+tdj60hM4ymKsCLrfsL+gcGHgwGg/MSkjcMnZ98fz4Bf8JvTaDWDBzu" greg@262: "HFRn+pybUDSs+JhtoawYwopwzXyV+24LcNdtZTh0D83tg4xH4zMAFBYWGoVFRefbWls/BaYM" greg@262: "EAKKgGX/u3XrhmefffY50zRFwupsvjuPu1czY6NmltxO4M9evqTkppMZGyU60sFA03b6Gt9D" greg@262: "WhZCURGqE1VzExUedtYO8PuPmjh8vG1y3ooVKygpKenZtnXrLUATdsw0uQL5QH5ff/8TQ0ND" greg@262: "OQnpF+en8bdVOsiZIYjLtxDvgvWXDUBRnTgMP+mF68hcsIHhtk8xoyNIK440xxBWmCWFGlXr" greg@262: "S83himXCOegRfX19ABQVFXl0l8vo7uraTZJDU4UdXV5rGMbDYDsoRVF48tuZ5Gemjp+uFEAy" greg@262: "aa5M3NllDJ7ZwWRUIU2kGeWr/pGBwz7LPc+by+J5y1BVFZfLha7r+c3NzR8DPUysggJoTzz5" greg@262: "5A2Kokxu3KXF6VSWWlz9pr04eXIrSC+6eVqv5MDwWGgsFmGooIfx2Bh+vx8pJbqu5wPfJMkn" greg@262: "aIBobGy8TtM0LMtCCMHNyzVkCtWZC50//juG2/YCoDjceOaVk1F8G3pGIOV4w7+U4dbPJq9H" greg@262: "4zJ6Ik3PEUikM47pj6L0XPBhK8rL1x2tr38PO5+wNCBjeHh4maqqCCHQNJW1ixWmbfY50/hw" greg@262: "K6GuLyevh9uqGQru4ZpNb6Uc7zD8U67f6ol1Cr+xIHFt5Y1PAZCVlXUtsARoB6IK8A1VVUXC" greg@262: "9i8qcJHuujLpz0ZjQy12zJTq3mDz5O/fn4u2ns1yTVkqmRudYv3dbvcCYBF29IDy8COP+JMd" greg@262: "V0muQP4pdV8oZC2qQijajFtWfIyh4B4AXu2ItnyZZQQQQkkeI3UL6bpgxoUQaobXOw/bfwlt" greg@262: "ZGTEr6oqUkqEEPjT5YVw+Aooa1EVnrxKAFSnB3fON1Cd6TPGSStGR+0/yR0tHZ2fWIoSz76g" greg@262: "Nkno7XDZZULkAi6Px5M5PDSUA3Rr4+PjOckb2Jd2ZbqfICN7GUb2souOGeiqN1/d/uOuz8Mt" greg@262: "GUqGq2C2cQJQhIJ0WrbTEwIhBG63OwO7wKAoQE4iXNY0jQwjkel9fRQx/Gppxe0Fvkz/zKWZ" greg@262: "hkCgIHVJPB7HNE1M08TlcmVg5y5CAULJeyBuJbB/fTTfW8j3rnuMf7vvTUr9iy46VhECxVQm" greg@262: "mTdNE2wn5gSEJqXsUBRbvxRFYXRc4WIrYEnk6bAyFLAlMIP2t3zGye7jgO3Vi32lLM8vZ15a" greg@262: "3oyx89Ly+PuNr/DYu3+Fac20fAKBoqhEw/YKKIqCoijE4/EwdhqA9uGOHZ13bdo0+UIbQGqq" greg@262: "GYz3vCdV520BMr85y5gv2mrY8dX7M/o3r/0b7lvxnRn9Bd4ibiy5lb1nPk4JQBUqsRFbfRJB" greg@262: "pmmaISaySQVoS540MKqmZGx7T6z9PaczS7i1lJK/FH341R9nvXdN9pIUvfaGdahOxoaiU1Ro" greg@262: "AsAYIDXgdPK0pm59xqPeOhcNNviM4kuV8S5Gq4pumPVemjNtJvsCNEVD9GuMj4xPpraqqkbi" greg@262: "8XgEGEkAOIBdm/QBDIZVuoY08ry2Oe2OmKFjma6CuTKf4cogNz3fZgLBgqxSyudXsn7xn886" greg@262: "p6GrfkafIhScmk64KYZpmliWxUTA2WbZhaXBBIC4lPJDRVEeTITSp3rSyMscBin5db/VJ7LF" greg@262: "pfO+CXpk9Q94ZPUP5jocgPrOL6dc27qvYTjcdJzowjRNhBAJZ3u2o73dBLoBSwH4cMeOPyqK" greg@262: "gsfjwe/3c2aoALQMxoUjNpDtKRTYn6+DDgb30Td6fkqfIhRcDhei08Fg19CkDwDMWCzWBIwy" greg@262: "kRMkTM5OVVVHdF2npqaGSNxFMFZBt6M45PP4VJfDQFU0hFD+pEC+aDvIy7temNKX2LjpupfT" greg@262: "H7VP2bxpaWldIyMjo9iGJwp2PgAQMgzjX/Py8l5sbGykoaGBbdvyuOPOG9wl31pMJD3EQKSf" greg@262: "0PgI4/ExFDG7qZ0LtQ+1UhPcx5u1vyRqRpPZR1M00vR0osclnWfOTdp+KaU0DKN2ZGRkDDjJ" greg@262: "RLk9eW/mv/DiiyfeeecdbzAYnOxUNZXrb72OVXeuIHOJh/PhXjJ0L6sKr8eUJnIOlbloPMpA" greg@262: "pI/+cB9tg0G6R87NGCMQqIpKmp5OjjqfT392kOH+kcnaVFFRUUc0Gn23tqamCXgbCMGFFQAY" greg@262: "9vv9v8nLy3sqGYAZNzmwu4YDu2vIL8rjpm+vxXeLn+rmj+kN9TAaDWFa5lWF4AmP63GmMc+d" greg@262: "T90vjtHX3Z+QPE6n08rJydl3+PDhEFBPUpE3eQVcwMq7Nm3a88EHHxgXe6GmqZTduISlf1lC" greg@262: "xBghGo9eMQAhbIvjcXrIzyjk2K+bOHbgK5Irg+Xl5c1DQ0Nbaw4ePINdpR5MzE9WZgsYzMvL" greg@262: "23epl1pSUrSkEHe2a0rJ/bIYR9i2XtXJNHwEvCWc/l0Hh/bWT1qdeDxOIBAYcrlcu2oOHhwA" greg@262: "qplQnQQlq5AEIgsXLjwObJztxf4cP1tefoxI3hDB/masOVenLzCOEGiKiksz8Lmz8Ms8dv1k" greg@262: "LycbGiclL6WksLAwumzZsh01NTX9wDHsotaUhCUZgAWMvfD8881chAJFATLcGVjqGG6nh7gV" greg@262: "J2qOY0kLS14kmxMCRdhWxqnqZLi85KblEzoU4zf/+S7ne89PqoyUEp/PZ95000276+rqOhtP" greg@262: "nmwFPseOf6Y/dpIU7DzzZs3heD8Wi6WO6rDD7o13buD676wkkj7CYKSf0HiIaHwcU8ZtIEkS" greg@262: "FxOMuxwG6XoGWW4/Yyctdr65h+PHLoTeCZNZUFAQ37hx4ycnTpxo+HzfvhbgfaCTpJJiKgAC" greg@262: "SANWFxYV/aGtrS0TQNd16fV6RU9PTwqhClasXEHlrSuZf10O+EzGYhFiVgzTshACVKHi1HQM" greg@262: "zc1Y0OT0vha+3HeIlpaWlMJZs2bN+Jo1a3bU1dUF93/+eTuwFbsGlLJUMh2AASwvX7nynSNH" greg@262: "jizKzs6Obt68+ZDf74/s3Llzza5du4yLbdrMzExKF5biy/aBkCiKQnQsxrn2Ttra2olEZpwQ" greg@262: "TZLT6eTuu+8+n5ub+2FtbW3Xl198EQR2AkEuUqSaDsAJBNasXfvPXV1dy6uqqurOnDnTE4lE" greg@262: "Rj7evTv01NNPV23fvn3V2bNnr84VJ5Gqqtxyyy3hioqKA8FgsKmhoeH8qcbGr4DdQD+zHXCn" greg@262: "AAAXTmtWA+6KVauuO3zoUA+wB/vUfNEPf/SjO7q6uu6pr69fVFdX54jHr6yK4fF4qKysjFRW" greg@262: "Vh7q7e09cerUqYG62to+oBaoww7YLmmjpwNI/C9Cxz4rrsS2vX3YG0jHzhtueOLJJ9fqur6s" greg@262: "tbW14ty5c9nNzc3Ozs7OWSM9VVXJycmRZWVl4dLS0jav13u0vb194OzZs8N1tbUD2GayDlvq" greg@262: "cz6ES5WnJLo07BUZ54IkEgfkLuz/MiwFKr738MMlPp8vDfBHIpFcKaXHNE0jGo3idDrDHo+n" greg@262: "z+Vy9YRCoUhvb2+ku7s73NPTM9p48mQPdkZ4DHuFL/sQ7kqzxMQ0xwSYTOxzhvyJ5l934406" greg@262: "IGKxmBUOh83jDQ1j2JnfAHAOOJvEdPxyGb9aADOeg+1HkhvYADVs5sax1XB6uyr6fzqK/HuW" greg@262: "ycvmAAAAAElFTkSuQmCC") greg@262: greg@262: def usage(): greg@262: print """ greg@262: Usage of Beremiz PLC execution service :\n greg@262: %s {[-n name] [-i ip] [-p port]|-h|--help} working_dir greg@262: -n - zeroconf service name greg@262: -i - ip of interface to bind to (x.x.x.x) greg@262: -p - port number greg@262: -h - print this help text and quit greg@262: greg@262: working_dir - directory where are stored PLC files greg@262: """%sys.argv[0] greg@262: greg@262: try: greg@262: opts, args = getopt.getopt(sys.argv[1:], "i:p:n:h") greg@262: except getopt.GetoptError, err: greg@262: # print help information and exit: greg@262: print str(err) # will print something like "option -a not recognized" greg@262: usage() greg@262: sys.exit(2) greg@262: greg@262: # default values greg@262: ip = "" greg@262: port = 3000 greg@262: name = os.environ[{ greg@262: "linux2":"USER", greg@262: "win32":"USERNAME", greg@262: }.get(sys.platform, "USER")] greg@262: greg@262: for o, a in opts: greg@262: if o == "-h": greg@262: usage() greg@262: sys.exit() greg@262: elif o == "-i": greg@262: if len(a.split(".")) == 4 or a == "localhost": greg@262: ip = a greg@262: elif o == "-p": greg@262: # port: port that the service runs on greg@262: port = int(a) greg@262: elif o == "-n": greg@262: name = a greg@262: else: greg@262: usage() greg@262: sys.exit() greg@262: greg@262: if len(args) > 1: greg@262: usage() greg@262: sys.exit() greg@262: elif len(args) == 1: greg@262: WorkingDir = args[0] greg@262: elif len(args) == 0: greg@262: WorkingDir = os.getcwd() greg@262: args=[WorkingDir] greg@262: greg@262: from runtime import PLCObject, ServicePublisher greg@262: import Pyro.core as pyro greg@262: greg@262: if not os.path.isdir(WorkingDir): greg@262: os.mkdir(WorkingDir) greg@262: greg@262: greg@262: greg@262: class Server(): greg@262: def __init__(self, name, ip, port, workdir, args): greg@262: self.continueloop = True greg@262: self.daemon = None greg@262: self.name = name greg@262: self.ip = ip greg@262: self.port = port greg@262: self.workdir = workdir greg@262: self.args = args greg@262: self.plcobj = None greg@263: self.servicepublisher = None greg@262: greg@262: def Loop(self): greg@262: while self.continueloop: greg@262: self.Start() greg@262: greg@262: def Restart(self): greg@262: self.Stop() greg@262: greg@262: def Quit(self): greg@262: self.continueloop = False greg@262: self.Stop() greg@262: greg@262: def Start(self): greg@262: pyro.initServer() greg@262: self.daemon=pyro.Daemon(host=self.ip, port=self.port) greg@262: self.plcobj = PLCObject(self.workdir, self.daemon, self.args) greg@262: uri = self.daemon.connect(self.plcobj,"PLCObject") greg@262: greg@262: print "The daemon runs on port :",self.port greg@262: print "The object's uri is :",uri greg@262: print "The working directory :",self.workdir greg@262: greg@262: # Configure and publish service greg@262: # Not publish service if localhost in address params greg@262: if self.ip != "localhost" and self.ip != "127.0.0.1": greg@263: print "Publish service on local network" greg@263: self.servicepublisher = ServicePublisher.ServicePublisher() greg@262: self.servicepublisher.RegisterService(self.name, self.ip, self.port) greg@262: greg@262: sys.stdout.flush() greg@262: greg@262: self.daemon.requestLoop() greg@262: greg@262: def Stop(self): greg@263: if self.servicepublisher is not None: greg@263: self.servicepublisher.UnRegisterService() greg@263: del self.servicepublisher greg@262: self.daemon.shutdown(True) greg@262: greg@262: class ParamsEntryDialog(wx.TextEntryDialog): greg@262: if wx.VERSION < (2, 6, 0): greg@262: def Bind(self, event, function, id = None): greg@262: if id is not None: greg@262: event(self, id, function) greg@262: else: greg@262: event(self, function) greg@262: greg@262: greg@262: def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", greg@262: style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): greg@262: wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) greg@262: greg@262: self.Tests = [] greg@262: if wx.VERSION >= (2, 8, 0): greg@262: self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) greg@262: elif wx.VERSION >= (2, 6, 0): greg@262: self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId()) greg@262: else: greg@262: self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId()) greg@262: greg@262: def OnOK(self, event): greg@262: value = self.GetValue() greg@262: texts = {"value" : value} greg@262: for function, message in self.Tests: greg@262: if not function(value): greg@262: message = wx.MessageDialog(self, message%texts, "Error", wx.OK|wx.ICON_ERROR) greg@262: message.ShowModal() greg@262: message.Destroy() greg@262: return greg@262: self.EndModal(wx.ID_OK) greg@262: greg@262: def GetValue(self): greg@262: return self.GetSizer().GetItem(1).GetWindow().GetValue() greg@262: greg@262: def SetTests(self, tests): greg@262: self.Tests = tests greg@262: greg@262: class DemoTaskBarIcon(wx.TaskBarIcon): greg@262: TBMENU_CHANGE_NAME = wx.NewId() greg@262: TBMENU_CHANGE_PORT = wx.NewId() greg@262: TBMENU_CHANGE_INTERFACE = wx.NewId() greg@262: TBMENU_CHANGE_WD = wx.NewId() greg@262: TBMENU_QUIT = wx.NewId() greg@262: greg@262: def __init__(self, pyroserver): greg@262: wx.TaskBarIcon.__init__(self) greg@262: # Set the image greg@262: icon = self.MakeIcon(BeremizIcon.GetImage()) greg@262: self.SetIcon(icon, "Beremiz Service") greg@262: # bind some events greg@262: #self.Bind(wx.EVT_TASKBAR_CLICK, self.OnClick) greg@262: #self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate) greg@262: self.Bind(wx.EVT_MENU, self.OnTaskBarChangeName, id=self.TBMENU_CHANGE_NAME) greg@262: self.Bind(wx.EVT_MENU, self.OnTaskBarChangeInterface, id=self.TBMENU_CHANGE_INTERFACE) greg@262: self.Bind(wx.EVT_MENU, self.OnTaskBarChangePort, id=self.TBMENU_CHANGE_PORT) greg@262: self.Bind(wx.EVT_MENU, self.OnTaskBarChangeWorkingDir, id=self.TBMENU_CHANGE_WD) greg@262: self.Bind(wx.EVT_MENU, self.OnTaskBarQuit, id=self.TBMENU_QUIT) greg@262: greg@262: def CreatePopupMenu(self): greg@262: """ greg@262: This method is called by the base class when it needs to popup greg@262: the menu for the default EVT_RIGHT_DOWN event. Just create greg@262: the menu how you want it and return it from this function, greg@262: the base class takes care of the rest. greg@262: """ greg@262: menu = wx.Menu() greg@262: menu.Append(self.TBMENU_CHANGE_NAME, "Change Name") greg@262: menu.Append(self.TBMENU_CHANGE_INTERFACE, "Change IP of interface to bind") greg@262: menu.Append(self.TBMENU_CHANGE_PORT, "Change Port Number") greg@262: menu.AppendSeparator() greg@262: menu.Append(self.TBMENU_CHANGE_WD, "Change working directory") greg@262: menu.Append(self.TBMENU_QUIT, "Quit") greg@262: return menu greg@262: greg@262: def MakeIcon(self, img): greg@262: """ greg@262: The various platforms have different requirements for the greg@262: icon size... greg@262: """ greg@262: if "wxMSW" in wx.PlatformInfo: greg@262: img = img.Scale(16, 16) greg@262: elif "wxGTK" in wx.PlatformInfo: greg@262: img = img.Scale(22, 22) greg@262: # wxMac can be any size upto 128x128, so leave the source img alone.... greg@262: icon = wx.IconFromBitmap(img.ConvertToBitmap() ) greg@262: return icon greg@262: greg@262: def OnTaskBarChangeInterface(self,evt): greg@262: dlg = ParamsEntryDialog(None, "Enter the ip of the interface to bind", defaultValue=pyroserver.ip) greg@262: dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, "Ip is not valid!"), greg@262: ( lambda ip :len([x for x in ip.split(".") if 0 <= int(x) <= 255]) == 4, "Ip is not valid!") greg@262: ]) greg@262: if dlg.ShowModal() == wx.ID_OK: greg@262: pyroserver.ip = dlg.GetValue() greg@262: pyroserver.Stop() greg@262: greg@262: def OnTaskBarChangePort(self,evt): greg@262: dlg = ParamsEntryDialog(None, "Enter a port number ", defaultValue=str(pyroserver.port)) greg@262: dlg.SetTests([(UnicodeType.isdigit, "Port number must be an integer!"), (lambda port : 0 <= int(port) <= 65535 , "Port number must be 0 <= port <= 65535!")]) greg@262: if dlg.ShowModal() == wx.ID_OK: greg@262: pyroserver.port = int(dlg.GetValue()) greg@262: pyroserver.Stop() greg@262: greg@262: greg@262: def OnTaskBarChangeWorkingDir(self,evt): greg@262: dlg = wx.DirDialog(None, "Choose a working directory ", pyroserver.workdir, wx.DD_NEW_DIR_BUTTON) greg@262: if dlg.ShowModal() == wx.ID_OK: greg@262: pyroserver.workdir = dlg.GetPath() greg@262: pyroserver.Stop() greg@262: greg@262: def OnTaskBarChangeName(self,evt): greg@262: dlg = ParamsEntryDialog(None, "Enter a name ", defaultValue=pyroserver.name) greg@262: dlg.SetTests([(lambda name : len(name) is not 0 , "Name must not be null!")]) greg@262: if dlg.ShowModal() == wx.ID_OK: greg@262: pyroserver.name = dlg.GetValue() greg@262: pyroserver.Restart() greg@262: greg@262: def OnTaskBarQuit(self,evt): greg@262: pyroserver.Quit() greg@262: self.RemoveIcon() greg@263: wx.GetApp().ExitMainLoop() greg@262: greg@262: pyroserver = Server(name, ip, port, WorkingDir, args) greg@262: greg@262: if havewx: greg@262: app=wx.App(redirect=False) greg@262: taskbar_instance = DemoTaskBarIcon(pyroserver) greg@262: pyro_thread=Thread(target=pyroserver.Loop) greg@262: pyro_thread.start() greg@262: app.MainLoop() greg@262: else: greg@262: pyroserver.Loop()