how can I change the foregroundcolor of a disabled TextCtrl from wxPython?
I mean, when I change the color with SetForegroundColour it only changes for enabled status. When I disable the TextCtrl, it remains dark grey even if I set it red, for example.
Thanks in advance!
import wx
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(*args, **kwargs)
self.InitUI()
self.Fit()
self.Show(True)
def InitUI(self):
text = wx.TextCtrl(self)
text.SetForegroundColour((255,0,0))
text.SetValue('Example')
text.Enable(False)
def main():
app = wx.App()
MainFrame(None)
app.MainLoop()
if __name__ == '__main__':
main()
The short answer is that you can't.
The background and foreground colours are overridden by the fact that you have disabled them. Your operating system environment determines how the disabled items will look.
Of course that doesn't mean that you can't get around the issue.
If instead of disabling/enabling the item, you set a True/False flag instead, you can then check that flag when the event is triggered and depending on whether the flag is True or False, you process the event or not.
This allows you to process events and display whatever colours that you like.
Related
This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 2 months ago.
Writing my first TKInter application that was given to me by a designer. Lots of images and event actions.
TKInter is pretty slick and I like it a lot! But certain things are giving me fits, and it feels like I am missing something going on in the background, or that Windows+TKInter is finicky
As an example, if I have a basic file like app.py:
app = MyApp()
app.mainloop()
and another file controller.py
class MyApp(tk.Tk):
"""Application root window and Controller for all event handling """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
bg = Image.open("./images/my_bg.png")
bg = bg.resize((1024,600))
bg_img = ImageTk.PhotoImage(bg)
bg_label = ttk.Label(self, image=bg_img, background='black')
bg_label.place(x=0, y=0)
The image is not rendered. Even if I move it to its own function and invoke that after MyApp is instantiated, I get nothing.
Things that I store in MyApp, like StringVars, work just fine and I can update those from MyApp based on an event and it's reflected in the GUI. But I've figured out that images and the Canvas/plots will not update.
I got by with jamming all UI generation in the main file, but I need to update the plot on the fly based on user input and it's just not happening.
This also fails to render the background image:
app.py:
app = MyApp()
app.setBgImage()
app.mainloop()
controller.py
class MyApp(tk.Tk):
"""Application root window and Controller for all event handling """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def setBgImage(self):
bg = Image.open("./images/my_bg.png")
bg = bg.resize((1024,600))
bg_img = ImageTk.PhotoImage(bg)
bg_label = ttk.Label(self, image=bg_img, background='black')
bg_label.place(x=0, y=0)
Anyone with experience on what's going on behind the scenes with TKInter to tell me what's going on?
My next step is to try it on a Linux OS and see if maybe Windows+TKInter has issues.
Any suggestions are much appreciated.
Try using pack instead of place for the label that contains the photo, then set it to fill the available space
bg_label.pack(expand=True, fill=tk.BOTH) # 'BOTH' means fill both 'x' and 'y' axes
How do I alter my code to show Loading in the center of MainWindow relative to wherever MainWindow is at on the screen?
What it is doing now is placing the Loading animation on the top left corner, ignoring the position of the MainWindow.
Even if I add geomtery to the MainWindow, the result will be the same.
Here is the code, both of the classes has been imported from a py.file:
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.loading = Loading(parent=self)
self.ui.check.clicked.connect(self.show_animation)
self.show()
def show_animation(self):
self.loading.show()
class Loading(QWidget):
def __init__(self, parent=None):
self.parent = parent
QWidget.__init__(self)
self.ui = Ui_loading()
self.ui.setupUi(self)
self.setWindowModality(QtCore.Qt.ApplicationModal)
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.label_animation = QLabel(self)
self.label_animation.setGeometry(QtCore.QRect(25, 25, 256, 256))
self.movie = QMovie('giphy1.gif')
self.label_animation.setMovie(self.movie)
self.movie.start()
geo = self.geometry()
geo.moveCenter(self.parent.geometry().center())
self.setGeometry(geo)
Your code doesn't work because you're already setting the geometry when you create the Loading instance, and that's too soon: not only when you click the button it's possible that the parent window has been moved or resized, but you're creating the instance when it's not been even shown yet, so it still has a default geometry (usually 640x480 unless it has minimum or maximum size constraints based on its contents).
In order to center the widget on the parent, you have to consider the geometries when it is going to be shown.
class Loading(QWidget):
# ...
def showEvent(self, event):
if not event.spontaneous():
geo = self.geometry()
geo.moveCenter(self.parent.geometry().center())
QtCore.QTimer.singleShot(0, lambda: self.setGeometry(geo))
The check on event.spontaneous() is done in order to only move the window when it's explicitly shown (using show() or setVisible(True)), otherwise it would happen in any case, for example when restoring the window after it's been minimized.
The delayed setGeometry() is required because in certain cases (most commonly, on Linux) some amount of time is required between the request Qt makes to the underlying window system and the moment the window is actually mapped on the screen.
I want a black window to fade in. When in fullscreen, it perfectly works but I need a specific size and there when its opened, it first appears black before it becomes transparent and the fading starts. Do you have any ideas how to achieve the same smooth effect as for the fullscreen version?
import tkinter as tk
class Fader(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.parent.attributes("-alpha",0.0)
#self.parent.attributes("-fullscreen",True)
self.parent.geometry("600x800")
self.configure(bg='black')
self.fade_in()
def fade_in(self):
alpha = self.parent.attributes("-alpha")
if alpha < 1:
alpha += .01
self.parent.attributes("-alpha", alpha)
self.after(100, self.fade_in)
if __name__ == "__main__":
root = tk.Tk()
root.bind("<Escape>",lambda e: root.destroy())
Fader(root).pack(fill="both", expand=True)
root.mainloop()
You can use withdraw() to hide the window and deiconify() to show to window later on and increase the alpha. But it seems to not work unless you update the tasks or wait for the window to be visible.
Method 1:
Was able to fix this by using update_idletasks(), like:
class Fader(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.parent.attributes('-alpha',0.0)
self.parent.withdraw() #hiding the window
#self.parent.attributes("-fullscreen",True)
self.parent.update_idletasks()
self.parent.geometry("600x800")
self.configure(bg='black')
self.fade_in()
def fade_in(self):
self.parent.deiconify() #bringing it back
..... #same code
Method 2:
Or like said by acw1668, you can use wait_visibility(), like:
class Fader(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.parent.wait_visibility(self.parent)
self.parent.attributes('-alpha',0.0)
self.parent.withdraw()
# self.parent.attributes("-fullscreen",True)
self.parent.geometry("600x800")
self.configure(bg='black')
self.fade_in()
def fade_in(self):
self.parent.deiconify()
...... #same code
A bit more about wait_visibility():
wait_visibility(window=None)
Wait for the given widget to become visible. This is typically used to wait until a new toplevel window appears on the screen. Like wait_variable, this method enters a local event loop, so other parts of the application will still work as usual.
A bit more about update_idletasks():
update_idletasks()
Calls all pending idle tasks, without processing any other events. This can be used to carry out geometry management and redraw widgets if necessary, without calling any callbacks.
Source :- https://effbot.org/tkinterbook/widget.htm
I am making a GUI that had the Welcome page and the main page. The purpose is to let user agree on the welcome page, the welcome page is dismissed and the main page will show up for further step. However, the icon in the taskbar only shows up in the welcome page, when we click into the main window the icon is disappeared and the app appeared to be a minimized window on the bottom left corner in the screen.
The starting page and main window layout is appear like this.
class welcome_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(welcome_window, self).__init__(parent)
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
Main_layout = QtWidgets.QHBoxLayout()
Main_layout.addWidget(self.confirm_button)
self.main.setLayout(Main_layout)
def startup(self):
self.close()
dialog = Main_window(self)
self.dialogs.append(dialog)
dialog.show()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
def main():
app = QtWidgets.QApplication(sys.argv)
main = welcome_window()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I expected that if the icon located in the taskbar could always stay on, it would be great for my GUI. Thank you all.
First of all, the MRE you gave is not reproducible. When I tried to run it it just didn't work. In this case you had a simple issue so I could just guess what was intended, but when you get more complicated problems people might not be able to help you. So in the future please make sure that we can just copy-paste-execute your code.
The reason that the main window disappears is that it's a member of the Welcome window. When you close the Welcome window, the corresponding python object will deleted and therefore Python will no longer have a reference to the main window. The main window object will be garbage-collected and all kinds of strange things might happen (I would expect it to just disappear).
The solution is to have a reference to the main window that stays valid until the program closes. This can be done by defining it in the main function (and then giving it as a parameter to the Welcome window). Like this...
import sys
from PyQt5 import QtWidgets
# Use a QWidget if you don't need toolbars.
class welcome_window(QtWidgets.QWidget):
def __init__(self, main_window=None, parent = None):
super(welcome_window, self).__init__(parent)
self.main_window = main_window
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
main_layout = QtWidgets.QHBoxLayout() # use lower case for variable names
main_layout.addWidget(self.confirm_button)
self.setLayout(main_layout)
def startup(self):
self.main_window.show()
self.close()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
# Don't use self.setLayout on a QMainWindow,
# use a central widget and set a layout on that.
self.main_widget = QtWidgets.QWidget()
self.setCentralWidget(self.main_widget)
main_layout = QtWidgets.QHBoxLayout()
self.main_widget.setLayout(main_layout)
main_layout.addWidget(QtWidgets.QLabel("Hello"))
def main():
app = QtWidgets.QApplication(sys.argv)
main = Main_window()
welcome = welcome_window(main_window=main)
welcome.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Some more tips. Don't use setLayout on a QMainWindow. Use a central widget and add your widgets to the layout of the central widget. The layout of the main window is for toolbars and such. See: https://doc.qt.io/qt-5/qmainwindow.html#qt-main-window-framework
Just use a QWidget if you want a simple window without toolbars (like your welcome window),
Best to use lower case for variable names and upper case for class names. E.g. I renamed Main_layout to main_layout. Look at the difference in syntax highlighting by Stack Overflow above.
This question already has an answer here:
how to accept/ignore QKeyEvent
(1 answer)
Closed 3 years ago.
I have two PyQt5 widgets and both need keyboard input. Initially, one widget has the setFocusPolicy set to QtCore.Qt.StrongFocus, but when both widgets has this property activated, both of them get the input. I would like to initially set the input in one of them and if the user clicks in the other widget, the focus would be changed to the clicked widget or vice versa.
MRE:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtWidgets import QOpenGLWidget, QWidget
import sys
class Renderizador(QOpenGLWidget):
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_W:
print("OpenGL")
super().keyPressEvent(event)
class Diedrico(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
def paintEvent(self, event):
qp = QPainter(self)
qp.setPen(QPen(Qt.black))
qp.drawRect(0, 0, 1000, 1000) # Marco
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_W:
print("Widget")
super().keyPressEvent(event)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(1500, 1015)
self.setFixedSize(1500, 1015)
self.statusBar().setSizeGripEnabled(False)
self.widget_central = QtWidgets.QWidget(self)
self.Renderizador = Renderizador(self.widget_central)
self.Renderizador.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
self.Renderizador.setFocusPolicy(QtCore.Qt.StrongFocus)
visor = QtWidgets.QWidget(self.widget_central)
visor.setGeometry(1010, 510, 470, 460)
self.scene = QtWidgets.QGraphicsScene(visor)
self.view = QtWidgets.QGraphicsView(self.scene)
self.diedrico = Diedrico(visor)
self.diedrico.setFixedSize(470, 460)
self.view.setFocusPolicy(QtCore.Qt.StrongFocus)
self.scene.addWidget(self.diedrico)
self.setCentralWidget(self.widget_central)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_W:
print("Ui")
super().keyPressEvent(event)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
Based on your code, I'm not getting key events from the Diedrico widget, but only from the Renderizador and UiVentana instancies; with your implementation it seems very unlikely that you get key events from both the Renderizador and Diedrico widgets, since their parent hierarchy is completely different and they actually are members of different "physical" windows.
If you really get the Widget and OpenGL outputs from a single key event, it might be a bug, but, frankly, I doubt that, and that's mostly because there are some issues in your implementation, mostly due to confusing parent/child relationship.
If that's not the case (meaning that you're getting the key events from Renderizador and UiVentana), the explanation is simple: a QOpenGLWidget, as any basic QWidget class, doesn't process nor "consume" a key event; as soon as you call the base implementation with super() the event is propagated to its parents (UiVentana, in your case). If you want to stop the event propagation, just return without calling the base implementation of keyPressEvent.
Finally, some notes about your example.
When you want to add a widget to a scene, it must have a parent that is already embedded in the scene or it shouldn't have a parent at all (as in a top level widget). In your code you created the Diedrico widget setting its parent to a child of the "widget_central", which at that point is a widget without no parent (meaning that it would be a top level widget, as in a "window"): no matter what you do afterwards (setting it as the central widget), the topmost parent has not been embedded, and you can't add any of its child to a scene.
Qt itself warns about this when executing your code:
StdErr: QGraphicsProxyWidget::setWidget: cannot embed widget 0x911ae78 which is not a toplevel widget, and is not a child of an embedded widget
Then, you created the view, but you never actually add it to the main window or any of its children. You can see the Diedrico instance only because of the aforementioned problem: the widget is added to the main widget because of the parent set in the class initialization, but it's never added to the scene.
Be very careful when initializing widgets and setting parents, expecially when you're going to embed them into a QGraphicsScene: QWidgets added to a scene are actually QGraphicsProxyWidgets, not "actual" widgets, and some special care is required when dealing with them.