Re: [Tutor] tkinter code executes before function returned
On 15/04/18 18:10, Alan Gauld via Tutor wrote: On 15/04/18 03:57, Chris Roy-Smith wrote: I am trying to get tkinter to return a number from a window, which then sets how many times to print a sign. I don;t jhave time to look at this in detail just now, maybe later. But first impressions is that you have a very unorthodox style of Tkinter programming. Its more traditional to build the entire GUI up front rather than creating and destroying widgets each time you execute an event handler. Its less disturbing to the user than having things appear/disappear etc, as you seem to be doing. You can make widget hide/show/deactivate themselves without destroying them just by withdrawing/unpacking them etc or changing their status, if that's really what you want to do. The code does not wait till the function returns a value, resulting in the signcount variable in having a None value, giving an output like below. I'll look at this a bit more closely later if nobody else answers by then... This is where you call your function. Looking at it quickly I think you would be as well using the standard Tkinter simpledialogs/messagebox modules to get user input. Have you looked at the simpledialogs? Thank you Alan, I didn't know of simpledialogs. That was all I needed to search documentation for these. I have now achieved what I was trying to do, with user interface as I was planning. Or better still having a static entry field on your GUI and just reading that? I'll have to figure out how to achieve that! Perhaps my intended design is not in line with modern styles? Regards, Chris Roy-Smith ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] tkinter code executes before function returned
On 04/15/2018 03:13 PM, Chris Roy-Smith wrote: > On 15/04/18 23:36, Alan Gauld via Tutor wrote: >> On 15/04/18 14:24, Alan Gauld via Tutor wrote: >> >>> OK, I've had a closet look now and can confirm the >> A closer look! Not a closet look. Ooops! :-/ >> >> > Thank you Alan, I have even more to learn than I thought. I have been > bashing away at several OOP tutorials, but the penny still hasn't > dropped on making that work yet, I get the concept, but don't seem to > understand how to make it work for methods. Events just add to my lack > of understanding. I'll have to try your tutorial. Hopefully I won't have > too many questions after that. By all means work through a tutorial that appeals to you! If there are OOP examples that aren't working for you, the people at this list can help: just like you did with the tkinter example, if you post the code, people have something to explain against. There are plenty of "lectures" on the Internet, we don't need to reproduce that role :) Object-oriented features help make Python more powerful, but you don't have to use them unless they help solve the problem at hand, unlike languages like C# and Java, where everything is object-oriented by design. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] tkinter code executes before function returned
On 15/04/18 23:36, Alan Gauld via Tutor wrote: On 15/04/18 14:24, Alan Gauld via Tutor wrote: OK, I've had a closet look now and can confirm the A closer look! Not a closet look. Ooops! :-/ Thank you Alan, I have even more to learn than I thought. I have been bashing away at several OOP tutorials, but the penny still hasn't dropped on making that work yet, I get the concept, but don't seem to understand how to make it work for methods. Events just add to my lack of understanding. I'll have to try your tutorial. Hopefully I won't have too many questions after that. Regards, Chris Roy-Smith ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] tkinter code executes before function returned
On 15/04/18 14:24, Alan Gauld via Tutor wrote: > OK, I've had a closet look now and can confirm the A closer look! Not a closet look. Ooops! :-/ -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] tkinter code executes before function returned
On 15/04/18 03:57, Chris Roy-Smith wrote: > The code does not wait till the function returns a value, OK, I've had a closet look now and can confirm the problem lies in your code structure. Its not event based. You need to understand event-driven programming better. In event driven code you initialise your program (including the GUI) and then wait for events - usually from the user. When you receive an event you handle that event - and only that specific event - before returning to the wait state until the user makes the next move. The problem with your code is that you build a basic GUI then wait. But when the user presses a button you create a second set of GUI elements and then *without waiting for the user to do anything* try to process the data in the new elements. That is why you get nothing back, the user hasn't done anything yet. The other, related, problem is that event handlers do NOT return values. You can't assign the value of an event callback method to a variable you have to use a global variable inside the event function. (Note: This is one reason GUIs are often built using objects because you can assign to an window object's attribute rather than a global variable, which makes for slightly cleaner code, easier to maintain.) So what to do? You need to break your data processing code out to a separate function(*) then call that only when you know there is data to be processed. That wont happen until you get the submit action from the user. So you need to call the data processing in your final callback - ReturnCount in your case. (*)Technically, you could just move the code into ReturnCount but that makes the callback code overly complex, better to keep reading and displaying as distinct operations. So the flow of action should be: initialise GUI and wait. on clock -> create extended GUI and wait on click -> create input dialog and wait on click -> read data and if valid -> call data processing function delete input dialog wait else -> reset fields and/or generate error message wait Note how the end of each step waits for the user to initiate the next action? That's the event driven bit. How do we wait? We do nothing, the GUI mainloop handles all that for us. > #!/usr/bin/python3 > from tkinter import * > import os > from reportlab.lib.units import cm > from reportlab.lib.pagesizes import A4 > from reportlab.pdfgen import canvas > from reportlab.lib.utils import ImageReader > > def printSign(): > global gc, packages, rows > myCanvas = canvas.Canvas("Signs.pdf", pagesize=A4) > width, height = A4 #keep for > myCanvas.rotate(90) > myCanvas.setFillColorRGB(0,0,0) > myCanvas.setFont("Helvetica-Bold", 400) > TopMargin=-20 > LeftMargin=1 > Width=14 > Height=19 > VertPos=-15 > Bottom=-1 > a=[" " for i in range(rows)] > i=0 > sign=0 All the stuff below here should go in a function called something like display??? where ??? is whatever your data represents. > for line in packages: > signcount=getcount(line[1]) > print('line 27 ### required sign count for {} is {} > ###'.format(line[1], str(signcount))) > for x in range(signcount): > #draw rectangle > myCanvas.rect(LeftMargin*cm+Width*sign*cm, TopMargin*cm, > Width*cm, Height*cm, stroke=0, fill=1) > myCanvas.drawCentredString((LeftMargin+(0.5*Width))*cm+(Width*sign)*cm, > VertPos*cm, line[0]) > if sign==1: > myCanvas.showPage() > sign=0 > myCanvas.rotate(90) > i+=1 > else: > sign+=1 > i+=1 > > myCanvas.showPage() > myCanvas.save() > if os.name == "posix": > os.popen("evince %s" % ("Signs.pdf")) > if os.name == "nt": > os.startfile('Signs.pdf') > > def getcount(SignText): > global gc,e > gc=Toplevel(master) > MsgText='How many copies of {} do you want to print?'.format(SignText) > Label(gc, text=MsgText).grid(row=0, column=0, sticky=(W,E)) > e = Entry(gc) > e.grid(row=0, column=1) > Button(gc, text='Okay', command=ReturnCount).grid(row=1, column=0, > sticky=(W,E)) > Button(gc, text='Cancel', command=gc.destroy).grid(row=1, column=1, > sticky=(W,E)) > > def ReturnCount(): > global gc,e > b0=e.get() > if b0 == None: > b0=0 call display??? here > gc.destroy() > print('line 64 ### The required number of signs is {} > ###'.format(b0)) > return b0 You need to make b0 (can't you think of a more informative name? count say?) Then you can access b0 from your display??? code > > master = Tk() > master.title("Testing") > packages = [[0,'D','drill'],[1,'J','Jointer'],[2,'B','Bandsaw']] > rows = 3 > b2 = Button(master, text="Print Signs", command=printSign).grid(row=4, > column=0, sticky=(W,E), padx=5,
Re: [Tutor] tkinter code executes before function returned
On 15/04/18 03:57, Chris Roy-Smith wrote: > I am trying to get tkinter to return a number from a window, which then > sets how many times to print a sign. I don;t jhave time to look at this in detail just now, maybe later. But first impressions is that you have a very unorthodox style of Tkinter programming. Its more traditional to build the entire GUI up front rather than creating and destroying widgets each time you execute an event handler. Its less disturbing to the user than having things appear/disappear etc, as you seem to be doing. You can make widget hide/show/deactivate themselves without destroying them just by withdrawing/unpacking them etc or changing their status, if that's really what you want to do. > The code does not wait till the function returns a value, resulting in > the signcount variable in having a None value, giving an output like > below. I'll look at this a bit more closely later if nobody else answers by then... > Code: > > #!/usr/bin/python3 > from tkinter import * > import os > from reportlab.lib.units import cm > from reportlab.lib.pagesizes import A4 > from reportlab.pdfgen import canvas > from reportlab.lib.utils import ImageReader > > def printSign(): > global gc, packages, rows > myCanvas = canvas.Canvas("Signs.pdf", pagesize=A4) > width, height = A4 #keep for > myCanvas.rotate(90) > myCanvas.setFillColorRGB(0,0,0) > myCanvas.setFont("Helvetica-Bold", 400) > TopMargin=-20 > LeftMargin=1 > Width=14 > Height=19 > VertPos=-15 > Bottom=-1 All of the above could be done as part of your GUI initialisation. Its not needed in the event handler where it gets done every time the function is called. > a=[" " for i in range(rows)] > i=0 > sign=0 > for line in packages: > signcount=getcount(line[1]) This is where you call your function. Looking at it quickly I think you would be as well using the standard Tkinter simpledialogs/messagebox modules to get user input. Have you looked at the simpledialogs? Or better still having a static entry field on your GUI and just reading that? > print('line 27 ### required sign count for {} is {} > ###'.format(line[1], str(signcount))) > for x in range(signcount): > #draw rectangle > myCanvas.rect(LeftMargin*cm+Width*sign*cm, TopMargin*cm, > Width*cm, Height*cm, stroke=0, fill=1) > myCanvas.drawCentredString((LeftMargin+(0.5*Width))*cm+(Width*sign)*cm, > VertPos*cm, line[0]) > if sign==1: > myCanvas.showPage() > sign=0 > myCanvas.rotate(90) > i+=1 > else: > sign+=1 > i+=1 > > myCanvas.showPage() > myCanvas.save() > if os.name == "posix": > os.popen("evince %s" % ("Signs.pdf")) > if os.name == "nt": > os.startfile('Signs.pdf') > > def getcount(SignText): > global gc,e > gc=Toplevel(master) > MsgText='How many copies of {} do you want to print?'.format(SignText) > Label(gc, text=MsgText).grid(row=0, column=0, sticky=(W,E)) > e = Entry(gc) > e.grid(row=0, column=1) > Button(gc, text='Okay', command=ReturnCount).grid(row=1, column=0, > sticky=(W,E)) > Button(gc, text='Cancel', command=gc.destroy).grid(row=1, column=1, > sticky=(W,E)) I suspect the problem is because you have three callbacks each inside the other the second returns to your first before the third has a chance to run. This is all compounded by your dynamic GUI creation stuff. You need to restructure the code. More later. > def ReturnCount(): > global gc,e > b0=e.get() > if b0 == None: > b0=0 > gc.destroy() > print('line 64 ### The required number of signs is {} > ###'.format(b0)) > return b0 > > master = Tk() > master.title("Testing") > packages = [[0,'D','drill'],[1,'J','Jointer'],[2,'B','Bandsaw']] > rows = 3 > b2 = Button(master, text="Print Signs", command=printSign).grid(row=4, > column=0, sticky=(W,E), padx=5, pady=5) > b3 = Button(master, text="Quit", command=master.destroy).grid(row=4, > column=3, sticky=(W,E)) > > master.mainloop() -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor