Python Forum
moving from tkinter to wxpython - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: Web Scraping & Web Development (https://python-forum.io/forum-13.html)
+--- Thread: moving from tkinter to wxpython (/thread-8405.html)

Pages: 1 2 3 4 5 6 7 8 9 10


moving from tkinter to wxpython - Barrowman - Feb-18-2018

I have been using tkinter for some time but I have seen so much about how much better wxpython is that I am trying to start using it. I am trying to create a program which reads from a list and creates buttons which are in a grid and which I can bind to an event handler. I have been searching for a few hours now but cannot figure out how to do it so I have started with this I found:
import wx
  
class Example(wx.Frame): 
   
   def __init__(self, parent, title): 
      super(Example, self).__init__(parent, title = title,size = (300,200)) 
             
      self.InitUI() 
      self.Centre() 
      self.Show()      
         
   def InitUI(self): 
	
      p = wx.Panel(self) 
         
      gs = wx.GridSizer(4, 4, 5, 5) 
		
      for i in range(1,17): 
         btn = "Btn"+str(i) 
         gs.Add(wx.Button(p,label = btn),0,wx.EXPAND) 

         p.SetSizer(gs) 
# Adding the line below caused an error  
         btn.bind('<Button-1>', onButton) 
# I have added this def to the original so I can test it         
   def onButton(self, event):
        """
        This method is fired when its corresponding button is pressed
        """
        print ("Button pressed!")     
app = wx.App() 
Example(None, title = 'Grid demo') 
app.MainLoop()
It gives a good layout and I can figure out how to adjust it to manage my list of 66 items. I cannot seem to work out how to get bind into the for loop.
Error:
Traceback (most recent call last): File "./first2wx.py", line 31, in <module> Example(None, title = 'Grid demo') File "./first2wx.py", line 8, in __init__ self.InitUI() File "./first2wx.py", line 23, in InitUI btn.bind('<Button-1>', onButton) AttributeError: 'str' object has no attribute 'bind'



RE: moving from tkinter to wxpython - Larz60+ - Feb-18-2018

Let me make a suggestion that will help you out tremendously: Each demo contains three tabs in a wx notebook window.
you can search for the control that you are interested in, and click on that demo.
Take a look at the demo and see if it's close to what you want.
the middle tab contains the source code for the demo, use it as a guide, or copy and modify for your application.

The demo is really complete and well documented.


RE: moving from tkinter to wxpython - Barrowman - Feb-19-2018

Thanks,
I have started to look at it and indeed it has already solved that problem for me.


RE: moving from tkinter to wxpython - Barrowman - Feb-19-2018

I have been trying to generate 66 buttons and bind them to a function. Buttons do appear fine but there have been errors when I click a button whichever demo code I have tried to modify. This is why I was trying to see if the ButtonPanel demo could give some hints.
Still getting problems i'm afraid. I have Python 3.5.2 on my system
Tried to run the ButtonPanel demo but 2 modules not installed so tried to install them.
sudo -H pip3 install run
Error:
Collecting run Downloading run-0.2.tar.gz Complete output from command python setup.py egg_info: Traceback (most recent call last): File "<string>", line 1, in <module> File "/tmp/pip-build-yixxz347/run/setup.py", line 12, in <module> long_description=file('README').read(), NameError: name 'file' is not defined ---------------------------------------- Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-yixxz347/run/
and
sudo -H pip3 install images
Error:
Collecting images Could not find a version that satisfies the requirement images (from versions: ) No matching distribution found for images



RE: moving from tkinter to wxpython - Larz60+ - Feb-19-2018

run.py is a program in the demo directory, it basically sets up the
wx.APP runs flash screen and other necessary things to run the demo. You should not use it to run your own programs,
but build your own start routine. I haven't done this in a while, and have basically reversed engineered run.py
each time.
I'll have to look at some of my own code to see what you have to include, this time, I'll put it in the snippets section (and here)
so I never have to do it again.

It's really simple, unless you forget how to do it, and that's where I'm at right now.
I'll be back later with the solution, unless one of the other moderators can fill in here now.


RE: moving from tkinter to wxpython - Larz60+ - Feb-20-2018

Almost there... but will pick up tomorrow.
One game of 'Heroes IV tribes of the East' and off to bed!


RE: moving from tkinter to wxpython - Larz60+ - Feb-20-2018

I forgot to mention, you can copy run.py from the demo directory to your development directory,
and then import it without a problem. It will work, but give you a lot of stuff that you don't want,
but at least let you work. Ok for now.

What I am creating is a new streamlined run.py, that Im trying to setup so that all you will have
to do is instantiate the main class, passing __file__, and I'll take care of the rest.
Just creating a single window that you can hide if not wanted.

I have it almost done, but there are a few things that aren't cooperating.
Will pick up in my A.M.


RE: moving from tkinter to wxpython - Larz60+ - Feb-20-2018

OK here's the modified run.py.
It will run any of the demo's (I only tried a few, if you find some that won't let me know)
but without menu which I deleted for your own programs.
It produces one window.
If you need more, you will have to add them
I also added the following options to the window:
# optional parameters:
# title='your title here'
# xpos=starting xpos
# ypos=starting ypos
# width=window width in pixels
# height=window height in pixels
# style=wx window style -- see Window Styles: https://docs.wxpython.org/wx.Frame.html
# name=windows name
I'm still working on a simple instantiate and good to go, to arrive shortly.
This will be good for the time being.
code:
#!/usr/bin/env python
#----------------------------------------------------------------------------
# Name:         run.py
# Purpose:      Simple framework for running individual demos
#
# Author:       Robin Dunn
# Mod version:  Larz60+ 20 Feb 2018
#
# Created:      6-March-2000
# Copyright:    (c) 2000-2017 by Total Control Software
# Licence:      wxWindows license
#----------------------------------------------------------------------------

