I'm currently using an embedded turtle canvas in a tkinter window. While it's intuitive that all I need to do is set my turtle to turtle.RawTurtle(canvas), there are some functions that just don't work, and I can't figure out why.
t.clear();t.pu();t.speed(0);t.ht();t.tracer(0)
But I get the error:
AttributeError: 'RawTurtle' object has no attribute 'tracer'
Despite this, many other functions work, such as clear, penup, speed, and hideturtle.
Is there any way of disabling screen updates until the drawing is finished, then manually updating the canvas, with RawTurtle?
The tracer() method is a method of the turtle's screen, not the turtle itself. To get access to it, when embedded under a tkinter window, wrap the canvas in a turtle screen:
screen = turtle.TurtleScreen(canvas)
t = turtle.RawTurtle(screen)
which should give you access to the various screen methods. Then you should be able to use screen.tracer(0) to turn off drawing updates and screen.update() to show the finished drawing. A more complete example:
import tkinter as tk
import turtle
root = tk.Tk()
canvas = turtle.ScrolledCanvas(root)
canvas.pack(side=tk.LEFT)
screen = turtle.TurtleScreen(canvas)
t = turtle.RawTurtle(screen)
t.hideturtle()
# t.speed('fastest')
screen.tracer(0)
t.penup()
t.sety(-100)
t.pendown()
t.circle(100)
screen.update()
screen.mainloop()
Related
So I'm working on a tkinter project and one issue I come across is finding a way to flip/rotate a button object's text vertically. One way I can kinda cheat into making this happen is putting a canvas object on top of the button with the canvas being drawn last (as shown below) but is there a cleaner way to approach this by just manipulating the Button object attributes?
from tkinter import*
root = Tk()
windowDimensions = (1300,600)
root.title("Mapper")
root.geometry(str(windowDimensions[0])+"x"+str(windowDimensions[1]))
button1=Button(root,text='',width=2,height=9)
button1.place(x=0,y=20)
can = Canvas(root,width=15,height=80)
can.place(x=2,y=30)
can.create_text(0, 80, anchor="nw", angle=90,text='hello',font=("Purisa", 12))
root.mainloop()
Edit: A problem I get with doing it this way is any place where the canvas is on the button, it obstructs the ability to click where the canvas is.
Your best option (which isn't a great option) is to screenshot the button, rotate it in an image editor, and then use that image in your button instead of text.
from tkinter import*
root = Tk()
# .gif file encoded as base64
vert_button_data = '''
R0lGODlhDQBCAKUkAAAAAAAANgAAYDYAADYANjYAYGAAAGAANmAAYAA2hzY2h4c2AGA2h4c2NgBg
qzZghzZgq2BgNqtgAKtgNjaHhzaHzmCHh6uHNs6HNmCr8PCrYIfOq4fO8PDOh6vwq6vw8M7w8PDw
q/DwzvDw8P//////////////////////////////////////////////////////////////////
/////////////////////////////////////////////ywAAAAADQBCAAAG/sCRcEgsGo+cpFJJ
PDgPAWeUGKpSMFUM5Qggdo2DDAiU+RY7BoXCoDmOQp1OqLisN5/4A7XqqfqPAm5EERVLRwcGeEd+
f0gZGR9uGQQSEgUcXB1CHQFcXkdhHx8ZA0doBQUGmotxc0QOhZFuGhgNCLAcskUiIbQNAg5HHxwV
DwYSRMQQCBMYckUHAxYbIG5wtQwOGRzVRrwaFwEJRiAcGQ4KCxhE5woNEhodIkXw8oL3+Pn35pCS
lJaYjABYxcnTEDNEQo0qZeRUqlVG4DxLVofJkDyKhjDaSLFiQCEYn+zZWEVYhWAjUBYRoOELwoMj
XILiAOBDBYZnoj201oqOIMePI/BEEanRDwBGbl4avKdUyBIAhi4+SURUCMlG+oIAADs=
'''
windowDimensions = (1300,600)
root.title("Mapper")
root.geometry("{}x{}".format(*windowDimensions))
button1_image = PhotoImage(data=vert_button_data)
button1=Button(root,image=button1_image)
button1.place(x=0,y=20)
root.mainloop()
You'll lose the hover animation but again that's something you can recreate with images.
To get the base64 encoded data from a .gif you can use this:
import codecs
with open('export.gif', 'rb') as f:
print(codecs.encode(f.read(), 'base64').decode())
I am trying to make a basic 4 frame animation, I cannot use a tkinter canvas as I want it to use the art which I have drawn (the files). There is nothing wrong with the file type as I have tested it on its own. However the code seems to just remove the window for the 6 seconds and then show the final frame.
import time
import tkinter
window=tkinter.Tk()
frame1=tkinter.PhotoImage(file="file1.ppm")
frame2=tkinter.PhotoImage(file="file2.ppm")
frame3=tkinter.PhotoImage(file="file3.ppm")
frame4=tkinter.PhotoImage(file="file4.ppm")
image=tkinter.Label(window,image=frame1)
image.pack()
time.sleep(2)
image.configure(image=frame2)
time.sleep(2)
image.configure(image=frame3)
time.sleep(2)
image.configure(image=frame4)
I'm not sure whether it is the "time.sleep" or the "image.configure" that is the problem but I have tried to mess around with different types of timing methods which also seem to fail.
import tkinter
window=tkinter.Tk()
frame1=tkinter.PhotoImage(file="file1.ppm")
frame2=tkinter.PhotoImage(file="file2.ppm")
frame3=tkinter.PhotoImage(file="file3.ppm")
frame4=tkinter.PhotoImage(file="file4.ppm")
image=tkinter.Label(window,image=frame1)
image.pack()
def loop(n):
frame = [frame1, frame2, frame3, frame4][n]
window.after(2000, lambda : image.configure(image=frame))
window.after(2000, lambda : loop((n+1)%4))
loop(1)
import pyglet
window = pyglet.window.Window()
label = pyglet.text.Label("Hello World!",
font_name="Times New Roman",
color=(255,255,255,255),
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x="center", anchor_y="center")
#window.event
def on_draw():
window.clear()
label.draw()
pyglet.app.run()
This code is taken from the pyglet tutorial at https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/programming_guide/quickstart.html but when run it doesn't draw the label until any key is pressed. I added the color as I thought the text may have defaulted to black.
Am I missing something really obvious as to why this behaviour is happening?
OK, having had my memory jogged by the comments, I installed the MS Fonts and it now works in python 2.x but I still need to press a key to see the text in python 3. Maybe the font thing is a red-herring and there is some incompatibility with python 3.
As mentioned in the comments.
Most examples found on the internet (even most guides) assume a Windows platform.
If there's a font= declaration with a Windows font but you're running Linux, make sure you got the proper fonts installed or revert to a font you've got installed.
$ fc-list
Also not declaring a font will work too:
label = pyglet.text.Label("Hello World!",
color=(255,255,255,255),
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x="center", anchor_y="center")
Because Pyglet will default to sans-serif:
If you do not particularly care which font is used, and just need to display some readable text, you can specify None as the family name, which will load a default sans-serif font (Helvetica on Mac OS X, Arial on Windows XP)
Your issue is probably that you don't manually update the screen after you drew something. Normally when you press a key, it forces a window.flip(), which basically means update the screen content.
window.clear() also triggers this behavior, but x.draw() does not. Why? Well the thing that takes time in computer graphics is not really the calculations, it's the updating them to the screen that takes time.. There for .draw() doesn't update the screen, it just puts the stuff in the graphical buffer ("page"), you decide when to flip the page and show the new buffer on the screen.
Try this out:
#window.event
def on_draw():
window.clear()
label.draw()
window.flip()
This might be a overkill solution, but it will probably solve the problem.
This overrides the default loop of pyglet and the default draw behavior, it's also one of the classes i use the most in my pyglet projects since it gives me the option to create my own framework.
import pyglet
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(vsync = False)
self.sprites = {}
self.sprites['testlabel'] = label = pyglet.text.Label("Hello World!",
color=(255,255,255,255),
font_size=36,
x=self.width//2, y=self.height//2, #self here, being pyglet.window.Window that we've inherited and instanciated with super().
anchor_x="center", anchor_y="center")
self.alive = 1
def on_draw(self):
self.render()
def render(self):
self.clear()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
# This is very important, this queries (and empties)
# the pyglet event queue, if this queue isn't cleared
# pyglet will hang because it can't input more events,
# and a full buffer is a bad buffer, so we **NEED** this!
event = self.dispatch_events()
win = Window()
win.run()
It's extremely basic, but you can add more sprites, objects and render them in def render(self):.
I wrote a script in python 3.4 where I'm making use of the tkinter module to build the GUI and pandas/matplotlib to visualise some data. The visualisation is being produced in a new window through the use of a button in the main window. The problem is that when I close the window that contains the graph (produced by pandas/matplot) the main window widgets are completely unresponsive. Furthermore, when I try to close the main window (through the 'X' button) I get a Fatal Python error: PyEval_RestoreThread: NULL tstate, which I don't get if I close the window without producing the graphs.
In simple words: the rest of the application behaves as it should if I never produce the graphs and becomes completely unresponsive if I do.
Below are the code segments of the caller of the plotting method in the main window and the method itself where the use of pandas and matplot is clearly shown.
Caller:
button_view_browsers = tk.Button(window, text="Display graph", command=lambda: self.threaded_browser_hist(doc))
button_view_browsers.grid(row=2, column=1, sticky=W+E+N+S, columnspan=3)
Visualisation method:
def display_browser_hist(self, data):
ser = Series(data.viewer_browser())
ser.value_counts().plot(kind='bar')
matplotlib.pyplot.show()
def threaded_browser_hist(self, doc):
Thread(target=self.display_browser_hist(doc)).start()
Any help would be greatly appreciated.
EDIT: Just to be sure I'm also including the modules I'm using and how I'm importing them:
from pandas import Series
import matplotlib
import tkinter as tk
from tkinter import W, E, N, S
I'm trying to set the minimum size of the buttons in this GtkButtonBox. Currently they seem to be fixed - approx 85 pixels I think.
Is this possible?
If not, is there another way in Gtk to get two small sized buttons to snuggle together like in the above picture rather than having them appear to be two separate buttons? For example GtkStackSwitcher may be something I could use but there doesn't appear to be a way to respond to click events for a button.
I've used this test program to create the above (Ubuntu 14.04, Gtk+3.10 and Python3):
from gi.repository import Gtk
import sys
class MyWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.Window.__init__(self, title="example", application=app)
self.set_default_size(350, 200)
self.set_border_width(10)
hbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL)
hbox.set_layout(Gtk.ButtonBoxStyle.EXPAND)
button = Gtk.Button(label="a")
hbox.add(button)
button2 = Gtk.Button(label="b")
hbox.add(button2)
self.add(hbox)
class MyApplication(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
win = MyWindow(self)
win.show_all()
def do_startup(self):
Gtk.Application.do_startup(self)
app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
With regards to a question about the desktop environment I'm using.
I've tried Mate, Unity and Gnome-Shell. All work the same way. I've removed the title and those controls. Still the same thing happens. To me this looks more like a GTK issue.
I believe that GtkButtonBox imposes some layout constraints on its buttons that you may not want here. Try using buttons in just a regular GtkGrid, but give them the GTK_STYLE_CLASS_LINKED CSS class.
For each button, do:
button.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)