How to find out which randomly created Button has been pressed - python-3.x

I created a working sign-out script using Python3 / tkinter. To finish it, I am trying to transfer the name of a button to a new window, then to a txt file.
In my sign-out program, when the person clicks on a category button, it opens a corresponding items.txt file and then creates a new window with a button for each item in the file. When one of the item button is clicked, a sign-out window is created with an Entry box for their name and a ‘Submit’ button. When the submit button is pressed, I want it to write the item name, person’s name, date, and time to a sign-out text file.
The answers I have found so far talk about a field of buttons that are created (like in Minesweeper).
Since these are no static buttons, I don’t know how to determine which button was pressed and what item name was assigned to that button. The names and the number of items in the file changes.
I tried to make the ‘name’ global in the button_click_pk section but it only returns the name of the last button created. I don’t know if it’s related, but it incorrectly puts the name on a different line.
Item Five
Jane Smith 2016-12-19 10:30:53
I want it to show on one line:
Item Five Jane Smith 2016-12-12 13:30:53
How do you tie a name to a button created in a for loop from a txt file?
class Writefiles:
def __init__(self):
global win3
win3 = Tk()
self.VarEnt = StringVar()
self.lab = Label(win3, text = "Signature")
self.lab.grid(padx = 10, pady = 10)
self.ent = Entry(win3, font = ('Lucida Handwriting', 10), textvariable = self.VarEnt, bd = 5, width = 45)
self.ent.focus()
self.ent.grid(padx = 10, pady = 10)
self.btn = Button(win3, text = 'Submit', width = 10, height = 2, background = 'gold', command = self.write_to_file)
self.btn.grid(padx = 10, pady = 10)
def write_to_file(self):
date = datetime.now().strftime(' %Y-%m-%d %H:%M:%S')
with open('sig.txt', 'a') as f:
f.write(name + self.ent.get() + date + '\n')
f.close()
win3.destroy()
def button_click_pk(): # creates the buttons from text file
global name
win2 = Tk()
file = open('item.txt', 'r')
rw = 0
cl = 0
for name in file:
b1 = Button(win2, text = name, wraplength = 100, width = 18, height = 5, background = 'gold', command = Writefiles)
b1.grid(row = rw, column = cl, padx = 11, pady = 13)
cl += 1
if cl == 4:
cl = 0
rw += 1

After 2 days and MANY errors, I finally figured out that I had to make 2 lambdas. You need one in the button_click_pk function to pass it to the Class, and a second in the Class to pass it to the write_to_file function. A hint that I would need to make 2 lambdas would have made it much easier, it’s not something that is explained in any of the lambda documentation I found.

Related

Python Print to String

I have some code that works perfectly, but am now trying to move it into a GUI. The issue so far is that I'm using pygame, and I have so far only managed to show text from a single assigned string.
# --- calculate all variables
for i in range(0, num_rows):
ticker['ticker{0}'.format(i+1)] = df['Ticker'][i] #could also use 'ticker'+str(i) instead of .format
tickerci['tickerci{0}'.format(i+1)] = df['Carbon Intensity'][i]
tickerobject['tickerobject{0}'.format(i+1)] = yf.Ticker(ticker['ticker{0}'.format(i+1)])
tickervalue['tickervalue{0}'.format(i+1)] = (tickerobject['tickerobject{0}'.format(i+1)]).info['marketCap']
tickerprice['tickerprice{0}'.format(i+1)] = (tickerobject['tickerobject{0}'.format(i+1)]).info['regularMarketPrice']
socialcost['socialcost{0}'.format(i+1)] = round((tickervalue['tickervalue{0}'.format(i+1)])*(tickerci['tickerci{0}'.format(i+1)])*carbonprice)
print ("the current price of ", ticker['ticker'+str(i+1)]," is €", tickerprice['tickerprice'+str(i+1)], ".")
print ("the current market cap of ", ticker['ticker'+str(i+1)]," is €", tickervalue['tickervalue'+str(i+1)], ".")
print ("the current social cost of ", ticker['ticker'+str(i+1)],"'s CO2 emissions is €", socialcost['socialcost'+str(i+1)])
print ()
# --- main ---
pygame.init()
screen = pygame.display.set_mode((960, 720))#, pygame.FULLSCREEN, vsync=1)
screen_rect = screen.get_rect()
font = pygame.font.SysFont(None, 50)
description = "put the text to display here"
desc_text = font.render(description, True, (255, 255, 255))
desc_rect = desc_text.get_rect(left=screen_rect.right)
Basically I want to assign the outputs of the print command in the loop to a string (or a string dictionary), which can then be called in the "description" part of the pygame module.
The end goal here is to have the strings that are currently printed, shown scrolling endlessly like a marquee.
There should be some way of modifying this basic bit of string io to work, I think, but I haven't been able to figure it out yet.
import io
def print_to_string(*args, **kwargs):
output = io.StringIO()
print(*args, file=output, **kwargs)
contents = output.getvalue()
output.close()
return contents

