1

I am trying to print to a specific tabbed panel in wxpython, but my below code just seems to print to the 3rd (Running Jobs) TAB panel window and I can't work out why. I want to print the 2nd (QueueList) TAB panel.

import wx
import sys
global queueList
queueList = []

class ScrolledWindow(wx.Frame):
    def __init__(self, parent, id, title):
        #global panel1
        wx.Frame.__init__(self, parent, id, title, size=(800, 700))
        self.tabbed = wx.Notebook(self, -1, style=(wx.NB_TOP))
        self.panel1 = wx.Panel(self.tabbed, -1)
        self.panel2 = wx.Panel(self.tabbed, -1)
        self.panel3 = wx.Panel(self.tabbed, -1)
        self.tabbed.AddPage(self.panel1, "Submit Job")
        self.tabbed.AddPage(self.panel2, "Queue")
        self.tabbed.AddPage(self.panel3, "Running Jobs")
        self.CreateStatusBar()
        menuBar = wx.MenuBar()
        menu = wx.Menu()
        self.SetMenuBar(menuBar)
        self.Centre()
        self.submit(self)
        self.queue(self)
        self.running(self)

    def submit(self, event):
        self.Show()
        dt1 = MyFileDropTarget(self)
        self.tc_files = wx.TextCtrl(self.panel1, wx.ID_ANY, pos=(42, 120), size=(500, 25))
        self.tc_files.SetDropTarget(dt1)
        self.buttonGo = wx.Button(self.panel1, -1, "Submit", pos=(90,530))
        self.buttonGo.Bind(wx.EVT_BUTTON, self.submit1)
        self.buttonClose = wx.Button(self.panel1, -1, "Quit", pos=(195,530))
        self.buttonClose.Bind(wx.EVT_BUTTON, self.OnClose)
        outputtxt3 = '''Drag & Drop Folder of Packages to Verify'''
        wx.StaticText(self.panel1, -1, outputtxt3, (33, 64), style=wx.ALIGN_CENTRE)

    def notify(self, indir):
        """Update file in testcontrol after drag and drop"""
        self.tc_files.SetValue(indir[0])
        global indirTemp
        indirTemp = indir

    def submit1(self, edit):
        list1 = '\n'.join(indirTemp)
        queueList.append(list1)
        print queueList
        wx.MessageBox('Job Submitted')

    def queue(self, event):
        self.Show()
        self.buttonClose2 = wx.Button(self.panel2, -1, "Quit", pos=(195,170))
        self.buttonClose2.Bind(wx.EVT_BUTTON, self.OnClose)
        global log2
        log2 = wx.TextCtrl(self.panel2, -1, pos=(35, 210), size=(720,400),
                              style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        self.redir2=RedirectText(log2)
        sys.stdout=self.redir2

    def showQueue(self, edit):
        global queueList
        print queueList

    def running(self, event):
        self.Show()
        self.buttonClose3 = wx.Button(self.panel3, -1, "Quit", pos=(195,170))
        self.buttonClose3.Bind(wx.EVT_BUTTON, self.OnClose)

        global log3
        log3 = wx.TextCtrl(self.panel3, -1, pos=(35, 210), size=(720,400),
                              style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        self.redir3=RedirectText(log3)
        sys.stdout=self.redir3

    def go3(self, edit):
        print "do something"

    def OnClose(self, e):
        self.Close(True)

class MyFileDropTarget(wx.FileDropTarget):
    """"""
    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window

    def OnDropFiles(self, x, y, filenames):
        self.window.notify(filenames)

class RedirectText:
    def __init__(self,aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self,string):
        self.out.WriteText(string)

app = wx.App()
ScrolledWindow(None, -1, 'Application')
app.MainLoop()
speedyrazor
  • 3,127
  • 7
  • 33
  • 51
  • Out of curiosity, why are you declaring the TextCtrls to be global? – GreenAsJade Feb 15 '14 at 22:48
  • I dragged a folder to the drop target and hit "submit". I did not get anything printed to tab3, I got an error message about queueList not being defined. – GreenAsJade Feb 15 '14 at 22:55
  • ... and (sorry for the diatribe) I would look hard for different way to do this than redirecting stdout. I think that comandeering stdout as a path between two parts of your code is going to be a very fragile, hard to maintain, error prone thing to do... – GreenAsJade Feb 15 '14 at 22:57
  • ... pubsub might be a good alternative. – GreenAsJade Feb 15 '14 at 23:25
  • Sorry, I have amended the original question to show the example, which now runs as described, but still prints to the wrong window. – speedyrazor Feb 16 '14 at 05:33
  • Another note (not related to the question) your `submit()` (and others) are taking an event as parameter, but not using it. Which is just as well, because your `__init__` is calling them with `self` as the parameter - a wx.Frame is not an event. – GreenAsJade Feb 16 '14 at 08:23

2 Answers2

1

The reason is because you call running() after you call queue() in __init__(), and this is the last thing that redirects stdout before you do some printing.

Here is a version of your code that makes this obvious when you run it, by outputting a trace of what it is doing as it does it...

import wx
import sys
global queueList
queueList = []

class ScrolledWindow(wx.Frame):
    def __init__(self, parent, id, title):
        #global panel1
        wx.Frame.__init__(self, parent, id, title, size=(800, 700))
        self.tabbed = wx.Notebook(self, -1, style=(wx.NB_TOP))
        self.panel1 = wx.Panel(self.tabbed, -1)
        self.panel2 = wx.Panel(self.tabbed, -1)
        self.panel3 = wx.Panel(self.tabbed, -1)
        self.tabbed.AddPage(self.panel1, "Submit Job")
        self.tabbed.AddPage(self.panel2, "Queue")
        self.tabbed.AddPage(self.panel3, "Running Jobs")
        self.CreateStatusBar()
        menuBar = wx.MenuBar()
        menu = wx.Menu()
        self.SetMenuBar(menuBar)
        self.Centre()
        self.submit(self)
        self.queue(self)
        self.running(self)

    def submit(self, event):
        self.Show()
        dt1 = MyFileDropTarget(self)
        self.tc_files = wx.TextCtrl(self.panel1, wx.ID_ANY, pos=(42, 120), size=(500, 25))
        self.tc_files.SetDropTarget(dt1)
        self.buttonGo = wx.Button(self.panel1, -1, "Submit", pos=(90,530))
        self.buttonGo.Bind(wx.EVT_BUTTON, self.submit1)
        self.buttonClose = wx.Button(self.panel1, -1, "Quit", pos=(195,530))
        self.buttonClose.Bind(wx.EVT_BUTTON, self.OnClose)
        outputtxt3 = '''Drag & Drop Folder of Packages to Verify'''
        wx.StaticText(self.panel1, -1, outputtxt3, (33, 64), style=wx.ALIGN_CENTRE)

    def notify(self, indir):
        """Update file in testcontrol after drag and drop"""
        self.tc_files.SetValue(indir[0])
        global indirTemp
        indirTemp = indir

    def submit1(self, edit):
        list1 = '\n'.join(indirTemp)
        queueList.append(list1)
        sys.stderr.write("submit1 printing\n")
        print queueList
        wx.MessageBox('Job Submitted')

    def queue(self, event):
        self.Show()
        self.buttonClose2 = wx.Button(self.panel2, -1, "Quit", pos=(195,170))
        self.buttonClose2.Bind(wx.EVT_BUTTON, self.OnClose)
        global log2
        log2 = wx.TextCtrl(self.panel2, -1, pos=(35, 210), size=(720,400),
                              style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        sys.stderr.write("redirecting stdout to log2\n")
        self.redir2=RedirectText(log2)
        sys.stdout=self.redir2


    def showQueue(self, edit):
        sys.stderr.write("showQueue printing\n")
        global queueList
        print queueList

    def running(self, event):
        self.Show()
        self.buttonClose3 = wx.Button(self.panel3, -1, "Quit", pos=(195,170))
        self.buttonClose3.Bind(wx.EVT_BUTTON, self.OnClose)

        global log3
        log3 = wx.TextCtrl(self.panel3, -1, pos=(35, 210), size=(720,400),
                              style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        sys.stderr.write("redirecting stdout to log3\n")
        self.redir3=RedirectText(log3)
        sys.stdout=self.redir3

    def go3(self, edit):
        sys.stderr.write("go3 printing\n")
        print "do something"

    def OnClose(self, e):
        self.Close(True)

class MyFileDropTarget(wx.FileDropTarget):
    """"""
    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window

    def OnDropFiles(self, x, y, filenames):
        self.window.notify(filenames)

class RedirectText:
    def __init__(self,aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self,string):
        self.out.WriteText(string)

app = wx.App()
ScrolledWindow(None, -1, 'Application')
app.MainLoop()

Note the trouble that you have to go to to do some simple debugging, because you have hijacked stdout. I still strongly recommend you find a better way to do this, when you come to actually completing your application, once you have satisfied yourself with this experiment. It is not robust or maintainable doing it the way that you are doing it. Other evidence for this is the need for globals: a big warning sign that your code will become fragile and hard to maintain.

GreenAsJade
  • 14,459
  • 11
  • 63
  • 98
  • Thanks GreenAsJade, that makes sense, but i am unsure of how to go about using pubsub in this situation. could you point me in the right direction please? – speedyrazor Feb 16 '14 at 07:36
  • OK pubsub is a good way to communicate between separate entities anonymously (IE without the sender needing to know where it's going and without the receiver knowing where it is coming from). This is what you are achieving with your print. But ... it might not be the right answer in your case. You will only know when your overall app is structured better, which is the first thing to tackle. Ideally, you would restructure your app into the "Model/View/Controller" architecture, or something similar. You will benefit from this as your app grows. – GreenAsJade Feb 16 '14 at 08:00
  • I'll think about how to do it within the your current structure, while you google MVC :) – GreenAsJade Feb 16 '14 at 08:00
1

As I mentioned in my other answer, the superficial reason why it's not working is because there was a bug :) IE the logic written was not what was intended. The deeper reason is because the app needs some better design. Appended is an example of how that might be done.

The main feature of this design is that it separates the logic of each function (each of which is carried by the user on a separate tab of the notebook) into separate classes. The classes communicate by method calls, and the connections are static at construction (for example, a reference to the queue manager is passed to the submission controller on construction).

This is by no means supposed to be "the best" design, it's just an example of possible improvement - the improvements reduce the chance of the types of hard to find logic errors in the original.

import wx

class MainWindow(wx.Frame):
    def __init__(self, parent, id, title):

        wx.Frame.__init__(self, parent, id, title, size=(800, 700))
        self.tabbed = wx.Notebook(self, -1, style=(wx.NB_TOP))
        self.running = RunningPane(self.tabbed)
        self.queue = QueuePane(self.tabbed, self.running)
        self.submissions = SubmissionPane(self.tabbed, self.queue)

        self.tabbed.AddPage(self.submissions, "Submit Job")
        self.tabbed.AddPage(self.queue, "Queue")
        self.tabbed.AddPage(self.running, "Running Jobs")

        self.CreateStatusBar()
        menuBar = wx.MenuBar()
        menu = wx.Menu()
        self.SetMenuBar(menuBar)
        self.Centre()
        self.Show()


#---
#

class SubmissionPane(wx.Panel):
    def __init__(self, parent, queue_control):
        wx.Panel.__init__(self, parent, -1)
        self.parent = parent
        self.queue_control = queue_control
        self.selected_folder = None

        self.drop_target = MyFileDropTarget(self)
        self.tc_files = wx.TextCtrl(self, wx.ID_ANY, pos=(42, 120), size=(500, 25))
        self.tc_files.SetDropTarget(self.drop_target)
        self.buttonGo = wx.Button(self, -1, "Submit", pos=(90,530))
        self.buttonGo.Bind(wx.EVT_BUTTON, self.OnSubmit)

        self.buttonClose = wx.Button(self, -1, "Quit", pos=(195,530))
        self.buttonClose.Bind(wx.EVT_BUTTON, self.OnClose)

        outputtxt3 = '''Drag & Drop Folder of Packages to Verify'''
        wx.StaticText(self, -1, outputtxt3, (33, 64), style=wx.ALIGN_CENTRE)
        self.Show()


    def SetSubmissionFolders(self, folder_list):
        """Called by the FileDropTarget when files are dropped"""

        self.tc_files.SetValue(','.join(folder_list))  
        self.selected_folders = folder_list


    def OnSubmit(self, event):
        self.queue_control.QueueFolders(self.selected_folders)
        wx.MessageBox('Job Submitted')


    def OnClose(self, e):
        self.Close(True)


class MyFileDropTarget(wx.FileDropTarget):
    """"""
    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window


    def OnDropFiles(self, x, y, filenames):
        self.window.SetSubmissionFolders(filenames)

#---
#

class QueuePane(wx.Panel):
    def __init__(self, parent, run_control):
        wx.Panel.__init__(self, parent, -1)

        self.parent = parent
        self.run_control = run_control

        self.queue = []

        self.buttonClose2 = wx.Button(self, -1, "Quit", pos=(195,170))
        self.buttonClose2.Bind(wx.EVT_BUTTON, self.OnClose)

        self.log_text = wx.TextCtrl(self, -1, pos=(35, 210), size=(720,400),
                                    style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        self.Show()


    def QueueFolders(self, folder_list):
        """Called by anyone with a list of folders to queue.
            In this code, that is the submission pane."""
        self.queue.extend(folder_list)
        self.log_text.AppendText("\n".join(folder_list))


    def OnClose(self, e):
        self.Close(True)

#---
#

class RunningPane(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1)

        self.parent = parent

        self.buttonClose3 = wx.Button(self, -1, "Quit", pos=(195,170))
        self.buttonClose3.Bind(wx.EVT_BUTTON, self.OnClose)

        self.running_log = wx.TextCtrl(self, -1, pos=(35, 210), size=(720,400),
                                       style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        self.Show()


    def OnClose(self, e):
        self.Close(True)
#
#---

app = wx.App()
MainWindow(None, -1, 'Application')
app.MainLoop()
GreenAsJade
  • 14,459
  • 11
  • 63
  • 98
  • I appreciate the time you have put into this GreenAsJade, I'm going to have to sit down and decipher this bit by bit. Many thanks again. – speedyrazor Feb 16 '14 at 09:26
  • Adding back in the rest of the application, I am using quite a few global's, you mentioned this is bad practice? – speedyrazor Feb 16 '14 at 21:38
  • It really really is, for very good reasons - not just "dogma". I think that I showed you how to communicate between sections of your code without globals in the above example. The communication between the queue and the running list should be the same. If there are other things that need to communicated, once again they should communicate by method calls (as above) or notifications (pubsub). – GreenAsJade Feb 17 '14 at 00:22
  • OK, I have been studying your code..... I now need to pass a few other variables into, lets say, the running class / function. To start I need to pass the self.queue value through to the running class / function, but am unsure of how to do that, could you point me in the right direction please? – speedyrazor Feb 17 '14 at 10:25
  • I guess we're pushing the boundaries of what we're supposed to talk about in a question/answer/comment. So I made a room: http://chat.stackoverflow.com/rooms/47656/tips-for-good-design-in-python . See you there. – GreenAsJade Feb 17 '14 at 11:25