Kivy and Python creating a slideshow that Moves when gets clicked - user-interface

I am having trouble with making a picture slideshow that is operated with python and executed with kivy.
I am using asynch but I want to make the slideshow so that I open the photo and then when I click with the right it goes forward, but then if the mouse gets clicked on the left, then it goes to the previous page (picture).
Thanks for the help.

You can remove the comment# from the buttons and move left/right methods if you want but I think the carousel direction feature solves that problem. Mind you, I am a newbie so this is just my way of helping out. I figured I'll help some else since I have gotten a lot of help here. Thanks
from kivy.app import App
from kivy.loader import Loader
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.clock import Clock
from kivy.properties import *
from kivy.uix.image import AsyncImage
from kivy.uix.gridlayout import GridLayout
from kivy.uix.carousel import Carousel
from kivy.uix.button import Button
Builder.load_string('''
<MyWidget>:
carousel: carousel
cols: 1
Button:
pos_hint: {"center_x":0.5, "center_y":0.1}
size_hint: .3,.1
font_size: 35
text: str(root.on_off)
on_release: root.start_slide()
Carousel:
pos_hint: {"center_x":0.5, "center_y":0.9}
id: carousel
direction: 'right'
loop: True
index: 0
''')
class MyWidget(GridLayout):
on_off = StringProperty('Start')
slide_count = NumericProperty(11)
def __init__(self, **kwargs):
super(MyWidget, self).__init__()
self.carousel = Carousel(direction='right')
self.add_widget(self.carousel)
for i in range(self.slide_count):
src = "http://placehold.it/480x270.png&text=slide-%d&.png" % i
image = AsyncImage(source=src, allow_stretch=True)
self.carousel.add_widget(image)
#self.start_slide()### Uncomment this to start slideshow when the app starts
def start_slide(self, *args):
if self.on_off == 'Start':
self.on_off = 'Stop'
self.clock = Clock.schedule_interval(self.slide_next, 3) ##move right every 3 seconds
return
if self.on_off == 'Stop':
self.on_off = 'Start'
Clock.unschedule(self.clock)
self.carousel.index = 0
def slide_next(self, *args):
if self.carousel.index == (self.slide_count - 1):
self.carousel.index = 0### This keeps the loops intact
#### if you want to end the slideshow at the last image, use 'Clock.unschedule(self.clock)' instead
return
self.carousel.load_next()
class SlideShowApp(App):
def build(self):
mywidget = MyWidget()
return mywidget
if __name__ == '__main__':
SlideShowApp().run()
Hope this is what you needed

Related

kivy_garden.contextmenu: how to prevent its touch events from reaching other widgets?

