Tkinter - Multiple windows opening - python-2.7

Like the title says I'm having a problem with multiple windows being able to open. New window (same window) every time I click the button. I haven't been able to find an answer to this specific scenario. I've even tried disabling the button after opening window (which led to the problem of re-enabling).
Below is enough code to work with, you can see the problem by clicking the button a few times. I am hoping for a somewhat easy solution as I am fairly new to Tkinter. Also, the smaller window needs to resize (expand) with the main window, so I dont think a Toplevel window would work.
There may be others having this same problem, and thanks in advance!
Note: I'm using Python 2.7 (Tkinter)
#!/usr/bin/python
import os
import sys
import Tkinter as tk
from Tkinter import *
from ScrolledText import *
import tkFileDialog
import tkMessageBox
# Main
root = tk.Tk(className = "tex")
root.geometry("500x300")
root.title("tex")
tex = ScrolledText(root, padx=2, pady=2, undo=True, font=('Arial 11'))
def note_area():
btn_frame = Frame()
note = LabelFrame(tex, bd=1, relief='ridge')
tx = Text(note, width=18, relief='flat', padx=2, pady=2)
tx.insert('1.0', "Notes..")
tx.pack(side='top', fill=BOTH, expand=True)
note.pack(side='right', fill=Y)
btn_frame.pack(side='bottom', fill=Y)
# ToolBar Button (should only open one instance of note_area)
toolbar = Frame(root, bd=2, relief='groove')
b4 = Button(toolbar, text="Notes", width=4, command=note_area)
b4.pack(side=RIGHT, padx=4, pady=2)
toolbar.pack(side=TOP, fill=X)
tex.pack(fill="both", expand=True)
root.mainloop()

You just need make up a variable to keep track of if you have a note window open or not.
tex.notes_open = False
def note_area():
if tex.notes_open:
return # abort the function, notes already open
else:
tex.notes_open = True # set the flag for the next time
# rest of your code

Related

PYQT5 connect two QSpinBox

i want know how to connect two QSpinBox with condition when we change the value of one of them the second one changed
i tried this using Qt designer
self.spinA.valueChanged['int'].connect(self.spinB.setValue)
the value is always the same ; i tried to connect label to spinA and use its value to get new value for spinB but i don't know how to do the same to change spinA value based on spinB value
and sorry for my english ; i can explain better with my native language
Add action to the first spin box for every value changed in the spin box, inside the action change the value of second spin box according to the relation between the values, do same for the second spin box as well below is the sample code.
importing libraries
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
# setting title
self.setWindowTitle("Python ")
# setting geometry
self.setGeometry(100, 100, 600, 400)
# calling method
self.UiComponents()
# showing all the widgets
self.show()
# method for widgets
def UiComponents(self):
# creating spin box
self.spin1 = QSpinBox(self)
# setting geometry to spin box
self.spin1.setGeometry(100, 100, 150, 40)
# setting prefix to spin
self.spin1.setPrefix("Width : ")
# add action to this spin box
self.spin1.valueChanged.connect(self.action_spin1)
# creating another spin box
self.spin2 = QSpinBox(self)
# setting geometry to spin box
self.spin2.setGeometry(300, 100, 150, 40)
# setting prefix to spin box
self.spin2.setPrefix("Height : ")
# add action to this spin box
self.spin2.valueChanged.connect(self.action_spin2)
# method called after editing finished
def action_spin1(self):
# getting current value of spin box
current = self.spin1.value()
self.spin2.setValue(current)
# method called after editing finished
def action_spin2(self):
# getting current value of spin box
current = self.spin2.value()
self.spin1.setValue(current)
create pyqt5 app
App = QApplication(sys.argv)
create the instance of our Window
window = Window()
start the app
sys.exit(App.exec())

Tkinter: Text in Frame in Nootbook tab not painted

OS is Win7 64bit, Python is 2.7.16 64bit. I have a simple Tkinter GUI: Root containing a Notebook with two tabs. First tab contains a Frame which contains a Button. Second tab contains a Frame which contains a Text. The command bound to the Button spawns a thread with sets the content of the Text.
import Tkinter
import ttk
import threading
r = Tkinter.Tk()
n = ttk.Notebook(r)
n.pack(expand=1, fill="both")
control = ttk.Frame(n)
info = ttk.Frame(n)
tInfo = Tkinter.Text(info)
tInfo.pack(expand=1, fill="both")
n.add(control, text='Control')
n.add(info, text='Info')
infoMutex = threading.Lock()
def doGuiTest():
try:
infoMutex.acquire()
tInfo.config(state='normal')
tInfo.delete('1.0', Tkinter.END)
tInfo.insert(Tkinter.END, 'Test')
tInfo.config(state='disabled')
finally:
infoMutex.release()
def workerThread():
doGuiTest()
def execute():
worker=threading.Thread(target=workerThread)
worker.start()
bExecute=Tkinter.Button(control, text='Execute', command=execute)
bExecute.pack()
r.mainloop()
Expected result: The Text is reliably visible with the set content after the Button is clicked.
Actual result: The Text is only visible when the tab containing the Text has been manually brought to the foreground before the Button is clicked.
When I set the content of the Text directly from the Button's command everything works as expected. Sadly, in the real application I am working on the functionality triggered by the Button will be running for several minutes so using another thread is a must.
What am I missing to achieve a consistent behavior?

