Is there a way to "freeze" the state of a checkbox in PyQt5? - checkbox

I want a checkbox being automatically checked when I check another checkbox and not being uncheckable until I uncheck the other checkbox.
I thought "freezing" would be an easy way to do this but I'm also interested in other solutions.
An example program try this out would be:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QCheckBox
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(500, 50, 500, 500)
self.box1 = QCheckBox('Box1', self)
self.box1.setGeometry(250, 10, 50, 50)
self.box1.stateChanged.connect(self.dothething)
self.box2 = QCheckBox('Box2', self)
self.box2.setGeometry(10, 10, 50, 50)
self.show()
def dothething(self):
if not self.box2.isChecked():
self.box2.toggle()
self.box2.freeze()
else:
self.box2.unfreeze()
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = Window()
sys.exit(app.exec_())
Obviously the .freeze() and .unfreeze() functions don't exist.
I also tried .setCheckable but with this the CheckBox can only be frozen in an unchecked state so that's the opposite of what I want.

There are various available options.
The most obvious is to disable the checkbox, which is the best option as it makes clear to the user that it cannot be unchecked while still showing that it is checked.
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(500, 50, 500, 500)
central = QWidget()
self.setCentralWidget(central)
self.box1 = QCheckBox('Box1', self)
self.box1.toggled.connect(self.dothething)
self.box2 = QCheckBox('Box2', self)
layout = QHBoxLayout(central)
layout.addWidget(self.box1)
layout.addWidget(self.box2)
self.show()
def dothething(self, checked):
if checked:
self.box2.setChecked(True)
self.box2.setEnabled(False)
else:
self.box2.setEnabled(True)
Note that in the above example I'm using a central widget and set a layout; this is because you should always use a central widget in a QMainWindow and also because using fixed geometries has lots of issues and layout managers should always be preferred; in this case, for example, I wasn't able to tell apart box1 from box2 because the geometries you used for them resulted in hiding the number in their label. Remember that what you see in your screen rarely is what other people will see.
The simpler alternative is to check the box again if the other is checked; this will work in any case, no matter if the user uses the mouse or the keyboard:
class Window(QMainWindow):
def __init__(self):
# ...
self.box2.toggled.connect(self.verifyBox2)
def verifyBox2(self, checked):
if not checked and self.box1.isChecked():
self.box2.setChecked(True)
def dothething(self, checked):
if checked:
self.box2.setChecked(True)
Just for the sake of completenes, there are other two possibilities, but remember that both rely on mouse events, so if the user uses keyboard navigation (through tab or mnemonics) and presses Space the checkbox will still be toggled.
The first possibility is to set the WA_TransparentForMouseEvents flag, which will ignore all mouse events:
class Window(QMainWindow):
# ...
def dothething(self, checked):
if checked:
self.box2.setChecked(True)
self.box2.setAttribute(Qt.WA_TransparentForMouseEvents, True)
else:
self.box2.setAttribute(Qt.WA_TransparentForMouseEvents, False)
Alternatively, you can ignore left click events (including double click, as QCheckBox is able to toggle with them too) by using an event filter:
class Window(QMainWindow):
def __init__(self):
# ...
self.box2.installEventFilter(self)
def eventFilter(self, source, event):
if source == self.box2:
if event.type() in (QEvent.MouseButtonPress, QEvent.MouseButtonDblClick):
return event.button() == Qt.LeftButton and self.box1.isChecked()
return super().eventFilter(source, event)
def dothething(self, checked):
if checked:
self.box2.setChecked(True)

Related

pysimpleGUI - creating a thread on the fly

