I am trying to create a custom Gtk text entry. The basic idea is to put a button inside of a text entry. Here is a shortened version of my full code:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
builder = Gtk.Builder()
button = Gtk.Button('button')
entry = Gtk.Entry()
entry.add_child(builder, button, "button")
The button does not get shown and it fails with the error:
(pygtk_foobar.py:26622): Gtk-CRITICAL **: gtk_buildable_add_child:
assertion 'iface->add_child != NULL' failed
Any suggestions?
A GtkEntry widget is not a GtkContainer, and thus it cannot have child widgets added or removed to it.
Only widgets that inherit from the GtkContainer class can have children in GTK+.
If you want to put a button next to a text entry, you should pack both widgets into a container like GtkBox, e.g.:
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=2)
entry = Gtk.Entry()
button = Gtk.Button(label='button', hexpand=True)
box.add(entry)
box.add(button)
box.show_all()
This will create a box with an entry and a button.
Related
I am facing some issue with the display style of Qcombobox items. Currently am hardcoding the data to be shown in the combobox.
here is the code :
self.Dummy = QComboBox(self)
self.Dummy.setGeometry(200,600, 350, 50)
self.Dummy.setStyleSheet("QComboBox {background-color: white;border-style: outset;" border-width: 2px;border-radius: 5px;border-color: #448aff; font: 12px; min-width: 10em; padding: 3px;}")
self.Dummy.addItems(["-Select-", "2", "3","4","5","6","7","8","9","0","11",])
The issue is that the dropdown "list" postion keeps changing after each selection. Here is the image of the issue am facing.
Below is my combobox
The list contains items <-Select->,2,3,4,5,6,7,8,9,0,11 , where <-Select-> will be the first element shown.
Now when I click the box, the box list "down" the elements and suppose I selected '2'. Then, if I try to select another item, the list will be dropped in a "downwards" direction. see below
Now, say if selected the last element from the items, '11'. Now if I try to select a new item by clicking on the box, the list will be popped "up" instead of down. see below
What should be done to fix it ? I don't think its an issue with stylesheet, without it also, this issue is happening. The reason I need this to be fixed is that when the list is popping up, its covering the label above it
What you see is a behavior that is OS and style dependent.
To avoid it, the best way is to subclass QComboBox and overwrite showPopup(), then we call the base class implementation (which is responsible of showing, resizing and positioning the popup view) and move it if necessary.
class Combo(QtWidgets.QComboBox):
def showPopup(self):
super().showPopup()
# find the widget that contains the list; note that this is *not* the view
# that QComboBox.view() returns, but what is used to show it.
popup = self.view().window()
rect = popup.geometry()
if not rect.contains(self.mapToGlobal(self.rect().center())):
# the popup is not over the combo, there's no need to move it
return
# move the popup at the bottom left of the combo
rect.moveTopLeft(self.mapToGlobal(self.rect().bottomLeft()))
# ensure that the popup is always inside the edges of the screen
# we use the center of the popup as a reference, since with multiple
# screens the combo might be between two screens, but that position
# could also be completely outside the screen, so the cursor position
# is used as a fallback to decide on what screen we'll show it
done = False
for i, pos in enumerate((rect.center(), QtGui.QCursor.pos())):
for screen in QtWidgets.QApplication.screens():
if pos in screen.geometry():
screen = screen.geometry()
if rect.x() < screen.x():
rect.moveLeft(screen.x())
elif rect.right() > screen.right():
rect.moveRight(screen.right())
if rect.y() < screen.y():
rect.moveTop(screen.y())
elif rect.bottom() > screen.bottom():
# if the popup goes below the screen, move its bottom
# *over* the combo, so that the its current selected
# item will always be visible
rect.moveBottom(self.mapToGlobal(QtCore.QPoint()).y())
done = True
break
if done:
break
popup.move(rect.topLeft())
This can also be done without subclassing (for example if you have many combos, you created the UI from Designer and don't want to use promoted widgets), but you'll have to remember to change all referencies to the combo.
class MyWindow(QtWidgets.QWidget):
def __init__(self):
# ...
self.combo = QtWidgets.QComboBox()
self.combo.showPopup = self.showPopupAndCheck
def showPopupAndCheck(self):
QtWidgets.QComboBox.showPopup(self.combo)
popup = self.view().window()
rect = popup.geometry()
if not rect.contains(self.combo.mapToGlobal(self.combo.rect().center())):
# the popup is not over the combo, there's no need to move it
return
# change everything from self to self.combo
Alternatively, if you want to keep this behavior consistent through all your program without always using the subclass, you can use some sort of monkey patching hack.
The advantage is that any QComboBox you create (even when loading UI files or creating a combo at runtime) will always use the new behavior.
Important: this MUST be at the very beginning of the main file of your program, possibly just after the import section.
from PyQt5 import QtCore, QtGui, QtWidgets
def customShowPopup(self):
# we can't use super(), because we're not in the class definition, but
# calling the class method with "self" as first argument is practically the
# same thing; note the underscore!
QtWidgets.QComboBox._showPopup(self)
popup = self.view().window()
# ... go on, exactly as above
# create a new reference to the showPopup method, which is the one we've used
# in the function above
QtWidgets.QComboBox._showPopup = QtWidgets.QComboBox.showPopup
# overwrite the original reference with the new function
QtWidgets.QComboBox.showPopup = customShowPopup
this is the current application looks like this:
It has a Qlistwidget listWidget_links where each item has own itemwidget set (combobox, checkbox, button, ...) Now I came upon a problem that neither google or I can solve.
If the user presses the create button or any other item's itemwidget how do I let a method know the item the widget was pressed changed inside.
item_widget.comboBox_type.currentIndexChanged.connect(self.itemupdate_linktype)
item_widget.checkBox_hide.stateChanged.connect(self.itemupdate_hidden)
cbox = self.sender() # gives the widget that released the signal
cbox.parent() #I discovered by a lucky try, returns NodeLinkItemWidgetUI.Ui_Form object which is a item's itemwidget
Here is how items get created to in order to understand the program structure better:
def createNewLink(self, nodename, nodeclass):
item = QtWidgets.QListWidgetItem(self.listWidget_links)
item_widget = NodeLinkItemWidgetUI.Ui_Form(nodename, nodeclass)
item.nodename = nodename
item.nodeclass = nodeclass
item.setSizeHint(QtCore.QSize(130, 160))
self.listWidget_links.addItem(item)
self.listWidget_links.setItemWidget(item, item_widget)
Edit: solved setting the variable item_widget.item = item seems to work, but is there a more elegant way?
I have a problem with QFileSystemModel.index
When I select files or folders from treeview with one click of the mouse, the code prints the item selected twice.
This is the part of the code where I am having the problem :
import sys
import os
import sip
sip.setapi('QVariant',2)
.....
.....
self.pathRoot = QtCore.QDir.rootPath()
self.model = QtGui.QFileSystemModel(self)
self.model.setRootPath(self.pathRoot)
self.fsindex = self.model.setRootPath(self.model.myComputer())
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.fsindex)
self.treeView.clicked.connect(self.on_treeView_clicked)
self.treeView.setColumnHidden(3, True)
self.treeView.setColumnHidden(2, True)
self.treeView.setColumnWidth(0, 320)
self.treeView.setColumnWidth(1, 30)
self.treeView.resizeColumnToContents(True)
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeView_clicked(self, index):
indexItem = self.model.index(index.row(), 0, index.parent())
filePath = self.model.filePath(indexItem)
print filePath
The problem is caused by the following line in the file generated when compiling Qt Designer.
QtCore.QMetaObject.connectSlotsByName(SOME_OBJECT)
According to the docs:
QMetaObject.connectSlotsByName (QObject o)
Searches recursively for all child objects of the given object, and
connects matching signals from them to slots of object that follow the
following form:
void on_<object name>_<signal name>(<signal parameters>); Let's
assume our object has a child object of type QPushButton with the
object name button1. The slot to catch the button's clicked() signal
would be:
void on_button1_clicked();
that is to say that it connects the slots and the signals that contain that syntax, and in your case that happens so you have 2 options:
Delete the connection you make.
Or rename your slot.
which consist a combo box 4 buttons. Once i select an entry from combo box, it will enable a button upon clicking one button it enables the rest. I want to send a command once the buttons is enabled on clicking it.
Below is my code:
import wx
import xlrd
import os,sys,time
folderpath = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
print folderpath
site_lib_path = os.path.join(folderpath, "site_lib")
files = os.listdir(site_lib_path)
for file in files:
sys.path.append(os.path.join(site_lib_path, file))
from printercx import printercx
from resttestservice.resttestservice import UITest
class ui(wx.Frame):
"""
This Class will create a Sample Frame and Create Two Buttons on tha Panel.
"""
def __init__(self,parent,id):
"""
This Fucntion will create a Frame and a Panel which has Two buttons: "OK" and "Cancel"
"""
"""-----SALQE Connecttion-----------"""
self.connection = printercx.deviceConnection()
self.ui = UITest(self.connection)
"""-----------Window Bar Name------"""
wx.Frame.__init__(self,parent,id,'GEN-2 Tool',size=(600,500))
panel=wx.Panel(self)
"""-----------Heading-------"""
header_text = wx.StaticText(panel, label="GEN-2 Tool", pos=(250,30))
font = wx.Font(15, wx.DECORATIVE, wx.NORMAL, wx.BOLD)
header_text.SetFont(font)
wx.StaticLine(panel, pos=(10, 75), size=(690,3))
"""-----------Buttons-------"""
self.pre_button=wx.Button(panel,label="Precondition",pos=(50,250),size=(100,40))
self.act_button=wx.Button(panel,label="Action",pos=(450,250),size=(100,40))
self.pass_button=wx.Button(panel,label="Pass",pos=(50,350),size=(100,40))
self.fail_button=wx.Button(panel,label="Fail",pos=(450,350),size=(100,40))
"""-------------------------------Excel-------------------------------------------------------"""
self.mainList=[]
self.val_list=[]
dic={}
book=xlrd.open_workbook("Reference_Mapping.xlsx")
sheet=book.sheet_by_name("TestCases")
n_row= sheet.nrows-1
n_col=sheet.ncols
row=1
while row<=n_row:
smallList=[]
col=0
while col<n_col:
cel=sheet.cell(row,0)
if cel.value!="":
self.val_list.append(cel.value)
key=sheet.cell(0,col).value
val=sheet.cell(row,col).value
dic[key]=val
col+=1
smallList.append(dic.copy())
self.mainList.append(smallList)
row+=1
self.val_list= list(set(self.val_list))
"""-------------------------------------------------------------------------------------------------"""
"""-----------Combo Box with Text-------"""
text=wx.StaticText(panel, label="Test Case: ", pos=(150,130))
font = wx.Font(10,wx.DECORATIVE, wx.NORMAL, wx.BOLD)
text.SetFont(font)
self.val_list.insert(0, "Select")
self.combobox=wx.ComboBox(panel, value=self.val_list[0], pos=(300,130), choices=self.val_list,style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.onTestCaseSelection, self.combobox)
print "-----------"
def onTestCaseSelection(self,event):
if self.combobox.GetSelection()>0:
print self.combobox.GetValue()
"""---------- Compairing Key's values--------------"""
for each in range(len(self.mainList)):
for every in range(len(self.mainList[each])):
if self.mainList[each][every]['TC_ID']==self.combobox.GetValue():
if self.mainList[each][every]['Ref_ID_Pre']=="":
if self.mainList[each][every]['Ref_ID_Post']!="":
self.pre_button.Enable(False)
self.act_button.Enable(True)
self.Bind(wx.EVT_BUTTON,self.send_udw,self.act_button)
self.pass_button.Enable(True)
self.fail_button.Enable(True)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']=="":
self.pre_button.Enable(True)
self.Bind(wx.EVT_BUTTON,self.send_udw,self.pre_button)
self.act_button.Enable(False)
self.pass_button.Enable(False)
self.fail_button.Enable(False)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']!="":
action_button_cmd=self.mainList[each][every]['Ref_ID_Post']
self.pre_button.Enable(True)
self.Bind(wx.EVT_BUTTON,self.send_udw,self.pre_button)
self.act_button.Enable(False)
self.pass_button.Enable(False)
self.fail_button.Enable(False)
else:
self.disableAllControls(without=None)
def disableAllControls(self, without=None):
if without==None:
self.pre_button.Enable(False)
self.act_button.Enable(False)
self.pass_button.Enable(False)
self.fail_button.Enable(False)
def send_udw(self,event):
for each in range(len(self.mainList)):
for every in range(len(self.mainList[each])):
if self.mainList[each][every]['TC_ID']==self.combobox.GetValue():
if self.mainList[each][every]['Ref_ID_Pre']=="":
if self.mainList[each][every]['Ref_ID_Post']!="":
if self.act_button.IsEnabled()==True:
post_command=self.mainList[each][every]['Ref_ID_Post']
post_udw="ui_v3.move_to_state "+post_command+" 1"
self.connection.udw(post_udw)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']=="":
if self.pre_button.IsEnabled()==True:
post_command=self.mainList[each][every]['Ref_ID_Pre']
post_udw="ui_v3.move_to_state "+post_command+" 1"
self.connection.udw(post_udw)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']!="":
if self.pre_button.IsEnabled()==True:
post_command=self.mainList[each][every]['Ref_ID_Pre']
post_udw="ui_v3.move_to_state "+post_command+" 1"
print post_udw
self.connection.udw(post_udw)
"""----Enabling button---"""
self.pre_button.Enable(False)
self.act_button.Enable(True)
self.pass_button.Enable(True)
self.fail_button.Enable(True)
time.sleep(1)
I want to send a command once this button self.act_button.Enable(True) gets enabled.
You can bind the button's to events before you disable them. They aren't going to react to events (other than maybe mouse events) until you enable them. There is no reason to bind events when you enable the button.
If you want to call a function after the enabling process (i.e. self.act_button.Enable(True)), then just call the function right after that:
self.act_button.Enable(True)
self.myFunction(*args, **kwargs)
If you want to create some kind of custom event, then you'll want to look into how to use wx.PostEvent and wx.lib.newevent. The following resources might interest you as well:
https://wiki.wxpython.org/CustomEventClasses
https://wxpython.org/Phoenix/docs/html/events_overview.html
http://wiki.ozanh.com/doku.php?id=python:misc:wxpython_postevent_threading
I am making MDI application , and without using splitter my document has multiple views. Now i want to change the document view from the MainFrame of an application...
here it is what i am doing , i have outlookbar with some menu buttons, when user will click those buttons then i will show CFormView inside the document as a child instead of popup dialog. Now i dont know how to change the view from MainFrame where the menu handler has been written.
Kindly suggest any tip if you know any...there are more than 5 different views and 4 of them are CFormView.
MainFrame ->MenuhandlerFunction (Menu Clicks)
MenuHandlerFunction -> Open New Document with New View Based on CFormView
(total 5 different CFormView and their handlers inside MainFrame Written)
I'm not very sure how you select which view to display, but here is some code to iterate through the views of the current document in your MainFrame:
EDIT: modified code for MDI
CMDIChildWnd *pChild = (CMDIChildWnd*)GetActiveFrame(); // EDIT: added line
CDocument *pDoc = pChild->GetActiveDocument(); // EDIT: added pChild->
POSITION pos = pDoc->GetFirstViewPosition();
while (pos != NULL)
{ CView* pView = GetNextView(pos);
// if this is the view you want to activate
// pChild->SetActiveView(pView); // EDIT: added pChild->
}