"""
This program will load and run one of the individual demos in this
directory within its own frame window.  Just specify the module name
on the command line.
"""

import wx
import wx.lib.inspection
import wx.lib.mixins.inspection
import sys, os

# stuff for debugging
print("Python %s" % sys.version)
print("wx.version: %s" % wx.version())
##print("pid: %s" % os.getpid()); input("Press Enter...")

assertMode = wx.APP_ASSERT_DIALOG
##assertMode = wx.APP_ASSERT_EXCEPTION


#----------------------------------------------------------------------------

class Log:
    def WriteText(self, text):
        if text[-1:] == '\n':
            text = text[:-1]
        wx.LogMessage(text)
    write = WriteText


class RunDemoApp(wx.App, wx.lib.mixins.inspection.InspectionMixin):
    def __init__(self, name, module, useShell, title='My title here', xpos=20, ypos=20, width=600,
                 height=338, style=wx.DEFAULT_FRAME_STYLE, winname=wx.FrameNameStr):
        self.title = title
        self.name = name
        self.demoModule = module
        self.useShell = useShell
        self.xpos = xpos
        self.ypos=ypos
        self.width=width
        self.height=height
        self.winname = winname
        self.style=style
        wx.App.__init__(self, redirect=False)


    def OnInit(self):
        wx.Log.SetActiveTarget(wx.LogStderr())

        self.SetAssertMode(assertMode)
        self.InitInspection()  # for the InspectionMixin base class

        frame = wx.Frame(None, id=wx.ID_ANY, title=self.title, pos=(self.xpos,self.ypos),
                         size=(self.width,self.height), style=self.style, name=self.winname)
        frame.CreateStatusBar()

        ns = {}
        ns['wx'] = wx
        ns['app'] = self
        ns['module'] = self.demoModule
        ns['frame'] = frame

        frame.Show(True)
        frame.Bind(wx.EVT_CLOSE, self.OnCloseFrame)

        win = self.demoModule.runTest(frame, frame, Log())

        # a window will be returned if the demo does not create
        # its own top-level window
        if win:
            # so set the frame to a good size for showing stuff
            frame.SetSize((self.width, self.height))
            win.SetFocus()
            self.window = win
            ns['win'] = win
            frect = frame.GetRect()
        else:
            # It was probably a dialog or something that is already
            # gone, so we're done.
            frame.Destroy()
            return True

        self.SetTopWindow(frame)
        self.frame = frame
        #wx.Log.SetActiveTarget(wx.LogStderr())
        #wx.Log.SetTraceMask(wx.TraceMessages)

        if self.useShell:
            # Make a PyShell window, and position it below our test window
            from wx import py
            shell = py.shell.ShellFrame(None, locals=ns)
            frect.OffsetXY(0, frect.height)
            frect.height = 400
            shell.SetRect(frect)
            shell.Show()

            # Hook the close event of the test window so that we close
            # the shell at the same time
            def CloseShell(evt):
                if shell:
                    shell.Close()
                evt.Skip()
            frame.Bind(wx.EVT_CLOSE, CloseShell)

        return True


    def OnExitApp(self, evt):
        self.frame.Close(True)


    def OnCloseFrame(self, evt):
        if hasattr(self, "window") and hasattr(self.window, "ShutdownDemo"):
            self.window.ShutdownDemo()
        evt.Skip()

    def OnWidgetInspector(self, evt):
        wx.lib.inspection.InspectionTool().Show()


#----------------------------------------------------------------------------


def main(argv):
    useShell = False
    for x in range(len(sys.argv)):
        if sys.argv[x] in ['--shell', '-shell', '-s']:
            useShell = True
            del sys.argv[x]
            break

    if len(argv) < 2:
        print("Please specify a demo module name on the command-line")
        raise SystemExit

    # ensure the CWD is the demo folder
    demoFolder = os.path.realpath(os.path.dirname(__file__))
    os.chdir(demoFolder)

    sys.path.insert(0, os.path.join(demoFolder, 'agw'))
    sys.path.insert(0, '.')

    name, ext  = os.path.splitext(argv[1])
    module = __import__(name)

    # optional parameters:
    # title='your title here'
    # xpos=starting xpos
    # ypos=starting ypos
    # width=window width in pixels
    # height=window height in pixels
    # style=wx window style -- see Window Styles: https://docs.wxpython.org/wx.Frame.html
    # name=windows name
    app = RunDemoApp(name=name, module=module, useShell=useShell)
    app.MainLoop()