I have created a GUI with PysimpleGUI that has multiple buttons, the intentionss is that users click on a button and continue to work on othe task while the action on the first clicked button is running, and when the action of the clicked button finished, then the thread exits (distroys the current thread),
the code is throwing : RuntimeError: main thread is not in main loop
Can someone help me create the _thread.start_new_thread process and incorporated to the main loop or maybe a solution to avoid RuntimeError
for threading i am using : _thread
the code:
class Windows:
def newOpenGraph(self, window, event, values):
'''
opens a new graph with no problem
'''
def newThread(self, window, event, values):
isRunning = True
if event == 'OPEN GRAPH':
_thread.start_new_thread(self.newOpenGraph, (window, event, values ))
isRunning = False
while isRunning:
schedule.run_pending()
time.sleep(1)
def mainLayout(self):
'''
layout frame work
'''
while True:
event, values = window.read()
if event == 'OPEN GRAPH':
# self.newOpenGraph(window, event, values)
self.newThread(window, event, values)
the image:
Use library schedule in your thread, not in main loop, also no GUI update in your thread.
Maybe code like this,
import time
import _thread
import schedule
import PySimpleGUI as sg
def func(window):
global i
window.write_event_value('Update', i)
i += 1
def new_thread(window, event, values):
global running
schedule.every().second.do(func, window=window)
running = True
while running:
schedule.run_pending()
time.sleep(0.1)
layout = [
[sg.Button("New"), sg.Button('Exit')],
[sg.Text('', size=40, key='STATUS')],
]
window = sg.Window("Multithread", layout, finalize=True)
i = 0
threads = []
while True:
event, values = window.read(timeout=100)
if event in (sg.WIN_CLOSED, 'Exit'):
running = False
break
elif event == 'New':
_thread.start_new_thread(new_thread, (window, event, values))
elif event == 'Update':
window['STATUS'].update(f'Index {values[event]}')
window.close()

hexagon buttons in GTK+

I am trying to create a button in GTK+ that has the shape of a Hexagon.
How can I do it without using CSS?
And more general, how can I create my button in any shape I want?
Is it possible to do such things in Glade(User interface edior for GTK+)?
When I put my comment I was bluffing because I never did the circular button.. I just did an example of an Hexagonal button using my original idea.. I got surprised that it was simpler than I thought!
( and by the way, no, it is not possible to do it in glade! )
# sorry but the example is in Python! :/
from gi.repository import Gtk
def hexagon(coord_x, coord_y):
# because of the symetry I take only the absolute value
coord_x, coord_y= abs(coord_x), abs(coord_y)
# I got the constants by clicling in the image and printing the coord_x and coord_y values
if coord_x <= 13 and coord_y <= 25: # define a rectangle
return True
else:
# I cut the coord x to define a triangle
coord_x=coord_x-13/2
# line equation
ymax=(-25/31)*coord_x+25
if coord_y < ymax:
return True
else:
return False
class GUI(Gtk.Window):
def __init__(self):
self.window_root=Gtk.Window()
# Create an event box to handle the click's
self.eventbox=Gtk.EventBox()
self.eventbox.connect('button-press-event' , self.on_eventbox_pressed)
self.eventbox.connect('button-release-event' , self.on_eventbox_released)
# Load the images
self.hexagon1=Gtk.Image.new_from_file('./3uSFN.png')
self.hexagon2=Gtk.Image.new_from_file('./cWmUA.png')
# init the event box
self.eventbox.add(self.hexagon1)
self.window_root.add(self.eventbox)
self.window_root.show_all()
# a variable to store the state of the button
self.state=False
def on_eventbox_pressed(self, widget , event):
if 'GDK_BUTTON_PRESS' in str(event.type): # If the user made a "single click"
if event.button == 1: # if it is a left click
# get the x,y of the mouse from the center of the image
pos_x, pos_y=self.window_root.get_position()
siz_x, siz_y=self.window_root.get_size()
mouse_x,mouse_y=event.x-siz_x/2, siz_y/2-event.y
if hexagon(mouse_x, mouse_y):
self.eventbox.remove(self.hexagon1)
self.eventbox.add(self.hexagon2)
self.eventbox.show_all()
self.state=True
def on_eventbox_released(self, widget , event):
if self.state:
self.eventbox.remove(self.hexagon2)
self.eventbox.add(self.hexagon1)
self.state=False
main=GUI()
Gtk.main()
I think that the only inconvenient of using this to solve your problem is that the theme of the user is not respected. If that is a problem, instead of using an image you could draw your button by using a DrawingArea and by getting the theme colors as I suggested here!
I hope it be useful :)

On App Engine, how do you make an ndb Property immutable once set?

