i'm creating a dialog that finds out what is focused element.
that's what i wrote:
import gtk
import gobject
class FocusedElementPath(gtk.Dialog):
def __init__(self, parent, title=None):
gtk.Dialog.__init__(self, title or 'Show path', parent)
self.catch_within = parent
self.catch_focus = True
self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
again_btn = gtk.Button('',gtk.STOCK_REFRESH)
again_btn.connect('activate', self.refresh_pressed)
again_btn.show()
self.action_area.add(again_btn)
self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
self.action_area.set_layout(gtk.BUTTONBOX_EDGE)
self.path = gtk.Label()
self.path.show()
self.vbox.add(self.path)
def refresh_pressed(self, btn):
self.catch_focus = True
def do_focus_out_event(self, evt):
nl = self.catch_within.get_focus()
if nl:
self.catch_within.activate_focus()
self.path.set_text(repr(nl))
else:
self.path.set_text('None')
gtk.Dialog.on_focus_event(self, evt)
gobject.type_register(FocusedElementPath)
the problem is it returns previously focused element.
is there any way to find out currently focused element?
i've tried different events (for dialog and for window), but nothing helped :(
what am i doing wrong or how do i do this correctly?
gtk.Window.get_focus (also available in gtk.Dialog) will return the currently focused child.
Anyway I don't quite understand what you want to achieve here...
Related
I want to run the function removeHi(self) only once in my program, how to accomplish this. Please advise me. My entire code below:
import sys
from PyQt5.QtWidgets import *
from functools import wraps
class TestWidget(QWidget):
gee = ''
def __init__(self):
global gee
gee = 'Hi'
QWidget.__init__(self, windowTitle="A Simple Example for PyQt.")
self.outputArea=QTextBrowser(self)
self.outputArea.append(gee)
self.helloButton=QPushButton("reply", self)
self.setLayout(QVBoxLayout())
self.layout().addWidget(self.outputArea)
self.layout().addWidget(self.helloButton)
self.helloButton.clicked.connect(self.removeHi)
self.helloButton.clicked.connect(self.sayHello)
def removeHi(self):
self.outputArea.clear()
def sayHello(self):
yourName, okay=QInputDialog.getText(self, "whats your name?", "name")
if not okay or yourName=="":
self.outputArea.append("hi stranger!")
else:
self.outputArea.append(f"hi,{yourName}")
app=QApplication(sys.argv)
testWidget=TestWidget()
testWidget.show()
sys.exit(app.exec_())
The GUI will show "Hi" when the program runs. I want the "Hi" in QTextBrowser removed after I push the button reply, but the program will clear everything in the text browser whenever I clicked the button.
My goal is: only the first Hi be removed, and the name from function sayHello(self) will remain whenever I push the reply button.
The problem resides in the logic of your program: you should check whether the text must be cleared or not, using a default value that would be changed whenever the dialog changes the output:
class TestWidget(QWidget):
clearHi = True
def __init__(self):
QWidget.__init__(self, windowTitle="A Simple Example for PyQt.")
self.outputArea = QTextBrowser()
self.outputArea.append('Hi')
self.helloButton = QPushButton("reply")
layout = QVBoxLayout(self)
layout.addWidget(self.outputArea)
layout.addWidget(self.helloButton)
self.helloButton.clicked.connect(self.removeHi)
self.helloButton.clicked.connect(self.sayHello)
def removeHi(self):
if self.clearHi:
self.outputArea.clear()
def sayHello(self):
yourName, okay = QInputDialog.getText(
self, "whats your name?", "name")
if not okay:
return
self.clearHi = False
if not yourName:
self.outputArea.append("hi stranger!")
else:
self.outputArea.append(f"hi, {yourName}")
Note: do not use globals.
I am trying to have a series of QTableView created at runtime and added to newly created pages of a multipage QTabWidget.
All seems to go fine, but the QTableView don't show up.
The QTabWidget gets zeroed (reset to no pages) and refurbished (...) flawlessly (at least it looks like so) depending on the selection of a combobox (and the dictionaries therein related).
I am also using a delegate callback to include a column of checkboxes to the QTableView (thanks to https://stackoverflow.com/a/50314085/7710452), which works fine stand alone.
Here is the code.
Main Window
EDIT
as recommended by eyllanesc, here is the standalone module (jump to the end of the post for details on the part I think is problematic):
"""
qt5 template
"""
import os
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from PyQt5 import uic
from configparser import ConfigParser, ExtendedInterpolation
from lib.SearchControllers import findGuis, get_controller_dict, show_critical, show_exception
import resources.resources
from lib.CheckBoxesDelegate import CheckBoxDelegate
myForm_2, baseClass = uic.loadUiType('./forms/setup.ui')
class MainWindow(baseClass):
def __init__(self, config_obj: ConfigParser,
config_name: str,
proj_name: str,
*args,
**kwargs):
super().__init__(*args, **kwargs)
self.ui = myForm_2()
self.ui.setupUi(self)
# your code begins here
self.setWindowTitle(proj_name + " Setup")
self.ui.logo_lbl.setPixmap(qtg.QPixmap(':/logo_Small.png'))
self.config_obj = config_obj
self.config_name = config_name
self.proj_filename = proj_name
self.proj_config = ConfigParser(interpolation=ExtendedInterpolation())
self.proj_config.read(proj_name)
self.guis_dict = {}
self.components = {}
self.cdp_signals = {}
self.root_path = self.config_obj['active']['controllers']
self.tableViews = []
self.tabs = []
self.iniControllersBox()
self.setActSignals()
self.load_bulk()
self.set_signals_table()
self.update_CurController_lbl()
self.update_ControllersTab() # here is where the action gets hot
# your code ends here
self.show() # here crashes if I passed the new tab to the instance of
# QTabView. otherwise it shows empty tabs
#########################################################
def load_bulk(self):
# get the list of running components into a dictionary
for i in self.list_controllers:
i_path = os.path.join(self.root_path, i)
print(i)
self.components[i] = get_controller_dict(i_path,
self.config_obj,
'Application.xml',
'Subcomponents/Subcomponent',
'Name',
'src')
for j in self.components[i]:
print(j)
signals_key = (i , j)
tgt = os.path.join(self.root_path, self.components[i][j])
self.cdp_signals[signals_key] = get_controller_dict(i_path,
self.config_obj,
self.components[i][j],
'Signals/Signal',
'Name',
'Type',
'Routing')
def set_signals_table(self):
self.ui.MonitoredDevicesTable.setHorizontalHeaderItem(0, qtw.QTableWidgetItem('GUI caption'))
self.ui.MonitoredDevicesTable.setHorizontalHeaderItem(1, qtw.QTableWidgetItem('Monitored Signal'))
def setActSignals(self):
self.ui.controllersBox.currentIndexChanged.connect(self.update_guis_list)
self.ui.controllersBox.currentIndexChanged.connect(self.update_CurController_lbl)
self.ui.controllersBox.currentIndexChanged.connect(self.update_ControllersTab)
def update_ControllersTab(self):
self.ui.componentsTab.clear() # this is the QTabWidget
self.tabs = []
self.tableViews = []
curr_controller = self.ui.controllersBox.currentText()
for i in self.components[curr_controller]:
if len(self.cdp_signals[curr_controller, i]) == 0:
continue
self.tabs.append(qtw.QWidget())
tabs_index = len(self.tabs) - 1
header_labels = ['', 'Signal', 'Type', 'Routing', 'Input']
model = qtg.QStandardItemModel(len(self.cdp_signals[curr_controller, i]), 5)
model.setHorizontalHeaderLabels(header_labels)
# in the next line I try to create a new QTableView passing
# the last tab as parameter, in the attempt to embed the QTableView
# into the QWidget Tab
self.tableViews.append(qtw.QTableView(self.tabs[tabs_index]))
tbw_Index = len(self.tableViews) - 1
self.tableViews[tbw_Index].setModel(model)
delegate = CheckBoxDelegate(None)
self.tableViews[tbw_Index].setItemDelegateForColumn(0, delegate)
rowCount = 0
for row in self.cdp_signals[curr_controller, i]:
for col in range(len(self.cdp_signals[curr_controller, i][row])):
index = model.index(rowCount, col, qtc.QModelIndex())
model.setData(index, self.cdp_signals[curr_controller, i][row][col])
try:
self.ui.componentsTab.addTab(self.tabs[tabs_index], i) # no problems, some controllers ask up to
except Exception as ex:
print(ex)
def update_CurController_lbl(self):
self.ui.active_controller_lbl.setText(self.ui.controllersBox.currentText())
def iniControllersBox(self):
self.list_controllers = [os.path.basename(f.path) for f in os.scandir(self.root_path) if f.is_dir() and str(
f.path).upper().endswith('NC')]
self.ui.controllersBox.addItems(self.list_controllers)
for i in range(self.ui.controllersBox.count()):
self.ui.controllersBox.setCurrentIndex(i)
newKey = self.ui.controllersBox.currentText()
cur_cntrlr = os.path.join(self.config_obj['active']['controllers'], self.ui.controllersBox.currentText())
self.guis_dict[newKey] = findGuis(cur_cntrlr, self.config_obj)
self.ui.controllersBox.setCurrentIndex(0)
self.update_guis_list()
def update_guis_list(self, index=0):
self.ui.GuisListBox.clear()
self.ui.GuisListBox.addItems(self.guis_dict[self.ui.controllersBox.currentText()])
if __name__ == '__main__':
config = ConfigParser()
config.read('./config.ini')
app = qtw.QApplication([sys.argv])
w = MainWindow(config, './config.ini',
'./test_setup_1.proj')
sys.exit(app.exec_())
and here the external to add the checkboxes column:
class CheckBoxDelegate(QtWidgets.QItemDelegate):
"""
A delegate that places a fully functioning QCheckBox cell of the column to which it's applied.
"""
def __init__(self, parent):
QtWidgets.QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
"""
Important, otherwise an editor is created if the user clicks in this cell.
"""
return None
def paint(self, painter, option, index):
"""
Paint a checkbox without the label.
"""
self.drawCheck(painter, option, option.rect, QtCore.Qt.Unchecked if int(index.data()) == 0 else QtCore.Qt.Checked)
def editorEvent(self, event, model, option, index):
'''
Change the data in the model and the state of the checkbox
if the user presses the left mousebutton and this cell is editable. Otherwise do nothing.
'''
if not int(index.flags() & QtCore.Qt.ItemIsEditable) > 0:
return False
if event.type() == QtCore.QEvent.MouseButtonRelease and event.button() == QtCore.Qt.LeftButton:
# Change the checkbox-state
self.setModelData(None, model, index)
return True
if event.type() == QtCore.QEvent.MouseButtonPress or event.type() == QtCore.QEvent.MouseMove:
return False
return False
def setModelData (self, editor, model, index):
'''
The user wanted to change the old state in the opposite.
'''
model.setData(index, 1 if int(index.data()) == 0 else 0, QtCore.Qt.EditRole)
The 1st picture shows the layout in QTDesigner, the 2nd the result (emtpy tabs) when avoiding the crashing.
the QTabWidget has no problems in zeroing, or scale up, back to as many tab as I need, it's just that I have no clue on how to show the QTabview. My approach was to try to embed the QTabView in the tabpage passing it as parameter to the line creating the new QTabView.
Since I am using rather convoluted dictionaries, calling an XML parser to fill them up, not to mention the config files, I know even this version of my script is hardly reproduceable/runnable.
If someone had the patience of focusing on the update_ControllersTab method though, and tell me what I am doing wrong handling the QWidgets, it'd be great.
Again the basic idea is to clear the QTabWidget any time the user selects a different controller (combo box on the left):
self.ui.componentsTab.clear() # this is the QTabWidget
self.tabs = [] # list to hold QTabView QWidgets (pages) throughout the scope
self.tableViews = [] # list to hold QTabView(s) thorughout the scope
count how many tabs (pages) and hence embedded TabViews I need with the new controllers selected.
and then for each tab needed:
create a new tab (page)
self.tabs.append(qtw.QWidget())
tabs_index = len(self.tabs) - 1
create a new QTabView using a model:
header_labels = ['', 'Signal', 'Type', 'Routing', 'Input']
model = qtg.QStandardItemModel(len(self.cdp_signals[curr_controller, i]), 5)
model.setHorizontalHeaderLabels(header_labels)
self.tableViews.append(qtw.QTableView(self.tabs[tabs_index]))
tbw_Index = len(self.tableViews) - 1
self.tableViews[tbw_Index].setModel(model)
populate the TableView with data, and then finally add the tab widget (with the suppposedly embedded QTableView to the QTabWidget (the i argument is a string from my dbases Names:
self.ui.componentsTab.addTab(self.tabs[tabs_index], i)
This method is called also by the __init__ to initialize and apparently all goes error free, until the last 'init' statement:
`self.show()`
at which point the app crashes with:
Process finished with exit code 1073741845
on the other hand, if here instead of trying to embed the QTableView:
self.tableViews.append(qtw.QTableView(self.tabs[tabs_index]))
I omit the parameter, that is:
self.tableViews.append(qtw.QTableView())
the app doesn't crash anymore, but of course no QtableViews are shown, only empty tabpages:
As stupid as this may sound the problem is in... the delegate class that creates the checkboxes in the first column (see https://stackoverflow.com/a/50314085/7710452)
I commented out those two lines:
delegate = CheckBoxDelegate(None)
self.tableViews[tbw_Index].setItemDelegateForColumn(0, delegate)
and... bingo!
the CheckBoxDelegate works fine in the example shown in the post (a single QTableView form). I also tinkered around adding columns and rows, and moving the checkbox column back and forth with no problems. In that standalone. But as soon as I add the class and set the delegate, i am back at square zero, app crashing with:
Process finished with exit code 1073741845
so I am left with this problem now. Thnx to whomever read this.
Problem solved, see comment to post above.
I have issue with GTK's TreeView with ListStore. Records are updated, but sometime only when I hover it.
Bigger problem are new records - It's like it stops to displaying new ones unless I scroll to bottom all the time - which is weird.
I use Glade.
My code (slightly simplified)
class UI(SampleUI):
gladefile = 'ui.glade'
iterdict = {}
def __init__(self, module):
super().__init__(module)
def start(self):
self.fetch_widgets()
self.connect_events()
self.window.show()
def fetch_widgets(self):
self.window = self.builder.get_object('window')
self.liststore = self.builder.get_object('store')
def connect_events(self):
handlers = {
"on_window_close" : Gtk.main_quit,
"on_start_working": self.start_working,
"on_stop_working": self.stop_working
}
self.builder.connect_signals(handlers)
self.module.url_pending_action = self.url_pending
self.module.url_succeed_action = self.url_update
def start_working(self, button):
self.module.start()
def stop_stop(self, button):
self.module.stop()
def url_pending(self, data):
self.iterdict[data['url']] = self.liststore.append([0, data['url'], 0, '', data['text']])
def url_update(self, data):
_iter = self.iterdict[data['url']]
self.liststore[_iter][1] = data['some_field1']
self.liststore[_iter][2] = data['some_field2']
Methods self.url_pending and self.url_update are called by threads (at most 30 running at the same time) created in self.module
I checked and new records are correctly appended into ListStore - I can read data from it. Window is working fine, but there are no new items at the bottom.
Ideas?
Ok, I made research and I figured out that GTK don't like being called from outside of main thread.
There was methods in Gdk to lock GTK calls
Gtk.threads_enter()
...
Gtk.threads_leave()
But now it's deprecated and GTK documentation says that every GTK call should be in main thread.
My workaround:
# [...]
def start(self):
# [...]
GObject.timeout_add(100, self.query_do)
# [...]
def query_do(self):
while len(self.gtk_calls):
self.gtk_calls.pop()()
GObject.timeout_add(100, self.query_do)
And I'm just adding into query
self.gtk_calls.insert(0, lambda: anything())
The code isn't as clear as it was before, but works perfect.
How can I bind a checkbox to a string such that when the checkbox is checked/unchecked, the value of the string changes? I have this (with CheckAll as my checkbox):
class MyWindow(Window):
def __init__(self):
wpf.LoadComponent(self, 'BioApp1.xaml')
openDialog = SequenceFileOperations()
self.Sequences = openDialog.Open()
object = MyObjects(self.Sequences)
self.CheckAll.DataContext = object
self.IDLabel.DataContext = object
class MyObjects(object):
def __init__(self, Sequences):
self.CurrentSeq = Sequences[0]
self.ID = self.CurrentSeq.ID
and
<Label Height="28" HorizontalAlignment="Left" Margin="152,221,0,0" VerticalAlignment="Top" Width="98" Name="IDLabel" Content="{Binding Path=ID}"/>
I want that when the checkbox is unchecked, the label should display the sequence ID, but when it is checked, it should simply display “All”. For this I need to change the ID property of CurrentSeq to “All”. How do I do that by data binding? Is there any other way I can do this?
EDIT: I feel really stupid but I just can’t get this to work. I have been trying to follow the suggestion about using getter/setter but I guess I don’t know enough. Before doing anything more complicated, I simply want to make a button disabled when I tick the checkbox and enable it when I uncheck it. This is what I wrote:
class MyWindow(Window):
def __init__(self):
wpf.LoadComponent(self, 'App1.xaml')
object = BindingClass(self.Check, self.PreviousBtn)
self.PreviousBtn.DataContext = object
class BindingClass(object):
def __init__(self, Check, PreviousBtn):
self.Check = Check
self.PreviousBtn = PreviousBtn
def GetEnabledConverter(self):
if self.CheckAll.IsChecked:
return self.PreviousBtn.IsEnabled
def SetEnabledConverter(self):
if self.CheckAll.IsChecked:
self.PreviousBtn.IsEnabled = False
else:
self.PreviousBtn.IsEnabled = True
EnabledConverter = property(GetEnabledConverter, SetEnabledConverter)
And:
<Button Content="Previous" IsEnabled="{Binding Path=EnabledConverter}" />
Unfortunately there is no error but no effect either. The code does not do anything. Would really appreciate if you could help me out with this.
EDIT2: Using the notify_property, I tried this:
class MyWindow(Window):
def __init__(self):
wpf.LoadComponent(self, 'Test.xaml')
c = Converters(self.check1, self.Button)
self.Button.DataContext = c
class Converters(NotifyPropertyChangedBase):
def __init__(self, check, button):
super(Converters, self).__init__()
self.Check = check
self.Button = button
#notify_property
def ButtonEnabled(self):
return self.Button.IsEnabled
#ButtonEnabled.setter
def ButtonEnabled(self):
if self.Check.IsChecked:
self.Button.IsEnabled = False
else:
self.Button.IsEnabled = True
Still the same result: no effect. I just cannot understand where the problem is.
I would use Converter.
Edit:
You can implement converter in Python:
class BoolToVisibilityConverter(IValueConverter):
def Convert(self, value, targetType, parameter, culture):
return Visibility.Visible if value != val else Visibility.Collapsed
Last time I worked with WPF in IronPython, you could not use it directly in .xaml. I am not sure whether it has improved in 2.7.
Another possibility is to add another property which does the conversion (converted_ID) in its setter/getter. Thinking more about it, I would do rather this, because the code is in one place.
Edit 2:
Make sure, you are using notify_property instead of classic Python property.
I want to make a checklist with a accordion style in a wxPython widget. I know about checklistbox, but I couldn't find anything in the official docs concerning it. Has anyone done this?
You can use a wx.combo.ComboCtrl which allows any custom popup, and combine this with a wx.CheckListBox.
Here's what is could look like, folded:
and unfolded:
To get this, I started with the ComboCtrl example in the demo, and in the ListCtrlComboPopup list class I replaced ListCtrl with CheckListBox everywhere (and made a few other small changes to make the commands consistent with a CheckListBox control rather than a ListCtrl).
You have to use a wx.PreCheckListBox() for the two part initialization that is required for this.
Here's my implementation. Combine this with a ComboCtrl and you're all set.
import wx
from wx import combo
class checkListComboPopup(combo.ComboPopup):
def __init__(self):
combo.ComboPopup.__init__(self)
self.checklist = wx.PreCheckListBox()
self._value = -1
def Init(self):
self._value = -1
def Create(self, parent):
return self.checklist.Create(parent, 1, wx.Point(0,0), wx.DefaultSize)
def GetControl(self):
return self.checklist
def SetStringValue(self, s):
pass
def GetStringValue(self):
if (self._value >= 0):
return self.checklist.GetItemText(self, self._value)
else:
return wx.EmptyString
def OnMouseMove(self, event):
pass
def GetPreCheckList(self):
return self.checklist
def OnMouseClick(self, event):
pass
Two part creation