How to place a button on the TopLevel window in tkinter python?

Answer as pointed out down for other innocent newbies like me
in python2, we have Tkinter
In python3, we have tkinter.
Notice the difference in casing. Thats the reason, why error was coming up.
I have two screens: window(child) and root(master)
I am trying to place a button on the "window" screen created by the method: command(). I have written this piece of code.
from tkinter import *
root = Tk()
def writeText():
print "hello"
def command():
window=Toplevel(root)
Button(window,text="Button2",command=writeText).grid()
Label(window,text="hello").grid()
button = Button(root, text="New Window", command=command)
button.grid()
root.mainloop()
But this button2 is not appearing on the second screen. Meanwhile, Label is appearing on this screen. And the control is going into the writeText() function.
And when I remove the command argument from the Button of window screen, the button appears.
Can anyone please help me with this?
Here is my suggestion.
From your question you have put from tkinter import * however in your tags you put Python 2.7. This is contradictory because tkinter(all lower case) is used in Python 3.x and Tkinter should instead be used for python 2.x. That said try fixing your import first. If you are in fact using Python 3 then you will need to correct your print statement to include brackets. print("hello")
2nd I would try to follow PEP8 more closely however in this case I don't see anything out of the ordinary that would cause this issue.
Take my below example and let me know if you are still having the same problem.
Python 2.x example:
import Tkinter as tk # Upper case T in Tkinter for Python 2.x
root = tk.Tk()
def write_text():
print "hello"
def command():
window = tk.Toplevel(root)
tk.Button(window,text="Button2",command=write_text).grid()
tk.Label(window,text="hello").grid()
button = tk.Button(root, text="New Window", command=command)
button.grid()
root.mainloop()
Python 3.x example:
import tkinter as tk # all lowercase tkinter for Python 3.x
root = tk.Tk()
def write_text():
print("hello") # Python 3.x requires brackets for print statements.
def command():
window = tk.Toplevel(root)
tk.Button(window,text="Button2",command=write_text).grid()
tk.Label(window,text="hello").grid()
button = tk.Button(root, text="New Window", command=command)
button.grid()
root.mainloop()
If you are still having problems can you tell me if you are using Windows, Linux or Mac?
Have you guys ever tried button with image on Toplevel? It seems it cannot be work with code below on Toplevel(Prompt out windows). Root level is ok.
tp = Toplevel()
tp.geometry("400x400")
btnphotoAdd=PhotoImage(file="32adduser.png")
btnAdd = Button(tp, text="Add User", font="Helvetica 20 bold", image=btnphotoAdd,compound=TOP)
btnAdd.grid(row=10, column=0, sticky=W)

Why does Python crash when my PyQt window becomes inactive/is resized?

While playing around with different ways to show a webcam feed (obtained using imageio/ffmpeg) in a PyQt4 window, I stumbled upon this answer. After implementing this in Python 2.7 as an ImageDisplayWidget class (as summarized below), everything seems to work just fine: A window opens, showing my webcam feed without a glitch. If I close the window, everything is stopped and closed neatly.
But... Whenever I click anywhere outside this PyQt window (while it is showing the webcam feed), causing it to lose focus, Python.exe crashes with an unhandled win32 exception. The same happens when I try to resize the window.
I am probably making some kind of exceedingly silly beginner's mistake, but I just don't see it. Can anyone point me in the right direction? Am I breaking some basic rule of (Py)Qt or even Python?
Here's a minimal example:
import sys
import numpy
from PIL import Image, ImageQt # pillow
from PyQt4 import QtGui, QtCore
class DummyVideoGrabber(QtCore.QTimer):
signal_image_available = QtCore.pyqtSignal(QtGui.QImage)
def __init__(self):
super(DummyVideoGrabber, self).__init__()
self.timeout.connect(self.update_image)
self.counter = 0
def update_image(self):
# Dummy rgb image (in reality we get a numpy array from imageio's Reader)
self.counter += 1
numpy_image = numpy.zeros(shape=(480, 640, 3), dtype=numpy.int8)
numpy_image[:, :, self.counter%3] = 255
qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
# Emit image
self.signal_image_available.emit(qt_image)
class ImageDisplayWidget(QtGui.QWidget):
"""
Custom widget that displays an image using QPainter.
Mostly copied from: https://stackoverflow.com/a/22355028/4720018
"""
def __init__(self, size_wxh=None, parent=None):
super(ImageDisplayWidget, self).__init__(parent)
self.image = QtGui.QImage()
def set_image(self, qimage, resize_window=False):
self.image = qimage
self.repaint()
def paintEvent(self, QPaintEvent):
if not self.image:
return
painter = QtGui.QPainter(self)
painter.drawImage(self.rect(), self.image, self.image.rect())
app = QtGui.QApplication(sys.argv)
# instantiate a display object
display = ImageDisplayWidget()
display.resize(640, 480)
display.show()
# instantiate a grabber object
grabber = DummyVideoGrabber()
grabber.signal_image_available.connect(display.set_image)
grabber.start(100) # timer interval in ms
# start the event loop
app.exec_()
I found that the crash can be prevented by adding a wasActiveWindow flag (initialized to True in the constructor) and encapsulating the drawImage() call in some logic like so:
if self.isActiveWindow():
if self.wasActiveWindow:
painter.drawImage(self.rect(), self.image, self.image.rect())
self.wasActiveWindow = True
else:
self.wasActiveWindow = False
However, resizing the window still crashes python.
Problem solved by keeping a reference to the qt_image as self.qt_image:
...
# Emit image
self.qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
self.signal_image_available.emit(self.qt_image)
...
This way it works as it should. Don't need the self.wasActiveWindow workaround anymore.
Still not sure why not keeping a reference would lead to a low-level python crash though...