This seems like such a commonplace requirement, it'd be builtin, but anyway: If you have a model like the following one, how do you stop the eggs property being mutated after it's been set?
class Spam(ndb.Model):
eggs = ndb.StringProperty()
The goal is is to have the property not required, so it'd default to None, but once it's been mutated from None, to a string in the above case, it can never be changed again, but any insight into defining an immutable property would be appreciated.
I had considered using the validator argument of the ndb.Property to pass in a function; see the answers below for why that wouldn't work here. It's useful for understanding the objects and namespaces involved.
One approach which does not require you to use custom properties is to use hooks See docs https://developers.google.com/appengine/docs/python/ndb/modelclass#Model__post_get_hook
You would use a _post_get_hook(cls, key, future) and a _pre_put_hook(self)
In _post_get_hook you would store away the original value of the property
and in _pre_put_hook you would check that it is the same as the original value unless the original value is None.
ie
class Spam(ndb.Model):
eggs = ndb.StringProperty()
#classmethod
def _post_get_hook(cls,key,future):
obj = future.get_result()
obj._my_eggs_prop = obj.eggs
def _pre_put_hook(self):
if hasattr(self,'_my_eggs_prop'):
if self.eggs != self._my_eggs_prop:
if self._my_eggs_prop != None:
# do some logging.
raise ValueError
else:
setattr(self,'_my_eggs_prop',self.eggs)
# if the saved value doesn't exist, create it and store
# the value in case an update occurs after the initial put
# this also means the object was created and not get()
Here is an example of it working
s~lightning-catfish> import spam
s~lightning-catfish> x = spam.Spam(id='canned')
s~lightning-catfish> x.eggs = 'green'
s~lightning-catfish> x.put()
Key('Spam', 'canned')
s~lightning-catfish> y = x.key.get()
s~lightning-catfish> y._my_eggs_prop
u'green'
s~lightning-catfish> y.eggs = 'blue'
s~lightning-catfish> y.put()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 3232, in _put
return self._put_async(**ctx_options).get_result()
File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 3247, in _put_async
self._pre_put_hook()
File "spam.py", line 18, in _pre_put_hook
raise ValueError
ValueError
The downside of this approach is you could change the property rely in it for some additional code and then only find out about it when you do the put. However that may not be so bad, as you should in theory not have any code that is modifying the property once changed. So you want to log, and track down how this could be occurring. Alternately you could reset the value to the original setting, but then you leave incorrect code in place.
A custom property takes a little more thought ;-)
Unfortunately you can't do what you want with a validator.
(prop,value) are the property instance and value to set. You don't have a handle in the validator for the instance of the class and therefore the pre-existing value. All the methods of the property you need to get the existing value need a model instance as an argument - like _has_value . The docs
Will be called with arguments (prop, value) and should either return
the (possibly coerced) value or raise an exception. Calling the
function again on a coerced value should not modify the value further.
(For example, returning value.strip() or value.lower() is fine, but
not value + '$'.) May also return None, which means "no change". See
also Writing Property Subclasses
`https://developers.google.com/appengine/docs/python/ndb/properties#options
You will need to write a custom property to manage the state of the value and prevent overwriting once set.
See the example below showing you that you haven't got access to the pre-existing value of the property and a validator isn't passed it or the instance of the model which would have the value.
s~lightning-catfish> from pdb import set_trace
s~lightning-catfish> def valid(prop,val):
... set_trace()
... return val
...
s~lightning-catfish> class X(ndb.Model):
... x = ndb.StringProperty(validator=valid)
...
s~lightning-catfish> y = X(x="abc")
> <console>(3)valid()
(Pdb) p prop
StringProperty('x', validator=<function valid at 0xaf2d02c>)
(Pdb) p prop._has_value
<bound method StringProperty._has_value of StringProperty('x', validator=<function valid at 0xaf2d02c>)>
(Pdb) p prop._has_value()
*** TypeError: TypeError('_has_value() takes at least 2 arguments (1 given)',)
(Pdb) c
s~lightning-catfish> y
X(x='abc')

wxPython: get data in variables from Google Spreadsheet and also from user, work with all variables, return results to user

I hope I didn't miss any topic that could answer my problem. I'm here now because I'm terribly frustrated and tired with the following task:
- I have a Spreasheet with Drive.Google with lots of data in it
- I would like to create an application with wxPython that would pull data from this spreeadsheet (in the most easy way possible)
- Would also like to get multiple data from a user who will access this application through a nice interface (panel aka window)
- The multiple data introduced by the user should be able to work with the data pulled out from the Spreasheet. For example to see if the data introduced by the user is in the Spreadsheet or not and also some other operations with the next data introduced by the user.
- Finally and most importantly show the results to the user (later I would also like to add some functions to save somehow the results)
I hope I managed to express clearly what I would like to do. Now I'm new to Google API's, Python adn wxPython, but I have experience with C++ , php, html .
I've spent 2 weeks now with discovering Drive.Google and learning Python and wxPython. I did follow all tuturials on these, made my notes, read tones of stackoverflow questions-answers, wiki.wxpython.org etc. I learn every single day and I can do now many things separately but to have all functions like I described above I just couldn't work out how to do. At least please orient me in the direction. Awfel lot of times I spend hours doing examples and getting nowhere. I have Python, wxPython extention, GoogleAppEngine Launcher and even pyCharm demo. Please be kind. This is my first question ever.
here's the mess I made so far combining relevant examples:
import wx
import gdata.docs
import gdata.docs.service
import gdata.spreadsheet.service
import re, os
class Form(wx.Panel):
def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
self.createControls()
self.bindEvents()
self.doLayout()
self.spreasht()
def createControls(self):
self.logger = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_READONLY)
self.saveButton = wx.Button(self, label="Elvegzes")
self.nameLabel = wx.StaticText(self, label="type Name1:")
self.nameTextCtrl = wx.TextCtrl(self, value="type here")
self.name2Label = wx.StaticText(self, label="type Name2:")
self.name2TextCtrl = wx.TextCtrl(self, value="type here")
def bindEvents(self):
for control, event, handler in \
[(self.saveButton, wx.EVT_BUTTON, self.onSave),
(self.nameTextCtrl, wx.EVT_TEXT, self.onNameEntered),
(self.nameTextCtrl, wx.EVT_CHAR, self.onNameChanged)]:
control.Bind(event, handler)
def doLayout(self):
raise NotImplementedError
def spreadsht(self):
gd_client = gdata.spreadsheet.service.SpreadsheetsService()
gd_client.email = 'my email address'
gd_client.password = 'my password to it'
gd_client.source = 'payne.org-example-1'
gd_client.ProgrammaticLogin()
q = gdata.spreadsheet.service.DocumentQuery()
q['title'] = 'stationcenter'
q['title-exact'] = 'true'
feed = gd_client.GetSpreadsheetsFeed(query=q)
spreadsheet_id = feed.entry[0].id.text.rsplit('/',1)[1]
feed = gd_client.GetWorksheetsFeed(spreadsheet_id)
worksheet_id = feed.entry[0].id.text.rsplit('/',1)[1]
al1 = raw_input('Name1: ')
print al1
al2 = raw_input('Name2: ')
print al2
rows = gd_client.GetListFeed(spreadsheet_id, worksheet_id).entry
for row in rows:
for key in row.custom:
if al1 == row.custom[key].text:
print ' %s: %s' % (key, row.custom[key].text)
def onColorchanged(self, event):
self.__log('User wants color: %s'%self.colors[event.GetInt()])
def onReferrerEntered(self, event):
self.__log('User entered referrer: %s'%event.GetString())
def onSave(self,event):
self.__log('User clicked on button with id %d'%event.GetId())
def onNameEntered(self, event):
self.__log('User entered name: %s'%event.GetString())
def onNameChanged(self, event):
self.__log('User typed character: %d'%event.GetKeyCode())
event.Skip()
def onInsuranceChanged(self, event):
self.__log('User wants insurance: %s'%bool(event.Checked()))
# Helper method(s):
def __log(self, message):
''' Private method to append a string to the logger text
control. '''
self.logger.AppendText('%s\n'%message)
class FormWithSizer(Form):
def doLayout(self):
''' Layout the controls by means of sizers. '''
boxSizer = wx.BoxSizer(orient=wx.HORIZONTAL)
gridSizer = wx.FlexGridSizer(rows=5, cols=2, vgap=10, hgap=10)
# Prepare some reusable arguments for calling sizer.Add():
expandOption = dict(flag=wx.EXPAND)
noOptions = dict()
emptySpace = ((0, 0), noOptions)
# Add the controls to the sizers:
for control, options in \
[(self.nameLabel, noOptions),
(self.nameTextCtrl, expandOption),
(self.allomas2Label, noOptions),
(self.allomas2TextCtrl, expandOption),
emptySpace,
(self.saveButton, dict(flag=wx.ALIGN_CENTER))]:
gridSizer.Add(control, **options)
for control, options in \
[(gridSizer, dict(border=5, flag=wx.ALL)),
(self.logger, dict(border=5, flag=wx.ALL|wx.EXPAND,
proportion=1))]:
boxSizer.Add(control, **options)
self.SetSizerAndFit(boxSizer)
class FrameWithForms(wx.Frame):
def __init__(self, *args, **kwargs):
super(FrameWithForms, self).__init__(*args, **kwargs)
notebook = wx.Notebook(self)
form2 = FormWithSizer(notebook)
notebook.AddPage(form2, 'CALTH')
self.SetClientSize(notebook.GetBestSize())
if __name__ == '__main__':
app = wx.App(0)
frame = FrameWithForms(None, title='Relevant title˝')
frame.Show()
app.MainLoop()
THANK YOU AGAIN!!!!!!!!!!!
First, make sure you can download the data you want with just Python. Then create a wxPython GUI with a single button. In that button's handler, have it call the script that can download the data you want.
If that causes your GUI to become unresponsive, then you'll need to use a thread to do the downloading. I recommend the following articles if that's the case:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
http://wiki.wxpython.org/LongRunningTasks
Okay, so now you have the data downloading appropriately. Now you add a grid widget or a listctrl / object list view widget. Pick one of those. I prefer object list view, which you can read about here. Then in your button handler you can call your downloader script or thread and when that's done, you can load the widget with that data. If you're using a thread, then the thread will have to call the next step (i.e. the widget loading bit).
Now you should have your data displayed. All that's left is making it look pretty and maybe putting the downloading part into a menu item.

PyQT and QTreeView: Need to ask for childrens when user clicks on an item

I need to create a QTreeView based on requests.
So, when the user open the application, it should make a request to get the root item for the tree. Once the user clicks on that item, it should ask for the children, and so on.
I couldn't find any working example with requests as I want and I don't even know if this is possible.
It's quite simple really.
Firstly, connect the tree's expanded signal to a handler, and populate the tree with the root's top-level items.
When the signal is fired, it will pass the index of the expanded item to the handler. The handler can then use this to check whether the item has any children using, say, the model's hasChildren method.
If the item already has children, do nothing; otherwise, populate it with whatever top-level items are appropriate for that item.
UPDATE
Below is a script demonstrates how to build a tree dynamically.
For simplicity, the demo uses a QTreeWidget, thus avoiding the need for a separate model. Additional data is stored within the tree by using QTreeWidgetItem.setData.
Note that the sip import at the top is only needed for compatibilty between Python 2 and 3 (see here for details). If you're using Python 2, it's not needed.
import sip
sip.setapi('QVariant', 1)
from PyQt4 import QtGui, QtCore
class Window(QtGui.QTreeWidget):
def __init__(self):
QtGui.QTreeWidget.__init__(self)
self.setHeaderHidden(True)
self.itemExpanded.connect(self.handleExpanded)
self.itemClicked.connect(self.handleClicked)
self.handleExpanded(self.invisibleRootItem())
def depth(self, item):
depth = 0
while item is not None:
item = item.parent()
depth += 1
return depth
def requestData(self):
for title in 'One Two Three Four Five'.split():
yield title, 'additional data'
def addItems(self, parent):
depth = self.depth(parent)
for title, data in self.requestData():
item = QtGui.QTreeWidgetItem(parent, [title])
item.setData(0, QtCore.Qt.UserRole, data)
if depth < 3:
item.setChildIndicatorPolicy(
QtGui.QTreeWidgetItem.ShowIndicator)
def handleExpanded(self, item):
if item is not None and not item.childCount():
self.addItems(item)
def handleClicked(self, item, column):
print(item.data(column, QtCore.Qt.UserRole).toPyObject())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Resources