At the moment I have one Floatlayout in SHeditorMain, Inside the class SHeditormain i've declared a bunch of widgets (buttons, popups, labels, etc..) and used self.add_widget to add them.
Now i want to create a new window that opens up inside/over the FloatLayout and i can't seem to get it to works. All the examples i've seen so far regarding multiple windows is either using App as main class for the creation of widgets inside the layouts. Any suggestions or do i have to restructure the code?
class SHeditorMain(FloatLayout):
def __init__(self, **kwargs):
super(SHeditorMain, self).__init__(**kwargs)as
self.add_widget(blabla)
self.add_widget(blabla)
self.add_widget(blabla)
self.add_widget(blabla)
self.dbconnection = DBconnection()
#declare popups etc
def functionEvents(self, instance):
yaddayadda
def functionEvents(self, instance):
yaddayadda
def functionEvents(self, instance):
yaddayadda
class SHeditor(App):
def build(self):
self.root = SHeditorMain()
return self.root
Please read this answer about multiple "windows" and why it may not be desired. I can recommend you to have kivy window maximized and using ModalView as a pseudo-form that can be dragged and has some button that would close it. More control over everything, cleaner code, beginner friendly. Create a custom widget that inherits from ModalView and what it does is up to you.
You can place modals wherever you want them to be - look into popup source code in repo how the positions are set. You'll basically end up with a custom Popup that you can control without crippling the original widget.
Another way may be to use ScreenManager(or PageLayout, Carousel, xyz other widgets) which provide mobile-/presentation-like view. The sooner you get to kv language, the sooner you'll have less troubles to imagine that stuff out of pocket e.g. you see a fancy app, you can open kivy-designer or for more control/less fancy go for KivyCatalog and just type what you see that's how straightforward the kvlang is.
Example with Modals:(old attempt to create windows-like... something via Kivy = FloatLayout with Scatter(only drag enabled) as "windows")
Which leads me to the conclusion that if you think you need multiple windows or another window would be nice for whatever reason but you are not sure if it'd work or something - you actually don't and you are overcomplicating things, which steal your attention from the important parts of your code to UI.
(PS, I'd edit the answer mentioned at the top, but in that question user has a little bit more complicated problem)
Related
I've been unable to successfully launch separate windows - the main window Tk, naturally, has to be the initiator of .mainloop, and originally, that was the only window in the GUI. After I completed the GUI, tested it's functionality, and was slightly proud of myself, I got all confident and decided to incorporate a user login window as well.
The program itself is just a question-and-answer quiz that randomizes a test on the 50 United State's Capital cities - an idea I'd sort of stolen from one of my Python PDF guides, however, I wrote the code independently, partly as an exercise and partly to learn hands-on for the experience. But after running into difficulties trying to get the Toplevel window to actually launch, I couldn't manage to pull it off even with every resource and guide I had for Tkinter open at the same time!
Tried wait_window... tried grab_set() and focus_set() ... and again, I know the main window has to initiate the mainloop. What in the world am I doing wrong, here?! Is it the way I have my code structured altogether?! Bad structure? Anyway, here's the code... I hope this site formats code to consolidate after a certain number of lines because it's quite long.
All that code, so little skill... LOL. Thing needs some exception handling, too... I know that. Any advise, guidance, and/or assistance towards any aspect of my code is not only welcome, it's greatly appreciated, so please, if there's something beyond this two-window dilemma I can't seem to conquer, feel free to put it on front street! Thank you in advance! :)
My Code
class MainWin(tk.Tk):
def __init__(self):
super().__init__()
self.title('Minimal Code')
class Login(tk.Toplevel):
def __init__(self, master=MainWin()):
super().__init__()
self.name = tk.StringVar()
enter code here
userlbl = tk.Label('Enter Name: ')
userent = tk.Entry(width=18, textvariable=self.name)
Ok, so for minimal coding to replicate the basic issue I'm having, please assume I've entered in the .grid() info for the label and entry widgets... ultimately, how I'd like this to work is for the login screen (Toplevel) to pop up - and if the Main window is behind it, that's no problem, just disable until the login window is closed - yet I can't, for the life of me, seem to accomplish this.
I really don't want to have to rewrite the entire thing, but I'm considering it... to see if using Canvas can more easily execute this particular action.
The order of the classes is back to front.
so the Main fails because it is initialised befor it is called:
class Login(tk.Toplevel):
def __init__(self, master=Main(), takefocus=True):
super().__init__()
I switched the order around and this error goes.
first this: class Main(tk.Tk):
then this: class Login(tk.Toplevel):
I dont know if this makes the code work, but it removes the error.
Also, when you instantiate a class it needs the brackets, so
master=Main
becomes
master=Main()
I'm writing some test functions for a form I made. There are a couple of QMessageBox that are invoked(one through QMessageBox.question method and one through the QMessageBox.information method. While my custom widget is not shown on screen, these two actually show up on screen.
I tried dismissing them by looping through widgets I get in QApplication.topLevelWidgets() and dismissing the right one, however, it seems my code only continues executing after I manually dismiss the MessageBox.
So my question is two-fold:
1) How do I keep the QMessageBox (or any widget really) from showing on screen during testing.
2) How can I programmatically accept/reject/dismiss this widget.
You can set up a timer to automatically accept the dialog. If the timeout is long, the dialog will still display for a while:
w = QtGui.QDialog(None)
t = QtCore.QTimer(None)
t.timeout.connect(w.accept)
t.start(1)
w.exec_()
For your specific case, if you don't want to touch the code being testes, you can have the timer run a function to accept all current modal widgets, as you were suggesting:
def accept_all():
for wid in app.topLevelWidgets():
if wid.__class__ == QtGui.QDialog: #or QMessageBox, etc:
wid.accept()
t = QtCore.QTimer(None)
t.timeout.connect(accept_all)
t.start(10)
I decided to use the mock module instead. It seemed better since the other solution would actually draw on screen, which is not optimal for testing.
If you have the same problem and would like to mock a question QMessageBox you can something like this:
#patch.object(path.QMessageBox, "question", return_value=QtGui.QMessageBox.Yes)
Would simulate a MessageBox in which the Yes button was clicked.
I think it makes sense with Qt testing (including PySide/PyQt) to mock your GUI interaction and do dedicated GUI testing separately as necessary.
For mocking GUI interaction, I'd use the mock library, as I myself do regularly. The drawback of this is that you have to depend on mock definitions, which may drift out of sync with respect to your production application. On the other hand, your tests will be speedier than involving the actual GUI.
For testing the GUI itself, I'd write a separate layer of tests using a GUI testing tool such as Froglogic Squish. It'll typically lead to more involved/slower tests, but you'll test your application directly, and not merely simulate the GUI layer. My approach in this regard is invest in such a tool if the budget allows, and run these tests as necessary keeping in mind they'll be relatively slow.
How I can write an OpenGl application using Enthought Framework? I created a TasksApplication and I am stuck on this tutorial, I don't know what I can use instead PythonEditor there. I need to create something where I will be able to render.
UPDATE:
I changed the code to
def create(self, parent):
widget = GLWidget(parent)
self.control = widget
Where GlWidget is implemented like in this example.
And I have a runtime crash. But I am able to run the GL script from the example above.
UPDATE2:
Log file
UPDATE4:
Code was updated according to #Robert Kern suggestions. Now it works.
Min Example
MinExample 7z
The control trait of a TaskPane is just the Qt widget object that you are using. In the example that you link, we happen to be getting it from another PyFace widget that wraps a Qt widget in a similar fashion, so we just grab the control attribute from it. You should just directly use a QGLWidget as the control of your pane. Please consult the Qt documentation for how to use it. You can use PyOpenGL in the paintGL(), etc. methods to do the actual rendering.
How to suppress end user ability to edit/add/delete text in a Text widget? (Python v3.2.. and tkinter)
The point is to suppress only the ability to change/add/delete text but not to castrate other features. Perhaps a NoEdit Text widged would be a better name.
I've tried .text['state'] = 'disabled' and it works almost OK in Windows (it still allows user to select/copy text highlights the selection, page up/down and up/down buttons work. The only thing broken seems to be the cursor made invisible.)
But on MacIntosh everything is broken. No highlights, no select/copy,... UGH
Since Tkinter has practically no documentation in Python, I've searched and found some TCL advise, to derive a new class and suppress the insert and delete functions.
So, I've tried as so:
class roText(tk.Text):
def insert(self,*args,**kwargs):
print(" Hey - Im inside roText.insert")
pass
def delete(self,*args,**twargs):
pass
def pInsert(self,*args,**twargs):
super().insert(*args,**twargs)
Unfortunately it didn't work right. Apparently tkinter does not use those insert and delete functions when end user enters/deletes code. Perhaps those TCL insert/delete are something else, and I lost something in translation from TCL and Swahili. What functions does tkinter.Text use for end user editing text? Hopefully they are not internal...
So, is there a way to modify the Text widget to suppress only end user editing?
Is there a way to do it without diving inside and overriding internal Tkinter code, so the stuff doesn't get broken by next releases of Tkinter?
Looking at the Idle shell window, I see that they've managed to suppress edits (except for the last line). So there is a way. But what is it and how costly?
Sorry for bumping an old question, but I was searching for an answer to this question also and finally found a solution. The solution I found involves overriding the key bindings when the text widget has focus and is pretty simple. Found here.
To override the bindings of a widget there is a bind function where you pass a string of what is to be overridden and the new function you want it to call.
self.txtBox.bind("<Key>", self.empty)
Somewhere else in the class you'll need to define the function to handle the event.
def empty(self, event):
return "break"
By returning the string "break" the event handler knows to stop after your function, instead of continuing with the default action.
I hope this answers your question. Cheers.
The reason the disabled state doesn't seem to work on the Mac is because it turns off the binding that gives focus to the widget. Without focus, the highlighting on a Mac doesn't show up. If you set the state to disabled but then assign a binding to <ButtonPress-1> to explicitly set focus to the disabled text widget, you can then select and copy text and the highlighting will show.
As for the cursor disappearing... arguably, that's what's supposed to happen. The cursor tells the user "this is where text will get inserted". Since no text will get inserted, having that visual clue would be confusing to the user. What you could do instead, if it was really important, is to insert a small image wherever they click to simulate the cursor.
To answer your question about whether the widget actually uses the insert and delete methods: the methods on the actual underlying widget are what the default bindings use, so overriding them in a subclass has no effect. You would need to redo all the default bindings for that to work. It's doable, but a lot of work.
Unfortunately, this is one area where programming in Tcl really shines, because you can simply disable the insert and delete commands of the widget. Of course, you can do that directly in Tkinter also since ultimately it runs tcl code to do everything, but that would involve writing some tcl code which is not a very good solution from the perspective of a Python coder.
I think the best solution is to use the disabled state, then add in just enough bindings to do what you want.
Here's a simple example that works by explicitly setting focus on a mouse button click. With this code I'm able to click and swipe to select a region, or double- or triple-click to select words and lines:
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.text = tk.Text(width=40, height=20)
self.text.bind("<1>", self.set_focus)
self.text.insert("end", "\n".join(dir(tk.Tk)))
self.text.configure(state="disabled")
self.text.pack(fill="both", expand=True)
def set_focus(self, event):
'''Explicitly set focus, so user can select and copy text'''
self.text.focus_set()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
#BryanOakley It took me a while to test your suggestion since I have no Mac.
Unfortunately Mac implementation of Python is buggy.
I've added focus, ie my disable function which I call after creating a window and inserting text, now calls first:
self.txt['state'] = 'disabled'
and then
self.txt.focus_set()
Which is what I think you've suggested.
It "kind of" worked. Ie: when selecting text (click and drag or double-click) highlighting works most of the time. Python must have some bad memory references or such bugs: Sometimes highlighting doesn't work at first, then it starts working (in the same window) after more clicking. Sometimes when program is invoked it works right of the bat. Sometimes selecting with Shift-rightArrow key will work but selecting with the mouse will not. Then starts working again. Or it will work fine in one window but not in another one (both of the same class), then starts working in all windows...etc...
The good thing is that adding focus did not affect badly Windows (ie all works fine as without focus.
I guess at this point I will just hope that future/next release of Python for Mac will fix those bugs..
BTW, it seems that Mac is a bit of an orphan for Python. Implementation is much uglier then for Windows. I mean the fonts look worse, the buttons, etc.. Or it could be due to different screen resolutions and Python ports that poorly account for those. Not sure
Anyway. Thank you for your help and suggestion to use focus for Mac.
I am writing a simple application and am using glade (gtk) for the UI. I need many windows (~10), of which one will open depending upon the command line flags, other contextual stuff etc.
Now, all these windows are pretty much similar, they have 3 top level tabs, the last tab is the same in all, all have a OK and Quit button etc., so I am looking for a way to build these windows in glade. I could copy paste one window and make the changes in that, but I am looking for a better way, that will allow me to reuse the common parts of the windows.
Also, I am using pygtk for loading up the windows.
Design a widget with the common aspects you mention. Wherever you need to implement something different, put a GtkAlignment with an appropriate name. Don't forget to change the alignment and fill values of the GtkAlignment.
In PyGTK you can gtk.Builder.get_object(name) to get access to these empty regions and add the extra components within them (which can also be designed with Glade).
Ok, with the help of detly's answer, I am able to get something working. For anyone who needs it, here is what I did.
main.glade contains the window and all the common cruft that I need to be displayed in all windows. comp.glade contains a window, with a vbox component with the extra stuff I need, lets call it 'top_comp'.
Now, in main.glade, I put a gtk.Alignment component in the place where I need the extra component to load, and call it, say, 'comp_holder'. With the builder I have, I do
builder = gtk.Builder()
builder.add_from_file('main.glade'))
builder.add_from_file('comp.glade'))
builder.get_object('top_comp').reparent(builder.get_object('comp_holder'))
This method seems to work for now, but I don't know if it is the correct way to do this thing.
Any suggestions for the above welcome.