Tkinter show grid on canvas - loops

I would like to show/hide a grid on a tkinter canvas, as seen in Gimp for instance.
I have this code:
is_on = True
def my_grid():
global is_on
global hor
global ver
if is_on:
y=0
x=0
for i in range(8):
x+=64
y+=64
hor=A.create_line(0,y,512,y, fill="black")
ver=A.create_line(x,0, x, 512, fill="black")
is_on=False
else:
A.delete(hor)
A.delete(ver)
is_on=True
This delete only the last row and the last column. I've also tried to loop the delete action but it doesn't work.
Hope someone could help me.
Best

If you add a tag to the lines, you can manipulate all of the lines with a single statement.
Also, there's no need to delete and recreate the lines each time. You can hide the grid by setting their state to "hidden".
For example, here's how to create, hide, and show the grid:
def create_grid():
x = y = 0
for i in range(8):
x+=64
y+=64
A.create_line(0,y,512,y, fill="black", tags=("grid",))
A.create_line(x,0, x, 512, fill="black", tags=("grid",))
def show_grid():
A.itemconfigure("grid", state="normal")
def hide_grid():
A.itemconfigure("grid", state="hidden")

Try something like this:
import tkinter as tk
class CanvasGrid(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.lines = []
self.lines_displayed = False
def display_lines(self):
# If the lines are already displayed don't do anything
if self.lines_displayed:
return None
self.lines_displayed = True
# Clear the list of the lines as they are no longer on the screen
self.lines.clear()
# Get the width and height of the canvas
width = super().winfo_reqwidth()
height = super().winfo_reqheight()
# Loop through and create the vertical lines
for x in range(0, width-width//8, width//8):
line = super().create_line(x, 0, x, height, fill="black")
self.lines.append(line)
# Loop through and create the horizontal lines
for y in range(0, height-height//8, height//8):
line = super().create_line(0, y, width, y, fill="black")
self.lines.append(line)
def hide_lines(self):
# For each line that is on the screen
for line in self.lines:
# Delete the line
super().delete(line)
self.lines_displayed = False
# Example code that uses the class
def toggle_lines(event):
if canvas.lines_displayed:
canvas.hide_lines()
else:
canvas.display_lines()
root = tk.Tk()
canvas = CanvasGrid(root, width=400, height=400)
canvas.pack()
canvas.display_lines()
canvas.bind("<Button-1>", toggle_lines)
root.mainloop()
I created an extension to the tkinter.Canvas class by adding display_lines and hide_lines. You can use the class as if you are using tkinter.Canvas but it has those 2 methods. To try out the example code click on the canvas to show/hide the lines.

Here is another way of doing this:
from tkinter import Tk, Canvas, Button
vertical = 8
horizontal = 8
canvas_width = 700
canvas_height = 500
grid_lst = []
def grid(mode):
if mode == 'show':
if len(grid_lst):
return
for hor in range(horizontal):
y = (hor + 1) * (canvas_height / (horizontal + 1))
grid_lst.append(canvas.create_line((0, y, canvas_width, y), fill='black'))
for ver in range(vertical):
x = (ver + 1) * (canvas_width / (vertical + 1))
grid_lst.append(canvas.create_line((x, 0, x, canvas_height), fill='black'))
if mode == 'hide':
for line in grid_lst:
canvas.delete(line)
grid_lst.clear()
root = Tk()
canvas = Canvas(root, width=canvas_width, height=canvas_height, bg='grey')
canvas.pack()
Button(root, text='Show Grid', command=lambda: grid('show')).pack(side='left', fill='x', expand=True)
Button(root, text='Hide Grid', command=lambda: grid('hide')).pack(side='right', fill='x', expand=True)
root.mainloop()
This method uses just one function that has an argument which determines what to do with grid: either show or hide. It also uses variables in a way that changing them would also auto-adjust the grid: vertical, horizontal, canvas_width, canvas_height. And they do what they say. vertical determines the amount of vertical lines and horizontal determines the amount of horizontal lines so this is pretty adjustable and "dynamic".
EDIT: improved performance so that lines don't add up to the list if the grid('show') is called multiple times and so that the list gets cleared when grid('hide') is called.

Related

why are there no objects added to my table

I'm working on a lua script where I need a list of buttons to display. I have the following method to add a button to the list buttons:
function addButton(name, text, func, activeColor, passiveColor, xmin, xmax, ymin, ymax, type)
buttons[name] = {}
buttons[name]["text"] = text
buttons[name]["func"] = func
buttons[name]["active"] = false
buttons[name]["activeColor"] = getColorOrDefault(activeColor, ACTIVECOLORDEFAULT)
buttons[name]["passiveColor"] = getColorOrDefault(passiveColor, PASSIVECOLORDEFAULT)
buttons[name]["xmin"] = xmin
buttons[name]["xmax"] = xmax
buttons[name]["ymin"] = ymin
buttons[name]["ymax"] = ymax
buttons[name]["type"] = type
print("added: "..table.getn(buttons))
end
This function is called twice to add 2 buttons, but the output is:
added: 0
added: 0
what could be a reason my elements are not added to the table?
They're being added--You're just using the wrong method to detect them.
As you can read about in this question, using table.getn only returns the amount of array slots (numeric indexes, such as buttons[0] or buttons[23]. Note that this only counts as an array slot if there are no null indexes in between buttons[1] and buttons[23]!
Using this debugging code, we'll see that your items are indeed being added to the buttons table:
for name, button in pairs(buttons) do
print(name, button)
end
This is also how you'd get a list of your buttons :-)

Choregraphe: How to change NAO's Chest and Feet LEDs

Pretty much what the title says. How can i change NAO's Chest and Feet LEDs. I know they support RGB but how do i command what colours they stay at?
I found a way to do it. It was actually quite simple.
I altered the code of the "Eye LEDs" box.
If you double click it it will show you 2 other boxes, "Color Edit" and "Eyes LEDs".
I simply altered the code of the second "Eyes LEDs" box with this one:
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self, False)
def onLoad(self):
self.fadeOps = []
self.leds = self.session().service("ALLeds")
def onUnload(self):
#~ puts code for box cleanup here
pass
def onInput_color(self, p):
if( self.getParameter("Side") == "Left" ):
sGroup = "AllLeds"
elif( self.getParameter("Side") == "Right" ):
sGroup = "AllLeds"
else:
sGroup = "AllLeds"
fadeOp = self.leds.fadeRGB(sGroup, 256*256*p[0] + 256*p[1] + p[2], self.getParameter("Duration (s)"), _async=True)
self.fadeOps.append(fadeOp)
fadeOp.wait()
self.fadeOps.remove(fadeOp)
if( self.fadeOps == [] ):
self.onDone() # activate output of the box
Technically all I did is change all the "sGroup = ... " to sGroup = "AllLeds"

Windows From Custom Label FromPosition and ToPosition after Xaxis min and max Range setting

I am using Windows From to generate custom charts.
I have axis data from 0 to 10 with interval of 1.0
The AxisX is in default range from -1 to 11 as shown in attached image.
I want to place custom labels on Top of the chart (AxisX2), I am using FromPosition=0.5-Interval & FromPosition=0.5+Interval, this is placing the custom label aligned with bottom X axis labels.
But when i limit the X axis range to 0 to 10, then custom label is not aligned with bottom X axis labels.
What i notice is custom label position remain fixed, but after setting the range position of the Grid position of the bottom X axis changed, so the is no alignment with bottom X axis and top secondary X axis custom label. Is there any way to align top X2 axis Custom label with bottom X axis labels ?
I am attaching my code, i would highly appreciate your help here.
Screenshot of custom label setting alignemnt
class MainForm(Form):
def __init__(self):
self.InitializeComponent()
def InitializeComponent(self):
global series11
chartArea11 = System.Windows.Forms.DataVisualization.Charting.ChartArea()
ChartType11 = System.Windows.Forms.DataVisualization.Charting.SeriesChartType()
legend11 = System.Windows.Forms.DataVisualization.Charting.Legend()
series11 = System.Windows.Forms.DataVisualization.Charting.Series()
series12 = System.Windows.Forms.DataVisualization.Charting.Series()
series13 = System.Windows.Forms.DataVisualization.Charting.Series()
title1=System.Windows.Forms.DataVisualization.Charting.Title()
Marker=System.Windows.Forms.DataVisualization.Charting.MarkerStyle()
self._chart1 = System.Windows.Forms.DataVisualization.Charting.Chart()
self._chart1.BeginInit()
self.SuspendLayout()
##### Form and Chart size
Width = 800
Height = 450
Buffer = 50
self._chart1.ChartAreas.Add(chartArea11)
series11.ChartArea = "ChartArea1"
series11.Legend = "Legend1"
self._chart1.Size = System.Drawing.Size(Width-Buffer , Height-Buffer ) # Chart Area Size
self._chart1.TabIndex = 1
self._chart1.Text = "chart1"
lista=[0,1,2,3,4,5,6,7,8,9,10]
listb=[0,8.27458E-06,0.003971857,-0.009233615,-0.022420625,-0.035589211,0.096930249,0.163891257,-0.009233615,0.003971857,-0.035589211]
Time_1=[23,23,20,30,40,50,-50,-100,30,20,50]
# Chart Type
self._chart1.Series.Add(series11)
series11.ChartType=ChartType11.Line
self._chart1.Series[0].Points.DataBindXY(lista,listb)
### Secondary Axis as Lables
chartArea11.AxisX2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.True
chartArea11.AxisX2.Title = " Temperature [\xb0 C]"
chartArea11.AxisX2.TitleFont = Font("Arial", 10)
chartArea11.AxisX2.MajorGrid.Enabled = False;
chartArea11.AxisX2.MajorTickMark.Enabled = False;
CL_0=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_1=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_2=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_3=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_4=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_5=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_6=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_7=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_8=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_9=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CL_10=System.Windows.Forms.DataVisualization.Charting.CustomLabel()
CLs=[CL_0,CL_1,CL_2,CL_3,CL_4,CL_5,CL_6,CL_7,CL_8,CL_9,CL_10]
offset=0.5
for i in range(0,self._chart1.Series[0].Points.Count):
CLs[i].Text=str(Time_1[i])
CLs[i].FromPosition=self._chart1.Series[0].Points[i].XValue - offset
CLs[i].ToPosition=self._chart1.Series[0].Points[i].XValue+offset
chartArea11.AxisX2.CustomLabels.Add(CLs[i])
chartArea11.AxisX.Minimum = 0
chartArea11.AxisX.Maximum = max(lista)
chartArea11.AxisX.Interval=1.0
chartArea11.AxisX2.IsLabelAutoFit=False
############ End secondary axis #####################
############## MainForm############
self.ClientSize = System.Drawing.Size(Width, Height+100) ### Windows Form Area Size
self.Controls.Add(self._chart1)
self.Name = "MainForm"
self.Text = "Santhosh M WB Custom Charts Demo"
self._chart1.EndInit()
self.ResumeLayout(False)
from System.Windows.Forms import Application
Application.EnableVisualStyles()
form =MainForm()
form.ShowDialog()
#Application.Run(form)
I got it. I need to set both primary and secondary X axes range and interval same to align the custom label on secondary X Axis same as primary x axis label.
chartArea11.AxisX.Minimum = 0
chartArea11.AxisX.Maximum = max(lista)
chartArea11.AxisX.Interval=1.0
chartArea11.AxisX2.Minimum = 0
chartArea11.AxisX2.Maximum = max(lista)
chartArea11.AxisX2.Interval=1.0

How to pass input data from a tablewidget to a combo box by clicking pushbutton?

I want to input data in my table and then have it appear in my combo box after pressing 'ok' pushbutton in pyqt. May I know how to go about doing that? Whenever I run my code it just says there is an error at self.comboBox.addItem(item). I don't know which other command to use. Here is part of my code:
def setup(self, Dialog):
...
...
self.comboBox = QtGui.QComboBox(Dialog)
self.comboBox.setGeometry(QtCore.QRect(20, 100, 431, 22))
self.comboBox.setObjectName(_fromUtf8("comboBox"))
self.tableWidget = QtGui.QTableWidget(Dialog)
self.tableWidget.setGeometry(QtCore.QRect(20, 470, 651, 71))
self.tableWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidget.setTextElideMode(QtCore.Qt.ElideRight)
self.tableWidget.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerItem)
self.tableWidget.setRowCount(1)
self.tableWidget.setColumnCount(129)
self.tableWidget.setObjectName(_fromUtf8("tableWidget"))
self.tableWidget.horizontalHeader().setVisible(True)
self.tableWidget.horizontalHeader().setDefaultSectionSize(25)
self.tableWidget.horizontalHeader().setMinimumSectionSize(26)
self.tableWidget.verticalHeader().setDefaultSectionSize(25)
self.tableWidget.verticalHeader().setHighlightSections(True)
self.pushButton_7 = QtGui.QPushButton(Dialog)
self.pushButton_7.setGeometry(QtCore.QRect(220, 650, 75, 23))
self.pushButton_7.setObjectName(_fromUtf8("pushButton_7"))
self.pushButton_7.clicked.connect(self.additem)
def retranslateUi(self, Dialog):
...
...
self.tableWidget.setSortingEnabled(False)
def additem(self):
item = self.tableWidget.item(0,0)
self.comboBox.addItem(item)
UPDATE: The solution only works for the first box of my tablewidget. I tried to do it like this:
def additem(self):
while true:
item = self.tableWidget.item(0, 0).text()
self.comboBox.addItem(item)
item1 = self.tableWidget.item(0, 1).text()
self.comboBox.addItem(item1)
However, I just keep getting the error that 'NoneType' object has no attribute 'text'
UPDATE: I tried your suggestion. I tried to upload images so it would be clearer but I need 10 reputation in order to do so. Anyway, I typed this in the tablewidget: 11 22 33 44 each one in one box for four boxes. But only 11 appeared in combobox after pressing 'ok'. It doesn't work even if I typed my value on the other boxes, it only works for the first box. What I need is for them to appear as '11, 22, 33, 44' in my combo box. Would it be possible to do this? As well as for all 128 columns of my tablewidget?
in addItem(self) item is tableWidgetItem type. What you need is QString to add as comboBox item so try to correct this line only:
item = self.tableWidget.item(0,0).text()
here is working example:
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import Qt
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
self.layout = QtGui.QVBoxLayout(self)
self.comboBox = QtGui.QComboBox(self)
self.comboBox.setGeometry(QtCore.QRect(20, 100, 431, 22))
self.tableWidget = QtGui.QTableWidget(self)
self.tableWidget.setGeometry(QtCore.QRect(20, 470, 651, 71))
self.tableWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.tableWidget.setTextElideMode(QtCore.Qt.ElideRight)
self.tableWidget.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerItem)
self.tableWidget.setRowCount(1)
self.tableWidget.setColumnCount(3)
self.tableWidget.horizontalHeader().setVisible(True)
self.tableWidget.horizontalHeader().setDefaultSectionSize(25)
self.tableWidget.horizontalHeader().setMinimumSectionSize(26)
self.tableWidget.verticalHeader().setDefaultSectionSize(25)
self.tableWidget.verticalHeader().setHighlightSections(True)
self.tableWidget.setItem(0, 0, QtGui.QTableWidgetItem("first"))
self.tableWidget.setItem(0, 1, QtGui.QTableWidgetItem("second"))
self.tableWidget.setItem(0, 2, QtGui.QTableWidgetItem("third"))
self.tableWidget.resizeColumnsToContents()
self.pushButton_7 = QtGui.QPushButton("Add item 0.0 to combo")
self.pushButton_7.setGeometry(QtCore.QRect(220, 650, 75, 23))
self.pushButton_7.clicked.connect(self.additem)
self.layout.addWidget(self.comboBox)
self.layout.addWidget(self.tableWidget)
self.layout.addWidget(self.pushButton_7)
def additem(self):
item = self.tableWidget.item(0,0)#.text()
self.comboBox.addItem(item)
if __name__ == '__main__':
app = QtGui.QApplication([])
w = Widget()
w.show()
sys.exit(app.exec_())
EDIT
if you want to add all cells from table row to combo box, your additem should look like this:
def additem(self):
for i in range(0, self.tableWidget.columnCount()):
item = self.tableWidget.item(0,i)
if item != None:
text = item.text()
self.comboBox.addItem(text)
Keep in mind that if table cell is empty (item == None) nothing will be added to combo box

How to update a combo box with a list of items

I am trying to update the list of items in one combobox2 depending on the item selected in another - combobox1.
For example, if the user selects a file.mp3 in combobox1 then combobox2 will display a list of audio extension (.aac, .wav, .wma). However, if the user selects a file.flv from combobox1, combobox2 will display a list of video extensions (.mpg, mp4, .avi, .mov).
I initially thought I could accomplish this with if statements. The initial selection works, but there after, if you continue to choose different files, the combobox2 is not updated. I tried using an Event, but it didn't work.
Below if a very stripped-down version of the code so that you can get the gist:
import wx
import os
import sys
import time
from wx.lib.delayedresult import startWorker
class udCombo(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'd-Converter', size=(500, 310))
panel = wx.Panel(self, wx.ID_ANY)#Creates a panel over the widget
toolbar = self.CreateToolBar()
toolbar.Realize()
font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD)
font2 = wx.Font(7, wx.DECORATIVE, wx.NORMAL, wx.NORMAL)
directory = wx.StaticText(panel, -1, 'Path to media files: c:\\ffmpeg\\bin', (300, 13))
directory.SetFont(font2)
convertfile = wx.StaticText(panel, -1, 'File:', (270, 53))
convertfile.SetFont(font)
convertfile2 = wx.StaticText(panel, -1, 'Format:', (245, 83))
#Select Media
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
filelist = os.listdir(wrkdir)
self.formats1 = []
for filename in filelist:
(head, filename) = os.path.split(filename)
if filename.endswith(".avi") or filename.endswith(".mp4") or filename.endswith(".flv") or filename.endswith(".mov") or filename.endswith(".mpeg4") or filename.endswith(".mpeg") or filename.endswith(".mpg2") or filename.endswith(".wav") or filename.endswith(".mp3"):
self.formats1.append(filename)
self.format_combo1=wx.ComboBox(panel, size=(140, -1),value='Select Media', choices=self.formats1, style=wx.CB_DROPDOWN, pos=(300,50))
self.Bind(wx.EVT_COMBOBOX, self.fileFormats, self.format_combo1)
self.format_combo2=wx.ComboBox(panel, size=(100, -1),pos=(300,81))
self.Bind(wx.EVT_COMBOBOX, self.fileFormats, self.format_combo2)
def fileFormats(self, e):
myFormats = {'audio': ('.wav', '.wma', '.mp3'), 'video': ('.mpg', '.mp4', '.mpeg')}
bad_file = ['Media not supported']
myFile = self.format_combo1.GetValue()
f_exten = [x for x in myFormats['audio'] or myFormats['video'] if myFile.endswith(x)]
if f_exten[0] in myFormats['audio']:
self.format_combo2.SetItems(myFormats['audio'])
elif f_exten[0] in myFormats['video']:
self.format_combo2.SetItems(myFormats['video'])
else:
self.format_combo2.SetItems(bad_file)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = udCombo()
frame.SetSizeHints(500,310,500,310)
frame.Show()
app.MainLoop()
Traceback error:
Traceback (most recent call last):
File "C:\Users\GVRSQA004\Desktop\udCombo.py", line 86, in fileFormats
if f_exten[0] in myFormats['audio']:
IndexError: list index out of range
Use a dictionary to hold the two lists. Then when the user clicks something in the first widget, you can call the second combobox's SetItems(myDict[selection]) method or something along those lines. The error message is because you're trying to do something with a CommandEvent that it doesn't support. They don't have an "rfind" attribute, for example.
EDIT: The new code the OP posted doesn't work because it's only running the list comprehension against the first half of the OR statement. It never runs against the "video" portion, so it returns an empty list if the user chooses anything with a video format extension. It WILL work if you select an audio selection.
Personally, I would recommend creating a video extension list and an audio list. That would be easier to understand in the future should you need to fix it later.

Resources