How to get the the vector of mouse position in python ursina

I'm making a Platform-Maker and I need to get the position of the mouse, then I place a object in there. Then I need to store it into a array so I can convert it to code later on. Right now I have to store it into a array, but when I do that it Gives me an empty array:
Edit On Image: This array is suppose to be containing A Vector3 Position where the mouse was clicked.
from ursina import *
from ursina.prefabs.sky import Sky
from ursina.shaders import basic_lighting_shader
app = Ursina(borderless = False)
window.fullscreen = True
window.fps_counter.enabled = False
editor = EditorCamera()
camera.fov = 90
class StartPlatform(Entity):
def __init__(self):
super().__init__(
parent = scene,
model = "cube",
position = (0, 0, 0),
scale = (10, 2, 10),
collider = "mesh",
shader = basic_lighting_shader,
texture = "white_cube",
color = color.lime
)
class AddPlatform(Entity):
def __init__(self, position = (0, 0, 0)):
super().__init__(
parent = scene,
model = "cube",
position = position,
scale = (10, 2, 10),
collider = "mesh",
shader = basic_lighting_shader,
texture = "white_cube",
color = color.azure
)
def input(key):
if held_keys["escape"]:
quit()
def update():
if held_keys["left mouse"]:
platform= AddPlatform(position = (mouse.world_point, mouse.world_point, mouse.world_point))
platforms = []
for i in platforms:
platform[i].append(platforms)
print(platforms)
Sky()
StartPlatform()
app.run()
Firt off: py position = (mouse.world_point, mouse.world_point, mouse.world_point)
is wrong, it should be
position = mouse.world_point
Secondly, this part doesn't make sense.
platforms = []
for i in platforms:
platform[i].append(platforms)
print(platforms)
Here's what it does:
platforms = [] Every frame, create a new list called platforms
for i in platforms: loop through every element. However you just set it to be an empty list, so this will just be skipped since there's nothing there.
platform[i].append(platforms) platform is a Entity you instantiated earlier. What is [i] of that? You can't get a member of a an entity that way.
print(platforms) This would return [] since it's not been changed.
If what you want to do is to add the platform you created to a list, you'd define the list outside of the function and add the append the platform to it. You're also using update for this, which gets called every frame. This means you'll probably be creating about 60 platforms every second you're holding the mouse button down. You probably want to use input instead.
platforms = []
def input(key):
if key == 'left mouse down' and mouse.world_point:
platform = AddPlatform(position=mouse.world_point)
platforms.append(platform)

tkinter create undetermined number of checkboxes/IntVar

For a project I am working on, I am loading some csv datafile into a dataframe using read_csv. I then print the dataframe on a tkinter frame using some Entry widgets. The user can populate or edit some entries. I would like to create at the beginning of each row a checkbox so that once the user is happy with the edits I upload the "dataframe" for which checkboxes are checked into a database.
When loading the content of the dataframe I can create the desired checkboxes but unfortunately the number of rows of the input datafile is not fixed. For each checkbox I would like to have a unique variable and hence I would like to create an undetermined number of IntVar variables.
within my Tk-inherited class I have the fileLoad function
def fileLoad(self):
df = pd.read_csv(self.filename)
rows, cols = df.shape
for r in range(rows):
for c in range(cols):
e = Entry(self.ViewFrame)
e.insert(0, df.iloc[r, c])
e.grid(row=r, column=c + 1)
so how can I create a variable number of checkboxes positioned in column=0 please?
Ok I find a way as follow:
def fileLoad(self):
df = pd.read_csv(self.filename)
rows, cols = df.shape
vars = []
for r in range(rows):
var = IntVar()
vars.append(var)
ckbox = ttk.Checkbutton(self.ViewFrame, variable=var)
ckbox.grid(row=r+10, column=0)
for c in range(cols):
e = Entry(self.ViewFrame)
e.insert(0, df.iloc[r, c])
e.grid(row=r + 10, column=c + 1)
I store the variables var into a list called vars.