if __name__ == "__main__":
    main(sys.argv)
You will need to start your programs with this routine
if __name__ == '__main__':
    import sys,os
    import run
    run.main(['', __file__.split('/').pop(-1)])    
Ultimately, you want to create your own wx.App() in a class init.
That way you will be able to import the module into other code.
something like the first few initialize statements here: https://python-forum.io/Thread-Perfectly-proportioned-3-frame-wxpython-application-template


RE: moving from tkinter to wxpython - Barrowman - Feb-20-2018

Thanks for the help I am beginning to get a bit more of a grip on it now.
I have been playing around with some of the demos but I find that if I have some specific project in mind I find it easier.
Now I have an elderly friend who is blind. Before that happened she loved to read her Bible so I thought I would see what I might be able to do to help her. I have downloaded a copy in mp3 format. It is a whole series of files each of which is one chapter so I have started to work out if I can get it set up for her.
My current plan is to have a set of buttons each of which represents a book. Then clicking on one will show a frame which has buttons on it and each one when clicked will play the file that relates to that chapter.
So she can navigate I want to play another mp3 which I will record announcing the book or chapter when the mouse enters the button. This is what I have so far ( I know it's probably a poor way of doing it Smile )
I am using a script from here
http://www.java2s.com/Tutorial/Python/0380__wxPython/BindeventtobuttonMouseenterandleavebuttonclicked.htm
which I have been modifying
import wx
import wx.lib.buttons as buttons


books = [' ','Genesis050', 'Exodus040', 'Leviticus027', 'Numbers036', 'Deuteronomy034', 'Joshua024', 'Judges021', 'Ruth004', '1 Samuel031', '2 Samuel024', '1 Kings022', '2 Kings025', '1 Chronicles029', '2 Chronicles036', 'Ezra010', 'Nehemiah013', 'Esther010', 'Job042', 'Psalms150', 'Proverbs031', 'Ecclesiastes012', 'Song of Solomon008', 'Isaiah066', 'Jeremiah052', 'Lamentations005', 'Ezekiel048', 'Daniel012', 'Hosea014', 'Joel003', 'Amos009', 'Obadiah001', 'Jonah004', 'Micah007', 'Nahum003', 'Habakkuk003', 'Zephaniah003', 'Haggai002', 'Zechariah014', 'Malachi004', 'Matthew028', 'Mark016', 'Luke024', 'John021', 'Acts028', 'Romans016', '1 Corinthians016', '2 Corinthians013', 'Galatians006', 'Ephesians006', 'Philippians004', 'Colossians004', '1 Thessalonians005', '2 Thessalonians003', '1 Timothy006', '2 Timothy004', 'Titus003', 'Philemon001', 'Hebrews013', 'James005', '1 Peter005', '2 Peter003', '1 John005', '2 John001', '3 John001', 'Jude001', 'Revelation022']
global row, column
row = 0
column = 0
class MouseEventFrame(wx.Frame):
    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, 'Frame and Button', size=(1210, 400))
#        wx.Frame.__init__(self, parent, id, size=(1210, 400))
        self.panel = wx.Panel(self)
        row = 0
        column = 0
  
        for x in range(1,67):
            txt = books[x]
            myname = txt[:-3]
            while (len(myname) < 15):
               myname = " " + myname
            while (len(myname) < 20):
                myname += " "
            btn = 'butt' + str(x)
            btn = wx.Button(self.panel, -1, myname, pos=(column, row ) )#50, 50)) myname
            self.Bind(wx.EVT_BUTTON, self.OnButton,btn)
            self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow,btn)
            column += 120
            if column == 1200:
                column = 0
                row += 55
    def OnButton(self, event):
       obj = event.GetEventObject()
       print("You clicked %s\n"%obj.GetLabel())
       event.Skip()       

    def OnEnterWindow(self, event):
        self.button.SetMyname("Over Me!")
        event.Skip()
        

app = wx.App()
frame = MouseEventFrame(parent=None, id=-1)
#frame = MyFrame(None, 'wx.lib.buttons Test')
frame.Show()
app.MainLoop()
It's obviously at an early stage so far and I am developing it on my Linux machine but she has WIn7 Pro so no doubt it will need some changes. The 3 digits at the end of each name are how many chapters there are in the book
Problem is that the OnButton click event is detected and works but the OnEnterWindow doesn't.
So once again I am stuck.Can you spot the way to fix my problem please?


RE: moving from tkinter to wxpython - Larz60+ - Feb-20-2018

Here's an example (for older wxpython, but should work the same): https://wiki.wxpython.org/MouseOvers
I would create a panel (or frame) same size as button, and use panel mouse over event to update button