How do I set the minimum size of a Gtk ButtonBox child? - python-3.x

I'm trying to set the minimum size of the buttons in this GtkButtonBox. Currently they seem to be fixed - approx 85 pixels I think.
Is this possible?
If not, is there another way in Gtk to get two small sized buttons to snuggle together like in the above picture rather than having them appear to be two separate buttons? For example GtkStackSwitcher may be something I could use but there doesn't appear to be a way to respond to click events for a button.
I've used this test program to create the above (Ubuntu 14.04, Gtk+3.10 and Python3):
from gi.repository import Gtk
import sys
class MyWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.Window.__init__(self, title="example", application=app)
self.set_default_size(350, 200)
self.set_border_width(10)
hbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL)
hbox.set_layout(Gtk.ButtonBoxStyle.EXPAND)
button = Gtk.Button(label="a")
hbox.add(button)
button2 = Gtk.Button(label="b")
hbox.add(button2)
self.add(hbox)
class MyApplication(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
win = MyWindow(self)
win.show_all()
def do_startup(self):
Gtk.Application.do_startup(self)
app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
With regards to a question about the desktop environment I'm using.
I've tried Mate, Unity and Gnome-Shell. All work the same way. I've removed the title and those controls. Still the same thing happens. To me this looks more like a GTK issue.

I believe that GtkButtonBox imposes some layout constraints on its buttons that you may not want here. Try using buttons in just a regular GtkGrid, but give them the GTK_STYLE_CLASS_LINKED CSS class.
For each button, do:
button.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)

Related

Reload UI, Rather Than Recreating

import sys
import webbrowser
import hou
from PySide2 import QtCore, QtUiTools, QtWidgets, QtGui
# Calling UI File & Some Modification
class someWidget(QtWidgets.QWidget):
def __init__(self):
super(someWidget,self).__init__()
ui_file = 'C:/Users/XY_Ab/Documents/houdini18.5/Folder_CGI/someUI.ui'
self.ui = QtUiTools.QUiLoader().load(ui_file, parentWidget=self)
self.setParent(hou.qt.mainWindow(), QtCore.Qt.Window)
self.setFixedSize(437, 42)
self.setWindowTitle("Requesting For Help")
window_C = someWidget()
window_C.show()
So, I have created this small script that shows the UI, I have connected this to Houdini Menu Bar. Now The Problem is if I click the menu item multiple times it will create another instance of the same UI & the previous one stays back, What I want is something called "If Window Exist Delete It, Crate New One" sort of thing.
Can someone guide me? I am fairly new to python in Houdini and Qt so a little explanation will be hugely helpful. Also, why can't I use from PySide6 import?? Why do I have to use from PySide2?? Because otherwise Houdini is throwing errors.
For the same thing what used to do in maya is
# Check To See If Window Exists
if cmds.window(winID, exists=True):
cmds.deleteUI(winID)
Trying to do the same thing inside Houdini.
I don't have Maya or Houdini, so I can't help you too much.
According to https://www.sidefx.com/docs/houdini/hom/cb/qt.html
It looks like you can access Houdini's main window. The main reason the window is duplicated or deleted is how python retains the reference to window_C. You might be able to retain the reference to just show the same widget over and over again by accessing the main Houdini window.
In the examples below we are using references a different way. You probably do not need your code that has
self.setParent(hou.qt.mainWindow(), QtCore.Qt.Window)
Create the widget once and keep showing the same widget over and over.
import hou
# Create the widget class
class someWidget(QtWidgets.QWidget):
def __init__(self, parent=None, flags=QtCore.Qt.Window): # Note: added parent as an option
super(someWidget,self).__init__(parent, flags)
...
MAIN_WINDOW = hou.ui.mainQtWindow()
try:
MAIN_WINDOW.window_C.show()
except AttributeError:
# Widget has not been created yet!
# Save the widget reference to an object that will always exist and is accessible
# parent shouldn't really matter, because we are saving the reference to an object
# that will exist the life of the application
MAIN_WINDOW.window_C = someWidget(parent=MAIN_WINDOW)
MAIN_WINDOW.window_C.show()
To delete the previous window and create a new window.
import hou
# Create the widget class
class someWidget(QtWidgets.QWidget):
def __init__(self, parent=None, flags=QtCore.Qt.Window): # Note: added parent as an option
super(someWidget,self).__init__(parent, flags)
...
MAIN_WINDOW = hou.ui.mainQtWindow()
# Hide the previous window
try:
MAIN_WINDOW.window_C.close()
MAIN_WINDOW.window_C.deleteLater() # This is needed if you parent the widget
except AttributeError:
pass
# Create the new Widget and override the previous widget's reference
# Python's garbage collection should automatically delete the previous widget.
# You do not need to have a parent!
# If you do have a parent then deleteLater above is needed!
MAIN_WINDOW.window_C = someWidget() # Note: We do not parent this widget!
MAIN_WINDOW.window_C.show()
Another resource shows you can access the previous widget from the page level variable. https://echopraxia.co.uk/blog/pyqt-in-houdinimaya-basic This is possible, but seems odd to me. The module should only be imported once, so the page level variable "my_window" should never exist. However, it sounds like the Houdini plugin system either reloads the python script or re-runs the import. If that is the case every time you show a new window from the import of the script, you are creating a new window. If the previous window is not closed and deleted properly, Houdini could have an ever growing memory issue.
try:
my_window.close()
except (NameError, Exception):
pass # Normal python would always throw a NameError, because my_window is never defined
my_window = MyWindow()
#This is optional you can resize the window if youd like.
my_window.resize(471,577)
my_window.show()
PySide6
https://www.sidefx.com/docs/houdini/hom/cb/qt.html
The bottom of the page shows how to use PyQt5. The same would apply for PySide6. Houdini just happens to come with PySide2.