Creating unique comboboxes in tabs of a notebook using tkinter and python 3

I am trying to create a program which will hold the sweep values of an RF directional coupler.
I have 5 couplers and each coupler has 4 forward ports.
I have managed to get the 5 couplers on seperate tabs of a tk notebook and loading the stored data from excel for a single port. the problem comes when i want to make the UI reload data for a different port using a combobox.
The code i have at the moment is:
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Notebook_Tabbed = ttk.Notebook(self)
VHF_1, VHF_2 = ttk.Frame(Notebook_Tabbed), ttk.Frame(Notebook_Tabbed)
Notebook_Tabbed.grid(column = 2, row = 2, columnspan = 5, sticky = "nwes")
Notebook_Tabbed.add(VHF_1, text = ' VHF 1 '), Notebook_Tabbed.add(VHF_2, text = ' VHF 2 ')
# Dictionaries to hold each couplers variables
vhf_1_dict = {1 : [2, 7, 4, 5], 3 : [2, 7, 9, 10]}
vhf_2_dict = {1 : [9, 14, 4, 5], 3 : [9, 14, 9, 10]}
# Dictionary to hold above dictionaries
var_dict = {VHF_1 : vhf_1_dict, VHF_2 : vhf_2_dict}
def populate_table_tab(self, tab, Row_Start, Row_End, Column_Start,
Column_End, Coupler_Type, port):
print(tab, Row_Start, Row_End, Column_Start,
Column_End, Coupler_Type, port)
# Load data for chosen port
def change_port(self, tab, port):
populate_table_tab(self, tab, var_dict[tab][port][0], var_dict[tab][port][1],
var_dict[tab][port][2], var_dict[tab][port][3], 'VHF', port)
# Generate comboboxes for each tab of the notebook
ports = StringVar()
ports.set(1)
def generate_port_selections(self, tab, row_num):
port_selection = ttk.Combobox(tab, textvariable = ports, state = 'readonly',
justify = "center")
port_selection['values'] = (1, 3)
port_selection.grid(column = 2, row = row_num, sticky = "nwes")
port_selection.bind('<<ComboboxSelected>>',
lambda _: change_port(self, tab, int(ports.get())))
# _ denotes a throwaway argument required to make lambda work with a combobox event
# Generate port selection comboboxes on each tab
for tab, row_num in {VHF_1 : 4, VHF_2 : 11}.items():
generate_port_selections(self, tab, row_num)
This code does work but it changes the combobox on all of the other tabs at the same time, without changing the data loaded from excel.
I have tried: ('<<ComboboxSelected>>', lambda i = i: change_port(self, tab, port_selection_list[i].get())) but TypeError: list indices must be integers or slices, not Event occurs.
Any help would be greatly appreciated, also i posted this in a rush as i'm working and busy so if i have missed out any necessary code / information please ask.
Edit: Formatted code to be readable by making single lines into multiple.
Edit 2: Simplified code.
Edit 3: You should now be able to run the code within a blank container and frame from this code.
You have problem because you create only one StringVar (ports = StringVar()) and you use it with all Combobox( textvariable=ports ) so they share the same information.
You have to use ports = StringVar() inside generate_port_selections() to create new unique ports for new Combobox.

How to list only the visible items in QTableView in pyqt

