I'm studying automata theory and I was requested to program the graph (tree) of an automaton that looks more or less like:
So far I got this (I'm using tkinter
and canvas
to draw):
from tkinter import Tk, Canvas, mainloop
def circle(canvas, x, y, r, width):
id = canvas.create_oval (x-r, y-r, x+r, y+r, width = width)
return id
def line (canvas, x1, y1, x2, y2, width):
canvas.create_line (x1, y1, x2, y2, width = width)
def text (canvas, x, y, text):
canvas.create_text (x, y, text = text, font = ("bold", 20))
w = Canvas(Tk (), width=1000, height=600, bg = "white")
circle (w , 150, 300, 70, 3)
circle (w , 150, 300, 50, 3)
circle (w , 370, 300, 70, 3)
circle (w , 640, 300, 70, 3)
circle (w , 910, 300, 70, 3)
line (w, 10, 300, 80, 300, 3)
circle (w, 73, 300, 5, 6)
line (w, 220, 300, 300, 300, 3)
circle (w, 293, 300, 5, 6)
line (w, 440, 300, 570, 300, 3)
circle (w, 567, 300, 5, 6)
line (w, 710, 300, 840, 300, 3)
circle (w, 837, 300, 5, 6)
text (w, 150, 300, "q0")
text (w, 370, 300, "q1")
text (w, 640, 300, "q2")
text (w, 910, 300, "q3")
w.pack()
mainloop()
Which displays this:
I don't need the arrows, because I will use dots instead. The problem is that I need to draw a line from circle q3 to circle q0, and from circle q0 to circle q0, too (a "bucle"). I tried the canvas.create_arc()
method, but I can't get the handle of it. Is there an alternative? Any ideas on how to draw the "bucle"?
Here's some utility functions that provide an alternative way draw arcs on a tkinter.Canvas
. Instead of the usual specification of two-points, (x0, y0)
and (x1, y1)
to define an enclosing rectangle, the arc functions accept a starting and stopping angle in the open range of [0..360)
degrees.
It also illustrates how to have arrowheads drawn at the ends of straight lines (but not arcs) since you asked about that, too.
from tkinter import Canvas, mainloop, Tk
def circle(canvas, x, y, r, width):
return canvas.create_oval(x+r, y+r, x-r, y-r, width=width)
def circular_arc(canvas, x, y, r, t0, t1, width):
return canvas.create_arc(x-r, y-r, x+r, y+r, start=t0, extent=t1-t0,
style='arc', width=width)
def ellipse(canvas, x, y, r1, r2, width):
return canvas.create_oval(x+r1, y+r2, x-r1, y-r2, width=width)
def elliptical_arc(canvas, x, y, r1, r2, t0, t1, width):
return canvas.create_arc(x-r1, y-r2, x+r1, y+r2, start=t0, extent=t1-t0,
style='arc', width=width)
def line(canvas, x1, y1, x2, y2, width, start_arrow=0, end_arrow=0):
arrow_opts = start_arrow << 1 | end_arrow
arrows = {0b10: 'first', 0b01: 'last', 0b11: 'both'}.get(arrow_opts, None)
return canvas.create_line(x1, y1, x2, y2, width=width, arrow=arrows)
def text(canvas, x, y, text):
return canvas.create_text(x, y, text=text, font=('bold', 20))
w = Canvas(Tk(), width=1000, height=600, bg='white')
circle(w, 150, 300, 70, 3) # q0 outer edge
circle(w, 150, 300, 50, 3) # q0 inner edge
circle(w, 370, 300, 70, 3) # q1
circle(w, 640, 300, 70, 3) # q2
circle(w, 910, 300, 70, 3) # q3
# Draw arc from circle q3 to q0.
midx, midy = (150+910) / 2, 300
r1, r2 = 910-midx, 70+70
elliptical_arc(w, midx, midy, r1, r2, 30, 180-30, 3)
line(w, 10, 300, 80, 300, 3, end_arrow=1)
line(w, 220, 300, 300, 300, 3, end_arrow=1)
line(w, 440, 300, 570, 300, 3, end_arrow=1)
line(w, 710, 300, 840, 300, 3, end_arrow=1)
text(w, 150, 300, 'q0')
text(w, 370, 300, 'q1')
text(w, 640, 300, 'q2')
text(w, 910, 300, 'q3')
w.pack()
mainloop()
This is what it draws:
It doesn't draw a "buckle" like you want partly because drawing "a line from circle q3 to circle q0, and from circle q0 to circle q0" isn't like the illustration at the beginning of your question which is one drawn between two circles (if I understand correctly what you meant by the term).
However, it does provide another way for you to draw arcs on a canvas
.