Trying to learn Kivy.
I am using the kivy_garden.contextmenu which I open when I right click on a widget.
This works.
But when clicking un element in the context menu, I am receiving the on_touch_xxx events on the underneath widget anyway (here it is myWidget) .
My first thought was I could use flags to avoid this behaviour, e.g. setting a value menu_is_showing to True when I show the menu, and ignoring the underneath's widget's on_touch_up if the menu is showing. I did it and it was almost working (a was never notified the menu had been closed by click outside of it so the flag system would break in that case)...
Then I realised (while writing here) I could create a property for the context menu "visible", which made it all work as I expect a context menu would.
And here is the working code (which you are welcome to comment/correct):
from kivy.lang import Builder
from kivy.config import Config
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
import kivy_garden.contextmenu
from kivy.properties import ObjectProperty
kv = '''
<myWidget>:
on_touch_up: root._on_touch_up(*args[1:])
menu_visible: context_menu.visible
BoxLayout:
id: boxlayout1
pos_hint : {'x':0, 'y':0}
size_hint: 1,1
canvas.before:
Color:
rgba: (0, 0, 1, 1)
Rectangle:
#texture: self.texture
size: self.width-10, self.height-10
pos: self.x+5, self.y+5
ContextMenu:
id: context_menu
visible: False
cancel_handler_widget: root
ContextMenuTextItem:
text: "action 1"
on_release: root._action1(); root._hide_menu()
ContextMenuTextItem:
text: "action 2"
on_release: root._action2(); root._hide_menu()
<myLayout>:
BoxLayout:
Button:
text: "Dummy"
myWidget:
id: floatlayout1
'''
Builder.load_string(kv)
class myWidget(FloatLayout):
menu_visible = ObjectProperty()
def __init__(self, **kwargs):
super(myWidget, self).__init__(**kwargs)
def _action1(self):
print("Do Action 1")
def _action2(self):
print("Do Action 2")
def _on_touch_up(self, touch):
if self.menu_visible:
return
if self.collide_point(*touch.pos) and touch.button == 'left':
print("Left Click reached the blue area")
elif self.collide_point(*touch.pos) and touch.button == 'right':
print("Right Click reached the blue area")
self.ids.context_menu.show(*touch.pos)
def _hide_menu(self):
self.ids.context_menu.hide()
class myLayout(FloatLayout):
pass
class testApp(App):
def build_config(self, config):
Config.set('kivy', 'exit_on_escape', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
self.title = 'Testing Context Menu and Layouts'
def build(self):
return myLayout()
if __name__ == '__main__':
testApp().run()
Try clicking left and right in the blue area and check the console output. (the left button in the layout is only there to add an offset and make sure things I do in the blue area do not align by chance just because it would be in (0,0))
And that's fine, but I would have thought a context menu would already do that, and by default would not propagate the touch events to other widgets. Is there a way to do that?
If so, could you please be so kind to tell me how you'd do that?
Thanks for your time.
C

Multilpe screen in wxpython

all.
I'd like to be able to switch between multiple screens. Meaning, the first one is the main, then when with a button or an external switch is activated I can see the page #2, in that one I may have an other button to return to the first one, or going to #3, etc. Cause I have a main screen for a big RPM meter, but I may want to see instead all three meter on the same page, or view the raw data in an other page, or go to the set-up page or elsewhere in the future development. I'm using the full screen space for my graphic. Maybe something like "hide" or "show" a page with an event of some kind. I have a single class script for every pages so far, but unable to group them in a single one. Thanks for your help
I wrote about this concept several years ago here. I went ahead an reproduced the example from that article:
import wx
import wx.grid as gridlib
class PanelOne(wx.Panel):
""""""
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
txt = wx.TextCtrl(self)
class PanelTwo(wx.Panel):
""""""
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
grid = gridlib.Grid(self)
grid.CreateGrid(25,12)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 0, wx.EXPAND)
self.SetSizer(sizer)
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Panel Switcher Tutorial")
self.panel_one = PanelOne(self)
self.panel_two = PanelTwo(self)
self.panel_two.Hide()
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.panel_one, 1, wx.EXPAND)
self.sizer.Add(self.panel_two, 1, wx.EXPAND)
self.SetSizer(self.sizer)
menubar = wx.MenuBar()
fileMenu = wx.Menu()
switch_panels_menu_item = fileMenu.Append(wx.ID_ANY,
"Switch Panels",
"Some text")
self.Bind(wx.EVT_MENU, self.onSwitchPanels,
switch_panels_menu_item)
menubar.Append(fileMenu, '&File')
self.SetMenuBar(menubar)
def onSwitchPanels(self, event):
""""""
if self.panel_one.IsShown():
self.SetTitle("Panel Two Showing")
self.panel_one.Hide()
self.panel_two.Show()
else:
self.SetTitle("Panel One Showing")
self.panel_one.Show()
self.panel_two.Hide()
self.Layout()
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
The basic idea here is to Hide() one panel and Show() another. You might also want to look at the Notebook controls that wxPython provides as they have a similar functionality.

How to display an icon in the systray reflecting NumLk state

