I want to move hand of speedmeter (in 'wx' package) in Python with every second (same like clock) after click on a button. Action sequence would be like:
1. Click on a button
2. Hand of speedmeter starts moving after every 0.4 seconds.
But after clicking on button (to start hand of speedmeter), color of frame goes dim (seems as app is crashed but actually there is no error or exception). I think it is happening because I am writing an infinite loop (to update hand of speedmeter) which is blocking APP.MainLoop() function. And I also think that thread programming can help me but I don't know much about thread programming. So, help me for this.
Following is my code:
import threading
import os
import wx.lib.agw.speedmeter as SM
import wx.lib.agw.speedmeter as SM2
import sys
import wx
import math
from wx import animate
import time
from random import randint
import thread
class MyFrame(wx.Frame):
def __init__(self,parent,id,title):
# creation frame
l, h = wx.GetDisplaySize()
wx.Frame.__init__(self,parent,-1,title,size=(l, h))
self.speed = SM.SpeedMeter(self,agwStyle=SM.SM_DRAW_HAND|SM.SM_DRAW_SECTORS|SM.SM_DRAW_MIDDLE_TEXT|SM.SM_DRAW_SECONDARY_TICKS,pos=(15,10),size=(l/2,h/2))
# Set The Region Of Existence Of SpeedMeter 1
#self.speed.SetAngleRange(*-math.pi/4, math.pi/1.75)
#self.speed.SetAngleRange(-2*math.pi/4, 2.57*math.pi/1.75)
self.speed.SetAngleRange(-2*math.pi/4, 2.57*math.pi/1.75)
# SpeedMeter In Sectors
intervals = range(0, 5100, 100)
self.speed.SetIntervals(intervals)
# Assign The Same Colours To All Sectors
colours = [wx.WHITE]*50
self.speed.SetIntervalColours(colours)
# Assign The Ticks
ticks = [str(interval) for interval in intervals]
self.speed.SetTicks(ticks)
# Set The Ticks/Tick Markers Colour
self.speed.SetTicksColour(wx.RED)
# We Want To Draw 5 Secondary Ticks Between The Principal Ticks
self.speed.SetNumberOfSecondaryTicks(4)
# Set The Font For The Ticks Markers
self.speed.SetTicksFont(wx.Font(8.9, wx.SWISS, wx.NORMAL, wx.NORMAL))
# Set The Text In The Center Of SpeedMeter
self.speed.SetMiddleText("Actual")
# Assign The Colour To The Center Text
self.speed.SetMiddleTextColour(wx.BLACK)
# Assign A Font To The Center Text
self.speed.SetMiddleTextFont(wx.Font(22, wx.SWISS, wx.NORMAL, wx.BOLD))
# Set The Colour For The Hand Indicator
self.speed.SetHandColour(wx.BLUE)#wx.Colour(255, 250, 20))
# Do Not Draw The External (CONTAINER) Arc
self.speed.DrawExternalArc(True)
# adding button
self.btn = wx.Button(self,2,"OK", pos = (400, 820), size = (50, 30))
self.btn.Bind(wx.EVT_BUTTON, self.OnClicked)
def OnClicked(self, event):
try:
i = 0
while(True):
#r = randint(0, 5000)
if(i >= 5000):
i = 0
i += 50
self.speed.SetSpeedValue(i)
time.sleep(0.5)
except Exception, err:
print("Exception", Exception, err)
if __name__=='__main__' :
APP=wx.App()
frame=MyFrame(None,0,'Appsy')
APP.SetTopWindow(frame)
frame.Show()
APP.MainLoop()
Related
I am trying to write a python GUI coding to control servo motor movement with tkinter Scale widget.
Below if my coding.
import tkinter
from tkinter import *
from time import sleep
from pyfirmata import Arduino, SERVO
HomePageGeo = "900x400"
board = Arduino('COM3')
sleep(0)
pinS1 = 2
board.digital[pinS1].mode = SERVO
class App:
def __init__(self, window, window_title):
self.window = window
self.window.title(window_title)
self.HomePage(window)
self.window.mainloop()
def HomePage(self,window):
self.window.geometry(HomePageGeo)
self.ServoControl(window)
def ServoControl(self, window):
# Define Servo Motor #####################
def servo1(positions1):
board.digital[pinS1].write(positions1)
print(positions1)
### --> Angle 1 position bar
angle1 = Scale(window,command=servo1,troughcolor='gray', cursor='dot',
from_=0, to=180, orient=HORIZONTAL,label='Angle 1 Position',
length=300, width=30 )
angle1.pack()
App(tkinter.Tk(), 'Servo Motor Control')
Is there any idea or suggestion that when i run the program, the scalebar value shown is (eg. 60), but the range of scalebar still from 0 to 180 ?
Thanks for your help!
To set the default value for the scale, call the set method after creating it:
angle1.set(60)
I'm trying to learn tkinter, and I've found an animation of a ball (in this website).
I tried to modificate it to a ball that could be placed with the mouse. Until now I've done that first part, but I don't know how to add the movement. I suposse that the canvas need to be run before the code of the animation to obtain the ball coordinates.
import tkinter
import time
animation_window_width=800
animation_window_height=600
animation_ball_radius = 30
animation_ball_min_movement = 5
animation_refresh_seconds = 0.01
def animate_ball(window, canvas, xinc, yinc, ball):
while True:
canvas.move(ball,xinc,yinc)
window.update()
time.sleep(animation_refresh_seconds)
ball_pos = canvas.coords(ball)
xl,yl,xr,yr = ball_pos
if xl < abs(xinc) or xr > animation_window_width-abs(xinc):
xinc = -xinc
if yl < abs(yinc) or yr > animation_window_height-abs(yinc):
yinc = -yinc
def position_ball(event):
ball = canvas.create_oval(event.x-animation_ball_radius,
event.y-animation_ball_radius,
event.x+animation_ball_radius,
event.y+animation_ball_radius,
fill="blue", outline="white", width=4)
window = tkinter.Tk()
window.title("Tkinter Animation Demo")
window.geometry(f'{animation_window_width}x{animation_window_height}')
canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)
ball=canvas.bind("<Button-1>", position_ball)
ball
#animate_ball(window, canvas, animation_ball_min_movement, animation_ball_min_movement, ball)
That is a really bad example. For most GUIs you should never use an infinite loop with sleep() in a GUI thread since it blocks GUI mainloop (pygame is a notable exception to this). Most GUIs, including tkinter, are "event-driven", and you need to use events to do things. The event I think you want is the mouse movement. The event will contain the mouse x, y position, so all that's left is to transfer that to the ball.
import tkinter
animation_window_width=800
animation_window_height=600
animation_ball_radius = 30
animation_ball_min_movement = 5
animation_refresh_seconds = 0.01
def place_ball(event):
canvas.unbind("<Motion>") # stop responding to motion
canvas.xinc = animation_ball_min_movement
canvas.yinc = animation_ball_min_movement
animate_ball()
def locate_ball(event):
canvas.coords(ball,
event.x-animation_ball_radius,
event.y-animation_ball_radius,
event.x+animation_ball_radius,
event.y+animation_ball_radius)
# Create and animate ball in an infinite loop
def animate_ball(event=None):
canvas.move(ball,canvas.xinc,canvas.yinc) # move the ball
xl,yl,xr,yr = canvas.coords(ball) # get current coordinates
if xl < abs(canvas.xinc) or xr > animation_window_width-abs(canvas.xinc):
canvas.xinc = -canvas.xinc
if yl < abs(canvas.yinc) or yr > animation_window_height-abs(canvas.yinc):
canvas.yinc = -canvas.yinc
canvas.coords(ball, xl,yl,xr,yr) # set new coordinates
canvas.after(20, animate_ball) # set the loop event
window = tkinter.Tk()
window.title("Tkinter Animation Demo")
window.geometry(f'{animation_window_width}x{animation_window_height}')
canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)
ball = canvas.create_oval(0,0,0,0,fill="blue", outline="white", width=4)
canvas.bind('<Motion>', locate_ball)
canvas.bind('<Button-1>', place_ball)
window.mainloop()
I am trying to use PyQt5 to draw a round gauge (MacOS 11.0.1, Python 3.9).
I used the drawArc statement to created the gauge background, so I set the pen width to a large value (70). The resulting arc looks like a horseshoe, presumably because the "pen" is a 70 pixels square, not a line perpendicular to the direction of travel.
Is there a way of creating an arc - in PyQt5 - like the one on the right side of the picture?
I am open to suggestions: the application has already been written with Python+Tkinter, but thanks to the lack of anti-aliasing on Tkinter+Raspberry, I need re-write it.
(Plan B is to continue with PyQt, create a pie slice (drawPie) and cover the centre area with a circle of background colour - but this is not ideal, as it imposes some limitations to my design.)
# importing libraries
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
arcreading = 0
adder = .1
# creating a Gauge class
class Gauge(QMainWindow):
# constructor
def __init__(self):
super().__init__()
timer = QTimer(self) # create a timer object
timer.timeout.connect(self.update) # add action to the timer, update the whole code
timer.start(0) # update cycle in milliseconds
self.setGeometry(200, 200, 600, 600) # window location and size
self.setStyleSheet("background : black;") # background color
# -----------------------
# method for paint event
# -----------------------
def paintEvent(self, event):
global arcreading
global adder
# print('x')
kanvasx = 50 # binding box origin: x
kanvasy = 50 # binding box origin: y
kanvasheight = 150 # binding box height
kanvaswidth = 150 # binding box width
arcsize = 270 # arc angle between start and end.
arcwidth = 70 # arc width
painter = QPainter(self) # create a painter object
painter.setRenderHint(QPainter.Antialiasing) # tune up painter
painter.setPen(QPen(Qt.green, arcwidth)) # set color and width
# ---------- the following lines simulate sensor reading. -----------
if arcreading > arcsize or arcreading < 0: # variable to make arc move
adder = -adder # arcreading corresponds to the
# value to be indicated by the arc.
arcreading = arcreading + adder
# --------------------- end simulation ------------------------------
#print(arcreading)
# drawArc syntax:
# drawArc(x_axis, y_axis, width, length, startAngle, spanAngle)
painter.drawArc(kanvasx, kanvasy, # binding box: x0, y0, pixels
kanvasheight + arcwidth, # binding box: height
kanvaswidth + arcwidth, # binding box: width
int((arcsize + (180 - arcsize) / 2)*16), # arc start point, degrees (?)
int(-arcreading*16)) # arc span
painter.end() # end painter
# Driver code
if __name__ == '__main__':
app = QApplication(sys.argv)
# creating a Gauge object
win = Gauge()
# show
win.show()
exit(app.exec_())
You need to set the capStyle of the pen with the appropriate Qt.PenCapStyle, in your case you should use FlatCap, which ends exactly at the end of the line, while the default is SquareCap (which covers the end and extends by half the line width):
painter.setPen(QPen(Qt.green, arcwidth, cap=Qt.FlatCap))
I'm trying to create a simple slideshow using Tkinter and Python 3.7.2. I want the slide show to display images on secondary screen and in fullscreen mode. I have tried to use only one window and two windows as suggested here. This is the code I have written:
import tkinter as tk
from PIL import Image, ImageTk
class App(tk.Tk):
'''Tk window/label adjusts to size of image'''
def __init__(self, image_files, x, y, delay):
# the root will be self
tk.Tk.__init__(self)
# set width. height, x, y position
self.geometry('%dx%d+%d+%d'%(912,1140,0,0)) #Window on main screen
#create second screen window
self.top2 = tk.Toplevel(self,bg="grey85")
self.top2.geometry('%dx%d+%d+%d'%(912,1140,-912,0)) # The resolution of the second screen is 912x1140.
#The screen is on the left of the main screen
self.top2.attributes('-fullscreen', False) #Fullscreen mode
self.pictures = image_files
self.picture_display = tk.Label(self.top2, width=912, height=1140)
self.picture_display.pack()
self.delay = delay
self.index = 1
self.nImages = len(image_files)
def start_acquisition(self):
if self.index == self.nImages+1:
self.destroy()
return
self.load = Image.open(self.pictures[self.index-1])
self.render = ImageTk.PhotoImage(self.load)
self.picture_display['image'] = self.render
self.index += 1
self.after(self.delay, self.start_acquisition)
def run(self):
self.mainloop()
# set milliseconds time between slides
delay = 3500
image_files = [
'1805Circle Test Output.bmp', #images resolution is 912x1140
'8233Circle Test Input.bmp',
'cross.bmp'
]
x = 0 #Not used currently
y = 0 #Not used currently
app = App(image_files, x, y, delay)
app.start_acquisition()
app.run()
print('Finished')
The code works as expected when the fullscreen attribute is "False". As soon as I put this attribute "True" the "top2" window appears on the main screen. The same thing happen if only one window is used. Could you please help me find a solution for this problem? thx
I am attempting to create a quick turtle display using Tkinter, but some odd things are happening.
First two turtle windows are being created, (one blank, one with the turtles), secondly, any attempt of turning the tracer off is not working.
This might be a simple fix but at the moment I cannot find it.
Any help would be appreciated,
Below is the code:
import tkinter as tk
import turtle
window = tk.Tk()
window.title('Top 10\'s')
def loadingscreen():
canvas = tk.Canvas(master = window, width = 500, height = 500)
canvas.pack()
arc1 = turtle.RawTurtle(canvas)
arc2 = turtle.RawTurtle(canvas)
#clean up the turtles and release the window
def cleanup():
turtle.tracer(True)
arc1.ht()
arc2.ht()
turtle.done()
#animate the turtles
def moveTurtles(rangevar,radius,extent,decrease):
for distance in range(rangevar):
arc1.circle(-radius,extent = extent)
arc2.circle(-radius,extent = extent)
radius -= decrease
#Set the turtle
def setTurtle(turt,x,y,heading,pensize,color):
turt.pu()
turt.goto(x,y)
turt.pd()
turt.seth(heading)
turt.pensize(pensize)
turt.pencolor(color)
#draw on the canvas
def draw():
#set variables
rangevar = 200
radius = 200
decrease = 1
extent = 2
#setup and draw the outline
turtle.tracer(False)
setTurtle(arc1,0,200,0,40,'grey')
setTurtle(arc2,14,-165,180,40,'grey')
moveTurtles(rangevar,radius,extent,decrease)
#setup and animate the logo
turtle.tracer(True)
setTurtle(arc1,0,200,0,20,'black')
setTurtle(arc2,14,-165,180,20,'black')
moveTurtles(rangevar,radius,extent,decrease)
#main program
def main():
turtle.tracer(False)
arc1.speed(0)
arc2.speed(0)
draw()
cleanup()
if __name__ == "__main__":
try:
main()
except:
print("An error occurred!!")
loadingscreen()
Essentially I am creating a Tk window, then a canvas, then two turtles, and then animating these turtles
My guess is you're trying to call turtle screen methods without actually having a turtle screen. When turtle is embedded in tkinter like this, you can overlay a Canvas with a TurtleScreen instance which will provide some, but not all, of the screen features of the standalone turtle:
import tkinter as tk
from turtle import RawTurtle, TurtleScreen
def cleanup():
""" hide the turtles """
arc1.hideturtle()
arc2.hideturtle()
def moveTurtles(rangevar, radius, extent, decrease):
""" animate the turtles """
for _ in range(rangevar):
arc1.circle(-radius, extent=extent)
arc2.circle(-radius, extent=extent)
radius -= decrease
def setTurtle(turtle, x, y, heading, pensize, color):
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
turtle.setheading(heading)
turtle.pensize(pensize)
turtle.pencolor(color)
def draw():
# set variables
rangevar = 200
radius = 200
decrease = 1
extent = 2
screen.tracer(False) # turn off animation while drawing outline
# setup and draw the outline
setTurtle(arc1, 0, 200, 0, 40, 'grey')
setTurtle(arc2, 14, -165, 180, 40, 'grey')
moveTurtles(rangevar, radius, extent, decrease)
screen.tracer(True) # turn animation back on for the following
# setup and animate the logo
setTurtle(arc1, 0, 200, 0, 20, 'black')
setTurtle(arc2, 14, -165, 180, 20, 'black')
moveTurtles(rangevar, radius, extent, decrease)
# main program
window = tk.Tk()
window.title("Top 10's")
canvas = tk.Canvas(master=window, width=500, height=500)
canvas.pack()
screen = TurtleScreen(canvas)
arc1 = RawTurtle(screen)
arc1.speed('fastest')
arc2 = RawTurtle(screen)
arc2.speed('fastest')
draw()
cleanup()
Another suggestion: don't mess with tracer() until after everything else is working and then (re)read it's documentation carefully.
I just have the answer for the two windows that are being created: the one with the turtles is obvious, and the blank one is for the main root window that you define (window = tk.Tk()). If you want it not to appear at all, you can add the following line right after its definition:
window.withdraw()
I found this solution here and here.