I have below code to get filters for QTableView. But i am not able to filter multiple column at a time.
i.e., if filter column 2 with row 0 col 0 and try to filter column 2, it should show only the visible unique values of column 2 (probably it should show row 0 col 1 only) but now its showing all the elements of column 2 (row 0 col 1, row 1 col 1, row 2 col 1)
#-*- coding:utf-8 -*-
from PyQt4 import QtCore, QtGui
class myWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(myWindow, self).__init__(parent)
self.centralwidget = QtGui.QWidget(self)
self.lineEdit = QtGui.QLineEdit(self.centralwidget)
self.view = QtGui.QTableView(self.centralwidget)
self.comboBox = QtGui.QComboBox(self.centralwidget)
self.label = QtGui.QLabel(self.centralwidget)
self.gridLayout = QtGui.QGridLayout(self.centralwidget)
self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
self.gridLayout.addWidget(self.view, 1, 0, 1, 3)
self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
self.label.setText("Regex Filter")
self.model = QtGui.QStandardItemModel(self)
for rowName in range(3) * 5:
self.model.invisibleRootItem().appendRow(
[ QtGui.QStandardItem("row {0} col {1}".format(rowName, column))
for column in range(3)
]
)
self.proxy = QtGui.QSortFilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.view.setModel(self.proxy)
self.comboBox.addItems(["Column {0}".format(x) for x in range(self.model.columnCount())])
self.lineEdit.textChanged.connect(self.on_lineEdit_textChanged)
self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged)
self.horizontalHeader = self.view.horizontalHeader()
self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
#QtCore.pyqtSlot(int)
def on_view_horizontalHeader_sectionClicked(self, logicalIndex):
self.logicalIndex = logicalIndex
self.menuValues = QtGui.QMenu(self)
self.signalMapper = QtCore.QSignalMapper(self)
self.comboBox.blockSignals(True)
self.comboBox.setCurrentIndex(self.logicalIndex)
self.comboBox.blockSignals(True)
valuesUnique = [ self.model.item(row, self.logicalIndex).text()
for row in range(self.model.rowCount())
]
actionAll = QtGui.QAction("All", self)
actionAll.triggered.connect(self.on_actionAll_triggered)
self.menuValues.addAction(actionAll)
self.menuValues.addSeparator()
for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))):
action = QtGui.QAction(actionName, self)
self.signalMapper.setMapping(action, actionNumber)
action.triggered.connect(self.signalMapper.map)
self.menuValues.addAction(action)
self.signalMapper.mapped.connect(self.on_signalMapper_mapped)
headerPos = self.view.mapToGlobal(self.horizontalHeader.pos())
posY = headerPos.y() + self.horizontalHeader.height()
posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex)
self.menuValues.exec_(QtCore.QPoint(posX, posY))
#QtCore.pyqtSlot()
def on_actionAll_triggered(self):
filterColumn = self.logicalIndex
filterString = QtCore.QRegExp( "",
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.RegExp
)
self.proxy.setFilterRegExp(filterString)
self.proxy.setFilterKeyColumn(filterColumn)
#QtCore.pyqtSlot(int)
def on_signalMapper_mapped(self, i):
stringAction = self.signalMapper.mapping(i).text()
filterColumn = self.logicalIndex
filterString = QtCore.QRegExp( stringAction,
QtCore.Qt.CaseSensitive,
QtCore.QRegExp.FixedString
)
self.proxy.setFilterRegExp(filterString)
self.proxy.setFilterKeyColumn(filterColumn)
#QtCore.pyqtSlot(str)
def on_lineEdit_textChanged(self, text):
search = QtCore.QRegExp( text,
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.RegExp
)
self.proxy.setFilterRegExp(search)
#QtCore.pyqtSlot(int)
def on_comboBox_currentIndexChanged(self, index):
self.proxy.setFilterKeyColumn(index)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
main = myWindow()
main.show()
main.resize(400, 600)
sys.exit(app.exec_())
When i run the above code i get the below output
When i click on Column 2 Header the filter list displayed is as below and its showing correctly (Unique values in that column)...
When i select row 0 col 1 in the displayed filter, i get the following filtered list
But again when i click on the Column 2 header for filter it shows the same list as my 1st image. All the unique items of column 2 (from model view) and not from proxyfilter. Actually it should show only row 0 col 1 as unique items in Column 2 are only row 0 col 1
It is because you are still using the source model to look up the rows. Data in the original model does not change. Only the filter proxy model reflects the filtered changes.
So you just need to modify your lookup in your (really long named) on_view_horizontalHeader_sectionClicked slot:
valuesUnique = [
self.proxy.index(row, self.logicalIndex).data().toString()
for row in xrange(self.proxy.rowCount())
]
You could also remove a few of the conversion for the unique set:
valuesUnique = set(
self.proxy.index(row, self.logicalIndex).data().toString()
for row in xrange(self.proxy.rowCount())
)
...
for actionNumber, actionName in enumerate(sorted(valuesUnique)):
...
And just a small other thing I wanted to point out. You are keeping around a couple of temp objects that don't ever get deleted. In that same slot, you create a new QMenu and QSignalMapper every time but you never clean up the old ones. Over time you are just making more and more.
For the QMenu, just make it a local variable and don't parent it to self. That way it will get cleaned up after it disappears. And for the QSignalMapper, you can just use a deleteLater call before creating a new one:
# local variable, and no parent
menuValues = QtGui.QMenu()
# delete the previous one
try:
self.signalMapper.deleteLater()
except:
pass
self.signalMapper = QtCore.QSignalMapper(self)

Resources