Add a QWidget inside a QFrame

I'm developing a desktop software using Python3 and QtDesigner for the Graphic User Interface.
My problem is the seguent: i'm trying to automate the creation of many QRadioButtons over a QFrame (The RadioButtons must stay inside the frame [as...children?]).
Now, i see that i can only create new widgets inside a Layout (e.g. "MyLayout.addWidget(QRadioButton")) and it's not possible to do something like "MyFrame.addWidget(QRadioButton)". I need these widgets inside the frame cause then i can place them in the correct position with "MyRB.move(X,Y)".
With QtDesigner is possible to place many Widgets (like RadioButtons) in a frame that has a 'broken layout' so i can choose X,Y coordinates but i need to create and place a variable number of those.
Is it possible to create Qwidgets inside a QFrame?
[EDIT]
according to musicamante's comment, i got that's a parent problem.
I tried to insert a Label and a RadioButton in the main window:
def __init__(self):
super().__init__()
uic.loadUi('DSS_GUI2.ui',self) # i load the GUI with QtDesigner
LB1 = QLabel('MyLabel',self)
RB1 = QRadioButton('MyRadioButton',self)
...
This very simple example works fine but when i try to add a Label through a function
def myFunction(self):
LB1 = QLabel('MyLabel')
LB1.setObjectName('LABEL_1')
LB1.setParent(self.myFrame)
the Widget is inserted but it is not visible, in fact adding this lines to check his presence
WidgetList = self.myFrame.findChildren(QLabel)
for item in WidgetList:
print(item.objectName())
i see in the console that the Label is there.
Do you know why it's not visible?
Try
def myFunction(self):
LB1 = self.sender()
LB1.QLabel('MyLabel')
LB1.setObjectName('LABEL_1')
LB1.setParent(self.myFrame)
You can call self.myFunction() in parent.
If you wanted to pass label, you could:
def myFunction(self, label):
LB1 = self.sender()
LB1.QLabel(label)
LB1.setObjectName(label)
LB1.setParent(self.myFrame)

Pyglet hello world example doesn't show label until a key is pressed

