When using a QComboBox in PySide2 the popup menu seems to initially start about 10 pixels or so to the left until its finished animating down at which point it pops (about) 10 pixels to the right into the correct position.
How can I fix this? Or am I able to disable the animation so the menu just opens without animating? And am I able to control the animation time for the popup?
Here are two screenshots, the top one is while the combobox dropdown is animating down and the bottom one is after the dropdown is open:
Here's the simple example code use to produce the combobox above:
from PySide2 import QtCore, QtWidgets
import sys
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.setWindowTitle('Modal Dialogs')
self.setMinimumSize(300,80)
# remove help icon (question mark) from window
self.setWindowFlags(self.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint)
# create widgets, layouts and connections (signals and slots)
self.create_widgets()
self.create_layouts()
self.create_connections()
def create_widgets(self):
self.combo = QtWidgets.QComboBox()
self.combo.addItems(['one','two','three'])
def create_layouts(self):
# self must be passed to the main_layout so it is parented to the dialog instance
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(self.combo)
def create_connections(self):
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
my_dialog = MyDialog()
my_dialog.show() # Show the UI
sys.exit(app.exec_())
Related
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.
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.
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_()
When I minimize the application window on Windows XP and restore it later, the dock will be hidden. This has to do with view menu which has toggles to set visibility and of course is connected by signals.
I hope this will save someone a few hours of debugging.
Here is a full functional example with both wrong and right code:
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
class Ui_QMainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(200, 200)
self.menubar = QtGui.QMenuBar(self)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 27))
self.menuMenu = QtGui.QMenu(self.menubar)
self.setMenuBar(self.menubar)
self.dock = QtGui.QDockWidget(self)
self.dock.setObjectName("dock")
self.dockContents = QtGui.QWidget()
self.dockContents.setObjectName("dockContents")
self.dock.setWidget(self.dockContents)
self.addDockWidget(QtCore.Qt.DockWidgetArea(4), self.dock)
self.action = QtGui.QAction(self)
self.action.setCheckable(True)
self.action.setChecked(True)
self.action.setObjectName("action")
self.menuMenu.addAction(self.action)
self.menubar.addAction(self.menuMenu.menuAction())
self.setWindowTitle("Example of dock remaining minimized")
self.menuMenu.setTitle("Menu")
self.dock.setWindowTitle("I'm a dock")
self.action.setText("Dock visibility")
if True:
# This is NOT working on Windows XP.
# Minimize the window and restore again, the dock is gone.
# Other than that it works.
QtCore.QObject.connect(self.action,
QtCore.SIGNAL("toggled(bool)"),
self.dock.setVisible)
QtCore.QObject.connect(self.dock,
QtCore.SIGNAL("visibilityChanged(bool)"),
self.action.setChecked)
else:
# This DOES work, but boy it looks nasty, writing useless
# per dock is not nice.
QtCore.QObject.connect(self.action,
QtCore.SIGNAL("triggered()"),
self.toggle_dock)
QtCore.QObject.connect(self.dock,
QtCore.SIGNAL("visibilityChanged(bool)"),
self.action.setChecked)
def toggle_dock(self):
self.dock.setVisible(not self.dock.isVisible())
def main():
app = QtGui.QApplication(sys.argv)
ui = Ui_QMainWindow()
ui.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
There is a much simpler way to do this, using QDock.toggleViewAction. This function returns a ready-made action that handles the checked state automatically.
So your code would become simply:
self.action = self.dock.toggleViewAction()
self.action.setObjectName("action")
self.menuMenu.addAction(self.action)
self.menubar.addAction(self.menuMenu.menuAction())
self.setWindowTitle("Example of dock remaining minimized")
self.menuMenu.setTitle("Menu")
self.dock.setWindowTitle("I'm a dock")
self.action.setText("Dock visibility")
and you can then get rid of all the signal handling.
I'm totally new to pyside and I'm having a problem with my little program (and pyside layouts in general).
What I have is an UI with some QlineEdits, comboboxes and a button. After I have filled out the Qlines and press the button I want to either to open a new window with a completely new layout or preferably clear out the open window and fill it with a new layout based on the input from the qlines. Perhaps this is super basic but I can't get it to work. The reason is that I can't grasp how I would be able to replace or add new stuff to my gui when it's already set and shown.
Let's say I have a script like this:
import sys
import os
from PySide import QtCore, QtGui
class BasicGui(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.projectNameLbl1 = QtGui.QLabel('Label1')
self.projectNameLbl2 = QtGui.QLabel('Label2')
self.nextBtn = QtGui.QPushButton("Next")
self.projectNameEdit = QtGui.QLineEdit(self)
self.projectNameEdit2 = QtGui.QLineEdit(self)
grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.projectNameLbl1, 2, 0)
grid.addWidget(self.projectNameEdit, 2, 1)
grid.addWidget(self.projectNameLbl2, 3, 0)
grid.addWidget(self.projectNameEdit2, 3, 1)
grid.addWidget(self.nextBtn, 4, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('projectCreator')
self.show()
self.nextBtn.clicked.connect(self.nextPressed)
def nextPressed(self):
self.msgBox = QtGui.QMessageBox()
self.msgBox.setText("When this button is pressed I want to generate a new layout")
self.msgBox.exec_()
def main():
app = QtGui.QApplication(sys.argv)
ex = BasicGui()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Say that I enter 10 in the line next to label1 and 2 in the other and press Next.Now I want to clear everything out and create 2 new columns with 10 qlines in each (or something like that).
Excuse me if I'm being either to vague or if I'm just repeating myself. I'm tired and irritated and English is not my first language.
I would deeply appreciate any help I could get or a push in the right direction.
Edit: If it's easier to accomplish this with some other widgetype with tabs or something that's fine. All i want to do is generate new widgets after i have recieved input from the user.
What you'll want to do is used a QStackedLayout[1].
Create a QWidget.
Create your layout.
Call setLayout() on the widget with your layout as the argument.
Push the new widget onto the QStackedLayout.
Use QStackedLayout's setCurrentIndex() or setCurrentWidget() functions to set the current layout.
I did something similar in a project of mine. See https://github.com/shanet/Cryptully/blob/master/cryptully/qt/qChatTab.py for a more complete example. Also see Pyside Changing Layouts for a similar problem.
[1] http://qt-project.org/doc/qt-4.8/qstackedlayout.html