Fill a drop down menu with serial ports and saving the choosed label and using it later with Tkinter

I'm trying to do a GUI with Tkinter (python 2.7) that finds what serial COM's are in use and then the user can choose one to communicate with, much like Arduino's IDE in "Tools -> Port". I have two main problems: 1º When I insert the COM ports in the drop down menu, no matter which label I choose, always returns the higher COM number one. I've got an idea why but still can't solve it. 2º I don't know how to save the chosen label and use it later.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
from Tkinter import *
import tkMessageBox
from serial import *
import serial
def find_ports(): #find all active COM's
active_ports = []
for number in range(10):
try:
verify = serial.Serial('COM'+str(number))
active_ports.append((number, verify.portstr))
verify.close()
except serial.SerialException:
pass
return active_ports
def chooseCom(index): #choose COM by clicking on a label
choosedPort = portMenu.entrycget(index, "label")
print choosedPort
pass
numPorts = find_ports()
root = Tk()
# -------------------------- Main Frames --------------------------
toolbar = Frame(root)
data = Frame(root)
# -------------------------- Menu --------------------------
menu = Menu()
root.config(menu = menu)
subMenu = Menu(menu, tearoff = 0)
menu.add_cascade(label="Ports", menu = subMenu)
portMenu = Menu(subMenu, tearoff = 0)
for i,item in enumerate(numPorts):
portMenu.add_command(label=str(item[-1]), command = lambda: chooseCom(i))
serialPort = someVAr # someVar => Store the choosed label
baudRate = 9600
ser = Serial(serialPort , baudRate, timeout=0, writeTimeout=0)
subMenu.add_cascade(label="Ports", menu = portMenu)
root.mainloop()
For the 1º problem, I think that is the 'i' variable that stays with a high number by the end of the 'for'. The following code works instead of the 'for' loop:
portMenu.add_command(label="COM1", command = lambda: chooseCom(0))
portMenu.add_command(label="COM2", command = lambda: chooseCom(1))
unfortunately it doesn't work for me because I'll run this in different computers and I can't garantee that the COM ports will be the same.
The second problem is that I want to save the chosen label in some variable (someVar) and use it later to configure my Serial connection in:
serialPort = someVAr # someVar => Store the choosed label
baudRate = 9600
ser = Serial(serialPort , baudRate, timeout=0, writeTimeout=0)
Thank you.
Use partial instead of lambda (it's only there for backward compatibility).
from functools import partial
portMenu.add_command(label=str(item[-1]), command=partial(chooseCom, i))
Also, it is good to get in the habit of not using single letters than can look like numbers, i, l, O.
I would Strongly Suggest that you learn classes before going any further as it eliminates a lot of problems. Use a data attribute to store it, so it is visible anywhere within the class http://www.diveintopython.net/object_oriented_framework/userdict.html#fileinfo.userdict.init.example
import sys
if sys.version_info[0] < 3:
import Tkinter as tk ## Python 2.x
else:
import tkinter as tk ## Python 3.x
class StoreEntry():
def __init__(self, root):
""" shows how to store something in a variable, in this case
something entered in an Entry, and then print the stored
value some time later, in this case when the Exit Button
is pressed
"""
self.root=root
self.entered="**" #default=nothing entered
tk.Label(root, text="Enter Something").grid(row=0, column=0)
self.entry_1 = tk.Entry(root, width=10, bg="lightblue")
self.entry_1.grid(row=0, column=1, sticky="W")
tk.Button(root, text="return text entered", bg="lightgreen",
command=self.print_entry).grid(row=2, column=0)
tk.Button(root, text="Exit", bg="orange",
command=self.exit_this).grid(row=3, column=0)
def exit_this(self):
print "last text entered was", self.entered
self.root.quit()
def print_entry(self):
## store in a data attribute
self.entered=self.entry_1.get()
print self.entered
root=tk.Tk()
SE=StoreEntry(root)
root.mainloop()