My computer doesn't have any way of letting me know if my NumLk is on or off, so I am trying to add an icon in my systray that will changed depending on the state of my NumLk. This .py will always be running when my computer is on.
So far I was able to mix 3 codes and I am able to display the icon in the systray but it doesn't get updated when the state of NumLk change. Actually if I press NumLk twice, I still get the same icon (the on one) and I get this error:
QCoreApplication::exec: The event loop is already running
File "\systray_icon_NumLk_on_off.py", line 21, in on_key_press
main(on)
File "\systray_icon_NumLk_on_off.py", line 46, in main
sys.exit(app.exec_())
SystemExit: -1
My code may not be the best way to do it, so any alternative is welcome! Here is what I came up so far:
#####get the state of NumLk key
from win32api import GetKeyState
from win32con import VK_NUMLOCK
#how to use: print(GetKeyState(VK_NUMLOCK))
#source: http://stackoverflow.com/questions/21160100/python-3-x-getting-the-state-of-caps-lock-num-lock-scroll-lock-on-windows
#####Detect if NumLk is pressed
import pyglet
from pyglet.window import key
window = pyglet.window.Window()
#source: http://stackoverflow.com/questions/28324372/detecting-a-numlock-capslock-scrlock-keypress-keyup-in-python
on=r'on.png'
off=r'off.png'
#window.event
def on_key_press(symbol, modifiers):
if symbol == key.NUMLOCK:
if GetKeyState(VK_NUMLOCK):
#print(GetKeyState(VK_NUMLOCK))#should be 0 and 1 but
main(on)
else:
main(off)
#window.event
def on_draw():
window.clear()
### display icon in systray
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
#source: http://stackoverflow.com/questions/893984/pyqt-show-menu-in-a-system-tray-application - add answer PyQt5
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
self.setContextMenu(menu)
def main(image):
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
trayIcon = SystemTrayIcon(QtGui.QIcon(image), w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
pyglet.app.run()
The reason for QCoreApplication::exec: The event loop is already running is actually because you're trying to start app.run() twice. Qt will notice there's already an instance running and throw this exception. When instead, what you want to do is just swap the icon in the already running instance.
Your main problem here is actually the mix of libraries to solve one task if you ask me.
Rather two tasks, but using Qt5 for the graphical part is fine tho.
The way you use Pyglet is wrong from the get go.
Pyglet is intended to be a highly powerful and effective graphics library where you build a graphics engine ontop of it. For instance if you're making a game or a video-player or something.
The way you use win32api is also wrong because you're using it in a graphical window that only checks the value when a key is pressed inside that window.
Now, if you move your win32api code into a Thread (a QtThread to be precise) you can check the state no matter if you pressed your key inside your graphical window or not.
import sys
import win32api
import win32con
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread, enumerate
from time import sleep
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtWidgets.QMenu(parent)
exitAction = menu.addAction("Exit")
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(QtWidgets.qApp.quit)
self.setContextMenu(menu)
class KeyCheck(QtCore.QThread):
def __init__(self, mainWindow):
QtCore.QThread.__init__(self)
self.mainWindow = mainWindow
def run(self):
main = None
for t in enumerate():
if t.name == 'MainThread':
main = t
break
while main and main.isAlive():
x = win32api.GetAsyncKeyState(win32con.VK_NUMLOCK)
## Now, GetAsyncKeyState returns three values,
## 0 == No change since last time
## -3000 / 1 == State changed
##
## Either you use the positive and negative values to figure out which state you're at.
## Or you just swap it, but if you just swap it you need to get the startup-state correct.
if x == 1:
self.mainWindow.swap()
elif x < 0:
self.mainWindow.swap()
sleep(0.25)
class GUI():
def __init__(self):
self.app = QtWidgets.QApplication(sys.argv)
self.state = True
w = QtWidgets.QWidget()
self.modes = {
True : SystemTrayIcon(QtGui.QIcon('on.png'), w),
False : SystemTrayIcon(QtGui.QIcon('off.png'), w)
}
self.refresh()
keyChecker = KeyCheck(self)
keyChecker.start()
sys.exit(self.app.exec_())
def swap(self, state=None):
if state is not None:
self.state = state
else:
if self.state:
self.state = False
else:
self.state = True
self.refresh()
def refresh(self):
for mode in self.modes:
if self.state == mode:
self.modes[mode].show()
else:
self.modes[mode].hide()
GUI()
Note that I don't do Qt programming often (every 4 years or so).
So this code is buggy at it's best. You have to press Ctrl+C + Press "Exit" in your menu for this to stop.
I honestly don't want to put more time and effort in learning how to manage threads in Qt or how to exit the application properly, it's not my area of expertis. But this will give you a crude working example of how you can swap the icon in the lower corner instead of trying to re-instanciate the main() loop that you did.

PySide: Place/move animated QMovie GIF in QSplashScreen

I'm having trouble getting this to work. I'd like to be able to place an animated GIF in a specific spot on my QSplashScreen.
The GIF has to be animated using multiprocessing and the onNextFrame method so that it will play during the initial load (otherwise it just freezes on the first frame).
I've tried inserting self.move(500,500) everywhere but nothing is working (well not working well enough). Right now, the GIF will play in the spot I want, but then it will snap back to screen center on the next frame, then back to the spot I want, etc. Inserting the move method every possible place hasn't fixed this issue.
Here's the code:
from PySide import QtCore
from PySide import QtGui
from multiprocessing import Pool
class Form(QtGui.QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.browser = QtGui.QTextBrowser()
self.setWindowTitle('Just a dialog')
self.move(500,500)
class MySplashScreen(QtGui.QSplashScreen):
def __init__(self, animation, flags):
# run event dispatching in another thread
QtGui.QSplashScreen.__init__(self, QtGui.QPixmap(), flags)
self.movie = QtGui.QMovie(animation)
self.movie.frameChanged.connect(self.onNextFrame)
#self.connect(self.movie, SIGNAL('frameChanged(int)'), SLOT('onNextFrame()'))
self.movie.start()
self.move(500, 500)
def onNextFrame(self):
pixmap = self.movie.currentPixmap()
self.setPixmap(pixmap)
self.setMask(pixmap.mask())
self.move(500, 500)
# Put your initialization code here
def longInitialization(arg):
time.sleep(args)
return 0
if __name__ == "__main__":
import sys, time
app = QtGui.QApplication(sys.argv)
# Create and display the splash screen
# splash_pix = QPixmap('a.gif')
splash = MySplashScreen('S:\_Studio\_ASSETS\Tutorials\Maya\Coding\Python\_PySide\GIF\dragonGif.gif',
QtCore.Qt.WindowStaysOnTopHint)
# splash.setMask(splash_pix.mask())
#splash.raise_()
splash.move(500, 500)
splash.show()
# this event loop is needed for dispatching of Qt events
initLoop = QtCore.QEventLoop()
pool = Pool(processes=1)
pool.apply_async(longInitialization, [2], callback=lambda exitCode: initLoop.exit(exitCode))
initLoop.exec_()
form = Form()
form.show()
splash.finish(form)
app.exec_()

Kivy Carousel from a button

What is missing in this code to running carousel??
I´m trying to conect the carousel in a button. When I run, it´s show a button, but the on_press nothing happens.
What is missing in this code to running carousel??
Builder.load_string('''
<tela>:
Button:
text: 'ir'
font_size: 32
size_hint: None, None
pos_hint: {'right': 1}
size: 150, 50
on_press: root.ida()
''')
class acesso(BoxLayout):
def ida(self):
self.clear_widgets()
self.add_widget(tela1())
class tela(BoxLayout):
def ida(self):
self.parent.ida()
class tela1(App,Widget):
def livro(self):
carousel = Carousel(direction='right',loop='true')
for i in range(1,5):
src = "images/%d.png" % i
image = Image(source=src,pos=(1,10), size=(1250, 635))
carousel.add_widget(image)
return carousel
class CarroselApp(App):
def build(self):
self.acesso = acesso()
self.acesso.add_widget(tela())
return self.acesso
if __name__ == "__main__":
CarroselApp().run()
There is multiples issue with your code:
You inherith from App and Widget for tela1. I don't know the effect of that, but this would be wrong somehow. Thoses are not meant to be combined. Your CarroselApp is already here.
Your tela1 widget have a livro() method, but is never called. Plus, you create a widget Carousel without really adding it to the tela1.
tela inherith from a Widget, so it wont layout the children. I'm 200% this would not give what you wished in the first place.
I guess if you replace tela1 with this code, it might works:
class tela1(FloatLayout):
def __init__(self, **kwargs):
super(tela1, self).__init__(**kwargs)
self.add_widget(self.livro())
def livro(self):
carousel = Carousel(direction='right',loop='true')
for i in range(1,5):
src = "images/%d.png" % i
image = Image(source=src,pos=(1,10), size=(1250, 635))
carousel.add_widget(image)
return carousel
Note: please consider pep8 for your code. Using lowercase for class name is not usual and confusing.

Resources