Python PyQt4 .setText refusing variables? - python

I would say I'm fairly decent with Python, but creating GUIs is a new concept for me. I've used Qt Creator to format the GUI and pyuic to convert the code from the file.ui.
I have most of the GUI coded, but I'm having this problem updating the text for labels for line edits, push buttons etc. So this GUI has an options window that opens from the main program where the user can specify certain parameters. Currently, I open the options, set the values, close, reopen the option window, and the text has not changed to the new values which are variables. Plain strings do work however. Variables will 'stick,' only if the program is restarted.
I'm importing a config.py file where there is a variable containing the string of parameters. These are formatted and set alongside all other labels etc. But there not being set for some reason.
config.py
configAttrs="clientid,oauth,123,source,123"
A nested function of mainProgram.py used to set the text of the labels etc.
def retranslateUi(self, OptionsWindow):
OptionsWindow.setWindowTitle(_translate("OptionsWindow", "OptionsWindow", None))
self.label_MainOptions.setText(_translate("OptionsWindow", "Options", None))
confs = config.configAttrs.split(',')
clientid = str(confs[0])
oauth = str(confs[1])
cache = str(confs[2])
heightAdjust = str(confs[4])
#does NOT work when reopening options window
#does work with restart
self.lineEdit_ClientID.setText(_translate("OptionsWindow", clientid, None))
#does NOT work when reopening options window
#does work with restart
self.lineEdit_ClientID.setText('{0}'.format(clientid))
#does work when reopening options window
#does work with restart
self.lineEdit_ClientID.setText(_translate("OptionsWindow", 'string_clientid', None))
Shortened the code above.*

The problem is caused because although the config.py file is modified this is not automatically reloaded by python, in order to force it you must use reload, in your case:
def retranslateUi(self, OptionsWindow):
[...]
reload(config)
confs = config.configAttrs.split(',')
[...]

Related

Reload Tkinter application after code change

I am building a relatively large application based on Tkinter. Whenever I make a code change, I have to reload the application manually, and then use the GUI to go back to the desired state.
For instance, if I had open an image in the app before changing the code, and I want to open it again, I have first to open the GUI, select the image from the drop-down list and so on... This is a waste of time. Is there a way to automatically reload the application/GUI after a code change in the last state it was?
This question has been asked a number of times
Proper way to reload a python module from the console
You can use reload(module) for this, but beware of nasty side effects. For example, existing code will be based on the original code, it will not magically get new attributes or baseclasses added.
Another great resource on this would be https://code.activestate.com/recipes/580707-reload-tkinter-application-like-a-browser/
After you've described you're problem a bit further in the comments, I was able to reconstruct the issue better on my end.
Here's my basic implementation for a solution to your issue:
from tkinter import *
import json
application_state = {"background": "white"}
background_colors = ["white", "red", "blue", "yellow", "green"]
def main():
root = Tk()
root.title("Test")
root.geometry("400x400")
reload_state(root)
for color in background_colors:
def change_background_wrapper(color=color, root=root):
change_background(color, root)
Button(root, text=color,command=change_background_wrapper).pack()
root.mainloop()
def change_background(color, window):
print("new color: " + color)
window.configure(bg=color)
application_state["background"] = color
update_config()
def reload_state(window):
config_file = open("config.json")
conf = json.load(config_file)
window.configure(bg=conf["background"])
def update_config():
with open("config.json", "w") as conf:
conf.write(json.dumps(application_state))
if __name__ == "__main__":
main()
In this instance, we're able to update the background color of the GUI and it will persist on each rerun of the script, until it gets manually changed again. You can apply this concept to pretty much any sort of state you have inside your application, I think this should get you started!

PyQt5: Using DeleteOnClose when switching to new window