import pyglet
window = pyglet.window.Window()
label = pyglet.text.Label("Hello World!",
font_name="Times New Roman",
color=(255,255,255,255),
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x="center", anchor_y="center")
#window.event
def on_draw():
window.clear()
label.draw()
pyglet.app.run()
This code is taken from the pyglet tutorial at https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/programming_guide/quickstart.html but when run it doesn't draw the label until any key is pressed. I added the color as I thought the text may have defaulted to black.
Am I missing something really obvious as to why this behaviour is happening?
OK, having had my memory jogged by the comments, I installed the MS Fonts and it now works in python 2.x but I still need to press a key to see the text in python 3. Maybe the font thing is a red-herring and there is some incompatibility with python 3.
As mentioned in the comments.
Most examples found on the internet (even most guides) assume a Windows platform.
If there's a font= declaration with a Windows font but you're running Linux, make sure you got the proper fonts installed or revert to a font you've got installed.
$ fc-list
Also not declaring a font will work too:
label = pyglet.text.Label("Hello World!",
color=(255,255,255,255),
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x="center", anchor_y="center")
Because Pyglet will default to sans-serif:
If you do not particularly care which font is used, and just need to display some readable text, you can specify None as the family name, which will load a default sans-serif font (Helvetica on Mac OS X, Arial on Windows XP)
Your issue is probably that you don't manually update the screen after you drew something. Normally when you press a key, it forces a window.flip(), which basically means update the screen content.
window.clear() also triggers this behavior, but x.draw() does not. Why? Well the thing that takes time in computer graphics is not really the calculations, it's the updating them to the screen that takes time.. There for .draw() doesn't update the screen, it just puts the stuff in the graphical buffer ("page"), you decide when to flip the page and show the new buffer on the screen.
Try this out:
#window.event
def on_draw():
window.clear()
label.draw()
window.flip()
This might be a overkill solution, but it will probably solve the problem.
This overrides the default loop of pyglet and the default draw behavior, it's also one of the classes i use the most in my pyglet projects since it gives me the option to create my own framework.
import pyglet
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(vsync = False)
self.sprites = {}
self.sprites['testlabel'] = label = pyglet.text.Label("Hello World!",
color=(255,255,255,255),
font_size=36,
x=self.width//2, y=self.height//2, #self here, being pyglet.window.Window that we've inherited and instanciated with super().
anchor_x="center", anchor_y="center")
self.alive = 1
def on_draw(self):
self.render()
def render(self):
self.clear()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
# This is very important, this queries (and empties)
# the pyglet event queue, if this queue isn't cleared
# pyglet will hang because it can't input more events,
# and a full buffer is a bad buffer, so we **NEED** this!
event = self.dispatch_events()
win = Window()
win.run()
It's extremely basic, but you can add more sprites, objects and render them in def render(self):.

How to avoid mousePressEvent - left click to call paintEvent in PyQt

In this program below, I am testing the affect of mousePressEvent:
import sys
from PyQt4 import QtGui, Qt, QtCore
class Test(QtGui.QFrame):
def __init__(self):
QtGui.QFrame.__init__(self)
self.setGeometry(30,30,500, 500)
self.show()
def paintEvent(self, e=None):
print "paintEvent"
qp = QtGui.QPainter()
qp.begin(self)
qp.drawRect(30,30,80,80)
qp.end()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
print "mousePressEvent- Right"
else:
print "mousePressEvent- Left"
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Test()
sys.exit(app.exec_())
I see that in the first left-click, the frame's paintEvent is called. Is this because when the frame get the focus, it need to be repainted? I wonder if there is any way to avoid paintEvent to be called and every drawing in the frame still intact. The reason is because in my real program, the paintEvent is really heavy, I want to run it as less times as possible.
If that is not possible, is there a way to avoid the frame getting focus when left-click on that?
I don't know whether there are platform-specific differences at play here, but when I try the example code on Linux, there is no paintEvent when clicking on the frame.
This is not surprising really, because, by default, the QFrame is not configured to accept focus of any kind. To get the example to work, I had to explicitly set the focus policy, like this:
class Test(QtGui.QFrame):
def __init__(self):
QtGui.QFrame.__init__(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
But maybe the defaults are somehow different on other platforms, and so, to explicitly prevent the frame getting the focus, you might need to do this:
self.setFocusPolicy(QtCore.Qt.NoFocus)

Show QMainwindow in the middle of the screen

I want show my project main window in the middle of the screen . when i call "self.show()" then the window show in the middle of the screen .
I know you have already solved it, but I make this answer for those who have the same question. I post it mainly because you asked for pyQt and the other answer is for Qt (C++).
I found a workaround here: https://bashelton.com/2009/06/pyqt-center-on-screen/
Is so simple and works perfectly, I transmit it..
class ExampleWindow (QtGui.QMainWindow):
def __init__ (self, parent=None):
'''constructor'''
QtGui.QMainWindow.__init__(self, parent)
self.setGeometry(0, 0, 650, 550)
self.setWindowTitle("My Example Application")
self.centerOnScreen()
def centerOnScreen (self):
'''centerOnScreen()
Centers the window on the screen.'''
resolution = QtGui.QDesktopWidget().screenGeometry()
self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
(resolution.height() / 2) - (self.frameSize().height() / 2))
Good luck!
First I would recommend against trying to force a window position on your users and let the system's window manager decide where it should go. If you really insist on positioning it yourself (perhaps you are programming for a kiosk), you can find some information here in a previous question on stackoverflow.
A slightly more elegant calculation for doing this is discussed here.
When doing this calculation, it is important that it is done at the correct time, after Qt has resized everything and just before it is shown on screen. One method that might help is to create a one-shot timer and do the screen positioning in the slot for the timer.
This worked for me in Pyqt5:
from PyQt5.QtWidgets import *
qtRectangle = self.frameGeometry()
centerPoint = QDesktopWidget().availableGeometry().center()
qtRectangle.moveCenter(centerPoint)
self.move(qtRectangle.topLeft())

Resources