I've been following a tutorial in Kivy and encounter a problem. When I'm changing the size of my window the images doesn't display. My code is:
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.app import App
class Sprite(Image):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.size = self.texture_size
class Game(Widget):
def __init__(self):
super().__init__()
self.background = Sprite(source="Sprites/CartoonForest.png")
self.size = self.background.size
self.add_widget(self.background)
class GameApp(App):
def build(self):
game = Game()
Window.size = game.size
return game
if __name__ == '__main__':
GameApp().run()
I played around a bit, imported different images of different extensions and sizes, changed in the code and such, but only when I left out the line Window.size = game.size in the build method in GameApp it displayed the image. When I resized the window by dragging the corner of the image disappeared as well.
What is the reason for this and how could I fix the screen size to match the image size?
I'm using:
Mac OS X Yosemite 10.10.5
PyCharm Community Edition 2016.1.4
Python 3.5.2
Kivy v1.9.1
It was a bug of the current version I was using. Downloading the newer versions from github fixed the problem!
Related
I´m currently developing a software to process spectroscopic data using python. I´m using PyQt6 to design the gui and matplotlib to plot the data. To simplify the data selection I added a cursor widget from matplotlib to the axes. For improved performace I used the blitting mode.
Here´s a minimal code example:
# Import packages
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.widgets import Cursor
from matplotlib.figure import Figure
import numpy as np
# Main window
class MainWindow(QMainWindow):
def __init__(self):
# Initialize QMainWindow
super().__init__()
# Get MplWidget
self.mpl_widget = MplWidget()
# Set MplWidget as central widget
self.setCentralWidget(self.mpl_widget)
# Create axes
self.ax = self.mpl_widget.canvas.figure.add_subplot(111)
# Reference Cursor
self.cursor = Cursor(self.ax, useblit=True)
# Plot some random stuff
x, y = np.random.rand(100), np.random.rand(100)
self.ax.scatter(x, y)
# Show app
self.show()
# MplCanvas
class MplWidget(QWidget):
def __init__(self, parent=None):
# Initialize QWidget
super().__init__(parent)
# Create canvas
self.canvas = FigureCanvas(Figure())
# Set constrained layout
self.canvas.figure.set_constrained_layout(True)
# Create layout
layout = QVBoxLayout()
# Add canvas
layout.addWidget(self.canvas)
# Set layout
self.setLayout(layout)
# Initialize app
app = QApplication([])
UI = MainWindow()
app.exec()
Generally everything works fine, the cursor appears when hovering the axis and dissapears when leaving it, but when the mouse enters the axis, the whole axis content moves a little bit. Sometimes the figure edges also change their colors, e.g. from black to gray.
Here are two gifs to explain the phenomenon:
Does anyone know how to solve this problem?
I'm new to PySide2, started using it yesterday. I'm teaching myself from the API documentation and info I find online. I'm trying to make a small app with it to teach myself. My idea for the app was using a View like model, as you go pressing on certain buttons I delete the current layout on the window and present you with an entire new one. This foments modularity as each layout wouldn't be dependent on each other but instead fully individual. (Each part of the app is broadly different and requires almost all widgets to be deleted).
However whenever I search how to delete a layout or remove all of its widgets I always encounter a piece of code like this (This is actually from a question asked 6 months ago):
def clearLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.clearLayout(item.layout())
Yet when I implement something like that, even the exact same function and then call it using, self.clearLayout(self.layout) I get the following error:
File "GUI.py", line 45, in home
self.clearLayout(self.layout)
File "GUI.py", line 16, in clearLayout
while layout.count():
AttributeError: 'builtin_function_or_method' object has no attribute 'count'
I'm assuming I probably forgot to import something important, but I can't just seem to find what. Here is a list with my imports.
import sys
from PySide2.QtWidgets import QApplication, QLabel, QLineEdit, QWidget
from PySide2.QtWidgets import QDialog, QPushButton, QVBoxLayout, QLayout
I also installed PySide2 using pip install PySide2 on command line (Anaconda). Do I have to do something else?
Thank you so much, sorry post is so long, just wanted to give you the whole information from the beginning :)
EDIT:
Further testing has led me to realise I cannot use any of the virtual functions of QLayout, or its subclasses. Apparently I have to implement them myself within the code. I believe it could be this. I don't know how to implement them though. I'm going to keep trying.
EDIT 2: Some people asked for a reporducible example so here is the code that doesn't work.
import sys
from PySide2.QtWidgets import QApplication, QLabel, QLineEdit, QWidget
from PySide2.QtWidgets import QDialog, QPushButton, QVBoxLayout, QLayout
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Program")
self.setGeometry(300, 300, 300, 300)
self.start()
def clearLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.clearLayout(item.layout())
def start(self):
self.startbutton = QPushButton("Start App")
layout = QVBoxLayout()
layout.addWidget(self.startbutton)
self.setLayout(layout)
self.startbutton.clicked.connect(self.home)
def home(self):
self.clearLayout(self.layout)
self.message=QLabel("Welcome to homepage")
layout=QVBoxLayout()
layout.addWidget(self.message)
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication([])
window=Window()
window.show()
sys.exit(app.exec_())
my application is built from a .kv file.
I want to add buttons to a Screen from my python file.
class Profiles(Screen):
def suh(self):
for i in range(5):
self.add_widget(Button(text=i))
The suh function seem to have no effect. Is this because the function is called after the app is built? Is there a better way to do this?
Solution 1
Since Screen is a RelativeLayout, use on_pre_enter or on_enter to inovke method, suh(), plus add size and pos to Button widget.
Solution 2
Use a container e.g. BoxLayout or Gridayout on top of Screen.
Note
Whenever widget is added to a screen via on_pre_enter or on_enter, use on_pre_leave or on_leave to remove the widget. This is to prevent doubling your widget each time the screen is entered.
If the widgets are cleared too fast, use Clock.schedule_once with a time interval.
Note 1: Screen Events
Kivy Screen has the following events.
Kivy Screen » Events
on_pre_enter: ()
Event fired when the screen is about to be used: the entering
animation is started.
on_enter: ()
Event fired when the screen is displayed: the entering animation is
complete.
on_pre_leave: ()
Event fired when the screen is about to be removed: the leaving
animation is started.
on_leave: ()
Event fired when the screen is removed: the leaving animation is
finished.
Note 2: Screen is a RelativeLayout
Kivy Screen » RelativeLayout
Please note that by default, a Screen displays nothing: it’s just
a RelativeLayout. You need to use that class as a root widget for
your own screen, the best way being to subclass.
Warning
As Screen is a RelativeLayout, it is important to understand
the Common Pitfalls.
Example - Solution 1
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.lang import Builder
Builder.load_string("""
<Profiles>:
# on_pre_enter: self.suh()
on_enter: self.suh()
""")
class Profiles(Screen):
def suh(self):
for i in range(5):
self.add_widget(Button(text=str(i), size_hint=(0.1, 0.1), pos=(i*100, i*100)))
sm = ScreenManager()
sm.add_widget(Profiles(name='profiles'))
class TestApp(App):
def build(self):
return sm
if __name__ == "__main__":
TestApp().run()
Output - Solution 1
Example - Solution 2
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.lang import Builder
Builder.load_string("""
<Profiles>:
# on_pre_enter: self.suh()
on_enter: self.suh()
BoxLayout:
id: container
""")
class Profiles(Screen):
def suh(self):
for i in range(5):
self.ids.container.add_widget(Button(text=str(i), size_hint=(0.1, 0.1)))
sm = ScreenManager()
sm.add_widget(Profiles(name='profiles'))
class TestApp(App):
def build(self):
return sm
if __name__ == "__main__":
TestApp().run()
Output - Solution 2
Add the widgets through the App class in stead of the Screen. So you would have to create a function that creates the widgets, and in your build function, you would have to use Clock.schedule_once from the Clock module to run the other function. Example:
class TestApp(App):
def build(self):
Clock.schedule_once(self.yoyo, 0)
return MainWin()
def yoyo(self, *args):
for i in memu_acc:
self.root.ids["prof"].ids["pro"].add_widget(Button(text=i))
I have tried adding the pdf.js viewer files in my project and it works in browsers like Chrome, Mozilla, Safari, etc, but it's not loading some pages in node-webkit and PyQt webkit.
I am trying to load the file using an iframe, like this:
<iframe src="/test/?file=/assets/pdf/example.pdf#page=3"> </iframe>
Below are some more up-to-date demo scripts for using pdf.js with PyQt4/QtWebKit or PyQt5/QtWebEngine. To try these, first download the latest stable version of pdf.js and unpack the zip file into a suitable location. (NB: if you're on Linux your distro may already have a pdf.js package, so that could be installed instead).
UPDATE:
As of Qt-5.13.0, it is also possible to use the built-in Chromium PDF Viewer with QWebEngineView:
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
PDF = 'file://path/to/my/sample.pdf'
class Window(QtWebEngineWidgets.QWebEngineView):
def __init__(self):
super().__init__()
self.settings().setAttribute(
QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled, True)
self.settings().setAttribute(
QtWebEngineWidgets.QWebEngineSettings.PdfViewerEnabled, True)
self.load(QtCore.QUrl.fromUserInput(PDF))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 800, 600)
window.show()
sys.exit(app.exec_())
PyQt5/QtWebEngine pdfjs script:
UPDATE:
NB: as of Aug 2022, it may be necessary to use the legacy build of pdfjs (i.e. the build for "older browsers" on the download page) to keep things working with PyQt5. The stable build should work okay with PyQt6, though.
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html'
# PDFJS = 'file:///usr/share/pdf.js/web/viewer.html'
PDF = 'file:///path/to/my/sample.pdf'
class Window(QtWebEngineWidgets.QWebEngineView):
def __init__(self):
super().__init__()
self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 800, 600)
window.show()
sys.exit(app.exec_())
PyQt4/QtWebKit pdfjs script:
import sys
from PyQt4 import QtCore, QtGui, QtWebKit
PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html'
# PDFJS = 'file:///usr/share/pdf.js/web/viewer.html'
PDF = 'file:///path/to/my/sample.pdf'
class Window(QtWebKit.QWebView):
def __init__(self):
super().__init__()
self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 800, 600)
window.show()
sys.exit(app.exec_())
I've found this thread over at the Qt Forums, where thebeast44 posted a snippet of Qt code answering your question. My translation to python is below.
You'll also need to unpack the res folder from the author's original code, I think he just modified the viewer... I've also attached said code here.
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4 import QtNetwork
from PyQt4 import QtWebKit
class PDFViewer(QtWebKit.QWebView):
pdf_viewer_page = 'res/pdf-viewer.html'
def __init__(self, parent=None):
super().__init__(parent)
self.settings = QtWebKit.QWebSettings.globalSettings()
self.settings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessFileUrls, True )
self.settings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True )
self.settings.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True )
nam = QtNetwork.QNetworkAccessManager()
page = QtWebKit.QWebPage(self)
page.setNetworkAccessManager(nam)
self.setPage(page)
self.loadFinished.connect(self.onLoadFinish)
self.setUrl(QtCore.QUrl(self.pdf_viewer_page))
def onLoadFinish(self, success):
if success:
self.page().mainFrame().evaluateJavaScript("init();")
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
viewer = PDFViewer(parent=None)
viewer.show()
sys.exit(app.exec_())
From PyQt5 v5.13 you can load PDF files with the chromium API. According to the documentation https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing this option is by default enabled.
This minimal example is adapted from Simple Browser
import sys
from pathlib import Path
from PyQt5 import QAxContainer
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLineEdit, QApplication
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.main_layout = QVBoxLayout(self)
self.qlineedit = QLineEdit()
self.qlineedit.returnPressed.connect(self.go_action)
self.main_layout.addWidget(self.qlineedit)
self.read_btn = QPushButton('Test')
self.read_btn.clicked.connect(self.go_action)
self.main_layout.addWidget(self.read_btn)
self.WebBrowser = QAxContainer.QAxWidget(self)
self.WebBrowser.setFocusPolicy(Qt.StrongFocus)
self.WebBrowser.setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}")
self.main_layout.addWidget(self.WebBrowser)
def go_action(self):
# convert system path to web path
f = Path(self.qlineedit.text()).as_uri()
# load object
self.WebBrowser.dynamicCall('Navigate(const QString&)', f)
if __name__ == "__main__":
a = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(a.exec_())
This example:
How to make one single scroll bar for two QTextEdit, pyqt4, python. Or how to synchronize two scrollbars of two QTextEdit. For simultaneous scrolling texts.
Pyqt4, python.
Cross-connect the value changed signals of all the scrollbars:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.edit1 = QtGui.QTextEdit(self)
self.edit2 = QtGui.QTextEdit(self)
layout = QtGui.QHBoxLayout(self)
layout.addWidget(self.edit1)
layout.addWidget(self.edit2)
self.edit1.horizontalScrollBar().valueChanged.connect(
self.edit2.horizontalScrollBar().setValue)
self.edit1.verticalScrollBar().valueChanged.connect(
self.edit2.verticalScrollBar().setValue)
self.edit2.horizontalScrollBar().valueChanged.connect(
self.edit1.horizontalScrollBar().setValue)
self.edit2.verticalScrollBar().valueChanged.connect(
self.edit1.verticalScrollBar().setValue)
text = '\n'.join(name for name in dir(QtCore))
self.edit1.setText(text)
self.edit2.setText(text)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 600, 400)
window.show()
sys.exit(app.exec_())