I am currently creating a GUI in Python 3.7, using PyQt5 and Qt Designer in the Spyder environment. The GUI has many different windows. Basically I am starting with the UI_Start window and then open the next window when a button is pressed. The GUI is working kind of fine, however after approximately 50 windows the program suddenly doesn't show the next window anymore but also doesn't stop the execution. The weird thing about this issue is that:
the exact same window class has been called a lot of times beforehand and there have never been any issues
the problem does not only occur for one window but it can also occur for another window class (but after the same amount of windows being shown)
I tried to figure out why the .show() command is suddenly not working anymore. I used print statements to see where the program "breaks down". I saw that even the print statements after the .show() command are working but then as the window isn't shown I can't press any button to trigger the next event. So basically the program is hanging.
I am relatively new to programming in Python and creating GUIs but I thought that maybe the problem occurs due to memory leak. This is why I am now trying to open memory space when closing a window by using self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True). However, now I am facing the problem that the next window doesn't show up anymore. So how can I use DeleteOnClose if I want to show a new window afterwards?
Also if anyone has a suggestion for the original problem, please let me know. I am trying to figure out the problem since like a week but have not come any further.
Thank you already!
Some part of my code to work with:
class UI_Start(QtWidgets.QMainWindow):
def __init__(self):
super(UI_Start, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('Screen_Start.ui', self) # Load the .ui file
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) # added newly
self.Start_pushButton_Start.clicked.connect(self.openKommiScreen)
def openKommiScreen(self):
self.close()
self.KommiScreen = UI_Kommi(self)
class UI_Kommi(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super(UI_Kommi, self).__init__(parent)
uic.loadUi('Screen_Kommi.ui', self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
global sheetNo
sheetNo = 1
self.WeiterButton = self.findChild(QtWidgets.QPushButton,'pushButton_Weiter')
self.WeiterButton.clicked.connect(self.openScanScreen)
self.show()
def openScanScreen(self):
self.close()
self.ScanScreen = UI_Scan(self)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = UI_Start()
window.show()
sys.exit(app.exec_())
At first I would guess it's a garbage collection problem. The only reference to your new window is stored in your previous one. Which is deleted, so there is no more reference to your window object and python may delete it automatically.
In these cases I often goes for a global variable to store the current windows references.

tkinter checkbutton not displaying correct value when class imported from other file

I have a main program which does some cool stuff and I am currently setting up a 'settings editor' to let the user change some GUI related stuff and default values. It reads values from a text file, which is read in correctly and saves them to a dictionary self.propertiesDict. Some of the options are on/off switches, so I use checkbuttons for them. What is puzzling me is following behavior: the code works perfectly fine, when I execute the settingsEditor.py (the script creating the settings window) directly. All the checkbuttons are set to active / True. However, when I include my settingsEditor in my main program and call it, it creates fine but all the checkbuttons show the wrong value: False. I read a lot of topics here to find an answer, but I think I avoided the most common errors:
I use the tk variables
tk variables are created and set prior to the buttons
variables are not only in local scope (prefixed self.)
As you can see, I tried with an IntVar and a BooleanVar, but neither is working correctly. Something else is strange, when I use ttk.checkbuttons, I get the issue described here. I use Visual Studio for debugging and I can't see any difference in the process when going trough line by line, except for the wrong display result. I am happy for any suggestion. Sorry for not providing a full MWE, I will do, if nobody can help me from this here.
settingsEditor.py
import tkinter as tk
from tkinter import ttk
...
class mySettingsEditor:
def __init__(self):
...
def createGUI(self):
# Show main options on startup on/off
self.showOptionsVar = tk.IntVar()
self.showOptionsVar.set(str2int(self.propertiesDict['showMainOptionsExpanded']))
print(self.showOptionsVar.get())
self.checkBtn1 = tk.Checkbutton(Frame, text='Main Options Section', variable=self.showOptionsVar)
self.checkBtn1.grid(column=0,row=2)
# Show main STL section on startup on/off
self.showMainSTLVar = tk.BooleanVar()
self.showMainSTLVar.set(str2bool(self.propertiesDict['showMainSTLSectionExpanded']))
print(self.showMainSTLVar.get())
self.checkBtn2 = tk.Checkbutton(Frame, text='Main STL Section', variable=self.showMainSTLVar)
self.checkBtn2.grid(column=0,row=3)
main.py
from settingsEditor import mySettingsEditor
...
settEditor = mySettingsEditor()
This is how it looks in the GUI when executed separately (terminal with print output to the left):
Thats the result when I add it in main.py. The boxes are unchecked, but .get() tells me the values are correctly assigned to the tk variables.
As suggested by jasonharper, switching to Toplevel() for the child windows fixed the issue. Thanks alot!

How do I layout, and close this dialogbox in PyQt?

I'm writing a Flash Card application using Python and PyQt and I have a problem with dialogboxes. First, here's the section of my code which concerns the aforementioned dialogbox:
def openFile(self):
dialog = QDialog()
dialog.setGeometry(200, 100, 50, 100)
list = QListWidget(dialog)
for file in files:
list.addItem(file.replace('.flashcard', ''))
list.itemClicked.connect(self.listClicked)
dialog.exec_()
def listClicked(self, item):
file_to_read = item.text() + '.flashcard'
self.flashcard_list = FlashCard.read_card(file_to_read)
self.front_text.clear()
self.back_text.clear()
self.front_text.insertPlainText(self.flashcard_list[0].front)
self.back_text.insertPlainText(self.flashcard_list[0].back)
Although I'm using PyCharm, I can't actually debug the code since deubgging these sorts of applications is beyond my skill level. That's why I don't know why when I add codes like dialog.setLayout(layout) to the script, it won't run and it doesn't produce any errors. Also, I would like to know how to close this dialogbox since if I change the dialog to self.dialog to carry it over to another function, the code won't run, again, without producing any errors.
Thanks for your help.

PyQt file browsing - setting a default option?

I've been trying to implement a file browsing widget in the GUI I'm designing. I am using the QFileDialog module, which works great - I can browse and save a file with the following line of code:
filenames = QFileDialog.getOpenFileName()
My widget is set up with a QLineEdit, which I would like to display the name of the file selected, and a QPushButton, which I would like to initiate the above line of code. However, I'd like to know if there's a way I can set a "default" option. If the browse push button is not clicked, I would like the file to be the following:
filenames = str(glob.glob('*.npy')[0])
Which would be saved as the filename in question and show up in my LineEdit. My problem is coming from trying to display a different file name in the LineEdit, depending on whether or not the browse push button has been clicked. If it has been clicked, I would like the LineEdit to show the user-selected file instead of the default option. Here are the applicable lines of code in my retranslate function:
def retranslateUi(self, ROIGUI):
self.lineEdit.setText(_translate("ROIGUI", self.fileSelect(False), None))
self.Browse.setText(_translate("ROIGUI", "Browse...", None))
self.Browse.clicked.connect(self.fileSelect(True))
Which link to the following function. As you can see, this is currently not working correctly because in the LineEdit, tripped is always False. Very silly.
def fileSelect(self,tripped):
filenames = str(glob.glob('*.npy')[0])
if tripped==True:
filenames = QFileDialog.getOpenFileName()
self.lineEdit.setText(_translate("ROIGUI", filenames, None))
return filenames
I've been trying different ways of getting this to work, but everything I tried either (a) never updates my LineEdit after file browsing, or (b) runs file browsing immediately without ever using the default option. Thoughts? I'm sure there's a way of doing this that I'm just not seeing.
Thank you in advance.
Edited To Add
I think I have fixed most of my problem - my Browse button is now connected via buttonGroup to an integer, so my fileSelect looks like this:
def fileSelect(self):
signal = self.buttonGroup2.checkedId()
if signal==-1:
filenames = str(glob.glob('*.npy')[0])
elif signal==1:
filenames = QFileDialog.getOpenFileName()
if (filenames.isNull()):
filenames = str(glob.glob('*.npy')[0])
return filenames
And my "retranslate" browse button and lineEdit look like this:
self.lineEdit.setText(_translate("ROIGUI", str(self.fileSelect()), None))
self.Browse.clicked.connect(self.fileSelect)
My only problem is getting the text of my lineEdit to update; although the file in use itself updates after it's selected with Browse, the text itself doesn't update. Help?
If the cancel button is selected from the QFileDialog filenames variable will be a null QString so, you could:
filenames = QFileDialog.getOpenFileName()
if (filenames.isNull()):
self.lineEdit.setText(_translate("ROIGUI", filenames, None))
else:
# The alternative code. Set the default value here to the QLineEdit.
Reference: QFileDialog.getOpenFileName()

Categories